aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TyperState.scala
diff options
context:
space:
mode:
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)
}