Skip to content

Commit 4277d86

Browse files
authored
Add extension/conversion to GADT selection healing (#16638)
2 parents ed5b119 + f6a16cb commit 4277d86

File tree

2 files changed

+46
-14
lines changed

2 files changed

+46
-14
lines changed

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -616,11 +616,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
616616
val superAccess = qual.isInstanceOf[Super]
617617
val rawType = selectionType(tree, qual)
618618
val checkedType = accessibleType(rawType, superAccess)
619-
if checkedType.exists then
619+
620+
def finish(tree: untpd.Select, qual: Tree, checkedType: Type): Tree =
620621
val select = toNotNullTermRef(assignType(tree, checkedType), pt)
621622
if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, "type prefix")
622623
checkLegalValue(select, pt)
623624
ConstFold(select)
625+
626+
if checkedType.exists then
627+
finish(tree, qual, checkedType)
624628
else if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
625629
// Simplify `m.apply(...)` to `m(...)`
626630
qual
@@ -632,6 +636,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
632636
else
633637
val tree1 = tryExtensionOrConversion(
634638
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
639+
.orElse {
640+
if ctx.gadt.isNarrowing then
641+
// try GADT approximation if we're trying to select a member
642+
// Member lookup cannot take GADTs into account b/c of cache, so we
643+
// approximate types based on GADT constraints instead. For an example,
644+
// see MemberHealing in gadt-approximation-interaction.scala.
645+
val wtp = qual.tpe.widen
646+
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
647+
val gadtApprox = Inferencing.approximateGADT(wtp)
648+
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
649+
val qual1 = qual.cast(gadtApprox)
650+
val tree1 = cpy.Select(tree0)(qual1, selName)
651+
val checkedType1 = accessibleType(selectionType(tree1, qual1), superAccess = false)
652+
if checkedType1.exists then
653+
gadts.println(i"Member selection healed by GADT approximation")
654+
finish(tree1, qual1, checkedType1)
655+
else
656+
tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true)
657+
else EmptyTree
658+
}
635659
if !tree1.isEmpty then
636660
tree1
637661
else if canDefineFurther(qual.tpe.widen) then
@@ -3969,19 +3993,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
39693993

39703994
pt match
39713995
case pt: SelectionProto =>
3972-
if ctx.gadt.isNarrowing then
3973-
// try GADT approximation if we're trying to select a member
3974-
// Member lookup cannot take GADTs into account b/c of cache, so we
3975-
// approximate types based on GADT constraints instead. For an example,
3976-
// see MemberHealing in gadt-approximation-interaction.scala.
3977-
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
3978-
val gadtApprox = Inferencing.approximateGADT(wtp)
3979-
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
3980-
if pt.isMatchedBy(gadtApprox) then
3981-
gadts.println(i"Member selection healed by GADT approximation")
3982-
tree.cast(gadtApprox)
3983-
else tree
3984-
else if tree.tpe.derivesFrom(defn.PairClass) && !defn.isTupleNType(tree.tpe.widenDealias) then
3996+
if tree.tpe.derivesFrom(defn.PairClass) && !defn.isTupleNType(tree.tpe.widenDealias) then
39853997
// If this is a generic tuple we need to cast it to make the TupleN/ members accessible.
39863998
// This works only for generic tuples of known size up to 22.
39873999
defn.tupleTypes(tree.tpe.widenTermRefExpr) match

tests/pos/i16603.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
trait MyData
2+
3+
object MyData:
4+
extension (m: MyData)
5+
def printIt() = println("hey from my data")
6+
7+
class MyClass:
8+
def sel(s: String): Int = s.hashCode()
9+
10+
enum MyTag[A]:
11+
case MyDataTag extends MyTag[MyData]
12+
case MyClassTag extends MyTag[MyClass]
13+
14+
def callExtension[A](tag: MyTag[A], a:A): Unit =
15+
tag match
16+
case MyTag.MyDataTag => a.printIt()
17+
case MyTag.MyClassTag => a.sel("hi")
18+
19+
def callExtensionDirectly(m: MyData): Unit =
20+
m.printIt()

0 commit comments

Comments
 (0)