Skip to content

Commit e9a73f9

Browse files
committed
Introduce hard SingletonTypes
1 parent 25cdd57 commit e9a73f9

File tree

6 files changed

+63
-12
lines changed

6 files changed

+63
-12
lines changed

compiler/src/dotty/tools/dotc/core/Hashable.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ trait Hashable {
108108
protected final def doHash(bs: Binders, x1: Any, tp2: Type, tps3: List[Type]): Int =
109109
finishHash(bs, hashing.mix(hashSeed, x1.hashCode), 1, tp2, tps3)
110110

111+
protected final def doHash(x1: Any, x2: Int): Int =
112+
finishHash(hashing.mix(hashing.mix(hashSeed, x1.hashCode), x2), 1)
113+
111114
protected final def doHash(x1: Int, x2: Int): Int =
112115
finishHash(hashing.mix(hashing.mix(hashSeed, x1), x2), 1)
113116

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ object Types {
12171217
case _: TypeRef | _: MethodOrPoly => this // fast path for most frequent cases
12181218
case tp: TermRef => // fast path for next most frequent case
12191219
if tp.isOverloaded then tp else tp.underlying.widen
1220-
case tp: SingletonType => tp.underlying.widen
1220+
case tp: SingletonType if tp.isSoft => tp.underlying.widen
12211221
case tp: ExprType => tp.resultType.widen
12221222
case tp =>
12231223
val tp1 = tp.stripped
@@ -2025,8 +2025,12 @@ object Types {
20252025
/** A marker trait for types that are guaranteed to contain only a
20262026
* single non-null value (they might contain null in addition).
20272027
*/
2028-
trait SingletonType extends TypeProxy with ValueType {
2028+
trait SingletonType extends TypeProxy with ValueType with Softenable {
20292029
def isOverloaded(using Context): Boolean = false
2030+
2031+
/** Overriden in [[ConstantType]].
2032+
*/
2033+
override def isSoft = true
20302034
}
20312035

20322036
/** A trait for types that bind other types that refer to them.
@@ -2074,6 +2078,10 @@ object Types {
20742078
}
20752079
}
20762080

2081+
trait Softenable {
2082+
def isSoft: Boolean
2083+
}
2084+
20772085
// --- NamedTypes ------------------------------------------------------------------
20782086

20792087
abstract class NamedType extends CachedProxyType with ValueType { self =>
@@ -2855,15 +2863,16 @@ object Types {
28552863
abstract case class ConstantType(value: Constant) extends CachedProxyType with SingletonType {
28562864
override def underlying(using Context): Type = value.tpe
28572865

2858-
override def computeHash(bs: Binders): Int = doHash(value)
2866+
override def computeHash(bs: Binders): Int = doHash(value, if isSoft then 0 else 1)
28592867
}
28602868

2861-
final class CachedConstantType(value: Constant) extends ConstantType(value)
2869+
final class CachedConstantType(value: Constant, override val isSoft: Boolean = true) extends ConstantType(value)
28622870

28632871
object ConstantType {
2864-
def apply(value: Constant)(using Context): ConstantType = {
2872+
var i = 0
2873+
def apply(value: Constant, soft: Boolean = true)(using Context): ConstantType = {
28652874
assertUnerased()
2866-
unique(new CachedConstantType(value))
2875+
unique(new CachedConstantType(value, soft))
28672876
}
28682877
}
28692878

@@ -3206,9 +3215,8 @@ object Types {
32063215
TypeComparer.liftIfHK(tp1, tp2, AndType.make(_, _, checkValid = false), makeHk, _ | _)
32073216
}
32083217

3209-
abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType {
3218+
abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType, Softenable {
32103219
def isAnd: Boolean = false
3211-
def isSoft: Boolean
32123220
private var myBaseClassesPeriod: Period = Nowhere
32133221
private var myBaseClasses: List[ClassSymbol] = _
32143222
/** Base classes of are the intersection of the operand base classes. */
@@ -3282,7 +3290,12 @@ object Types {
32823290
else tp1.atoms | tp2.atoms
32833291
val tp1w = tp1.widenSingletons
32843292
val tp2w = tp2.widenSingletons
3285-
myWidened = if ((tp1 eq tp1w) && (tp2 eq tp2w)) this else tp1w | tp2w
3293+
myWidened =
3294+
if isSoft then
3295+
if ((tp1 eq tp1w) && (tp2 eq tp2w)) this else tp1w | tp2w
3296+
else
3297+
derivedOrType(tp1w, tp2w)
3298+
32863299
atomsRunId = ctx.runId
32873300

32883301
override def atoms(using Context): Atoms =

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ class TreeUnpickler(reader: TastyReader,
438438
case BYNAMEtype =>
439439
ExprType(readType())
440440
case _ =>
441-
ConstantType(readConstant(tag))
441+
ConstantType(readConstant(tag), soft = false)
442442
}
443443

444444
if (tag < firstLengthTreeTag) readSimpleType() else readLengthType()

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1879,7 +1879,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
18791879
def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(using Context): SingletonTypeTree = {
18801880
val ref1 = typedExpr(tree.ref)
18811881
checkStable(ref1.tpe, tree.srcPos, "singleton type")
1882-
assignType(cpy.SingletonTypeTree(tree)(ref1), ref1)
1882+
1883+
val ref2 = ref1.tpe match
1884+
case ConstantType(c) => ref1.withType(ConstantType(c, soft = false))
1885+
case _ => ref1
1886+
1887+
assignType(cpy.SingletonTypeTree(tree)(ref2), ref2)
18831888
}
18841889

18851890
def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(using Context): TypTree = {

tests/pos/widen-singletons.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object Test:
2+
def is2(x: 2) = true
3+
4+
def testValType() =
5+
val x: 2 = 2
6+
val v = x
7+
is2(v)
8+
9+
def testDefReturnType() =
10+
def f(): 2 = 2
11+
val v = f()
12+
is2(v)

tests/pos/widen-union.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,30 @@ object Test2:
1313
|| xs.corresponds(ys)(consistent(_, _)) // error, found: Any, required: Int | String
1414

1515
object Test3:
16-
1716
def g[X](x: X | String): Int = ???
1817
def y: Boolean | String = ???
1918
g[Boolean](y)
2019
g(y)
2120
g[Boolean](identity(y))
2221
g(identity(y))
2322

23+
object TestSingletonsInUnions:
24+
def is2Or3(a: 2 | 3) = true
25+
26+
def testValType() =
27+
val x: 2 | 3 = 2
28+
val v = x
29+
is2Or3(v)
30+
31+
def testDefReturnType() =
32+
def f(): 2 | 3 = 2
33+
val v = f()
34+
is2Or3(v)
35+
36+
def testSoftUnionInHardUnion() =
37+
def isStringOr3(a: String | 3) = true
2438

39+
def f(x: String): x.type | 3 = 3
40+
val b: Boolean = true
41+
val v = f(if b then "a" else "b")
42+
isStringOr3(v)

0 commit comments

Comments
 (0)