Skip to content

Harden erasure of PolyFunction apply's #14075

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand Down Expand Up @@ -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) =>
Expand Down
16 changes: 12 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand Down
11 changes: 11 additions & 0 deletions tests/run/i13950.scala
Original file line number Diff line number Diff line change
@@ -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)