Skip to content

Commit ab125eb

Browse files
authored
Merge pull request #14075 from dotty-staging/fix-13950
Harden erasure of PolyFunction apply's
2 parents 1e6f00b + e2b122f commit ab125eb

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
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: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -667,9 +667,17 @@ object Erasure {
667667
def mapOwner(sym: Symbol): Symbol =
668668
if !sym.exists && tree.name == nme.apply then
669669
// PolyFunction apply Selects will not have a symbol, so deduce the owner
670-
// from the typed the erasure of the original qualifier.
671-
val owner = erasure(tree.qualifier.typeOpt).typeSymbol
672-
if defn.isFunctionClass(owner) then owner else NoSymbol
670+
// from the typed tree of the erasure of the original qualifier's PolyFunction type.
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
680+
}
673681
else
674682
val owner = sym.maybeOwner
675683
if defn.specialErasure.contains(owner) then
@@ -687,7 +695,7 @@ object Erasure {
687695

688696
val owner = mapOwner(origSym)
689697
val sym = if (owner eq origSym.maybeOwner) origSym else owner.info.decl(tree.name).symbol
690-
assert(sym.exists, origSym.showLocated)
698+
assert(sym.exists, i"no owner from $owner/${origSym.showLocated} in $tree")
691699

692700
if owner == defn.ObjectClass then checkValue(qual1)
693701

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)