Skip to content

Commit c608177

Browse files
Do constraint.replace when addOneBound produces equal bounds
as an optimization In #20120, we reach constraints with equal bounds that are intersection types, they are formed from multiple successive calls to `addOneBound`. We miss the `replace` optimization in this case because the bounds only become equal progressively, and we are only checking for equality with the constraint being added. Additionally, we recheck for equal bounds after `constraint.updateEntry` as checking `isSub` can have narrowed the bounds further. #19955 is an example where this second optimization applies. Fix #20120 Close #20208 the original implementation
1 parent 73bb2aa commit c608177

File tree

1 file changed

+33
-23
lines changed

1 file changed

+33
-23
lines changed

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

+33-23
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ trait ConstraintHandling {
120120
*/
121121
private var myTrustBounds = true
122122

123-
inline def withUntrustedBounds(op: => Type): Type =
123+
transparent inline def withUntrustedBounds(op: => Type): Type =
124124
val saved = myTrustBounds
125125
myTrustBounds = false
126126
try op finally myTrustBounds = saved
@@ -301,34 +301,44 @@ trait ConstraintHandling {
301301
// so we shouldn't allow them as constraints either.
302302
false
303303
else
304-
val bound = legalBound(param, rawBound, isUpper)
305-
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
306-
val equalBounds = (if isUpper then lo else hi) eq bound
307-
if equalBounds && !bound.existsPart(_ eq param, StopAt.Static) then
308-
// The narrowed bounds are equal and not recursive,
309-
// so we can remove `param` from the constraint.
310-
constraint = constraint.replace(param, bound)
311-
true
312-
else
313-
// Narrow one of the bounds of type parameter `param`
314-
// If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
315-
// that `param >: bound`.
316-
val narrowedBounds =
317-
val saved = homogenizeArgs
318-
homogenizeArgs = Config.alignArgsInAnd
319-
try
320-
withUntrustedBounds(
321-
if isUpper then oldBounds.derivedTypeBounds(lo, hi & bound)
322-
else oldBounds.derivedTypeBounds(lo | bound, hi))
323-
finally
324-
homogenizeArgs = saved
304+
305+
// Narrow one of the bounds of type parameter `param`
306+
// If `isUpper` is true, ensure that `param <: `bound`,
307+
// otherwise ensure that `param >: bound`.
308+
val narrowedBounds: TypeBounds =
309+
val bound = legalBound(param, rawBound, isUpper)
310+
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
311+
312+
val saved = homogenizeArgs
313+
homogenizeArgs = Config.alignArgsInAnd
314+
try
315+
withUntrustedBounds(
316+
if isUpper then oldBounds.derivedTypeBounds(lo, hi & bound)
317+
else oldBounds.derivedTypeBounds(lo | bound, hi))
318+
finally
319+
homogenizeArgs = saved
320+
end narrowedBounds
321+
322+
// If the narrowed bounds are equal and not recursive,
323+
// we can remove `param` from the constraint.
324+
def tryReplace(newBounds: TypeBounds): Boolean =
325+
val TypeBounds(lo, hi) = newBounds
326+
val canReplace = (lo eq hi) && !newBounds.existsPart(_ eq param, StopAt.Static)
327+
if canReplace then constraint = constraint.replace(param, lo)
328+
canReplace
329+
330+
tryReplace(narrowedBounds) || locally:
325331
//println(i"narrow bounds for $param from $oldBounds to $narrowedBounds")
326332
val c1 = constraint.updateEntry(param, narrowedBounds)
327333
(c1 eq constraint)
328334
|| {
329335
constraint = c1
330336
val TypeBounds(lo, hi) = constraint.entry(param): @unchecked
331-
isSub(lo, hi)
337+
val isSat = isSub(lo, hi)
338+
if isSat then
339+
// isSub may have narrowed the bounds further
340+
tryReplace(constraint.nonParamBounds(param))
341+
isSat
332342
}
333343
end addOneBound
334344

0 commit comments

Comments
 (0)