From b63480a59bbdd284a5e32281fbb0037e509f4b1e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 17 Dec 2016 15:09:27 +0100 Subject: [PATCH 1/2] Make errors are not swept under the carpet Typer#ensureReported's comment outlines an example where errors could go unreported, resulting in error trees after typer without any reported error messages. This commit makes sure that at least one error is reported if a tree node has an error type. Fixes #1802. --- .../backend/jvm/DottyBackendInterface.scala | 2 +- .../tools/backend/jvm/scalaPrimitives.scala | 4 +-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 2 +- .../tools/dotc/core/SymDenotations.scala | 11 ++++---- .../src/dotty/tools/dotc/core/Symbols.scala | 7 +++-- .../dotty/tools/dotc/core/TypeComparer.scala | 4 +-- .../dotty/tools/dotc/core/TypeErasure.scala | 6 ++--- .../src/dotty/tools/dotc/core/TypeOps.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 27 +++++++++++-------- .../tools/dotc/parsing/JavaParsers.scala | 3 ++- .../tools/dotc/printing/Formatting.scala | 5 +++- .../tools/dotc/printing/PlainPrinter.scala | 2 +- .../dotty/tools/dotc/typer/Applications.scala | 6 ++--- .../src/dotty/tools/dotc/typer/Checking.scala | 5 ++-- .../src/dotty/tools/dotc/typer/Dynamic.scala | 11 ++++---- .../tools/dotc/typer/ErrorReporting.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 11 ++++---- .../src/dotty/tools/dotc/typer/Typer.scala | 23 ++++++++++++---- tests/neg/i1802.scala | 21 +++++++++++++++ 19 files changed, 99 insertions(+), 55 deletions(-) create mode 100644 tests/neg/i1802.scala diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index ed32d2df95f8..123dc345042b 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -1024,7 +1024,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma case JavaArrayType(elem) => elem case _ => ctx.error(s"JavaSeqArray with type ${field.tpe} reached backend: $field", field.pos) - ErrorType + UnspecifiedErrorType } def _2: List[Tree] = field.elems } diff --git a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala index eb4da5400fd0..89831e56b756 100644 --- a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala +++ b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala @@ -12,7 +12,7 @@ import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Names.TermName import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.Types.{JavaArrayType, ErrorType, Type} +import dotty.tools.dotc.core.Types.{JavaArrayType, UnspecifiedErrorType, Type} import scala.collection.{ mutable, immutable } @@ -73,7 +73,7 @@ class DottyPrimitives(ctx: Context) { case JavaArrayType(el) => el case _ => ctx.error(s"expected Array $tpe") - ErrorType + UnspecifiedErrorType } code match { diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 20273eb85c79..28ae89c4aedc 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -113,7 +113,7 @@ object Trees { * type. (Overridden by empty trees) */ def withType(tpe: Type)(implicit ctx: Context): ThisTree[Type] = { - if (tpe == ErrorType) assert(ctx.reporter.errorsReported) + if (tpe.isInstanceOf[ErrorType]) assert(ctx.reporter.errorsReported) withTypeUnchecked(tpe) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index e2bb0ac1ad62..aaae78c5714b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1937,12 +1937,12 @@ object SymDenotations { /** A completer for missing references */ class StubInfo() extends LazyType { - def initializeToDefaults(denot: SymDenotation)(implicit ctx: Context) = { + def initializeToDefaults(denot: SymDenotation, errMsg: => String)(implicit ctx: Context) = { denot.info = denot match { case denot: ClassDenotation => ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope) case _ => - ErrorType + new ErrorType(errMsg) } denot.privateWithin = NoSymbol } @@ -1954,13 +1954,14 @@ object SymDenotations { if (file != null) (s" in $file", file.toString) else ("", "the signature") val name = ctx.fresh.setSetting(ctx.settings.debugNames, true).nameString(denot.name) - ctx.error( + def errMsg = i"""bad symbolic reference. A signature$location |refers to $name in ${denot.owner.showKind} ${denot.owner.showFullName} which is not available. |It may be completely missing from the current classpath, or the version on - |the classpath might be incompatible with the version used when compiling $src.""") + |the classpath might be incompatible with the version used when compiling $src.""" + ctx.error(errMsg) if (ctx.debug) throw new Error() - initializeToDefaults(denot) + initializeToDefaults(denot, errMsg) } } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index cfd85c49cd0e..b10b94ad80f5 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -22,6 +22,7 @@ import NameOps._ import ast.tpd.Tree import ast.TreeTypeMap import Constants.Constant +import reporting.diagnostic.Message import Denotations.{ Denotation, SingleDenotation, MultiDenotation } import collection.mutable import io.AbstractFile @@ -290,9 +291,11 @@ trait Symbols { this: Context => */ def newSkolem(tp: Type) = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | Permanent, tp) - def newErrorSymbol(owner: Symbol, name: Name) = + def newErrorSymbol(owner: Symbol, name: Name, msg: => Message) = { + val errType = new ErrorType(msg) newSymbol(owner, name, SyntheticArtifact, - if (name.isTypeName) TypeAlias(ErrorType) else ErrorType) + if (name.isTypeName) TypeAlias(errType) else errType) + } /** Map given symbols, subjecting their attributes to the mappings * defined in the given TreeTypeMap `ttmap`. diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 743220f550a0..e8905320b862 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -265,7 +265,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case ConstantType(v1) => v1.value == v2.value case _ => secondTry(tp1, tp2) } - case ErrorType => + case _: FlexType => true case _ => secondTry(tp1, tp2) @@ -341,7 +341,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } joinOK || isSubType(tp11, tp2) && isSubType(tp12, tp2) - case ErrorType => + case _: FlexType => true case _ => thirdTry(tp1, tp2) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 82943377a866..2e7c1db04a05 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -55,7 +55,7 @@ object TypeErasure { tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType) case tp @ ClassInfo(pre, _, parents, decls, _) => isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType) - case NoType | NoPrefix | WildcardType | ErrorType | SuperType(_, _) => + case NoType | NoPrefix | WildcardType | _: ErrorType | SuperType(_, _) => true case _ => false @@ -398,7 +398,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType)) // can't replace selftype by NoType because this would lose the sourceModule link } - case NoType | NoPrefix | ErrorType | JavaArrayType(_) => + case NoType | NoPrefix | _: ErrorType | JavaArrayType(_) => tp case tp: WildcardType if wildcardOK => tp @@ -506,7 +506,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if (inst.exists) sigName(inst) else tpnme.Uninstantiated case tp: TypeProxy => sigName(tp.underlying) - case ErrorType | WildcardType => + case _: ErrorType | WildcardType => tpnme.WILDCARD case tp: WildcardType => sigName(tp.optBounds) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 39214dd0c7b0..f134412a77f8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -433,7 +433,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. formals = formals.updated(name, tp1.typeParamNamed(name)) } normalizeToRef(tp1) - case ErrorType => + case _: ErrorType => defn.AnyType case AnnotatedType(tpe, _) => normalizeToRef(tpe) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 4dffc4542541..8f43ed9bb873 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -15,9 +15,10 @@ import SymDenotations._ import Decorators._ import Denotations._ import Periods._ -import util.Positions.Position +import util.Positions.{Position, NoPosition} import util.Stats._ import util.{DotClass, SimpleMap} +import reporting.diagnostic.Message import ast.tpd._ import ast.TreeTypeMap import printing.Texts._ @@ -176,7 +177,7 @@ object Types { /** Is this type produced as a repair for an error? */ final def isError(implicit ctx: Context): Boolean = stripTypeVar match { - case ErrorType => true + case _: ErrorType => true case tp => (tp.typeSymbol is Erroneous) || (tp.termSymbol is Erroneous) } @@ -387,8 +388,8 @@ object Types { tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix) case tp: TypeProxy => tp.underlying.findDecl(name, excluded) - case ErrorType => - ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name) + case err: ErrorType => + ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name, err.msg) case _ => NoDenotation } @@ -453,8 +454,8 @@ object Types { go(tp.join) case tp: JavaArrayType => defn.ObjectType.findMember(name, pre, excluded) - case ErrorType => - ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name) + case err: ErrorType => + ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name, err.msg) case _ => NoDenotation } @@ -1497,7 +1498,7 @@ object Types { (lastDefRunId != sym.defRunId) || (lastDefRunId == NoRunId) } || - (lastSymbol.infoOrCompleter == ErrorType || + (lastSymbol.infoOrCompleter.isInstanceOf[ErrorType] || sym.owner != lastSymbol.owner && (sym.owner.derivesFrom(lastSymbol.owner) || selfTypeOf(sym).derivesFrom(lastSymbol.owner) || @@ -2693,7 +2694,7 @@ object Types { protected def checkInst(implicit ctx: Context): this.type = { def check(tycon: Type): Unit = tycon.stripTypeVar match { case tycon: TypeRef if !tycon.symbol.isClass => - case _: PolyParam | ErrorType | _: WildcardType => + case _: PolyParam | _: ErrorType | _: WildcardType => case _: PolyType => assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this") case tycon: AnnotatedType => @@ -3251,12 +3252,16 @@ object Types { override def computeHash = hashSeed } - abstract class ErrorType extends UncachedGroundType with ValueType + abstract class FlexType extends UncachedGroundType with ValueType - object ErrorType extends ErrorType + class ErrorType(_msg: => Message) extends FlexType { + val msg = _msg + } + + object UnspecifiedErrorType extends ErrorType("unspecified error") /* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */ - object TryDynamicCallType extends ErrorType + object TryDynamicCallType extends FlexType /** Wildcard type, possibly with bounds */ abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType { diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 0f63b25bb2fd..92ab10db986f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -90,7 +90,8 @@ object JavaParsers { if (skipIt) skip() } - def errorTypeTree = TypeTree().withType(ErrorType) withPos Position(in.offset) + + def errorTypeTree = TypeTree().withType(UnspecifiedErrorType) withPos Position(in.offset) // --------- tree building ----------------------------- diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index b321d3736e8c..05f1af9d7a1e 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -96,7 +96,10 @@ object Formatting { case tpe: Type => tpe.exists && !tpe.isErroneous case sym: Symbol if sym.isCompleted => - sym.info != ErrorType && sym.info != TypeAlias(ErrorType) && sym.info.exists + sym.info match { + case _: ErrorType | TypeAlias(_: ErrorType) | NoType => false + case _ => true + } case _ => true } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 15c382bb09ad..61f23c21413a 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -154,7 +154,7 @@ class PlainPrinter(_ctx: Context) extends Printer { changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) } case OrType(tp1, tp2) => changePrec(OrPrec) { toText(tp1) ~ " | " ~ toText(tp2) } - case ErrorType => + case _: ErrorType => "" case tp: WildcardType => if (tp.optBounds.exists) "(?" ~ toTextRHS(tp.bounds) ~ ")" else "?" diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 4203ab9b2edc..da0a59c7bad0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -561,7 +561,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => var typedArgs = typedArgBuf.toList def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = - if (!success) app0.withType(ErrorType) + if (!success) app0.withType(UnspecifiedErrorType) else { if (!sameSeq(args, orderedArgs)) { // need to lift arguments to maintain evaluation order in the @@ -654,7 +654,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } fun1.tpe match { - case ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(ErrorType) + case err: ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(err) case TryDynamicCallType => typedDynamicApply(tree, pt) case _ => tryEither { @@ -918,7 +918,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case tp => val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn) val typedArgsErr = args mapconserve (typed(_, defn.AnyType)) - cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType ErrorType + cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType unapplyErr.tpe } } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index f5f7bdbaada8..41d9f9572029 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -56,7 +56,7 @@ object Checking { def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = checkBounds(args, poly.paramBounds, _.substParams(poly, _)) - /** Check applied type trees for well-formedness. This means + /** Check applied type trees for well-formedness. This means * - all arguments are within their corresponding bounds * - if type is a higher-kinded application with wildcard arguments, * check that it or one of its supertypes can be reduced to a normal application. @@ -237,8 +237,7 @@ object Checking { catch { case ex: CyclicReference => if (reportErrors) { - ctx.error(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos) - ErrorType + errorType(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos) } else info } diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index b5ace87d38b4..4039c8b81a39 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.Decorators._ +import ErrorReporting._ object Dynamic { def isDynamicMethod(name: Name): Boolean = @@ -41,10 +42,9 @@ trait Dynamic { self: Typer with Applications => def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false } val args = tree.args val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic - if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) { - ctx.error("applyDynamicNamed does not support passing a vararg parameter", tree.pos) - tree.withType(ErrorType) - } else { + if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) + errorTree(tree, "applyDynamicNamed does not support passing a vararg parameter") + else { def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg)) def namedArgs = args.map { case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg) @@ -89,8 +89,7 @@ trait Dynamic { self: Typer with Applications => case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) => typedDynamicAssign(qual, name, targs) case _ => - ctx.error("reassignment to val", tree.pos) - tree.withType(ErrorType) + errorTree(tree, "reassignment to val") } } diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index a066fc04a43d..270ad6c8a103 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -23,7 +23,7 @@ object ErrorReporting { def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = { ctx.error(msg, pos) - ErrorType + new ErrorType(msg) } def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = { diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ee2d682785ba..ca0af0e2b1aa 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -189,9 +189,8 @@ trait TypeAssigner { val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) - if (!tpe.isError) - ctx.error(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos) - ErrorType + if (tpe.isError) tpe + else errorType(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos) } } else if (d.symbol is TypeParamAccessor) @@ -215,17 +214,17 @@ trait TypeAssigner { else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) { TryDynamicCallType } else { - if (!site.isErroneous) { + if (site.isErroneous) UnspecifiedErrorType + else { def kind = if (name.isTypeName) "type" else "value" def addendum = if (site.derivesFrom(defn.DynamicClass)) "\npossible cause: maybe a wrong Dynamic method signature?" else "" - ctx.error( + errorType( if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor" else NotAMember(site, name, kind), pos) } - ErrorType } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index eec3859f9700..081e46841c09 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -351,10 +351,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val ownType = if (rawType.exists) ensureAccessible(rawType, superAccess = false, tree.pos) - else { - error(new MissingIdent(tree, kind, name.show), tree.pos) - ErrorType - } + else + errorType(new MissingIdent(tree, kind, name.show), tree.pos) val tree1 = ownType match { case ownType: NamedType if !prefixIsElidable(ownType) => @@ -1965,10 +1963,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit else err.typeMismatch(tree1, pt) } + /** If tree has an error type but no errors are reported yet, issue + * the error message stored in the type. + * One way this can happen is if implicit search causes symbols and types + * to be completed. The types are stored by `typedAhead` so that they can be + * retrieved later and thus avoid duplication of typechecking work. + * But if the implicit search causing the `typedAhead` fails locally but + * another alternative succeeds we can be left with an ErrorType in the + * tree that went unreported. A scenario where this happens is i1802.scala. + */ + def ensureReported(tp: Type) = tp match { + case err: ErrorType if !ctx.reporter.hasErrors => ctx.error(err.msg, tree.pos) + case _ => + } + tree match { case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[_] => tree case _ => tree.tpe.widen match { - case _: ErrorType => + case tp: FlexType => + ensureReported(tp) tree case ref: TermRef => pt match { diff --git a/tests/neg/i1802.scala b/tests/neg/i1802.scala new file mode 100644 index 000000000000..56da672a80b8 --- /dev/null +++ b/tests/neg/i1802.scala @@ -0,0 +1,21 @@ +import scala.collection.immutable.List +import scala.reflect.{ ClassTag, classTag } +import scala.language.implicitConversions + +object Exception { + type Catcher[+T] = PartialFunction[Throwable, T] + + def mkCatcher[Ex <: Throwable: ClassTag, T](isDef: Ex => Boolean, f: Ex => T) = new Catcher[T] { + private def downcast(x: Throwable): Option[Ex] = + if (classTag[Ex].runtimeClass.isAssignableFrom(x.getClass)) Some(x.asInstanceOf[Ex]) + else None + + def isDefinedAt(x: Throwable) = downcast(x) exists isDef + def apply(x: Throwable): T = f(downcast(x).get) + } + + def mkThrowableCatcher[T](isDef: Throwable => Boolean, f: Throwable => T) = mkCatcher(isDef, f) + + implicit def throwableSubtypeToCatcher[Ex <: Throwable: ClassTag, T](pf: PartialFunction[Ex, T]) = // error: cyclic reference + mkCatcher(pf.isDefinedAt _, pf.apply _) +} From 1f9990b58c23ba74a8d96166700e755704b78a49 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 17 Dec 2016 22:02:29 +0100 Subject: [PATCH 2/2] Comment FlexType --- compiler/src/dotty/tools/dotc/core/Types.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8f43ed9bb873..9884cc70a88c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3252,6 +3252,9 @@ object Types { override def computeHash = hashSeed } + /** A common superclass of `ErrorType` and `TryDynamicCallSite`. Instances of this + * class are at the same time subtypes and supertypes of every other type. + */ abstract class FlexType extends UncachedGroundType with ValueType class ErrorType(_msg: => Message) extends FlexType {