From af0336ac36176f0b336df56add8fa6c700ebb1ef Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Jul 2017 18:58:06 +0200 Subject: [PATCH 1/4] Extra condition to harden IDE We observed a crash in ensureConforms when running the IDE because the expected type was not a value type. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c8f2c26874ad..77baa1fe3931 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -940,8 +940,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def caseRest(pat: Tree)(implicit ctx: Context) = { val pat1 = indexPattern.transform(pat) val guard1 = typedExpr(tree.guard, defn.BooleanType) - val body1 = ensureNoLocalRefs(typedExpr(tree.body, pt), pt, ctx.scope.toList) - .ensureConforms(pt)(originalCtx) // insert a cast if body does not conform to expected type if we disregard gadt bounds + var body1 = ensureNoLocalRefs(typedExpr(tree.body, pt), pt, ctx.scope.toList) + if (pt.isValueType) // insert a cast if body does not conform to expected type if we disregard gadt bounds + body1 = body1.ensureConforms(pt)(originalCtx) assignType(cpy.CaseDef(tree)(pat1, guard1, body1), body1) } @@ -1183,7 +1184,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val pt1 = fullyDefinedType(pt, "pattern variable", tree.pos) val body1 = typed(tree.body, pt1) body1 match { - case UnApply(fn, Nil, arg :: Nil) if fn.symbol.owner == defn.ClassTagClass && !body1.tpe.isError => + case UnApply(fn, Nil, arg :: Nil) + if fn.symbol.exists && fn.symbol.owner == defn.ClassTagClass && !body1.tpe.isError => // A typed pattern `x @ (e: T)` with an implicit `ctag: ClassTag[T]` // was rewritten to `x @ ctag(e)` by `tryWithClassTag`. // Rewrite further to `ctag(x @ e)` From 34a2b96bb5982b9bd7e9d23d7dc828952f07d1e0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Jul 2017 11:32:32 +0200 Subject: [PATCH 2/4] Harden constraint handling in IDE I observed assertion failures, that a param ref is not in the current constraint. Avoiding an assertion failure if that case is encountered in the IDE. --- .../tools/dotc/core/ConstraintHandling.scala | 15 ++++++++++----- compiler/src/dotty/tools/dotc/core/Types.scala | 1 - 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index fa41de5c1d8b..15f24a7fe50c 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -222,11 +222,16 @@ trait ConstraintHandling { } } } - assert(constraint.contains(param)) - val bound = if (fromBelow) constraint.fullLowerBound(param) else constraint.fullUpperBound(param) - val inst = avoidParam(bound) - typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}") - inst + if (constraint.contains(param)) { + val bound = if (fromBelow) constraint.fullLowerBound(param) else constraint.fullUpperBound(param) + val inst = avoidParam(bound) + typr.println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}") + inst + } + else { + assert(ctx.mode.is(Mode.Interactive)) + UnspecifiedErrorType + } } /** The instance type of `param` in the current constraint (which contains `param`). diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b2ae1daa40c0..4b9fce89ef55 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3194,7 +3194,6 @@ object Types { private def instantiateWith(tp: Type)(implicit ctx: Context): Type = { assert(tp ne this, s"self instantiation of ${tp.show}, constraint = ${ctx.typerState.constraint.show}") typr.println(s"instantiating ${this.show} with ${tp.show}") - assert(ctx.typerState.constraint contains this) // !!! DEBUG if ((ctx.typerState eq owningState) && !ctx.typeComparer.subtypeCheckInProgress) inst = tp ctx.typerState.constraint = ctx.typerState.constraint.replace(origin, tp) From c566e608dd6ba7843009f27fe22c17b5ae4ec380 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Jul 2017 14:20:32 +0200 Subject: [PATCH 3/4] Harden findGetter for IDE Crash was observed where the assert is. --- compiler/src/dotty/tools/dotc/typer/Applications.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index ac215ec95f3f..b37e6f4e3bdc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -366,8 +366,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic => else if (cx.scope != cx.outer.scope && cx.denotNamed(meth.name).hasAltWith(_.symbol == meth)) { val denot = cx.denotNamed(getterName) - assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)") - ref(TermRef(cx.owner.thisType, getterName, denot)) + if (denot.exists) ref(TermRef(cx.owner.thisType, getterName, denot)) + else { + assert(ctx.mode.is(Mode.Interactive), + s"non-existent getter denotation ($denot) for getter($getterName)") + findGetter(cx.outer) + } } else findGetter(cx.outer) } findGetter(ctx) From 7c379ce57fc2826ae75a6442b872592479bd5aa5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Jul 2017 10:02:20 +0200 Subject: [PATCH 4/4] Surivive TypeErrors when computing completions The underlying machinery has various ways to throw a TypeError. I observed a MergeError when accidentally writing the same method twice. CyclicReferences are also possible. These should not lead to crashes to the presentation compiler. --- .../src/dotty/tools/dotc/interactive/Interactive.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index 674e712d6499..0eb53856af3c 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -87,9 +87,13 @@ object Interactive { /** Possible completions of members of `prefix` which are accessible when called inside `boundary` */ def completions(prefix: Type, boundary: Symbol)(implicit ctx: Context): List[Symbol] = { val boundaryCtx = ctx.withOwner(boundary) - prefix.memberDenots(completionsFilter, (name, buf) => - buf ++= prefix.member(name).altsWith(_.symbol.isAccessibleFrom(prefix)(boundaryCtx)) - ).map(_.symbol).toList + try + prefix.memberDenots(completionsFilter, (name, buf) => + buf ++= prefix.member(name).altsWith(_.symbol.isAccessibleFrom(prefix)(boundaryCtx)) + ).map(_.symbol).toList + catch { + case ex: TypeError => Nil + } } /** Filter for names that should appear when looking for completions. */