Skip to content

Commit 1d4dec7

Browse files
authored
Type ascribe trees that require opaque type usage (#18101)
2 parents 5c66d7b + d124f58 commit 1d4dec7

File tree

5 files changed

+70
-4
lines changed

5 files changed

+70
-4
lines changed

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
4646
state = c.typerState
4747
monitored = false
4848
GADTused = false
49+
opaquesUsed = false
4950
recCount = 0
5051
needsGc = false
5152
if Config.checkTypeComparerReset then checkReset()
@@ -61,6 +62,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
6162
/** Indicates whether the subtype check used GADT bounds */
6263
private var GADTused: Boolean = false
6364

65+
/** Indicates whether the subtype check used opaque types */
66+
private var opaquesUsed: Boolean = false
67+
6468
private var myInstance: TypeComparer = this
6569
def currentInstance: TypeComparer = myInstance
6670

@@ -142,8 +146,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
142146

143147
def testSubType(tp1: Type, tp2: Type): CompareResult =
144148
GADTused = false
149+
opaquesUsed = false
145150
if !topLevelSubType(tp1, tp2) then CompareResult.Fail
146151
else if GADTused then CompareResult.OKwithGADTUsed
152+
else if opaquesUsed then CompareResult.OKwithOpaquesUsed // we cast on GADTused, so handles if both are used
147153
else CompareResult.OK
148154

149155
/** The current approximation state. See `ApproxState`. */
@@ -1483,12 +1489,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
14831489

14841490
def tryLiftedToThis1: Boolean = {
14851491
val tp1a = liftToThis(tp1)
1486-
(tp1a ne tp1) && recur(tp1a, tp2)
1492+
(tp1a ne tp1) && recur(tp1a, tp2) && { opaquesUsed = true; true }
14871493
}
14881494

14891495
def tryLiftedToThis2: Boolean = {
14901496
val tp2a = liftToThis(tp2)
1491-
(tp2a ne tp2) && recur(tp1, tp2a)
1497+
(tp2a ne tp2) && recur(tp1, tp2a) && { opaquesUsed = true; true }
14921498
}
14931499

14941500
// begin recur
@@ -2935,7 +2941,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
29352941
object TypeComparer {
29362942

29372943
enum CompareResult:
2938-
case OK, Fail, OKwithGADTUsed
2944+
case OK, Fail, OKwithGADTUsed, OKwithOpaquesUsed
29392945

29402946
/** Class for unification variables used in `natValue`. */
29412947
private class AnyConstantType extends UncachedGroundType with ValueType {

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package transform
34

45
import core.Names.Name

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4063,6 +4063,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
40634063
res
40644064
} =>
40654065
insertGadtCast(tree, wtp, pt)
4066+
case CompareResult.OKwithOpaquesUsed if !tree.tpe.frozen_<:<(pt)(using ctx.withOwner(defn.RootClass)) =>
4067+
// guard to avoid extra Typed trees, eg. from testSubType(O.T, O.T) which returns OKwithOpaquesUsed
4068+
Typed(tree, TypeTree(pt))
40664069
case _ =>
40674070
//typr.println(i"OK ${tree.tpe}\n${TypeComparer.explained(_.isSubType(tree.tpe, pt))}") // uncomment for unexpected successes
40684071
tree

tests/pos/i17948.all.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
object O:
2+
opaque type T = Int
3+
4+
inline def get0: Int = Do.get // no proxy needed
5+
inline def get1: Int = Do.get: O.T // no proxy needed
6+
inline def get2: Int = Do.get: T // proxied
7+
8+
inline def set0: Unit = Do.set(0) // was: broken
9+
inline def set1: Unit = Do.set(1: O.T) // no proxy needed
10+
inline def set2: Unit = Do.set(2: T) // proxied
11+
12+
inline def mod0: Int = Do.mod(0) // was: broken
13+
inline def mod1: Int = Do.mod(1): O.T // was: broken
14+
inline def mod2: Int = Do.mod(2): T // was: broken
15+
inline def mod3: Int = Do.mod(3: O.T) // no proxy needed
16+
inline def mod4: Int = Do.mod(4: O.T): O.T // no proxy needed
17+
inline def mod5: Int = Do.mod(5: O.T): T // proxied
18+
inline def mod6: Int = Do.mod(6: T) // proxied
19+
inline def mod7: Int = Do.mod(7: T): O.T // proxied
20+
inline def mod8: Int = Do.mod(8: T): T // proxied
21+
22+
class Test:
23+
def testGet0: Int = O.get0
24+
def testGet1: Int = O.get1
25+
def testGet2: Int = O.get2
26+
27+
def testSet0: Unit = O.set0
28+
def testSet1: Unit = O.set1
29+
def testSet2: Unit = O.set2
30+
31+
def testMod0: Int = O.mod0
32+
def testMod1: Int = O.mod1
33+
def testMod2: Int = O.mod2
34+
def testMod3: Int = O.mod3
35+
def testMod4: Int = O.mod4
36+
def testMod5: Int = O.mod5
37+
def testMod6: Int = O.mod6
38+
def testMod7: Int = O.mod7
39+
def testMod8: Int = O.mod8
40+
41+
object Do:
42+
def get: O.T = ???
43+
def set(x: O.T): Unit = ()
44+
def mod(x: O.T): O.T = x

tests/pos/i17948.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object O:
2+
opaque type T = Int
3+
inline def x: Int = P.id(2)
4+
5+
object P:
6+
def id(x: O.T): O.T = x
7+
8+
object Test {
9+
def main(args: Array[String]): Unit = println(foo())
10+
11+
def foo(): Int = O.x
12+
}

0 commit comments

Comments
 (0)