Skip to content

Commit e2b122f

Browse files
committed
Alternative approach to PolyFunction apply erasure
The previous approach did not handle situations where the qualifier was not directly an AndType, but a type whose underlying type is an AndType like in example2 and example3 (this commit also minimizes the original example further).
1 parent bc5dd76 commit e2b122f

File tree

4 files changed

+30
-21
lines changed

4 files changed

+30
-21
lines changed

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,15 @@ object TypeErasure {
524524
case tp: OrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2)
525525
case _ => false
526526
}
527+
528+
/** The erasure of `PolyFunction { def apply: $applyInfo }` */
529+
def erasePolyFunctionApply(applyInfo: Type)(using Context): Type =
530+
assert(applyInfo.isInstanceOf[PolyType])
531+
val res = applyInfo.resultType
532+
val paramss = res.paramNamess
533+
assert(paramss.length == 1)
534+
erasure(defn.FunctionType(paramss.head.length,
535+
isContextual = res.isImplicitMethod, isErased = res.isErasedMethod))
527536
}
528537

529538
import TypeErasure._
@@ -597,11 +606,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
597606
case ExprType(rt) =>
598607
defn.FunctionType(0)
599608
case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
600-
assert(refinedInfo.isInstanceOf[PolyType])
601-
val res = refinedInfo.resultType
602-
val paramss = res.paramNamess
603-
assert(paramss.length == 1)
604-
this(defn.FunctionType(paramss.head.length, isContextual = res.isImplicitMethod, isErased = res.isErasedMethod))
609+
erasePolyFunctionApply(refinedInfo)
605610
case tp: TypeProxy =>
606611
this(tp.underlying)
607612
case tp @ AndType(tp1, tp2) =>

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -668,18 +668,16 @@ object Erasure {
668668
if !sym.exists && tree.name == nme.apply then
669669
// PolyFunction apply Selects will not have a symbol, so deduce the owner
670670
// from the typed tree of the erasure of the original qualifier's PolyFunction type.
671-
// Need to sidestep information loss in the erasure of AndTypes, see pos/i13950.scala.
672-
val polyFunctionQualType = inContext(preErasureCtx) {
673-
def narrowToPolyFun(tp: Type)(using Context): Type =
674-
if tp.derivesFrom(defn.PolyFunctionClass) then
675-
tp match
676-
case AndType(tp1, tp2) => narrowToPolyFun(tp1).orElse(narrowToPolyFun(tp2))
677-
case _ => tp
678-
else NoType
679-
narrowToPolyFun(tree.qualifier.typeOpt.widen)
671+
// We cannot simply call `erasure` on the qualifier because its erasure might be
672+
// `Object` due to how we erase intersections (see pos/i13950.scala).
673+
// Instead, we manually lookup the type of `apply` in the qualifier.
674+
inContext(preErasureCtx) {
675+
val qualTp = tree.qualifier.typeOpt.widen
676+
if qualTp.derivesFrom(defn.PolyFunctionClass) then
677+
erasePolyFunctionApply(qualTp.select(nme.apply).widen).classSymbol
678+
else
679+
NoSymbol
680680
}
681-
val owner = erasure(polyFunctionQualType).typeSymbol
682-
if defn.isFunctionClass(owner) then owner else NoSymbol
683681
else
684682
val owner = sym.maybeOwner
685683
if defn.specialErasure.contains(owner) then

tests/pos/i13950.scala

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/run/i13950.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def example(x: Any & ([V] => V => Int)) =
2+
x[Int](1)
3+
def example2(x: (Any & ([V] => V => Int)) @unchecked) =
4+
x[Int](1)
5+
def example3[S <: Any & ([V] => V => Int)](x: S) =
6+
x[Int](1)
7+
8+
@main def Test =
9+
example([A] => (x: A) => 1)
10+
example2([A] => (x: A) => 1)
11+
example3([A] => (x: A) => 1)

0 commit comments

Comments
 (0)