Skip to content

Commit e55e139

Browse files
committed
Type ascribe trees that require opaque type usage
1 parent 6acaf95 commit e55e139

File tree

5 files changed

+73
-15
lines changed

5 files changed

+73
-15
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`. */
@@ -1472,12 +1478,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
14721478

14731479
def tryLiftedToThis1: Boolean = {
14741480
val tp1a = liftToThis(tp1)
1475-
(tp1a ne tp1) && recur(tp1a, tp2)
1481+
(tp1a ne tp1) && recur(tp1a, tp2) && { opaquesUsed = true; true }
14761482
}
14771483

14781484
def tryLiftedToThis2: Boolean = {
14791485
val tp2a = liftToThis(tp2)
1480-
(tp2a ne tp2) && recur(tp1, tp2a)
1486+
(tp2a ne tp2) && recur(tp1, tp2a) && { opaquesUsed = true; true }
14811487
}
14821488

14831489
// begin recur
@@ -2924,7 +2930,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
29242930
object TypeComparer {
29252931

29262932
enum CompareResult:
2927-
case OK, Fail, OKwithGADTUsed
2933+
case OK, Fail, OKwithGADTUsed, OKwithOpaquesUsed
29282934

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

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

Lines changed: 5 additions & 12 deletions
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
@@ -744,23 +745,15 @@ object TreeChecker {
744745
override def adapt(tree: Tree, pt: Type, locked: TypeVars)(using Context): Tree = {
745746
def isPrimaryConstructorReturn =
746747
ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass)
747-
def infoStr(tp: Type) = tp match {
748-
case tp: TypeRef =>
749-
val sym = tp.symbol
750-
i"${sym.showLocated} with ${tp.designator}, flags = ${sym.flagsString}, underlying = ${tp.underlyingIterator.toList}%, %"
751-
case _ =>
752-
"??"
753-
}
754748
if (ctx.mode.isExpr &&
755749
!tree.isEmpty &&
756750
!isPrimaryConstructorReturn &&
757751
!pt.isInstanceOf[FunOrPolyProto])
758752
assert(tree.tpe <:< pt, {
759753
val mismatch = TypeMismatch(tree.tpe, pt, Some(tree))
760-
i"""|${mismatch.msg}
761-
|found: ${infoStr(tree.tpe)}
762-
|expected: ${infoStr(pt)}
763-
|tree = $tree""".stripMargin
754+
i"""Type Mismatch:
755+
|${mismatch.message}
756+
|tree = $tree ${tree.className}""".stripMargin
764757
})
765758
tree
766759
}

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

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