Skip to content

Commit

Permalink
Type ascribe trees that require opaque type usage (#18101)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Jul 4, 2023
2 parents 5c66d7b + d124f58 commit 1d4dec7
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 4 deletions.
12 changes: 9 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
state = c.typerState
monitored = false
GADTused = false
opaquesUsed = false
recCount = 0
needsGc = false
if Config.checkTypeComparerReset then checkReset()
Expand All @@ -61,6 +62,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
/** Indicates whether the subtype check used GADT bounds */
private var GADTused: Boolean = false

/** Indicates whether the subtype check used opaque types */
private var opaquesUsed: Boolean = false

private var myInstance: TypeComparer = this
def currentInstance: TypeComparer = myInstance

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

def testSubType(tp1: Type, tp2: Type): CompareResult =
GADTused = false
opaquesUsed = false
if !topLevelSubType(tp1, tp2) then CompareResult.Fail
else if GADTused then CompareResult.OKwithGADTUsed
else if opaquesUsed then CompareResult.OKwithOpaquesUsed // we cast on GADTused, so handles if both are used
else CompareResult.OK

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

def tryLiftedToThis1: Boolean = {
val tp1a = liftToThis(tp1)
(tp1a ne tp1) && recur(tp1a, tp2)
(tp1a ne tp1) && recur(tp1a, tp2) && { opaquesUsed = true; true }
}

def tryLiftedToThis2: Boolean = {
val tp2a = liftToThis(tp2)
(tp2a ne tp2) && recur(tp1, tp2a)
(tp2a ne tp2) && recur(tp1, tp2a) && { opaquesUsed = true; true }
}

// begin recur
Expand Down Expand Up @@ -2935,7 +2941,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
object TypeComparer {

enum CompareResult:
case OK, Fail, OKwithGADTUsed
case OK, Fail, OKwithGADTUsed, OKwithOpaquesUsed

/** Class for unification variables used in `natValue`. */
private class AnyConstantType extends UncachedGroundType with ValueType {
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package dotty.tools.dotc
package dotty.tools
package dotc
package transform

import core.Names.Name
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4063,6 +4063,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
res
} =>
insertGadtCast(tree, wtp, pt)
case CompareResult.OKwithOpaquesUsed if !tree.tpe.frozen_<:<(pt)(using ctx.withOwner(defn.RootClass)) =>
// guard to avoid extra Typed trees, eg. from testSubType(O.T, O.T) which returns OKwithOpaquesUsed
Typed(tree, TypeTree(pt))
case _ =>
//typr.println(i"OK ${tree.tpe}\n${TypeComparer.explained(_.isSubType(tree.tpe, pt))}") // uncomment for unexpected successes
tree
Expand Down
44 changes: 44 additions & 0 deletions tests/pos/i17948.all.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
object O:
opaque type T = Int

inline def get0: Int = Do.get // no proxy needed
inline def get1: Int = Do.get: O.T // no proxy needed
inline def get2: Int = Do.get: T // proxied

inline def set0: Unit = Do.set(0) // was: broken
inline def set1: Unit = Do.set(1: O.T) // no proxy needed
inline def set2: Unit = Do.set(2: T) // proxied

inline def mod0: Int = Do.mod(0) // was: broken
inline def mod1: Int = Do.mod(1): O.T // was: broken
inline def mod2: Int = Do.mod(2): T // was: broken
inline def mod3: Int = Do.mod(3: O.T) // no proxy needed
inline def mod4: Int = Do.mod(4: O.T): O.T // no proxy needed
inline def mod5: Int = Do.mod(5: O.T): T // proxied
inline def mod6: Int = Do.mod(6: T) // proxied
inline def mod7: Int = Do.mod(7: T): O.T // proxied
inline def mod8: Int = Do.mod(8: T): T // proxied

class Test:
def testGet0: Int = O.get0
def testGet1: Int = O.get1
def testGet2: Int = O.get2

def testSet0: Unit = O.set0
def testSet1: Unit = O.set1
def testSet2: Unit = O.set2

def testMod0: Int = O.mod0
def testMod1: Int = O.mod1
def testMod2: Int = O.mod2
def testMod3: Int = O.mod3
def testMod4: Int = O.mod4
def testMod5: Int = O.mod5
def testMod6: Int = O.mod6
def testMod7: Int = O.mod7
def testMod8: Int = O.mod8

object Do:
def get: O.T = ???
def set(x: O.T): Unit = ()
def mod(x: O.T): O.T = x
12 changes: 12 additions & 0 deletions tests/pos/i17948.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
object O:
opaque type T = Int
inline def x: Int = P.id(2)

object P:
def id(x: O.T): O.T = x

object Test {
def main(args: Array[String]): Unit = println(foo())

def foo(): Int = O.x
}

0 comments on commit 1d4dec7

Please sign in to comment.