Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type ascribe trees that require opaque type usage #18101

Merged
merged 1 commit into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}