Skip to content

Commit 86d4e73

Browse files
committed
Generalize constraint handling when harmonizing
When harmonizing repeated arguments we needed to backtrack the constraint. It turns out we need to do the same thing in all other situations where harmonization takes place.
1 parent c50c164 commit 86d4e73

File tree

3 files changed

+46
-19
lines changed

3 files changed

+46
-19
lines changed

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -438,16 +438,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
438438
addTyped(arg, formal)
439439
case _ =>
440440
val elemFormal = formal.widenExpr.argTypesLo.head
441-
val origConstraint = ctx.typerState.constraint
442-
var typedArgs = args.map(typedArg(_, elemFormal))
443-
val harmonizedArgs = harmonizeArgs(typedArgs)
444-
if (harmonizedArgs ne typedArgs) {
445-
ctx.typerState.constraint = origConstraint
446-
// reset constraint, we will re-establish constraint anyway when we
447-
// compare against the seqliteral. The reset is needed
448-
// otherwise pos/harmonize.scala would fail on line 40.
449-
typedArgs = harmonizedArgs
450-
}
441+
val typedArgs = harmonic(harmonizeArgs)(args.map(typedArg(_, elemFormal)))
451442
typedArgs.foreach(addArg(_, elemFormal))
452443
makeVarArg(args.length, elemFormal)
453444
}
@@ -1494,6 +1485,23 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14941485
if (ctx.isAfterTyper) trees else harmonizeWith(trees)(_.tpe, adapt)
14951486
}
14961487

1488+
/** Apply a transformation `harmonize` on the results of operation `op`.
1489+
* If the result is different (wrt eq) from the original results of `op`,
1490+
* revert back to the constraint in force before computing `op`.
1491+
* This reset is needed because otherwise the original results might
1492+
* have added constraints to type parameters which are no longer
1493+
* implied after harmonization. No essential constraints are lost by this because
1494+
* the result of harmomization will be compared again with the expected type.
1495+
* Test cases where this matters are in pos/harmomize.scala.
1496+
*/
1497+
def harmonic[T](harmonize: List[T] => List[T])(op: => List[T])(implicit ctx: Context) = {
1498+
val origConstraint = ctx.typerState.constraint
1499+
val origElems = op
1500+
val harmonizedElems = harmonize(origElems)
1501+
if (harmonizedElems ne origElems) ctx.typerState.constraint = origConstraint
1502+
harmonizedElems
1503+
}
1504+
14971505
/** If all `types` are numeric value types, and they are not all the same type,
14981506
* pick a common numeric supertype and widen any constant types in `tpes` to it.
14991507
* If the resulting types are all the same, return them instead of the original ones.

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -674,9 +674,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
674674

675675
def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = track("typedIf") {
676676
val cond1 = typed(tree.cond, defn.BooleanType)
677-
val thenp1 = typed(tree.thenp, pt.notApplied)
678-
val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)
679-
val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil)
677+
val thenp2 :: elsep2 :: Nil = harmonic(harmonize) {
678+
val thenp1 = typed(tree.thenp, pt.notApplied)
679+
val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)
680+
thenp1 :: elsep1 :: Nil
681+
}
680682
assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2)
681683
}
682684

@@ -884,9 +886,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
884886
val sel1 = typedExpr(tree.selector)
885887
val selType = fullyDefinedType(sel1.tpe, "pattern selector", tree.pos).widen
886888

887-
val cases1 = typedCases(tree.cases, selType, pt.notApplied)
888-
val cases2 = harmonize(cases1).asInstanceOf[List[CaseDef]]
889-
assignType(cpy.Match(tree)(sel1, cases2), cases2)
889+
val cases1 = harmonic(harmonize)(typedCases(tree.cases, selType, pt.notApplied))
890+
.asInstanceOf[List[CaseDef]]
891+
assignType(cpy.Match(tree)(sel1, cases1), cases1)
890892
}
891893
}
892894

@@ -1011,10 +1013,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
10111013
}
10121014

10131015
def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") {
1014-
val expr1 = typed(tree.expr, pt.notApplied)
1015-
val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied)
1016+
val expr2 :: cases2x = harmonic(harmonize) {
1017+
val expr1 = typed(tree.expr, pt.notApplied)
1018+
val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied)
1019+
expr1 :: cases1
1020+
}
10161021
val finalizer1 = typed(tree.finalizer, defn.UnitType)
1017-
val expr2 :: cases2x = harmonize(expr1 :: cases1)
10181022
val cases2 = cases2x.asInstanceOf[List[CaseDef]]
10191023
assignType(cpy.Try(tree)(expr2, cases2, finalizer1), expr2, cases2)
10201024
}

tests/pos/harmonize.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,19 @@ object Test {
5555
val b5: Array[AnyVal] = a5
5656
val a6 = Array(1.0f, 1234567890)
5757
val b6: Array[AnyVal] = a6
58+
59+
def totalDuration(results: List[Long], cond: Boolean): Long =
60+
results.map(r => if (cond) r else 0).sum
61+
def totalDuration2(results: List[Long], cond: Boolean): Long =
62+
results.map{ r =>
63+
cond match {
64+
case true => r
65+
case false => 0
66+
}
67+
}.sum
68+
def totalDuration3(results: List[Long], cond: Boolean): Long =
69+
results.map{ r =>
70+
try r
71+
catch { case ex: Exception => 0 }
72+
}.sum
5873
}

0 commit comments

Comments
 (0)