@@ -87,7 +87,7 @@ trait Types extends api.Types { self: SymbolTable =>
87
87
/** Decrement depth unless it is a don't care. */
88
88
private final def decr (depth : Int ) = if (depth == AnyDepth ) AnyDepth else depth - 1
89
89
90
- private final val printLubs = sys.props contains " scala .debug.lub"
90
+ private final val printLubs = sys.props contains " scalac .debug.lub"
91
91
/** In case anyone wants to turn off lub verification without reverting anything. */
92
92
private final val verifyLubs = true
93
93
@@ -2390,13 +2390,20 @@ A type's typeSymbol should never be inspected directly.
2390
2390
new TypeVar (origin, constr, args, params)
2391
2391
}
2392
2392
2393
- /** A class representing a type variable
2394
- * Not used after phase `typer`.
2395
- * A higher-kinded type variable has type arguments (a list of Type's) and type parameters (list of Symbols)
2396
- * A TypeVar whose list of args is non-empty can only be instantiated by a higher-kinded type that can be applied to these args
2397
- * a typevar is much like a typeref, except it has special logic for type equality/subtyping
2393
+ /** A class representing a type variable: not used after phase `typer`.
2394
+ *
2395
+ * A higher-kinded TypeVar has params (Symbols) and typeArgs (Types).
2396
+ * A TypeVar with nonEmpty typeArgs can only be instantiated by a higher-kinded
2397
+ * type that can be applied to those args. A TypeVar is much like a TypeRef,
2398
+ * except it has special logic for equality and subtyping.
2398
2399
*/
2399
- class TypeVar (val origin : Type , val constr0 : TypeConstraint , override val typeArgs : List [Type ], override val params : List [Symbol ]) extends Type {
2400
+ class TypeVar (
2401
+ val origin : Type ,
2402
+ val constr0 : TypeConstraint ,
2403
+ override val typeArgs : List [Type ],
2404
+ override val params : List [Symbol ]
2405
+ ) extends Type {
2406
+ private val numArgs = typeArgs.length
2400
2407
// params are needed to keep track of variance (see mapOverArgs in SubstMap)
2401
2408
assert(typeArgs.isEmpty || sameLength(typeArgs, params))
2402
2409
// var tid = { tidCount += 1; tidCount } //DEBUG
@@ -2409,14 +2416,16 @@ A type's typeSymbol should never be inspected directly.
2409
2416
val level = skolemizationLevel
2410
2417
2411
2418
/** Two occurrences of a higher-kinded typevar, e.g. `?CC[Int]` and `?CC[String]`, correspond to
2412
- * ''two instances'' of `TypeVar` that share the ''same'' `TypeConstraint`
2413
- * `constr` for `?CC` only tracks type constructors anyway, so when `?CC[Int] <:< List[Int]` and `?CC[String] <:< Iterable[String]`
2414
- * `?CC's` hibounds contains List and Iterable
2419
+ * ''two instances'' of `TypeVar` that share the ''same'' `TypeConstraint`.
2420
+ *
2421
+ * `constr` for `?CC` only tracks type constructors anyway,
2422
+ * so when `?CC[Int] <:< List[Int]` and `?CC[String] <:< Iterable[String]`
2423
+ * `?CC's` hibounds contains List and Iterable.
2415
2424
*/
2416
2425
def applyArgs (newArgs : List [Type ]): TypeVar =
2417
2426
if (newArgs.isEmpty) this // SubstMap relies on this (though this check is redundant when called from appliedType...)
2418
2427
else TypeVar (origin, constr, newArgs, params) // @M TODO: interaction with undoLog??
2419
- // newArgs.length may differ from args.length (could've been empty before)
2428
+ // newArgs.length may differ from args.length (could've been empty before)
2420
2429
// example: when making new typevars, you start out with C[A], then you replace C by ?C, which should yield ?C[A], then A by ?A, ?C[?A]
2421
2430
// we need to track a TypeVar's arguments, and map over them (see TypeMap::mapOver)
2422
2431
// TypeVars get applied to different arguments over time (in asSeenFrom)
@@ -2427,15 +2436,16 @@ A type's typeSymbol should never be inspected directly.
2427
2436
// they share the same TypeConstraint instance
2428
2437
2429
2438
// <region name="constraint mutators + undoLog">
2430
- // invariant: before mutating constr, save old state in undoLog (undoLog is used to reset constraints to avoid piling up unrelated ones)
2439
+ // invariant: before mutating constr, save old state in undoLog
2440
+ // (undoLog is used to reset constraints to avoid piling up unrelated ones)
2431
2441
def setInst (tp : Type ) {
2432
2442
// assert(!(tp containsTp this), this)
2433
2443
undoLog record this
2434
2444
constr.inst = tp
2435
2445
}
2436
2446
2437
2447
def addLoBound (tp : Type , isNumericBound : Boolean = false ) {
2438
- assert(tp != this ) // implies there is a cycle somewhere (?)
2448
+ assert(tp != this , tp ) // implies there is a cycle somewhere (?)
2439
2449
// println("addLoBound: "+(safeToString, debugString(tp))) //DEBUG
2440
2450
undoLog record this
2441
2451
constr.addLoBound(tp, isNumericBound)
@@ -2463,22 +2473,21 @@ A type's typeSymbol should never be inspected directly.
2463
2473
*/
2464
2474
def registerBound (tp : Type , isLowerBound : Boolean , isNumericBound : Boolean = false ): Boolean = {
2465
2475
// println("regBound: "+(safeToString, debugString(tp), isLowerBound)) //@MDEBUG
2466
- if (isLowerBound) assert(tp != this )
2467
-
2468
- def checkSubtypeLower (tp1 : Type , tp2 : Type ) =
2469
- if (isNumericBound) tp1 weak_<:< tp2
2470
- else tp1 <:< tp2
2476
+ if (isLowerBound)
2477
+ assert(tp != this )
2471
2478
2472
- // swaps the arguments if it's an upper bound
2473
- def checkSubtype (tp1 : Type , tp2 : Type ) =
2474
- if (isLowerBound) checkSubtypeLower(tp1, tp2)
2475
- else checkSubtypeLower(tp2, tp1)
2476
-
2477
- def addBound (tp : Type ) = {
2479
+ // side effect: adds the type to upper or lower bounds
2480
+ def addBound (tp : Type ) {
2478
2481
if (isLowerBound) addLoBound(tp, isNumericBound)
2479
2482
else addHiBound(tp, isNumericBound)
2480
- // println("addedBound: "+(this, tp)) // @MDEBUG
2481
- true
2483
+ }
2484
+ // swaps the arguments if it's an upper bound
2485
+ def checkSubtype (tp1 : Type , tp2 : Type ) = {
2486
+ val lhs = if (isLowerBound) tp1 else tp2
2487
+ val rhs = if (isLowerBound) tp2 else tp1
2488
+
2489
+ if (isNumericBound) lhs weak_<:< rhs
2490
+ else lhs <:< rhs
2482
2491
}
2483
2492
2484
2493
/** Simple case: type arguments can be ignored, because either this typevar has
@@ -2494,8 +2503,12 @@ A type's typeSymbol should never be inspected directly.
2494
2503
* ?TC[?T] <: Any
2495
2504
* }}}
2496
2505
*/
2497
- def unifySimple = (params.isEmpty || tp.typeSymbol == NothingClass || tp.typeSymbol == AnyClass ) &&
2498
- addBound(tp)
2506
+ def unifySimple = (
2507
+ (params.isEmpty || tp.typeSymbol == NothingClass || tp.typeSymbol == AnyClass ) && {
2508
+ addBound(tp)
2509
+ true
2510
+ }
2511
+ )
2499
2512
2500
2513
/** Full case: involving a check of the form
2501
2514
* {{{
@@ -2504,27 +2517,67 @@ A type's typeSymbol should never be inspected directly.
2504
2517
* Checks subtyping of higher-order type vars, and uses variances as defined in the
2505
2518
* type parameter we're trying to infer (the result will be sanity-checked later).
2506
2519
*/
2507
- def unifyFull (tp : Type ) = sameLength(typeArgs, tp.typeArgs) && { // this is a higher-kinded type var with same arity as tp
2508
- // side effect: adds the type constructor itself as a bound
2509
- addBound(tp.typeConstructor)
2510
- if (isLowerBound) isSubArgs(tp.typeArgs, typeArgs, params)
2511
- else isSubArgs(typeArgs, tp.typeArgs, params)
2520
+ def unifyFull (tpe : Type ) = {
2521
+ // Since the alias/widen variations are often no-ops, this
2522
+ // keenly collects them in a Set to avoid redundant tests.
2523
+ val tpes = (
2524
+ if (isLowerBound) Set (tpe, tpe.widen, tpe.dealias, tpe.widen.dealias)
2525
+ else Set (tpe)
2526
+ )
2527
+ tpes exists { tp =>
2528
+ val lhs = if (isLowerBound) tp.typeArgs else typeArgs
2529
+ val rhs = if (isLowerBound) typeArgs else tp.typeArgs
2530
+
2531
+ sameLength(lhs, rhs) && {
2532
+ // this is a higher-kinded type var with same arity as tp.
2533
+ // side effect: adds the type constructor itself as a bound
2534
+ addBound(tp.typeConstructor)
2535
+ isSubArgs(lhs, rhs, params)
2536
+ }
2537
+ }
2512
2538
}
2513
2539
2514
- /** TODO: need positive/negative test cases demonstrating this is correct. */
2515
- def unifyParents =
2516
- if (isLowerBound) tp.parents exists unifyFull
2517
- else tp.parents forall unifyFull
2518
-
2519
- // TODO: fancier unification, maybe rewrite constraint as follows?
2520
- // val sym = constr.hiBounds map {_.typeSymbol} find { _.typeParams.length == typeArgs.length}
2521
- // this <: tp.baseType(sym)
2522
- if (suspended) checkSubtype(tp, origin)
2523
- else if (constr.instValid) checkSubtype(tp, constr.inst) // type var is already set
2524
- else isRelatable(tp) && { // gradually let go of some type precision in hopes of finding a type that has the same shape as the type variable
2525
- // okay, this just screams "CLEAN ME UP" -- I think we could use tp.widen instead of tp straight from the get-go in registerBound, since we don't infer singleton types anyway (but maybe that'll change?)
2526
- unifySimple || unifyFull(tp) || unifyFull(tp.dealias) || unifyFull(tp.widen) || unifyFull(tp.widen.dealias) || unifyParents
2527
- }
2540
+ // There's a <: test taking place right now, where tp is a concrete type and this is a typevar
2541
+ // attempting to satisfy that test. Either the test will be unsatisfiable, in which case
2542
+ // registerBound will return false; or the upper or lower bounds of this type var will be
2543
+ // supplemented with the type being tested against.
2544
+ //
2545
+ // Eventually the types which have accumulated in the upper and lower bounds will be lubbed
2546
+ // (resp. glbbed) to instantiate the typevar.
2547
+ //
2548
+ // The only types which are eligible for unification are those with the same number of
2549
+ // typeArgs as this typevar, or Any/Nothing, which are kind-polymorphic. For the upper bound,
2550
+ // any parent or base type of `tp` may be tested here (leading to a corresponding relaxation
2551
+ // in the upper bound.) The universe of possible glbs, being somewhat more infinite, is not
2552
+ // addressed here: all lower bounds are retained and their intersection calculated when the
2553
+ // bounds are solved.
2554
+ //
2555
+ // In a side-effect free universe, checking tp and tp.parents beofre checking tp.baseTypeSeq
2556
+ // would be pointless. In this case, each check we perform causes us to lose specificity: in
2557
+ // the end the best we'll do is the least specific type we tested against, since the typevar
2558
+ // does not see these checks as "probes" but as requirements to fulfill.
2559
+ //
2560
+ // So the strategy used here is to test first the type, then the direct parents, and finally
2561
+ // to fall back on the individual base types. This warrants eventual re-examination.
2562
+
2563
+ if (suspended) // constraint accumulation is disabled
2564
+ checkSubtype(tp, origin)
2565
+ else if (constr.instValid) // type var is already set
2566
+ checkSubtype(tp, constr.inst)
2567
+ else
2568
+ // isRelatable checks for type skolems which cannot be understood at this level
2569
+ isRelatable(tp) && (
2570
+ unifySimple || unifyFull(tp) || (
2571
+ // only look harder if our gaze is oriented toward Any
2572
+ isLowerBound && (
2573
+ (tp.parents exists unifyFull) || (
2574
+ // @PP: Is it going to be faster to filter out the parents we just checked?
2575
+ // That's what's done here but I'm not sure it matters.
2576
+ tp.baseTypeSeq.toList.tail filterNot (tp.parents contains _) exists unifyFull
2577
+ )
2578
+ )
2579
+ )
2580
+ )
2528
2581
}
2529
2582
2530
2583
def registerTypeEquality (tp : Type , typeVarLHS : Boolean ): Boolean = {
0 commit comments