Skip to content

Commit 1d237bb

Browse files
committed
Make alignArgsInAnd safe and turn it on by default
Turned out hmaps.scala requires the arg alignment to compile. So we have our first counterexample that we cannot drop this hack. Now it is made safe in the sense that no constraints get lost anymore.
1 parent 58b71ca commit 1d237bb

File tree

5 files changed

+30
-28
lines changed

5 files changed

+30
-28
lines changed

compiler/src/dotty/tools/dotc/config/Config.scala

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,14 @@ object Config {
7575
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
7676
final val fastPathForRefinedSubtype = true
7777

78-
/** If this flag is set, and we compute `T1 { X = S1 }` & `T2 { X = S2 }`,
79-
* try to align the refinements by computing `S1 =:= S2` (which might instantiate type parameters).
80-
* This rule is contentious because it cuts the constraint set. Also, it is
81-
* currently unsound because `&` gets called in computations on a constraint
82-
* itself. If the `=:=` test generates a new constraint, that constraint is then
83-
* out of sync with with the constraint on which the computation is performed.
84-
* The constraint resulting from `=:=` ends up to be thrown away whereas
85-
* its result is used, which is unsound. So if we want to turn this flag on
86-
* permanently instead of just for debugging, we have to refactor occurrences
87-
* of `&` in `OrderedConstraint` so that they take the `=:=` result into account.
78+
/** If this flag is set, and we compute `T1 { X = S1 }` & `T2 { X = S2 }` as a new
79+
* upper bound of a constrained parameter, try to align the refinements by computing
80+
* `S1 =:= S2` (which might instantiate type parameters).
81+
* This rule is contentious because it cuts the constraint set.
8882
*
8983
* For more info, see the comment in `TypeComparer#distributeAnd`.
9084
*/
91-
final val alignArgsInAnd = false
85+
final val alignArgsInAnd = true
9286

9387
/** If this flag is set, higher-kinded applications are checked for validity
9488
*/

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,6 @@ abstract class Constraint extends Showable {
111111
*/
112112
def replace(param: PolyParam, tp: Type)(implicit ctx: Context): This
113113

114-
/** Narrow one of the bounds of type parameter `param`
115-
* If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
116-
* that `param >: bound`.
117-
*/
118-
def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This
119-
120114
/** Is entry associated with `pt` removable? This is the case if
121115
* all type parameters of the entry are associated with type variables
122116
* which have their `inst` fields set.

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ trait ConstraintHandling {
4444
try op finally alwaysFluid = saved
4545
}
4646

47+
/** If set, align arguments `S1`, `S2`when taking the glb
48+
* `T1 { X = S1 } & T2 { X = S2 }` of a constraint upper bound for some type parameter.
49+
* Aligning means computing `S1 =:= S2` which may change the current constraint.
50+
* See note in TypeComparer#distributeAnd.
51+
*/
52+
protected var homogenizeArgs = false
53+
4754
/** We are currently comparing polytypes. Used as a flag for
4855
* optimization: when `false`, no need to do an expensive `pruneLambdaParams`
4956
*/
@@ -64,14 +71,29 @@ trait ConstraintHandling {
6471
}
6572
if (Config.checkConstraintsSeparated)
6673
assert(!occursIn(bound), s"$param occurs in $bound")
67-
val c1 = constraint.narrowBound(param, bound, isUpper)
74+
val newBound = narrowedBound(param, bound, isUpper)
75+
val c1 = constraint.updateEntry(param, newBound)
6876
(c1 eq constraint) || {
6977
constraint = c1
7078
val TypeBounds(lo, hi) = constraint.entry(param)
7179
isSubType(lo, hi)
7280
}
7381
}
7482

83+
/** Narrow one of the bounds of type parameter `param`
84+
* If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
85+
* that `param >: bound`.
86+
*/
87+
def narrowedBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = {
88+
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
89+
val saved = homogenizeArgs
90+
homogenizeArgs = Config.alignArgsInAnd
91+
try
92+
if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
93+
else oldBounds.derivedTypeBounds(lo | bound, hi)
94+
finally homogenizeArgs = saved
95+
}
96+
7597
protected def addUpperBound(param: PolyParam, bound: Type): Boolean = {
7698
def description = i"constraint $param <: $bound to\n$constraint"
7799
if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) {

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -354,14 +354,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
354354
updateEntry(p1, p1Bounds).replace(p2, p1)
355355
}
356356

357-
def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This = {
358-
val oldBounds @ TypeBounds(lo, hi) = nonParamBounds(param)
359-
val newBounds =
360-
if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
361-
else oldBounds.derivedTypeBounds(lo | bound, hi)
362-
updateEntry(param, newBounds)
363-
}
364-
365357
// ---------- Removals ------------------------------------------------------------
366358

367359
/** A new constraint which is derived from this constraint by removing

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,7 +1315,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
13151315
// Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }` rwrite to
13161316
// `T1 & T2 { X B }` where `B` is the conjunction of the bounds of `X` in `T1` and `T2`.
13171317
//
1318-
// However, if `Config.alignArgsInAnd` is set, and both aliases `X = Si` are
1318+
// However, if `homogenizeArgs` is set, and both aliases `X = Si` are
13191319
// nonvariant, and `S1 =:= S2` (possibly by instantiating type parameters),
13201320
// rewrite instead to `T1 & T2 { X = S1 }`. This rule is contentious because
13211321
// it cuts the constraint set. On the other hand, without it we would replace
@@ -1329,7 +1329,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
13291329
case tp: TypeAlias => tp.variance == 0
13301330
case _ => false
13311331
}
1332-
if (Config.alignArgsInAnd &&
1332+
if (homogenizeArgs &&
13331333
isNonvariantAlias(rinfo1) && isNonvariantAlias(rinfo2))
13341334
isSameType(rinfo1, rinfo2)
13351335

0 commit comments

Comments
 (0)