Skip to content

Commit

Permalink
Fix scala#8861: Avoid parameters when instantiating closure results
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed May 3, 2020
1 parent a7ef3e2 commit 5ef741e
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
11 changes: 11 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,17 @@ class Namer { typer: Typer =>
val termParamss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor)
sym.setParamss(typeParams, termParamss)
def wrapMethType(restpe: Type): Type = {
restpe match
case tv: TypeVar
if sym.isAnonymousFunction && ctx.typerState.constraint.contains(tv.origin) =>
// If the result type is a type variable R in an anonymous function, it comes from the
// environment of the function. In this case `instantiateDependent` risks
// instantiating R to a locally defined parameter, which violates avoidance rules.
// We instantiate instead to its avoided lower bound. See i8861.scala for a test.
val lowBound = ctx.typeComparer.fullLowerBound(tv.origin)
val lower = avoid(lowBound, termParamss.flatten)
if lower ne lowBound then tv.instantiateWith(lower)
case _ =>
instantiateDependent(restpe, typeParams, termParamss)
ctx.methodType(tparams map symbolOfTree, termParamss, restpe, isJava = ddef.mods.is(JavaDefined))
}
Expand Down
31 changes: 31 additions & 0 deletions tests/neg/i8861.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
object Test {
sealed trait Container { s =>
type A
def visit[R](int: IntV & s.type => R, str: StrV & s.type => R): R
}
final class IntV extends Container { s =>
type A = Int
val i: Int = 42
def visit[R](int: IntV & s.type => R, str: StrV & s.type => R): R = int(this)
}
final class StrV extends Container { s =>
type A = String
val t: String = "hello"
def visit[R](int: IntV & s.type => R, str: StrV & s.type => R): R = str(this)
}

def minimalOk[R](c: Container { type A = R }): R = c.visit[R](
int = vi => vi.i : vi.A,
str = vs => vs.t : vs.A
)
def minimalFail[M](c: Container { type A = M }): M = c.visit(
int = vi => vi.i : vi.A,
str = vs => vs.t : vs.A // error
)

def main(args: Array[String]): Unit = {
val e: Container { type A = String } = new StrV
println(minimalOk(e)) // this one prints "hello"
println(minimalFail(e)) // this one fails with ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer
}
}

0 comments on commit 5ef741e

Please sign in to comment.