diff options
author | Martin Odersky <odersky@gmail.com> | 2016-12-01 18:46:36 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-12-01 18:46:36 +0100 |
commit | 5491e59569b85949ffb9d6e825c9e337adccdf2d (patch) | |
tree | 53748004acc490249de0f1dfa0b093292d842d7c /compiler/src/dotty/tools/dotc/typer | |
parent | 47d208448e614125446c7f294f8231c3fb7108d6 (diff) | |
download | dotty-5491e59569b85949ffb9d6e825c9e337adccdf2d.tar.gz dotty-5491e59569b85949ffb9d6e825c9e337adccdf2d.tar.bz2 dotty-5491e59569b85949ffb9d6e825c9e337adccdf2d.zip |
Fix #1757: Be more careful about positions of type variable binders
We interpolate a type variable if the current tree contains the type variables
binding tree. Previously, this was the application owning the variable. However,
sometimes this tree is transformed so that the containment test fails, and
type variables are instantiated too late (in the case of #1757 this was never).
We fix this by
- setting the binding tree to the type tree that first contains the type variable
- making sure that tree is never copied literally anywhere else.
It's a tricky dance, but I believe we got it right now.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer')
4 files changed, 21 insertions, 11 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 6c398cd72..b10f64adb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -250,8 +250,15 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** Splice new method reference into existing application */ def spliceMeth(meth: Tree, app: Tree): Tree = app match { - case Apply(fn, args) => Apply(spliceMeth(meth, fn), args) - case TypeApply(fn, targs) => TypeApply(spliceMeth(meth, fn), targs) + case Apply(fn, args) => + spliceMeth(meth, fn).appliedToArgs(args) + case TypeApply(fn, targs) => + // Note: It is important that the type arguments `targs` are passed in new trees + // instead of being spliced in literally. Otherwise, a type argument to a default + // method could be constructed as the definition site of the type variable for + // that default constructor. This would interpolate type variables too early, + // causing lots of tests (among them tasty_unpickleScala2) to fail. + spliceMeth(meth, fn).appliedToTypes(targs.tpes) case _ => meth } @@ -333,7 +340,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val getter = findDefaultGetter(n + numArgs(normalizedFun)) if (getter.isEmpty) missingArg(n) else { - addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal) + addTyped(treeToArg(spliceMeth(getter withPos normalizedFun.pos, normalizedFun)), formal) matchArgs(args1, formals1, n + 1) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index aede4974a..1cb86dd72 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -216,10 +216,10 @@ object Inferencing { def interpolateUndetVars(tree: Tree, ownedBy: Symbol)(implicit ctx: Context): Unit = { val constraint = ctx.typerState.constraint val qualifies = (tvar: TypeVar) => - (tree contains tvar.owningTree) || ownedBy.exists && tvar.owner == ownedBy + (tree contains tvar.bindingTree) || ownedBy.exists && tvar.owner == ownedBy def interpolate() = Stats.track("interpolateUndetVars") { val tp = tree.tpe.widen - constr.println(s"interpolate undet vars in ${tp.show}, pos = ${tree.pos}, mode = ${ctx.mode}, undets = ${constraint.uninstVars map (tvar => s"${tvar.show}@${tvar.owningTree.pos}")}") + constr.println(s"interpolate undet vars in ${tp.show}, pos = ${tree.pos}, mode = ${ctx.mode}, undets = ${constraint.uninstVars map (tvar => s"${tvar.show}@${tvar.bindingTree.pos}")}") constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}, constraint: ${constraint.show}") val vs = variances(tp, qualifies) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 9a20a452e..ed6b95c3b 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -353,20 +353,23 @@ object ProtoTypes { * Also, if `owningTree` is non-empty, add a type variable for each parameter. * @return The added polytype, and the list of created type variables. */ - def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = { + def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeTree]) = { val state = ctx.typerState assert(!(ctx.typerState.isCommittable && owningTree.isEmpty), s"inconsistent: no typevars were added to committable constraint ${state.constraint}") - def newTypeVars(pt: PolyType): List[TypeVar] = + def newTypeVars(pt: PolyType): List[TypeTree] = for (n <- (0 until pt.paramNames.length).toList) - yield new TypeVar(PolyParam(pt, n), state, owningTree, ctx.owner) + yield { + val tt = new TypeTree().withPos(owningTree.pos) + tt.withType(new TypeVar(PolyParam(pt, n), state, tt, ctx.owner)) + } val added = if (state.constraint contains pt) pt.newLikeThis(pt.paramNames, pt.paramBounds, pt.resultType) else pt val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) - ctx.typeComparer.addToConstraint(added, tvars) + ctx.typeComparer.addToConstraint(added, tvars.tpes.asInstanceOf[List[TypeVar]]) (added, tvars) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4ec1c460f..f3417ceac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1962,12 +1962,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (pt.isInstanceOf[PolyProto]) tree else { var typeArgs = tree match { - case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo + case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree) case _ => Nil } if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2 convertNewGenericArray( - adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original)) + adaptInterpolated(tree.appliedToTypeTrees(typeArgs), pt, original)) } case wtp => pt match { |