Skip to content

Commit c676d7d

Browse files
committed
Change handling of lambda params in inference
Previously, removing them from the constraint set was broken: existentials.scala crashed with orphan params. We now treat type lambda parameters much more like polytype parameters: they get associated typevars and interpolateUndetVars decides when to get rid of them.
1 parent 2de8696 commit c676d7d

12 files changed

+94
-88
lines changed

src/dotty/tools/dotc/core/Constraint.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ abstract class Constraint extends Showable {
7272
/** The bounds of `param` including all known-to-be-smaller and -greater parameters */
7373
def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds
7474

75+
/** If `param` is instantiated to some type, the instance type, otherwise NoType */
76+
def instanceType(param: PolyParam): Type
77+
7578
/** A new constraint which is derived from this constraint by adding
7679
* entries for all type parameters of `poly`.
7780
* @param tvars A list of type variables associated with the params,

src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,8 @@ trait ConstraintHandling {
9494
def description = i"ordering $p1 <: $p2 to\n$constraint"
9595
val res =
9696
if (constraint.isLess(p2, p1))
97-
if (p2.binder.isInstanceOf[TypeLambda] && !p1.binder.isInstanceOf[TypeLambda])
98-
unify(p1, p2)
99-
else
100-
unify(p2, p1)
97+
if (p2.binder.isLambda && !p1.binder.isLambda) unify(p1, p2)
98+
else unify(p2, p1)
10199
else {
102100
val down1 = p1 :: constraint.exclusiveLower(p1, p2)
103101
val up2 = p2 :: constraint.exclusiveUpper(p2, p1)
@@ -174,7 +172,9 @@ trait ConstraintHandling {
174172
}
175173
}
176174
}
177-
val bound = if (fromBelow) constraint.fullLowerBound(param) else constraint.fullUpperBound(param)
175+
val bound = constraint.instanceType(param).orElse(
176+
if (fromBelow) constraint.fullLowerBound(param)
177+
else constraint.fullUpperBound(param))
178178
val inst = avoidParam(bound)
179179
typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}")
180180
inst
@@ -228,11 +228,6 @@ trait ConstraintHandling {
228228
// 3. If instance is from below, and upper bound has open named parameters
229229
// make sure the instance has all named parameters of the bound.
230230
if (fromBelow) inst = inst.widenToNamedTypeParams(param.namedTypeParams)
231-
232-
if (ctx.typerState.isGlobalCommittable)
233-
assert(!inst.isInstanceOf[PolyParam], i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}")
234-
// If this fails, you might want to turn on Config.debugCheckConstraintsClosed
235-
// to help find the root of the problem.
236231
inst
237232
}
238233

src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -477,15 +477,19 @@ object Denotations {
477477

478478
final def signature(implicit ctx: Context): Signature = {
479479
if (isType) Signature.NotAMethod // don't force info if this is a type SymDenotation
480-
else info match {
481-
case info: MethodicType =>
482-
try info.signature
483-
catch { // !!! DEBUG
484-
case scala.util.control.NonFatal(ex) =>
485-
ctx.echo(s"cannot take signature of ${info.show}")
486-
throw ex
487-
}
488-
case _ => Signature.NotAMethod
480+
else {
481+
def sig(info: Type): Signature = info match {
482+
case info: MethodicType =>
483+
try info.signature
484+
catch { // !!! DEBUG
485+
case scala.util.control.NonFatal(ex) =>
486+
ctx.echo(s"cannot take signature of ${info.show}")
487+
throw ex
488+
}
489+
case HKApply(tl: TypeLambda, _) => sig(tl.resType)
490+
case _ => Signature.NotAMethod
491+
}
492+
sig(info)
489493
}
490494
}
491495

src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
210210
}
211211
}
212212

213+
def instanceType(param: PolyParam): Type = entry(param) match {
214+
case _: TypeBounds => NoType
215+
case alias => alias
216+
}
217+
213218
// ---------- Adding PolyTypes --------------------------------------------------
214219

215220
/** The list of parameters P such that, for a fresh type parameter Q:
@@ -501,7 +506,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
501506

502507
override def checkClosed()(implicit ctx: Context): Unit = {
503508
def isFreePolyParam(tp: Type) = tp match {
504-
case PolyParam(binder: GenericType, _) => !contains(binder)
509+
case PolyParam(binder: PolyType, _) =>
510+
// note that free type lambda params may appear in the constraint
511+
!contains(binder)
505512
case _ => false
506513
}
507514
def checkClosedType(tp: Type, where: String) =

src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,6 +1824,7 @@ object SymDenotations {
18241824
override def isType = false
18251825
override def owner: Symbol = throw new AssertionError("NoDenotation.owner")
18261826
override def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = this
1827+
override def mapInfo(f: Type => Type)(implicit ctx: Context): SingleDenotation = this
18271828
validFor = Period.allInRun(NoRunId) // will be brought forward automatically
18281829
}
18291830

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
153153
case tp1 => tp1
154154
}
155155
case param: PolyParam =>
156-
if (param.binder.isInstanceOf[TypeLambda] && typerState.constraint.contains(param))
157-
typeComparer.instantiateLambdaParam(param, if (theMap == null) 1 else theMap.currentVariance)
158-
else
159-
typerState.constraint.typeVarOfParam(param) orElse param
156+
typerState.constraint.typeVarOfParam(param) orElse param
160157
case _: ThisType | _: BoundType | NoPrefix =>
161158
tp
162159
case tp: RefinedType =>

src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -140,30 +140,6 @@ extends TyperState(r) {
140140
}
141141
for (poly <- toCollect)
142142
constraint = constraint.remove(poly)
143-
144-
lazy val keptLambdas: collection.Set[TypeLambda] = {
145-
val toKeep = mutable.Set.empty[TypeLambda]
146-
def keep(p: PolyParam) = p.binder match {
147-
case tl: TypeLambda => toKeep += tl
148-
case _ =>
149-
}
150-
for (poly <- constraint.domainPolys)
151-
if (poly.isInstanceOf[PolyType])
152-
for (param <- poly.paramRefs) {
153-
constraint.lower(param).foreach(keep)
154-
constraint.upper(param).foreach(keep)
155-
constraint.entry(param).foreachPart {
156-
case p: PolyParam if constraint contains p => keep(p)
157-
case _ =>
158-
}
159-
}
160-
toKeep
161-
}
162-
constraint.domainPolys.foreach {
163-
case tl: TypeLambda if !keptLambdas.contains(tl) =>
164-
constraint = constraint.remove(tl)
165-
case _ =>
166-
}
167143
}
168144

169145
/** Try operation `op`; if it produces errors, execute `fallback` with constraint and

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,8 @@ object Types {
24942494

24952495
override def resultType(implicit ctx: Context) = resType
24962496

2497+
def isLambda = this.isInstanceOf[TypeLambda]
2498+
24972499
/** Unconditionally create a new generic type like this one with given elements */
24982500
def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, resType: Type)(implicit ctx: Context): GenericType
24992501

