summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Typers.scala
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-09-19 00:11:39 +1000
committerJason Zaugg <jzaugg@gmail.com>2014-10-02 21:00:29 +1000
commitee2b7d615be8e7d893b78d770cf68a95c8fa0621 (patch)
tree87dbd3814e5790714b8eb0047814836219135ff0 /src/compiler/scala/tools/nsc/typechecker/Typers.scala
parent79f59c5330122ee1ff1bac833bc77feca81d49af (diff)
downloadscala-ee2b7d615be8e7d893b78d770cf68a95c8fa0621.tar.gz
scala-ee2b7d615be8e7d893b78d770cf68a95c8fa0621.tar.bz2
scala-ee2b7d615be8e7d893b78d770cf68a95c8fa0621.zip
SI-8267 Avoid existentials after polymorphic overload resolution
... which can be introduced by `memberType` for methods with parameter types dependent on class type parameters. Here's an example of such a type: ``` scala> class Bippy { trait Foo[A] } defined class Bippy scala> final class RichBippy[C <: Bippy with Singleton](val c1: C) { | def g[A](x: A)(ev: c1.Foo[A]): Int = 2 | } defined class RichBippy scala> :power ** Power User mode enabled - BEEP WHIR GYVE ** ** :phase has been set to 'typer'. ** ** scala.tools.nsc._ has been imported ** ** global._, definitions._ also imported ** ** Try :help, :vals, power.<tab> ** scala> val g = typeOf[RichBippy[_]].member(TermName("g")) g: $r.intp.global.Symbol = method g scala> val c = new Bippy c: Bippy = Bippy@92e2c93 scala> val memberType = typeOf[RichBippy[c.type]].memberType(g) memberType: $r.intp.global.Type = ([A](x: A)(ev: _7.c1.Foo[A])Int) forSome { val _7: RichBippy[c.type] } ``` In this example, if we were to typecheck the selection `new RichBippy[c.type].g` that existential type would be short lived. Consider this approximation of `Typer#typedInternal`: ```scala val tree1: Tree = typed1(tree, mode, ptWild) val result = adapt(tree1, mode, ptPlugins, tree) ``` Given that `tree1.tpe` is not an overloaded, adapt will find its way to: ``` case tp if mode.typingExprNotLhs && isExistentialType(tp) => adapt(tree setType tp.dealias.skolemizeExistential(context.owner, tree), mode, pt, original) ``` Which would open the existential as per: ``` scala> memberType.skolemizeExistential res2: $r.intp.global.Type = [A](x: A)(ev: _7.c1.Foo[A])Int ``` However, if do have overloaded alternatives, as in the test case, we have to remember to call `adapt` again *after* we have picked the winning alternative. We actually don't have a centralised place where overload resolution occurs, as the process differs depending on the context of the selection. (Are there explicit type arguments? Inferred type arguments? Do we need to use the expected type to pick a winner?) This commit finds the existing places that call adapt after overloade resolution and routes those calls through a marker method. It then adds one more call to this in `inferPolyAlternatives`, which fixes the bug.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Typers.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala27
1 files changed, 22 insertions, 5 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 422b940cd3..b498d9e667 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1100,7 +1100,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
adaptConstant(value)
case OverloadedType(pre, alts) if !mode.inFunMode => // (1)
inferExprAlternative(tree, pt)
- adapt(tree, mode, pt, original)
+ adaptAfterOverloadResolution(tree, mode, pt, original)
case NullaryMethodType(restpe) => // (2)
adapt(tree setType restpe, mode, pt, original)
case TypeRef(_, ByNameParamClass, arg :: Nil) if mode.inExprMode => // (2)
@@ -1133,6 +1133,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
+ // This just exists to help keep track of the spots where we have to adapt a tree after
+ // overload resolution. These proved hard to find during the fix for SI-8267.
+ def adaptAfterOverloadResolution(tree: Tree, mode: Mode, pt: Type = WildcardType, original: Tree = EmptyTree): Tree = {
+ adapt(tree, mode, pt, original)
+ }
+
def instantiate(tree: Tree, mode: Mode, pt: Type): Tree = {
inferExprInstance(tree, context.extractUndetparams(), pt)
adapt(tree, mode, pt)
@@ -3171,7 +3177,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (sym1 != NoSymbol) sym = sym1
}
if (sym == NoSymbol) fun
- else adapt(fun setSymbol sym setType pre.memberType(sym), mode.forFunMode, WildcardType)
+ else adaptAfterOverloadResolution(fun setSymbol sym setType pre.memberType(sym), mode.forFunMode)
} else fun
}
@@ -3216,7 +3222,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
setError(tree)
else {
inferMethodAlternative(fun, undetparams, argTpes, pt)
- doTypedApply(tree, adapt(fun, mode.forFunMode, WildcardType), args1, mode, pt)
+ doTypedApply(tree, adaptAfterOverloadResolution(fun, mode.forFunMode, WildcardType), args1, mode, pt)
}
}
handleOverloaded
@@ -3799,7 +3805,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
protected def typedTypeApply(tree: Tree, mode: Mode, fun: Tree, args: List[Tree]): Tree = fun.tpe match {
case OverloadedType(pre, alts) =>
inferPolyAlternatives(fun, mapList(args)(treeTpe))
- val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree)
+
+ // SI-8267 `memberType` can introduce existentials *around* a PolyType/MethodType, see AsSeenFromMap#captureThis.
+ // If we had selected a non-overloaded symbol, `memberType` would have been called in `makeAccessible`
+ // and the resulting existential type would have been skolemized in `adapt` *before* we typechecked
+ // the enclosing type-/ value- application.
+ //
+ // However, if the selection is overloaded, we defer calling `memberType` until we can select a single
+ // alternative here. It is therefore necessary to skolemize the existential here.
+ //
+ val fun1 = adaptAfterOverloadResolution(fun, mode.forFunMode | TAPPmode)
+
+ val tparams = fun1.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree)
val args1 = if (sameLength(args, tparams)) {
//@M: in case TypeApply we can't check the kind-arities of the type arguments,
// as we don't know which alternative to choose... here we do
@@ -3813,7 +3830,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// ...actually this was looping anyway, see bug #278.
return TypedApplyWrongNumberOfTpeParametersError(fun, fun)
- typedTypeApply(tree, mode, fun, args1)
+ typedTypeApply(tree, mode, fun1, args1)
case SingleType(_, _) =>
typedTypeApply(tree, mode, fun setType fun.tpe.widen, args)
case PolyType(tparams, restpe) if tparams.nonEmpty =>