diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 6435d0622ebd..1473bcc559e2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -524,6 +524,15 @@ object TypeErasure { case tp: OrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) case _ => false } + + /** The erasure of `PolyFunction { def apply: $applyInfo }` */ + def erasePolyFunctionApply(applyInfo: Type)(using Context): Type = + assert(applyInfo.isInstanceOf[PolyType]) + val res = applyInfo.resultType + val paramss = res.paramNamess + assert(paramss.length == 1) + erasure(defn.FunctionType(paramss.head.length, + isContextual = res.isImplicitMethod, isErased = res.isErasedMethod)) } import TypeErasure._ @@ -597,11 +606,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst case ExprType(rt) => defn.FunctionType(0) case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass => - assert(refinedInfo.isInstanceOf[PolyType]) - val res = refinedInfo.resultType - val paramss = res.paramNamess - assert(paramss.length == 1) - this(defn.FunctionType(paramss.head.length, isContextual = res.isImplicitMethod, isErased = res.isErasedMethod)) + erasePolyFunctionApply(refinedInfo) case tp: TypeProxy => this(tp.underlying) case tp @ AndType(tp1, tp2) => diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index ab6f00dfc575..357ff1e6e3e6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -667,9 +667,17 @@ object Erasure { def mapOwner(sym: Symbol): Symbol = if !sym.exists && tree.name == nme.apply then // PolyFunction apply Selects will not have a symbol, so deduce the owner - // from the typed the erasure of the original qualifier. - val owner = erasure(tree.qualifier.typeOpt).typeSymbol - if defn.isFunctionClass(owner) then owner else NoSymbol + // from the typed tree of the erasure of the original qualifier's PolyFunction type. + // We cannot simply call `erasure` on the qualifier because its erasure might be + // `Object` due to how we erase intersections (see pos/i13950.scala). + // Instead, we manually lookup the type of `apply` in the qualifier. + inContext(preErasureCtx) { + val qualTp = tree.qualifier.typeOpt.widen + if qualTp.derivesFrom(defn.PolyFunctionClass) then + erasePolyFunctionApply(qualTp.select(nme.apply).widen).classSymbol + else + NoSymbol + } else val owner = sym.maybeOwner if defn.specialErasure.contains(owner) then @@ -687,7 +695,7 @@ object Erasure { val owner = mapOwner(origSym) val sym = if (owner eq origSym.maybeOwner) origSym else owner.info.decl(tree.name).symbol - assert(sym.exists, origSym.showLocated) + assert(sym.exists, i"no owner from $owner/${origSym.showLocated} in $tree") if owner == defn.ObjectClass then checkValue(qual1) diff --git a/tests/run/i13950.scala b/tests/run/i13950.scala new file mode 100644 index 000000000000..b8f93129beb8 --- /dev/null +++ b/tests/run/i13950.scala @@ -0,0 +1,11 @@ +def example(x: Any & ([V] => V => Int)) = + x[Int](1) +def example2(x: (Any & ([V] => V => Int)) @unchecked) = + x[Int](1) +def example3[S <: Any & ([V] => V => Int)](x: S) = + x[Int](1) + +@main def Test = + example([A] => (x: A) => 1) + example2([A] => (x: A) => 1) + example3([A] => (x: A) => 1)