Skip to content

Commit 2b3768d

Browse files
committed
Widen variable binders when eta expanding
1 parent b3767c8 commit 2b3768d

File tree

5 files changed

+30
-4
lines changed

5 files changed

+30
-4
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3286,7 +3286,7 @@ object Types {
32863286
* `owningTree` and `owner` are used to determine whether a type-variable can be instantiated
32873287
* at some given point. See `Inferencing#interpolateUndetVars`.
32883288
*/
3289-
final class TypeVar(val origin: TypeParamRef, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType {
3289+
final class TypeVar(val origin: TypeParamRef, creatorState: TyperState, var bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType {
32903290

32913291
/** The permanent instance type of the variable, or NoType is none is given yet */
32923292
private[this] var myInst: Type = NoType

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ object EtaExpansion extends LiftImpure {
192192
* more options open.
193193
*
194194
* In each case, the result is an untyped tree, with `es` and `expr` as typed splices.
195+
*
196+
* F[V](x) ==> (x => F[X])
195197
*/
196198
def etaExpand(tree: Tree, mt: MethodType, xarity: Int)(implicit ctx: Context): untpd.Tree = {
197199
import untpd._

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,20 @@ trait Inferencing { this: Typer =>
394394
}
395395
if (constraint.uninstVars exists qualifies) interpolate()
396396
}
397+
398+
/** The uninstantiated type variables introduced somehwere in `tree` */
399+
def uninstBoundVars(tree: Tree)(implicit ctx: Context): List[TypeVar] = {
400+
val buf = new mutable.ListBuffer[TypeVar]
401+
tree.foreachSubTree {
402+
case arg: TypeTree =>
403+
arg.tpe match {
404+
case tv: TypeVar if !tv.isInstantiated && tree.contains(tv.bindingTree) => buf += tv
405+
case _ =>
406+
}
407+
case _ =>
408+
}
409+
buf.toList
410+
}
397411
}
398412

399413
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2227,8 +2227,18 @@ class Typer extends Namer
22272227
if (arity >= 0 &&
22282228
!tree.symbol.isConstructor &&
22292229
!ctx.mode.is(Mode.Pattern) &&
2230-
!(isSyntheticApply(tree) && !isExpandableApply))
2231-
typed(etaExpand(tree, wtp, arity), pt)
2230+
!(isSyntheticApply(tree) && !isExpandableApply)) {
2231+
// Eta expansion interacts in tricky ways with type variable instantiation
2232+
// because it can extend the region where type variables are bound (and therefore may not
2233+
// be interpolated). To avoid premature interpolations, we need to extend the
2234+
// bindingTree of variables as we go along. Test case in pos/i3945.scala.
2235+
val boundtvs = uninstBoundVars(tree)
2236+
val uexpanded = etaExpand(tree, wtp, arity)
2237+
boundtvs.foreach(_.bindingTree = uexpanded) // make boundtvs point to uexpanded so that they are _not_ interpolated
2238+
val texpanded = typedUnadapted(uexpanded, pt)
2239+
boundtvs.foreach(_.bindingTree = texpanded) // make boundtvs point to texpanded so that they _can_ be interpolated
2240+
adapt(texpanded, pt)
2241+
}
22322242
else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
22332243
adaptInterpolated(tpd.Apply(tree, Nil), pt)
22342244
else if (wtp.isImplicitMethod)

tests/pos/i3945.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ object Test {
66

77
// unwrapping composition: ok in scalac, ok in dotc
88
val q: (String => Int => String) => (String) => (Int) => (Int => String) = int[Int => String]
9-
val p: (String => String) => (String) => (Int) => String = int[String]
9+
val p: (String => String) => (String) => (Int) => String = int
1010
val c2: (String => String) => (String) => (Int) => (Int) => String = q.compose(p)
1111
}

0 commit comments

Comments
 (0)