Skip to content

Fix #3252: Generalize constraint handling when harmonizing #3275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -438,16 +438,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
addTyped(arg, formal)
case _ =>
val elemFormal = formal.widenExpr.argTypesLo.head
val origConstraint = ctx.typerState.constraint
var typedArgs = args.map(typedArg(_, elemFormal))
val harmonizedArgs = harmonizeArgs(typedArgs)
if (harmonizedArgs ne typedArgs) {
ctx.typerState.constraint = origConstraint
// reset constraint, we will re-establish constraint anyway when we
// compare against the seqliteral. The reset is needed
// otherwise pos/harmonize.scala would fail on line 40.
typedArgs = harmonizedArgs
}
val typedArgs = harmonic(harmonizeArgs)(args.map(typedArg(_, elemFormal)))
typedArgs.foreach(addArg(_, elemFormal))
makeVarArg(args.length, elemFormal)
}
Expand Down Expand Up @@ -1494,6 +1485,23 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
if (ctx.isAfterTyper) trees else harmonizeWith(trees)(_.tpe, adapt)
}

/** Apply a transformation `harmonize` on the results of operation `op`.
* If the result is different (wrt eq) from the original results of `op`,
* revert back to the constraint in force before computing `op`.
* This reset is needed because otherwise the original results might
* have added constraints to type parameters which are no longer
* implied after harmonization. No essential constraints are lost by this because
* the result of harmomization will be compared again with the expected type.
* Test cases where this matters are in pos/harmomize.scala.
*/
def harmonic[T](harmonize: List[T] => List[T])(op: => List[T])(implicit ctx: Context) = {
val origConstraint = ctx.typerState.constraint
val origElems = op
val harmonizedElems = harmonize(origElems)
if (harmonizedElems ne origElems) ctx.typerState.constraint = origConstraint
harmonizedElems
}

/** If all `types` are numeric value types, and they are not all the same type,
* pick a common numeric supertype and widen any constant types in `tpes` to it.
* If the resulting types are all the same, return them instead of the original ones.
Expand Down
22 changes: 13 additions & 9 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -674,9 +674,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit

def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = track("typedIf") {
val cond1 = typed(tree.cond, defn.BooleanType)
val thenp1 = typed(tree.thenp, pt.notApplied)
val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)
val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil)
val thenp2 :: elsep2 :: Nil = harmonic(harmonize) {
val thenp1 = typed(tree.thenp, pt.notApplied)
val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)
thenp1 :: elsep1 :: Nil
}
assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2)
}

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

val cases1 = typedCases(tree.cases, selType, pt.notApplied)
val cases2 = harmonize(cases1).asInstanceOf[List[CaseDef]]
assignType(cpy.Match(tree)(sel1, cases2), cases2)
val cases1 = harmonic(harmonize)(typedCases(tree.cases, selType, pt.notApplied))
.asInstanceOf[List[CaseDef]]
assignType(cpy.Match(tree)(sel1, cases1), cases1)
}
}

Expand Down Expand Up @@ -1011,10 +1013,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}

def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") {
val expr1 = typed(tree.expr, pt.notApplied)
val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied)
val expr2 :: cases2x = harmonic(harmonize) {
val expr1 = typed(tree.expr, pt.notApplied)
val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied)
expr1 :: cases1
}
val finalizer1 = typed(tree.finalizer, defn.UnitType)
val expr2 :: cases2x = harmonize(expr1 :: cases1)
val cases2 = cases2x.asInstanceOf[List[CaseDef]]
assignType(cpy.Try(tree)(expr2, cases2, finalizer1), expr2, cases2)
}
Expand Down
15 changes: 15 additions & 0 deletions tests/pos/harmonize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,19 @@ object Test {
val b5: Array[AnyVal] = a5
val a6 = Array(1.0f, 1234567890)
val b6: Array[AnyVal] = a6

def totalDuration(results: List[Long], cond: Boolean): Long =
results.map(r => if (cond) r else 0).sum
def totalDuration2(results: List[Long], cond: Boolean): Long =
results.map{ r =>
cond match {
case true => r
case false => 0
}
}.sum
def totalDuration3(results: List[Long], cond: Boolean): Long =
results.map{ r =>
try r
catch { case ex: Exception => 0 }
}.sum
}