diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e65eba4d5dd8..c9ebd826de49 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -622,11 +622,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val superAccess = qual.isInstanceOf[Super] val rawType = selectionType(tree, qual) val checkedType = accessibleType(rawType, superAccess) - if checkedType.exists then + + def finish(tree: untpd.Select, qual: Tree, checkedType: Type): Tree = val select = toNotNullTermRef(assignType(tree, checkedType), pt) if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, "type prefix") checkLegalValue(select, pt) ConstFold(select) + + if checkedType.exists then + finish(tree, qual, checkedType) else if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then // Simplify `m.apply(...)` to `m(...)` qual @@ -638,6 +642,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else val tree1 = tryExtensionOrConversion( tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true) + .orElse { + if ctx.gadt.isNarrowing then + // try GADT approximation if we're trying to select a member + // Member lookup cannot take GADTs into account b/c of cache, so we + // approximate types based on GADT constraints instead. For an example, + // see MemberHealing in gadt-approximation-interaction.scala. + val wtp = qual.tpe.widen + gadts.println(i"Trying to heal member selection by GADT-approximating $wtp") + val gadtApprox = Inferencing.approximateGADT(wtp) + gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox") + val qual1 = qual.cast(gadtApprox) + val tree1 = cpy.Select(tree0)(qual1, selName) + val checkedType1 = accessibleType(selectionType(tree1, qual1), superAccess = false) + if checkedType1.exists then + gadts.println(i"Member selection healed by GADT approximation") + finish(tree1, qual1, checkedType1) + else + tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true) + else EmptyTree + } if !tree1.isEmpty then tree1 else if canDefineFurther(qual.tpe.widen) then @@ -3986,19 +4010,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer pt match case pt: SelectionProto => - if ctx.gadt.isNarrowing then - // try GADT approximation if we're trying to select a member - // Member lookup cannot take GADTs into account b/c of cache, so we - // approximate types based on GADT constraints instead. For an example, - // see MemberHealing in gadt-approximation-interaction.scala. - gadts.println(i"Trying to heal member selection by GADT-approximating $wtp") - val gadtApprox = Inferencing.approximateGADT(wtp) - gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox") - if pt.isMatchedBy(gadtApprox) then - gadts.println(i"Member selection healed by GADT approximation") - tree.cast(gadtApprox) - else tree - else if tree.tpe.derivesFrom(defn.PairClass) && !defn.isTupleNType(tree.tpe.widenDealias) then + if tree.tpe.derivesFrom(defn.PairClass) && !defn.isTupleNType(tree.tpe.widenDealias) then // If this is a generic tuple we need to cast it to make the TupleN/ members accessible. // This works only for generic tuples of known size up to 22. defn.tupleTypes(tree.tpe.widenTermRefExpr) match diff --git a/tests/pos/i16603.scala b/tests/pos/i16603.scala new file mode 100644 index 000000000000..5d5e6c9c9398 --- /dev/null +++ b/tests/pos/i16603.scala @@ -0,0 +1,20 @@ +trait MyData + +object MyData: + extension (m: MyData) + def printIt() = println("hey from my data") + +class MyClass: + def sel(s: String): Int = s.hashCode() + +enum MyTag[A]: + case MyDataTag extends MyTag[MyData] + case MyClassTag extends MyTag[MyClass] + +def callExtension[A](tag: MyTag[A], a:A): Unit = + tag match + case MyTag.MyDataTag => a.printIt() + case MyTag.MyClassTag => a.sel("hi") + +def callExtensionDirectly(m: MyData): Unit = + m.printIt()