diff options
Diffstat (limited to 'src/dotty/tools/dotc/core/TyperState.scala')
-rw-r--r-- | src/dotty/tools/dotc/core/TyperState.scala | 54 |
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) } |