Skip to content

Commit badc855

Browse files
authored
Merge pull request #3275 from dotty-staging/fix-#3253-2
Fix #3252: Generalize constraint handling when harmonizing
2 parents 28cfb1a + 86d4e73 commit badc855

File tree

3 files changed

+46
-19
lines changed

3 files changed

+46
-19
lines changed

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

+18-10
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
}
@@ -1506,6 +1497,23 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15061497
if (ctx.isAfterTyper) trees else harmonizeWith(trees)(_.tpe, adapt)
15071498
}
15081499

1500+
/** Apply a transformation `harmonize` on the results of operation `op`.
1501+
* If the result is different (wrt eq) from the original results of `op`,
1502+
* revert back to the constraint in force before computing `op`.
1503+
* This reset is needed because otherwise the original results might
1504+
* have added constraints to type parameters which are no longer
1505+
* implied after harmonization. No essential constraints are lost by this because
1506+
* the result of harmomization will be compared again with the expected type.
1507+
* Test cases where this matters are in pos/harmomize.scala.
1508+
*/
1509+
def harmonic[T](harmonize: List[T] => List[T])(op: => List[T])(implicit ctx: Context) = {
1510+
val origConstraint = ctx.typerState.constraint
1511+
val origElems = op
1512+
val harmonizedElems = harmonize(origElems)
1513+
if (harmonizedElems ne origElems) ctx.typerState.constraint = origConstraint
1514+
harmonizedElems
1515+
}
1516+
15091517
/** If all `types` are numeric value types, and they are not all the same type,
15101518
* pick a common numeric supertype and widen any constant types in `tpes` to it.
15111519
* If the resulting types are all the same, return them instead of the original ones.

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

+13-9
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

+15
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)