aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TyperState.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-05-09 18:55:10 +0200
committerSamuel Gruetter <samuel.gruetter@epfl.ch>2014-05-20 13:38:49 +0200
commit394b645dd2d44ff68597527c6c690c73653f1bcb (patch)
tree22e72a22f891c002d5fef946361d223d6f6fc1a4 /src/dotty/tools/dotc/core/TyperState.scala
parent67e5130e63b10631106490cbc1cec5adcd5273e6 (diff)
downloaddotty-394b645dd2d44ff68597527c6c690c73653f1bcb.tar.gz
dotty-394b645dd2d44ff68597527c6c690c73653f1bcb.tar.bz2
dotty-394b645dd2d44ff68597527c6c690c73653f1bcb.zip
Fix of pos/t2429
This was a hard nut to crack. The problem exemplified by t2429 is that in a situation like val x: T = foo(...) where `foo` needs implicit parameters the expected result type `T` is propagated into the typechecking of foo(...) and consequently also into the implicit parameter search. This is often necessary, for instance to instantiate type parameters. But it can lead to overconstrained searches if in fact the right expansion is val x: T = viewToT(foo(...)(implicit params)) where `viewToT` is some implicit conversion. The fix handles that case by re-hecking the application foo(...) with an empty result type, if the implicit parameter search fails. But the re-checking is very subtle as is explained in the comment to `TyperState#tryWithFallback`.
Diffstat (limited to 'src/dotty/tools/dotc/core/TyperState.scala')
-rw-r--r--src/dotty/tools/dotc/core/TyperState.scala54
1 files changed, 51 insertions, 3 deletions
diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala
index 59c934e0d..6a3ac4467 100644
--- a/src/dotty/tools/dotc/core/TyperState.scala
+++ b/src/dotty/tools/dotc/core/TyperState.scala
@@ -11,7 +11,10 @@ import printing.{Showable, Printer}
import printing.Texts._
import collection.mutable
-class TyperState(val reporter: Reporter) extends DotClass with Showable {
+class TyperState(r: Reporter) extends DotClass with Showable {
+
+ /** The current reporter */
+ def reporter = r
/** The current constraint set */
def constraint: Constraint = new Constraint(SimpleMap.Empty, SimpleMap.Empty)
@@ -56,11 +59,17 @@ class TyperState(val reporter: Reporter) extends DotClass with Showable {
/** Can this state be transitively committed until the top-level? */
def isGlobalCommittable: Boolean = false
+ def tryWithFallback[T](op: => T)(fallback: => T)(implicit ctx: Context): T = unsupported("tryWithFallBack")
+
override def toText(printer: Printer): Text = "ImmutableTyperState"
}
-class MutableTyperState(previous: TyperState, reporter: Reporter, override val isCommittable: Boolean)
-extends TyperState(reporter) {
+class MutableTyperState(previous: TyperState, r: Reporter, override val isCommittable: Boolean)
+extends TyperState(r) {
+
+ private var myReporter = r
+
+ override def reporter = myReporter
private var myConstraint: Constraint = previous.constraint
@@ -112,5 +121,44 @@ extends TyperState(reporter) {
constraint = constraint.remove(poly)
}
+ /** Try operation `op`; if it produces errors, execute `fallback` with constraint and
+ * reporter as they were before `op` was executed. This is similar to `typer/tryEither`,
+ * but with one important difference: Any type variable instantiations produced by `op`
+ * are persisted even if `op` fails. This is normally not what one wants and therefore
+ * it is recommended to use
+ *
+ * tryEither { implicit ctx => op } { (_, _) => fallBack }
+ *
+ * instead of
+ *
+ * ctx.tryWithFallback(op)(fallBack)
+ *
+ * `tryWithFallback` is only used when an implicit parameter search fails
+ * and the whole expression is subsequently retype-checked with a Wildcard
+ * expected type (so as to allow an implicit conversion on the result and
+ * avoid over-constraining the implicit parameter search). In this case,
+ * the only type variables that might be falsely instantiated by `op` but
+ * not by `fallBack` are type variables in the typed expression itself, and
+ * these will be thrown away and new ones will be created on re-typing.
+ * So `tryWithFallback` is safe. It is also necessary because without it
+ * we do not propagate enough instantiation information into the implicit search
+ * and this might lead to a missing parameter type error. This is exhibited
+ * at several places in the test suite (for instance in `pos_typers`).
+ * Overall, this is rather ugly, but despite trying for 2 days I have not
+ * found a better solution.
+ */
+ override def tryWithFallback[T](op: => T)(fallback: => T)(implicit ctx: Context): T = {
+ val savedReporter = myReporter
+ val savedConstraint = myConstraint
+ myReporter = new StoreReporter
+ val result = op
+ if (!reporter.hasErrors) result
+ else {
+ myReporter = savedReporter
+ myConstraint = savedConstraint
+ fallback
+ }
+ }
+
override def toText(printer: Printer): Text = constraint.toText(printer)
}