@@ -2871,8 +2873,18 @@ object Types {
28712873
}
28722874

28732875
/** Instantiate variable from the constraints over its `origin`. */
2874-
def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type =
2875-
instantiateWith(ctx.typeComparer.instanceType(origin, fromBelow))
2876+
def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = {
2877+
val inst = ctx.typeComparer.instanceType(origin, fromBelow)
2878+
if (ctx.typerState.isGlobalCommittable)
2879+
inst match {
2880+
case inst: PolyParam =>
2881+
assert(inst.binder.isLambda, i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}")
2882+
// If this fails, you might want to turn on Config.debugCheckConstraintsClosed
2883+
// to help find the root of the problem.
2884+
case _ =>
2885+
}
2886+
instantiateWith(inst)
2887+
}
28762888

28772889
/** Unwrap to instance (if instantiated) or origin (if not), until result
28782890
* is no longer a TypeVar

src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,15 @@ trait Implicits { self: Typer =>
592592

593593
/** Search a list of eligible implicit references */
594594
def searchImplicits(eligible: List[TermRef], contextual: Boolean): SearchResult = {
595+
// Implicit search was observed to create an infinite loop when comparing
596+
// overloaded variants of Set#+ on an existential type. The following code
597+
// would prevent this. But it's just a stop-gap, really. We still have to figure
598+
// out whether to support such existentials at all. If not, where to draw the line?
599+
// pt match
600+
// case HKApply(tl: TypeLambda, _) => return NoImplicitMatches
601+
// case _ =>
602+
// }
603+
595604
val constr = ctx.typerState.constraint
596605

597606
/** Try to typecheck an implicit reference */

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,7 @@ object ProtoTypes {
323323
*/
324324
def constrained(pt: GenericType, owningTree: untpd.Tree)(implicit ctx: Context): (GenericType, List[TypeVar]) = {
325325
val state = ctx.typerState
326-
assert(!(ctx.typerState.isCommittable && owningTree.isEmpty && pt.isInstanceOf[PolyType]),
327-
s"inconsistent: no typevars were added to committable constraint ${state.constraint}")
326+
val needsTypeVars = ctx.typerState.isCommittable || !owningTree.isEmpty
328327

329328
def newTypeVars(pt: GenericType): List[TypeVar] =
330329
for (n <- (0 until pt.paramNames.length).toList)
@@ -333,7 +332,7 @@ object ProtoTypes {
333332
val added =
334333
if (state.constraint contains pt) pt.duplicate(pt.paramNames, pt.paramBounds, pt.resultType)
335334
else pt
336-
val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added)
335+
val tvars = if (needsTypeVars) newTypeVars(added) else Nil
337336
ctx.typeComparer.addToConstraint(added, tvars)
338337
(added, tvars)
339338
}

tests/disabled/pos/flowops.scala

Lines changed: 0 additions & 31 deletions
This file was deleted.

tests/pos/existentials.scala

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,50 @@
11
object TestList {
22

33
var x: ([X] -> List[List[X]])[_] = List(List(1))
4+
var y: ([X] -> List[Seq[X]])[_] = List(List(1))
45

5-
var y: List[_] = x
6+
x = x
7+
y = y
8+
y = x
9+
// x = y // error
10+
11+
val h = x.head
12+
val x1: List[_] = h
13+
14+
var z: List[_] = x
615

716
}
817
object TestSet {
918

10-
class Set[S](x: S)
19+
var x: ([Y] -> Set[Set[Y]])[_] = Set(Set("a"))
20+
var y: ([Y] -> Set[Iterable[Y]])[_] = Set(Set("a"))
21+
22+
x = x
23+
y = y
24+
// y = x // error
25+
// x = y // error
1126

12-
var x: ([Y] -> Set[Set[Y]])[_] = new Set(new Set("a"))
27+
val h = x.head
28+
val h1: Set[_] = h
1329

14-
var y: Set[_] = x
30+
// val p = x.+(_) // error: not found: _$1 -- (this is not OK!)
31+
32+
// val p = x.+ // infinite loop in implicit search
33+
34+
var z: Set[_] = x
1535

1636
}
37+
object TestX {
38+
39+
class C[T](x: T) {
40+
def get: T = x
41+
def cmp: T => Boolean = (x == _)
42+
}
43+
44+
val x: ([Y] -> C[C[Y]])[_] = new C(new C("a"))
45+
46+
val g = x.get
47+
48+
val c = x.cmp
49+
}
50+

0 commit comments

Comments
 (0)