Skip to content

Commit 19d74d1

Browse files
committed
Make compile-time operations on stable arguments stable
1 parent 2d4844a commit 19d74d1

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ object Types {
165165
case _: SingletonType | NoPrefix => true
166166
case tp: RefinedOrRecType => tp.parent.isStable
167167
case tp: ExprType => tp.resultType.isStable
168+
case tp: AppliedType => tp.appliedTypeIsStable
168169
case tp: AnnotatedType =>
169170
// NOTE UncheckedStableAnnot was originally meant to be put on fields,
170171
// not on types. Allowing it on types is a Scala 3 extension. See:
@@ -4160,11 +4161,23 @@ object Types {
41604161
// Boolean caches: 0 = uninitialized, -1 = false, 1 = true
41614162
private var myStableHash: Byte = 0
41624163
private var myGround: Byte = 0
4164+
private var myIsStable: Byte = 0
4165+
41634166

41644167
def isGround(acc: TypeAccumulator[Boolean])(using Context): Boolean =
41654168
if myGround == 0 then myGround = if acc.foldOver(true, this) then 1 else -1
41664169
myGround > 0
41674170

4171+
inline def appliedTypeIsStable(using Context): Boolean =
4172+
if myIsStable == 0 then
4173+
val isStable: Byte = tycon match
4174+
case tycon: TypeRef if defn.isCompiletimeAppliedType(tycon.symbol) && args.forall(_.isStable) => 1
4175+
case _ => -1
4176+
if !isProvisional then myIsStable = isStable
4177+
isStable > 0
4178+
else
4179+
myIsStable > 0
4180+
41684181
override def underlying(using Context): Type = tycon
41694182

41704183
override def superType(using Context): Type =
@@ -4242,7 +4255,7 @@ object Types {
42424255
// final val one = 1
42434256
// type Two = one.type + one.type
42444257
// ```
4245-
case tp: TermRef => tp.underlying
4258+
case tp: TypeProxy if tp.underlying.isStable => tp.underlying.fixForEvaluation
42464259
case tp => tp
42474260
}
42484261

tests/neg/singleton-ops-int.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,24 @@ object Test {
116116

117117
val t83: ToDouble[1] = 1.0
118118
val t84: ToDouble[2] = 2 // error
119+
120+
// Singletons are dereferenced
121+
val t85: Int = 5
122+
val t86: t85.type = t85
123+
summon[t85.type + t85.type =:= t86.type + t86.type]
124+
125+
// Singletons are dereferenced recursively
126+
val t87: t86.type = t87
127+
summon[t85.type + t85.type =:= t87.type + t87.type]
128+
129+
// Skolems of compile-time types are dereferenced:
130+
// (?1 : (Test.x : Int) * (Test.x : Int)) --> (Test.x : Int) * (Test.x : Int)
131+
def mult(x: Int, y: Int): x.type * y.type = (x * y).asInstanceOf
132+
val t88: t85.type * t85.type * t85.type = mult(mult(t85, t85), t85)
133+
134+
// Compile-time operations with singleton arguments are singletons
135+
summon[t85.type + t86.type <:< Singleton]
136+
137+
// Compile-time operations with non-singleton arguments are not singletons
138+
summon[t85.type + Int <:< Singleton] // error
119139
}

0 commit comments

Comments
 (0)