Skip to content

Commit d900813

Browse files
authored
Merge pull request #1764 from dotty-staging/fix-#1757
Fix #1757: Be more careful about positions of type variable binders
2 parents b8e3126 + 76aed5e commit d900813

File tree

6 files changed

+52
-14
lines changed

6 files changed

+52
-14
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,14 +2862,14 @@ object Types {
28622862
*
28632863
* @param origin The parameter that's tracked by the type variable.
28642864
* @param creatorState The typer state in which the variable was created.
2865-
* @param owningTree The function part of the TypeApply tree tree that introduces
2866-
* the type variable.
2865+
* @param bindingTree The TypeTree which introduces the type variable, or EmptyTree
2866+
* if the type variable does not correspond to a source term.
28672867
* @paran owner The current owner if the context where the variable was created.
28682868
*
28692869
* `owningTree` and `owner` are used to determine whether a type-variable can be instantiated
28702870
* at some given point. See `Inferencing#interpolateUndetVars`.
28712871
*/
2872-
final class TypeVar(val origin: PolyParam, creatorState: TyperState, val owningTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType {
2872+
final class TypeVar(val origin: PolyParam, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType {
28732873

28742874
/** The permanent instance type of the variable, or NoType is none is given yet */
28752875
private[core] var inst: Type = NoType

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,37 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
250250

251251
/** Splice new method reference into existing application */
252252
def spliceMeth(meth: Tree, app: Tree): Tree = app match {
253-
case Apply(fn, args) => Apply(spliceMeth(meth, fn), args)
254-
case TypeApply(fn, targs) => TypeApply(spliceMeth(meth, fn), targs)
253+
case Apply(fn, args) =>
254+
spliceMeth(meth, fn).appliedToArgs(args)
255+
case TypeApply(fn, targs) =>
256+
// Note: It is important that the type arguments `targs` are passed in new trees
257+
// instead of being spliced in literally. Otherwise, a type argument to a default
258+
// method could be constructed as the definition site of the type variable for
259+
// that default constructor. This would interpolate type variables too early,
260+
// causing lots of tests (among them tasty_unpickleScala2) to fail.
261+
//
262+
// The test case is in i1757.scala. Here we have a variable `s` and a method `cpy`
263+
// defined like this:
264+
//
265+
// var s
266+
// def cpy[X](b: List[Int] = b): B[X] = new B[X](b)
267+
//
268+
// The call `s.cpy()` then gets expanded to
269+
//
270+
// { val $1$: B[Int] = this.s
271+
// $1$.cpy[X']($1$.cpy$default$1[X']
272+
// }
273+
//
274+
// A type variable gets interpolated if it does not appear in the type
275+
// of the current tree and the current tree contains the variable's "definition".
276+
// Previously, the polymorphic function tree to which the variable was first added
277+
// was taken as the variable's definition. But that fails here because that
278+
// tree was `s.cpy` but got transformed into `$1$.cpy`. We now take the type argument
279+
// [X'] of the variable as its definition tree, which is more robust. But then
280+
// it's crucial that the type tree is not copied directly as argument to
281+
// `cpy$default$1`. If it was, the variable `X'` would already be interpolated
282+
// when typing the default argument, which is too early.
283+
spliceMeth(meth, fn).appliedToTypes(targs.tpes)
255284
case _ => meth
256285
}
257286

@@ -333,7 +362,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
333362
val getter = findDefaultGetter(n + numArgs(normalizedFun))
334363
if (getter.isEmpty) missingArg(n)
335364
else {
336-
addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal)
365+
addTyped(treeToArg(spliceMeth(getter withPos normalizedFun.pos, normalizedFun)), formal)
337366
matchArgs(args1, formals1, n + 1)
338367
}
339368
}

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,10 @@ object Inferencing {
216216
def interpolateUndetVars(tree: Tree, ownedBy: Symbol)(implicit ctx: Context): Unit = {
217217
val constraint = ctx.typerState.constraint
218218
val qualifies = (tvar: TypeVar) =>
219-
(tree contains tvar.owningTree) || ownedBy.exists && tvar.owner == ownedBy
219+
(tree contains tvar.bindingTree) || ownedBy.exists && tvar.owner == ownedBy
220220
def interpolate() = Stats.track("interpolateUndetVars") {
221221
val tp = tree.tpe.widen
222-
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}")}")
222+
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}")}")
223223
constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}, constraint: ${constraint.show}")
224224

225225
val vs = variances(tp, qualifies)

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,20 +353,23 @@ object ProtoTypes {
353353
* Also, if `owningTree` is non-empty, add a type variable for each parameter.
354354
* @return The added polytype, and the list of created type variables.
355355
*/
356-
def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = {
356+
def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeTree]) = {
357357
val state = ctx.typerState
358358
assert(!(ctx.typerState.isCommittable && owningTree.isEmpty),
359359
s"inconsistent: no typevars were added to committable constraint ${state.constraint}")
360360

361-
def newTypeVars(pt: PolyType): List[TypeVar] =
361+
def newTypeVars(pt: PolyType): List[TypeTree] =
362362
for (n <- (0 until pt.paramNames.length).toList)
363-
yield new TypeVar(PolyParam(pt, n), state, owningTree, ctx.owner)
363+
yield {
364+
val tt = new TypeTree().withPos(owningTree.pos)
365+
tt.withType(new TypeVar(PolyParam(pt, n), state, tt, ctx.owner))
366+
}
364367

365368
val added =
366369
if (state.constraint contains pt) pt.newLikeThis(pt.paramNames, pt.paramBounds, pt.resultType)
367370
else pt
368371
val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added)
369-
ctx.typeComparer.addToConstraint(added, tvars)
372+
ctx.typeComparer.addToConstraint(added, tvars.tpes.asInstanceOf[List[TypeVar]])
370373
(added, tvars)
371374
}
372375

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,12 +1978,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
19781978
if (pt.isInstanceOf[PolyProto]) tree
19791979
else {
19801980
var typeArgs = tree match {
1981-
case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo
1981+
case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree)
19821982
case _ => Nil
19831983
}
19841984
if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2
19851985
convertNewGenericArray(
1986-
adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original))
1986+
adaptInterpolated(tree.appliedToTypeTrees(typeArgs), pt, original))
19871987
}
19881988
case wtp =>
19891989
pt match {

tests/pos/i1757.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
case class B[T](b: List[Int]) {
2+
var s: B[Int] = ???
3+
def cpy[X](b: List[Int] = b): B[X] = new B[X](b)
4+
s.cpy()
5+
s.copy()
6+
}

0 commit comments

Comments
 (0)