diff options
28 files changed, 2901 insertions, 922 deletions
diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index e5b9fafa00..6a5cbb4035 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -405,7 +405,8 @@ trait TreePrinters { self: SymbolTable => // SelectFromArray. // case SelectFromArray(qualifier, name, _) => // print(qualifier); print(".<arr>"); print(symName(tree, name)) - + case err: ErrorTreeWithPrettyPrinter => + print(err.toString()) case tree => xprintRaw(this, tree) diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index f9ca57fadc..dec0dcfdf9 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -117,6 +117,9 @@ trait Trees extends api.Trees { self: SymbolTable => def shallowDuplicate: Tree = new ShallowDuplicator(tree) transform tree def shortClass: String = tree.getClass.getName split "[.$]" last + + def containsErrorOrIsErrorTyped() = tree.containsError() || ((tree.tpe ne null) && tree.tpe.isError) + /** When you want to know a little more than the class, but a lot * less than the whole tree. */ @@ -271,6 +274,8 @@ trait Trees extends api.Trees { self: SymbolTable => } } + trait ErrorTreeWithPrettyPrinter extends AbsErrorTree + def atPos[T <: Tree](pos: Position)(tree: T): T = { posAssigner.pos = pos posAssigner.traverse(tree) diff --git a/src/compiler/scala/reflect/runtime/RuntimeTypes.scala b/src/compiler/scala/reflect/runtime/RuntimeTypes.scala index 818596d7b3..497290dbbc 100644 --- a/src/compiler/scala/reflect/runtime/RuntimeTypes.scala +++ b/src/compiler/scala/reflect/runtime/RuntimeTypes.scala @@ -10,7 +10,11 @@ trait RuntimeTypes extends Universe with api.RuntimeTypes { // to do: replace with generalized // case class Literal(x: Any), // once calls to the deprecated factory Literal(x: Any) has been eliminated from all code. - case class FreeValue(any: Any) extends Tree + case class FreeValue(any: Any) extends Tree { + protected def initErrorCheck { + hasErrorTree = Some(false) + } + } case class InstanceRefSymbol(value: AnyRef) extends TermSymbol(NoSymbol, NoPosition, nme.EMPTY) object InstanceRefSymbol extends InstanceRefSymbolExtractor diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index 613928856d..37dea497fc 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -36,13 +36,19 @@ abstract class TreeBrowsers { def create(): SwingBrowser = new SwingBrowser(); + trait ValidTree extends Tree { + protected def initErrorCheck { + hasErrorTree = Some(false) + } + } + /** Pseudo tree class, so that all JTree nodes are treated uniformly */ - case class ProgramTree(units: List[UnitTree]) extends Tree { + case class ProgramTree(units: List[UnitTree]) extends ValidTree { override def toString(): String = "Program" } /** Pseudo tree class, so that all JTree nodes are treated uniformly */ - case class UnitTree(unit: CompilationUnit) extends Tree { + case class UnitTree(unit: CompilationUnit) extends ValidTree { override def toString(): String = unit.toString() } @@ -490,6 +496,9 @@ abstract class TreeBrowsers { case Star(t) => ("Star", EMPTY) + + case _: AbsErrorTree => + ("ErrorTree", EMPTY) } /** Return a list of children for the given tree node */ @@ -631,6 +640,9 @@ abstract class TreeBrowsers { case Star(t) => List(t) + + case _: AbsErrorTree => + Nil } /** Return a textual representation of this t's symbol */ diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index acca7dac7d..ee0b28b02f 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -191,15 +191,21 @@ abstract class TreeGen extends reflect.internal.TreeGen { * symbol to its packed type, and an function for creating Idents * which refer to it. */ - private def mkPackedValDef(expr: Tree, owner: Symbol, name: Name): (ValDef, () => Ident) = { - val packedType = typer.packedType(expr, owner) + private def mkPackedValDef(expr: Tree, owner: Symbol, name: Name): (Tree, () => Ident) = { + val (packedType, errs) = typer.packedType(expr, owner) + // TODO ensure that they don't throw errors? + errs.foreach(_.emit(typer.context)) val sym = ( owner.newValue(expr.pos.makeTransparent, name) setFlag SYNTHETIC setInfo packedType ) - (ValDef(sym, expr), () => Ident(sym) setPos sym.pos.focus setType expr.tpe) + val identFn = () => Ident(sym) setPos sym.pos.focus setType expr.tpe + if (errs.isEmpty) + (ValDef(sym, expr), identFn) + else + (analyzer.PendingErrors(errs), identFn) } /** Used in situations where you need to access value of an expression several times @@ -218,7 +224,7 @@ abstract class TreeGen extends reflect.internal.TreeGen { } def evalOnceAll(exprs: List[Tree], owner: Symbol, unit: CompilationUnit)(within: (List[() => Tree]) => Tree): Tree = { - val vdefs = new ListBuffer[ValDef] + val vdefs = new ListBuffer[Tree] val exprs1 = new ListBuffer[() => Tree] val used = new Array[Boolean](exprs.length) var i = 0 diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 86aca26ba6..db5848f92b 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -17,9 +17,12 @@ import scala.reflect.internal.Flags.TRAIT trait Trees extends reflect.internal.Trees { self: Global => // --- additional cases -------------------------------------------------------- - /** Only used during parsing */ - case class Parens(args: List[Tree]) extends Tree + case class Parens(args: List[Tree]) extends Tree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(args) + } + } /** Documented definition, eliminated by analyzer */ case class DocDef(comment: DocComment, definition: Tree) @@ -29,6 +32,10 @@ trait Trees extends reflect.internal.Trees { self: Global => override def isDef = definition.isDef override def isTerm = definition.isTerm override def isType = definition.isType + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(definition) + } } @@ -36,14 +43,26 @@ trait Trees extends reflect.internal.Trees { self: Global => * eliminated by typecheck (doTypedApply) */ case class AssignOrNamedArg(lhs: Tree, rhs: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(lhs, rhs)) + } + } /** Array selection <qualifier> . <name> only used during erasure */ case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type) - extends TermTree with RefTree { } + extends TermTree with RefTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(qualifier) + } + } /** emitted by typer, eliminated by refchecks */ - case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree + case class TypeTreeWithDeferredRefCheck()(val check: () => Either[AbsErrorTree, TypeTree]) extends TypTree { + protected def initErrorCheck { + hasErrorTree = Some(false) + } + } // --- factory methods ---------------------------------------------------------- diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 362ec1bae0..69cd2d1ef7 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -813,17 +813,24 @@ class Global(settings: Settings, reporter: Reporter, projectName: String = "") private def typeMembers(pos: Position): Stream[List[TypeMember]] = { var tree = typedTreeAt(pos) + val context = doLocateContext(pos) + // if tree consists of just x. or x.fo where fo is not yet a full member name // ignore the selection and look in just x. tree match { case Select(qual, name) if tree.tpe == ErrorType => tree = qual + case ierr: analyzer.InteractiveErrorTree => + ierr.emit(context) + ierr.retrieveEmitted match { + case Select(qual, name) => tree = qual + case _ => + } case _ => } - val context = doLocateContext(pos) + if (tree.tpe == null) - // TODO: guard with try/catch to deal with ill-typed qualifiers. tree = analyzer.newTyper(context).typedQualifier(tree) debugLog("typeMembers at "+tree+" "+tree.tpe) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 56ac3e16ad..ca7ca5b41f 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -60,6 +60,7 @@ trait ScalaSettings extends AbsScalaSettings val assemextdirs = StringSetting ("-Xassem-extdirs", "dirs", "(Requires -target:msil) List of directories containing assemblies. default:lib", Defaults.scalaLibDir.path).dependsOn(target, "msil") val sourcedir = StringSetting ("-Xsourcedir", "directory", "(Requires -target:msil) Mirror source folder structure in output directory.", ".").dependsOn(target, "msil") val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") + val errortrees = BooleanSetting ("-Yerrortrees", "Provide more info about error trees." ) val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") val elidebelow = IntSetting ("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", elidable.MINIMUM, None, elidable.byName get _) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 12a17abe91..580014811b 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -59,7 +59,7 @@ abstract class Pickler extends SubComponent { } } // If there are any erroneous types in the tree, then we will crash - // when we pickle it: so let's report an erorr instead. We know next + // when we pickle it: so let's report an error instead. We know next // to nothing about what happened, but our supposition is a lot better // than "bad type: <error>" in terms of explanatory power. for (t <- unit.body ; if t.isErroneous) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index e3786c154f..60e1a6c747 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -22,6 +22,7 @@ trait Analyzer extends AnyRef with Unapplies with NamesDefaults with TypeDiagnostics + with ErrorTrees { val global : Global import global._ diff --git a/src/compiler/scala/tools/nsc/typechecker/ErrorTrees.scala b/src/compiler/scala/tools/nsc/typechecker/ErrorTrees.scala new file mode 100644 index 0000000000..714c4384ce --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/ErrorTrees.scala @@ -0,0 +1,1346 @@ +package scala.tools.nsc +package typechecker + +trait ErrorTrees { + self: Analyzer => + + import global._ + + trait ErrorTree extends AbsErrorTree { + + def emit(context: Context): Unit + def emit(): Unit = emit(typer.context.asInstanceOf[Context]) + protected def initErrorCheck { + hasErrorTree = Some(true) + } + def exception: TypeError = null // Once we get rid of all thrown type errors (apart from cyclic), remove + var reported = false + override def tpe = ErrorType + printName(this) + } + + trait InteractiveErrorTree extends ErrorTree { + def retrieveEmitted: Tree + } + + trait ContextError { + def errMsg: String + def errPos: Position + def emit(context: Context) = context.error(errPos, errMsg) + } + + // Debugging option + @inline private def printName(t: ErrorTree) { + if (settings.errortrees.value) + println("[ErrorTree instance] " + t.getClass) + } + + object errorTreesFinder extends Traverser { + import scala.collection.mutable + var trees: mutable.ListBuffer[ErrorTree] = _ + override def traverse(t: Tree) { + t match { + case e: ErrorTree if !e.reported => + trees += e + case e: ErrorTree => + case _ => + super.traverse(t) + } + } + def apply(t: Tree) = { + trees = new mutable.ListBuffer() + traverse(t) + trees + } + } + + object quickErrorTreeFinder extends Traverser { + import scala.collection.mutable + var found: Option[ErrorTree] = None + override def traverse(t: Tree) { + if (!found.isDefined) + t match { + case e: ErrorTree => + found = Some(e) + case _ => + super.traverse(t) + } + } + def apply(t: Tree) = { + found = None + traverse(t) + found.get + } + } + + abstract class TreeForwarder(forwardTo: Tree) extends Tree { + override def pos = forwardTo.pos + override def hasSymbol = forwardTo.hasSymbol + override def symbol = forwardTo.symbol + override def symbol_=(x: Symbol) = forwardTo.symbol = x + } + + // create trees for specific error trees + + trait TyperErrorTrees { + self: Typer => + + import infer.setError + + case class UnstableTreeError(tree: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + val msg = "stable identifier required, but "+tree+" found."+ + (if (isStableExceptVolatile(tree)) { + val tpe = tree.symbol.tpe match { + case PolyType(_, rtpe) => rtpe + case t => t + } + "\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile." + } else "") + + if (!tree.isErroneous) + context.error(tree.pos, msg) + } + } + + case class NoImplicitFoundError(fun: Tree, param: Symbol) + extends TreeForwarder(fun) with ErrorTree with ContextError { + + def errMsg = { + val paramName = param.name + val paramTp = param.tpe + paramTp.typeSymbol match { + case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) + case _ => + "could not find implicit value for "+ + (if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type " + else "parameter "+paramName+": ")+paramTp + } + } + def errPos = fun.pos + } + + case class TypeErrorTree(tree: Tree, pt: Type, override val exception: TypeError) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + reportTypeError(context, tree.pos, exception) + } + } + + case class AdaptToMemberWithArgsError(tree: Tree, override val exception: TypeError) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + reportTypeError(context, tree.pos, exception) + } + } + + case class WithFilterError(tree0: Tree, override val exception: TypeError) + extends TreeForwarder(tree0) with ErrorTree { + + def emit(context: Context) { + reportTypeError(context, tree0.pos, exception) + setError(tree0) + } + } + + case class ParentTypesError(templ: Template, override val exception: TypeError) + extends ErrorTree { + + def emit(context: Context) { + templ.tpe = null + reportTypeError(context, templ.pos, exception) + } + } + + // additional parentTypes errors + + case class ConstrArgsInTraitParentTpeError(arg: Tree, parent: Symbol) + extends ErrorTree with ContextError { + + def errMsg = parent + " is a trait; does not take constructor arguments" + def errPos = arg.pos + } + + case class MissingTypeArgumentsParentTpeError(supertpt: Tree) + extends ErrorTree with ContextError { + + def errMsg = "missing type arguments" + def errPos = supertpt.pos + } + + case class SilentTypeError(tree: Tree, override val exception: TypeError) + extends ErrorTree { + + def emit(context: Context) { + reportTypeError(context, tree.pos, exception) + } + } + + case class TypedApplyError(tree: Tree, override val exception: TypeError) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + reportTypeError(context, tree.pos, exception) + } + + override def pos = exception.pos + } + + case class AssignmentTypedApplyError(tree: Tree) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = "reassignment to val" + def errPos = tree.pos + } + + + // typedIdent + case class AmbiguousIdentError(tree: Tree, name: Name, msg: String) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = "reference to " + name + " is ambiguous;\n" + msg + def errPos = tree.pos + } + + case class SymbolNotFound(tree: Tree, name: Name, owner: Symbol) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = "not found: "+decodeWithKind(name, owner) + def errPos = tree.pos + } + + // typedAppliedTypeTree + case class AppliedTypeNoParametersError(tree: Tree, errTpe: Type) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, errTpe + " does not take type parameters") + } + } + + case class AppliedTypeWrongNumberOfArgsError(tree: Tree, msg: String) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, msg) + } + } + + // packagedef + case class RefTreeError(tree: Tree, name: Name) + extends ErrorTree with RefTree { + + def emit(context: Context) { + // Error was already reported + } + } + + // typedTypeDef + case class LowerBoundError(tree: TypeDef, lowB: Type, highB: Type) + extends ErrorTree with ContextError { + + def errMsg = "lower bound "+lowB+" does not conform to upper bound "+highB + def errPos = tree.pos + } + + // check privates + case class HiddenSymbolWithError(tree: Tree) + extends ErrorTree { + + def emit(context: Context) {} + } + + case class SymbolEscapesScopeError(tree: Tree, badSymbol: Symbol) + extends ErrorTree with ContextError { + + val treeTpe = tree.tpe + def errMsg = (if (badSymbol.isPrivate) "private " else "") + badSymbol + + " escapes its defining scope as part of type "+treeTpe + def errPos = tree.pos + } + + // typedDefDef + case class StarParamNotLastError(param: Tree) + extends ErrorTree with ContextError { + + def errMsg = "*-parameter must come last" + def errPos = param.pos + } + + case class StarWithDefaultError(meth: Symbol) + extends ErrorTree with ContextError { + + def errMsg = "a parameter section with a `*'-parameter is not allowed to have default arguments" + def errPos = meth.pos + } + + case class InvalidConstructorDefError(ddef: Tree) + extends ErrorTree with ContextError { + + def errMsg = "constructor definition not allowed here" + def errPos = ddef.pos + } + + case class DeprecatedParamNameError(param: Symbol, name: Name) + extends ErrorTree with ContextError { + + def errMsg = "deprecated parameter name "+ name +" has to be distinct from any other parameter name (deprecated or not)." + def errPos = param.pos + } + + // computeParamAliases + case class SuperConstrReferenceError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "super constructor cannot be passed a self reference unless parameter is declared by-name" + def errPos = tree.pos + } + + case class SuperConstrArgsThisReferenceError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "super constructor arguments cannot reference unconstructed `this`" + def errPos = tree.pos + } + + // typedValDef + case class VolatileValueError(vdef: Tree) + extends ErrorTree with ContextError { + + def errMsg = "values cannot be volatile" + def errPos = vdef.pos + } + + case class FinalVolatileVarError(vdef: Tree) + extends ErrorTree with ContextError { + + def errMsg = "final vars cannot be volatile" + def errPos = vdef.pos + } + + case class LocalVarUninitializedError(vdef: Tree) + extends ErrorTree with ContextError { + + def errMsg = "local variables must be initialized" + def errPos = vdef.pos + } + + //typedAssign + case class AssignmentError(tree: Tree, varSym: Symbol) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = + if (varSym != null && varSym.isValue) "reassignment to val" + else "assignment to non variable" + def errPos = tree.pos + } + + case class UnexpectedTreeAssignmentConversionError(tree: Tree) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = "Unexpected tree during assignment conversion." + def errPos = tree.pos + } + + case class MultiDimensionalArrayError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "cannot create a generic multi-dimensional array of more than "+ definitions.MaxArrayDims+" dimensions" + def errPos = tree.pos + } + + //typedSuper + case class MixinMissingParentClassNameError(tree: Tree, mix: Name, clazz: Symbol) + extends ErrorTree with ContextError { + + def errMsg = mix+" does not name a parent class of "+clazz + def errPos = tree.pos + } + + case class AmbiguousParentClassError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "ambiguous parent class qualifier" + def errPos = tree.pos + } + + //typedSelect + case class NotAMemberErroneous(tree: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { } + } + + case class NotAMemberInteractive(originalTree: Tree) + extends TreeForwarder(originalTree) with InteractiveErrorTree { + + private[this] var newTree = originalTree + + def emit(context: Context) { + def copyTree = { + val tree1 = originalTree match { + case Select(qual, name) => treeCopy.Select(originalTree, qual, name) + case SelectFromTypeTree(qual, name) => treeCopy.SelectFromTypeTree(originalTree, qual, name) + } + tree1 + } + newTree = copyTree + } + + def retrieveEmitted = newTree + } + + case class NotAMemberError(sel: Tree, qual: Tree, name: Name) + extends TreeForwarder(sel) with ErrorTree with ContextError { + + def errMsg = { + val owner = qual.tpe.typeSymbol + val target = qual.tpe.widen + def targetKindString = if (owner.isTypeParameterOrSkolem) "type parameter " else "" + def nameString = decodeWithKind(name, owner) + /** Illuminating some common situations and errors a bit further. */ + def addendum = { + val companion = { + if (name.isTermName && owner.isPackageClass) { + target.member(name.toTypeName) match { + case NoSymbol => "" + case sym => "\nNote: %s exists, but it has no companion object.".format(sym) + } + } + else "" + } + val semicolon = ( + if (linePrecedes(qual, sel)) + "\npossible cause: maybe a semicolon is missing before `"+nameString+"'?" + else + "" + ) + companion + semicolon + } + withAddendum(qual.pos)( + if (name == nme.CONSTRUCTOR) target + " does not have a constructor" + else nameString + " is not a member of " + targetKindString + target + addendum + ) + } + + def errPos = sel.pos + } + + //typedNew + case class IsAbstractError(tree: Tree, sym: Symbol) + extends ErrorTree with ContextError { + + def errMsg = sym + " is abstract; cannot be instantiated" + def errPos = tree.pos + } + + case class DoesNotConformToSelfTypeError(tree: Tree, sym: Symbol, tpe0: Type) + extends ErrorTree with ContextError { + + def errMsg = sym + + " cannot be instantiated because it does not conform to its self-type "+ + tpe0 + def errPos = tree.pos + } + + //typedEta + case class UnderscoreEtaError(tree: Tree) + extends ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "_ must follow method; cannot follow " + tree.tpe) + } + } + + //typedReturn + case class ReturnOutsideOfDefError(tree: Tree) + extends ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "return outside method definition") + } + } + + case class ReturnWithoutTypeError(tree: Tree, owner: Symbol) + extends ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, owner + " has return statement; needs result type") + } + } + + //typedBind + case class VariableInPatternAlternativeError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "illegal variable in pattern alternative" + def errPos = tree.pos + } + + //typedCase + case class StarPositionInPatternError(pos0: Position) + extends ErrorTree with ContextError { + + def errMsg = "_* may only come last" + def errPos = pos0 + } + + //typedFunction + case class MaxFunctionArityError(fun: Tree) + extends TreeForwarder(fun) with ErrorTree { + + def emit(context: Context) { + if (!fun.isErroneous) + context.error(fun.pos,"implementation restricts functions to " + definitions.MaxFunctionArity + " parameters") + } + } + + case class WrongNumberOfParametersError(tree: Tree, argpts: List[Type]) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos,"wrong number of parameters; expected = " + argpts.length) + } + } + + case class MissingParameterTypeError(fun: Tree, vparam: ValDef, pt: Type) + extends ErrorTree with ContextError { + + def anonMessage = ( + "\nThe argument types of an anonymous function must be fully known. (SLS 8.5)" + + "\nExpected type was: " + pt.toLongString + ) + + private val suffix = + if (!vparam.mods.isSynthetic) "" + else " for expanded function" + (fun match { + case Function(_, Match(_, _)) => anonMessage + case _ => " " + fun + }) + + def errMsg = "missing parameter type" + suffix + def errPos = vparam.pos + } + + case class ConstructorsOrderError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "called constructor's definition must precede calling constructor's definition" + def errPos = tree.pos + } + + case class OnlyDeclarationsError(tree: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "only declarations allowed here") + } + } + + // typedAnnotation + case class AnnotationNotAConstantError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "annotation argument needs to be a constant; found: " + tree + def errPos = tree.pos + } + + case class AnnotationArgNulError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "annotation argument cannot be null" + def errPos = tree.pos + } + + case class ArrayConstantsError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "Array constants have to be specified using the `Array(...)' factory method" + def errPos = tree.pos + } + + case class ArrayConstantsTypeMismatchError(tree: Tree, pt: Type) + extends ErrorTree with ContextError { + + def errMsg = "found array constant, expected argument of type " + pt + def errPos = tree.pos + } + + case class UnexpectedTreeAnnotation(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "unexpected tree in annotation: "+ tree + def errPos = tree.pos + } + + case class AnnotationTypeMismatchError(tree: Tree, expected: Type, found: Type) + extends ErrorTree with ContextError { + + def errMsg = "expected annotation of type "+ expected +", found "+ found + def errPos = tree.pos + } + + case class MultipleArgumentListForAnnotationError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "multiple argument lists on classfile annotation" + def errPos = tree.pos + } + + case class UnknownAnnotationNameError(tree: Tree, name: Name) + extends ErrorTree with ContextError { + + def errMsg = "unknown annotation argument name: " + name + def errPos = tree.pos + } + + case class DuplicateValueAnnotationError(tree: Tree, name: Name) + extends ErrorTree with ContextError { + + def errMsg = "duplicate value for annotation argument " + name + def errPos = tree.pos + } + + case class ClassfileAnnotationsAsNamedArgsError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "classfile annotation arguments have to be supplied as named arguments" + def errPos = tree.pos + } + + case class AnnotationMissingArgError(tree: Tree, annType: Type, name: Symbol) + extends ErrorTree with ContextError { + + def errMsg = "annotation " + annType.typeSymbol.fullName + " is missing argument " + name.name + def errPos = tree.pos + } + + case class NestedAnnotationError(tree: Tree, annType: Type) + extends ErrorTree with ContextError { + + def errMsg = "nested classfile annotations must be defined in java; found: "+ annType + def errPos = tree.pos + } + + case class UnexpectedTreeAnnotationError(tree: Tree, unexpected: Tree) + extends ErrorTree with ContextError { + + def errMsg = "unexpected tree after typing annotation: "+ unexpected + def errPos = tree.pos + } + + // TODO no test case + //typedExistentialTypeTree + case class AbstractionFromVolatileTypeError(vd: ValDef) + extends ErrorTree with ContextError { + + def errMsg = "illegal abstraction from value with volatile type "+vd.symbol.tpe + def errPos = vd.pos + } + + case class TypedApplyWrongNumberOfTpeParametersError(tree: Tree, fun: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + } + } + + case class TypedApplyDoesNotTakeTpeParametersError(tree: Tree, fun: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, treeSymTypeMsg(fun)+" does not take type parameters.") + } + } + + // doTypeApply + //tryNamesDefaults + case class WrongNumberOfArgsError(tree: Tree, fun: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "wrong number of arguments for "+ treeSymTypeMsg(fun)) + } + } + + case class TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "too many arguments for "+treeSymTypeMsg(fun)) + } + } + + case class MultipleVarargError(tree: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "when using named arguments, the vararg parameter "+ + "has to be specified exactly once") + } + } + + case class ModuleUsingCompanionClassDefaultArgsErrror(tree: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "module extending its companion class cannot use default constructor arguments") + } + } + + case class NotEnoughArgsError(tree: Tree, fun0: Tree, missing0: List[Symbol]) + extends TreeForwarder(tree) with ErrorTree { + def notEnoughArgumentsMsg(fun: Tree, missing: List[Symbol]): String = { + val suffix = { + if (missing.isEmpty) "" + else { + val keep = missing take 3 map (_.name) + ".\nUnspecified value parameter%s %s".format( + if (missing.tail.isEmpty) "" else "s", + if (missing drop 3 nonEmpty) (keep :+ "...").mkString(", ") + else keep.mkString("", ", ", ".") + ) + } + } + + "not enough arguments for " + treeSymTypeMsg(fun) + suffix + } + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, notEnoughArgumentsMsg(fun0, missing0)) + } + } + + //doTypedApply - ErrorType + case class ErroneousFunInTypeApplyError(fun: Tree, args: List[Tree]) + extends TreeForwarder(fun) with ErrorTree { + + def emit(context: Context) { + val all = errorTreesFinder(fun) ++ args.map(arg => errorTreesFinder(arg)).flatten + all.foreach(_.emit(context)) + } + } + + //doTypedApply - patternMode + // TODO: missing test case + case class TooManyArgsPatternError(fun: Tree) + extends TreeForwarder(fun) with ErrorTree with ContextError { + + def errMsg = "too many arguments for unapply pattern, maximum = "+definitions.MaxTupleArity + def errPos = fun.pos + } + + case class WrongNumberArgsPatternError(tree: Tree, fun: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "wrong number of arguments for "+treeSymTypeMsg(fun)) + } + } + + + // Extends ErrorTreeWithPrettyPrinter to pass presentation/ping-pong test case + case class ApplyWithoutArgsError(tree: Tree, fun: Tree) + extends TreeForwarder(tree) with ErrorTreeWithPrettyPrinter with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, fun.tpe+" does not take parameters") + } + + override def toString() = tree.toString() + } + + //checkClassType + // When validating parents we sometimes should continue to + // type the body of the template and sometimes not. + // trait BlockingError allows us to distinguish it + trait BlockingError + + case class TypeNotAStablePrefixError(pre: Type, pos0: Position) + extends ErrorTree with BlockingError with ContextError { + + def errMsg = "type "+pre+" is not a stable prefix" + def errPos = pos0 + } + + case class ClassTypeRequiredError(tree: Tree, found: AnyRef) + extends ErrorTree with BlockingError with ContextError { + + def errMsg = "class type required but "+found+" found" + def errPos = tree.pos + } + + // validateParentClasses + case class ParentSuperSubclassError(pos0: Position, superclazz: Symbol, + parentSym: Symbol, mixin: Symbol) + extends ErrorTree with ContextError { + + def errMsg = "illegal inheritance; super"+superclazz+ + "\n is not a subclass of the super"+parentSym+ + "\n of the mixin " + mixin + def errPos = pos0 + } + + case class ParentNotATraitMixinError(pos0: Position, mixin: Symbol) + extends ErrorTree with BlockingError with ContextError { + + def errMsg = mixin+" needs to be a trait to be mixed in" + def errPos = pos0 + } + + case class ParentFinalInheritanceError(pos0: Position, mixin: Symbol) + extends ErrorTree with BlockingError with ContextError { + + def errMsg = "illegal inheritance from final "+mixin + def errPos = pos0 + } + + case class ParentSealedInheritanceError(pos0: Position, mixin: Symbol) + extends ErrorTree with BlockingError with ContextError { + + def errMsg = "illegal inheritance from sealed "+mixin + def errPos = pos0 + } + + case class ParentSelfTypeConformanceError( + pos0: Position, selfType: Type, parent: Tree) + extends ErrorTree with ContextError { + + def errMsg = "illegal inheritance;\n self-type "+ + selfType+" does not conform to "+parent + + "'s selftype "+parent.tpe.typeOfThis + def errPos = pos0 + } + + case class ParentInheritedTwiceError(pos0: Position, parent: Symbol) + extends ErrorTree with BlockingError with ContextError { + + def errMsg = parent+" is inherited twice" + def errPos = pos0 + } + + //adapt + case class MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) + extends ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "missing arguments for "+meth+meth.locationString+ + (if (meth.isConstructor) "" + else ";\nfollow this method with `_' if you want to treat it as a partially applied function")) + } + } + + // This is really a workaround for a compiler bug + case class Bug4425Error(tree: Tree) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = "erroneous or inaccessible type" + def errPos = tree.pos + } + + case class MissingTypeParametersError(tree: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, tree.symbol+" takes type parameters") + setError(tree) + } + } + + case class KindArityMismatchError(tree: Tree, pt: Type) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + import scala.tools.util.StringOps.{countElementsAsString, countAsString} + if (!tree.isErroneous) + context.error(tree.pos, + tree.tpe+" takes "+countElementsAsString(tree.tpe.typeParams.length, "type parameter")+ + ", expected: "+countAsString(pt.typeParams.length)) + } + } + //case class ParamsNotConvertible + + case class CaseClassConstructorError(tree: Tree) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + import scala.tools.util.StringOps.{countElementsAsString, countAsString} + if (!tree.isErroneous) + context.error(tree.pos, + tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method") + } + } + + //TODO Needs test case + case class ConstructorPrefixError(tree: Tree, restpe: Type) + extends ErrorTree with ContextError { + + def errMsg = restpe.prefix+" is not a legal prefix for a constructor" + def errPos = tree.pos + } + + // SelectFromTypeTree + case class TypeSelectionFromVolatileTypeError(tree: Tree, qual: Tree) + extends ErrorTree with ContextError { + + def errMsg = "illegal type selection from volatile type "+qual.tpe + def errPos = tree.pos + } + + // packedType + case class InferTypeWithVolatileTypeSelectionError(tree: Tree, pre: Type) + extends ErrorTree with ContextError { + + def errMsg = "Inferred type "+tree.tpe+" contains type selection from volatile type "+pre + def errPos = tree.pos + } + + case class AbstractExistentiallyOverParamerizedTpeError(tree: Tree, tp: Type) + extends ErrorTree with ContextError { + + def errMsg = "can't existentially abstract over parameterized type " + tp + def errPos = tree.pos + } + + //manifestTreee + case class MissingManifestError(pos0: Position, full: Boolean, tp: Type) + extends ErrorTree with ContextError { + + def errMsg = "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp + def errPos = pos0 + } + + + // TODO needs test case + // cases where we do not necessairly return trees + case class DependentMethodTpeConversionToFunctionError(pos0: Position, tp: Type) + extends ErrorTree with ContextError { + + def errMsg = "method with dependent type "+tpe+" cannot be converted to function value" + def errPos = pos0 + + override def pos = pos0 + } + + //checkStarPatOK + case class StarPatternWithVarargParametersError(pos0: Position) + extends ErrorTree with ContextError { + + def errMsg = "star patterns must correspond with varargs parameters" + def errPos = pos0 + } + + case class GetterDefinedTwiceError(getter: Symbol) + extends ErrorTree with ContextError { + + def errMsg = getter+" is defined twice" + def errPos = getter.pos + } + + case class BeanPropertyAnnotationLimitationError(tree: Tree) + extends ErrorTree with ContextError { + + def errMsg = "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import" + def errPos = tree.pos + } + + // TODO missing test case + case class FinitiaryError(tparam: Symbol) + extends ErrorTree with ContextError { + + def errMsg = "class graph is not finitary because type parameter "+tparam.name+" is expansively recursive" + def errPos = tparam.pos + } + + // TODO missing test case for a second case + case class QualifyingClassError(tree: Tree, qual: Name) + extends ErrorTree with ContextError { + + def errMsg = + if (qual.isEmpty) tree + " can be used only in a class, object, or template" + else qual + " is not an enclosing class" + def errPos = tree.pos + } + + // def stabilize + case class NotAValueError(tree: Tree, sym: Symbol) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, sym.kindString + " " + sym.fullName + " is not a value") + } + } + + // checkNoDoubleDefs... + case class DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) + extends ErrorTree with ContextError { + + def errMsg = sym1+" is defined twice"+{if(!settings.debug.value) "" else " in "+context.unit.toString} + def errPos = sym0.pos + override def pos = sym0.pos + + } + + // cyclic errors + case class CyclicAliasingOrSubtypingError(pos0: Position, sym0: Symbol) + extends ErrorTree with ContextError { + + def errMsg = "cyclic aliasing or subtyping involving "+sym0 + def errPos = pos0 + + override def pos = pos0 + } + + case class CyclicReferenceError(pos0: Position, lockedSym: Symbol) + extends ErrorTree with ContextError { + + def errMsg = "illegal cyclic reference involving " + lockedSym + def errPos = pos0 + override def pos = pos0 + } + + } + + trait InferencerErrorTrees { + self: Inferencer => + + private def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) = { + def asParams(xs: List[Any]) = xs.mkString("(", ", ", ")") + + def resType = if (pt isWildcard) "" else " with expected result type " + pt + def allTypes = (alternatives(tree) flatMap (_.paramTypes)) ++ argtpes :+ pt + def locals = alternatives(tree) flatMap (_.typeParams) + + withDisambiguation(locals, allTypes: _*) { + treeSymTypeMsg(tree) + msg + asParams(argtpes) + resType + } + } + + case class AccessError(tree: Tree, sym: Symbol, pre: Type, + owner0: Symbol, explanation: String) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = { + val realsym = underlying(sym) + val location = if (sym.isClassConstructor) owner0 else pre.widen + + realsym.fullLocationString + " cannot be accessed in " + + location + explanation + } + def errPos = tree.pos + } + + case class NoMethodInstanceError(fn: Tree, args: List[Tree], + msg: String) + extends TreeForwarder(fn) with ErrorTree { + + def emit(context: Context) { + if (!fn.isErroneous) + context.error(fn.pos, "no type parameters for " + + applyErrorMsg(fn, " exist so that it can be applied to arguments ", args map (_.tpe.widen), WildcardType) + + "\n --- because ---\n" + msg) + } + } + + // TODO: no test case + case class NoConstructorInstanceError(tree: Tree, restpe: Type, + pt: Type, msg: String) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "constructor of type " + restpe + + " cannot be uniquely instantiated to expected type " + pt + + "\n --- because ---\n" + msg) + } + } + + case class ConstrInstantiationError(tree: Tree, restpe: Type, + pt: Type) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, "constructor cannot be instantiated to expected type" + + foundReqMsg(restpe, pt)) + } + + } + + case class NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], + pt: Type) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + if (!tree.isErroneous) + context.error(tree.pos, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) + } + + } + + case class AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, + firstCompeting: Symbol, argtpes: List[Type], pt: Type) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + context.ambiguousError(tree.pos, pre, best, firstCompeting, + "argument types " + argtpes.mkString("(", ",", ")") + + (if (pt == WildcardType) "" else " and expected result type " + pt)) + } + } + + case class NoBestExprAlternativeError(tree: Tree, pt: Type) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = withAddendum(tree.pos)(typeErrorMsg(tree.symbol.tpe, pt)) + def errPos = tree.pos + } + + case class AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, + firstCompeting: Symbol, pt: Type) + extends TreeForwarder(tree) with ErrorTree { + + def emit(context: Context) { + context.ambiguousError(tree.pos, pre, best, firstCompeting, + "expected type " + pt) + } + } + + // checkBounds + case class KindBoundErrors(pos0: Position, prefix: String, targs: List[Type], + tparams: List[Symbol], kindErrors: List[String]) + extends ErrorTree with ContextError { + + def errMsg = + prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") + + " do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." + + kindErrors.toList.mkString("\n", ", ", "") + def errPos = pos0 + + override def pos = pos0 + } + + case class NotWithinBounds(pos0: Position, prefix: String, targs: List[Type], + tparams: List[Symbol], kindErrors: List[String]) + extends ErrorTree { + + val savedContext = getContext + def emit(context: Context) { + val validContext = if (context.unit == NoCompilationUnit) savedContext else context + validContext.error(pos0, + prefix + "type arguments " + targs.mkString("[", ",", "]") + + " do not conform to " + tparams.head.owner + "'s type parameter bounds " + + (tparams map (_.defString)).mkString("[", ",", "]")) + if (settings.explaintypes.value) { + val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) + (targs, bounds).zipped foreach ((targ, bound) => explainTypes(bound.lo, targ)) + (targs, bounds).zipped foreach ((targ, bound) => explainTypes(targ, bound.hi)) + () + } + } + + override def pos = pos0 + } + + //substExpr + case class PolymorphicExpressionInstantiationError(tree: Tree, undetparams: List[Symbol], + pt: Type) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = + "polymorphic expression cannot be instantiated to expected type" + + foundReqMsg(polyType(undetparams, skipImplicit(tree.tpe)), pt) + def errPos = tree.pos + } + + + //checkCheckable + case class TypePatternOrIsInstanceTestError(pos0: Position, tp: Type) + extends ErrorTree with ContextError { + + def errMsg = "type "+tp+" cannot be used in a type pattern or isInstanceOf test" + def errPos = pos0 + override def pos = pos0 + } + + case class IncompletePatternTypeError(pos0: Position, pattp: Type, pt: Type) + extends ErrorTree with ContextError { + + def errMsg = "pattern type is incompatible with expected type" + foundReqMsg(pattp, pt) + def errPos = pos0 + override def pos = pos0 + } + + case class IncompatibleScrutineeTypeError(pos0: Position, pattp: Type, pt: Type) + extends ErrorTree with ContextError { + + def errMsg = "scrutinee is incompatible with pattern type" + foundReqMsg(pattp, pt) + def errPos = pos0 + override def pos = pos0 + } + + case class PatternTypeIncompatibleWithPtError(pat: Tree, pt1: Type, pt: Type) + extends TreeForwarder(pat) with ErrorTree with ContextError { + + def errMsg = { + val sym = pat.tpe.typeSymbol + val clazz = sym.companionClass + val addendum = ( + if (sym.isModuleClass && clazz.isCaseClass && (clazz isSubClass pt1.typeSymbol)) { + // TODO: move these somewhere reusable. + val typeString = clazz.typeParams match { + case Nil => "" + clazz.name + case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") + } + val caseString = ( + clazz.caseFieldAccessors + map (_ => "_") // could use the actual param names here + mkString (clazz.name + "(", ",", ")") + ) + ( + "\nNote: if you intended to match against the class, try `case _: " + + typeString + "` or `case " + caseString + "`" + ) + } + else "" + ) + "pattern type is incompatible with expected type"+foundReqMsg(pat.tpe, pt) + addendum + } + def errPos = pat.pos + } + + case class PolyAlternativeError(tree: Tree, msg: String) + extends TreeForwarder(tree) with ErrorTree with ContextError { + + def errMsg = msg + def errPos = tree.pos + } + } + + trait NamerErrorTrees { + self: Namer => + + // Currently too general + case class TypeSigError(tree: Tree, override val exception: TypeError) + extends ErrorTree { + + def emit(context: Context) { + typer.reportTypeError(context, tree.pos, exception) + } + } + } + + // General errors + case class PendingErrors(pending0: List[ErrorTree]) + extends ErrorTree { + assert(pending0.length != 0, "pending exceptions cannot be empty") + + + def emit(context: Context) { + // Try to report each, here we dont' care + // if any of those throws TypeError + // this is handled in the actual application code + pending0.foreach(_.emit(context)) + } + + override def pos = pending0.head.pos + override def exception: TypeError = pending0.head.exception + } + + case object NullErrorTree extends ErrorTree { + + def emit(context: Context) {} + } + + case class SetErrorTree(tree: Tree) extends ErrorTree { + + def emit(context: Context) { + typer.infer.setError(tree) + } + } + + //NamesDefaults errors, refactor to their own trait + case class NameClashError(sym: Symbol, arg: Tree) + extends ErrorTree with ContextError { + + def errMsg = + "%s definition needs %s because '%s' is used as a named argument in its body.".format( + "variable", // "method" + "type", // "result type" + sym.name) + def errPos = sym.pos + + override def emit(context: Context) = { + super.emit(context) + // This is ugly hack to avoid reporting double errors + // when dealing with AmbiguousReferences problem (error tree below) in names defaults. + typer.infer.setError(arg) + } + + override def pos = sym.pos + } + + case class AmbiguousReferenceInNamesDefaultError(arg: Tree, name: Name) + extends TreeForwarder(arg) with ErrorTree { + + def emit(context: Context) { + if (!arg.isErroneous) { + context.error( + arg.pos, + "reference to "+ name +" is ambiguous; it is both, a parameter\n"+ + "name of the method and the name of a variable currently in scope.") + } + } + } + + case class UnknwonParameterNameNamesDefaultError(arg: Tree, name: Name) + extends TreeForwarder(arg) with ErrorTree { + + def emit(context: Context) { + if (!arg.isErroneous) + context.error(arg.pos, "unknown parameter name: " + name) + } + } + + case class DoubleParamNamesDefaultError(arg: Tree, name: Name) + extends TreeForwarder(arg) with ErrorTree { + + def emit(context: Context) { + if (!arg.isErroneous) + context.error(arg.pos, "parameter specified twice: "+ name) + } + } + + case class PositionalAfterNamedNamesDefaultError(arg: Tree) + extends TreeForwarder(arg) with ErrorTree { + + def emit(context: Context) { + if (!arg.isErroneous) + context.error(arg.pos, "positional after named argument.") + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 169295e5c9..7ceff8d1d4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -522,6 +522,9 @@ trait Implicits { else typed1(itree, EXPRmode, wildPt) + if (itree1.containsError()) + return SearchFailure + incCounter(typedImplicits) printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt)) @@ -541,7 +544,7 @@ trait Implicits { } } - if (itree2.tpe.isError) + if (itree2.containsErrorOrIsErrorTyped()) SearchFailure else if (!hasMatchingSymbol(itree1)) fail("candidate implicit %s is shadowed by other implicit %s".format( @@ -565,7 +568,11 @@ trait Implicits { false, lubDepth(List(itree2.tpe, pt))) // #2421: check that we correctly instantiated type parameters outside of the implicit tree: - checkBounds(itree2.pos, NoPrefix, NoSymbol, undetParams, targs, "inferred ") + // TODO: refactoring needed, shouldn't emit it here + checkBounds(itree2.pos, NoPrefix, NoSymbol, undetParams, targs, "inferred ") match { + case Some(err) => err.emit(context) + case _ => + } // filter out failures from type inference, don't want to remove them from undetParams! // we must be conservative in leaving type params in undetparams @@ -590,12 +597,21 @@ trait Implicits { // re-typecheck) // TODO: the return tree is ignored. This seems to make // no difference, but it's bad practice regardless. - itree2 match { + + // we call typedTypeApply which can return an error tree, + // so we cannot ignore the tree + // TODO check if that is enough + val checked = itree2 match { case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args) case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c case t => t } - val result = new SearchResult(itree2, subst) + if (checked.containsError()) { + // TODO: for the moment workaround for situations where we get errortrees + val res = errorTreesFinder(checked) + res.foreach(t => t.emit(context)) + } + val result = new SearchResult(checked, subst) incCounter(foundImplicits) printInference("[typedImplicit1] SearchResult: " + result) result @@ -605,6 +621,8 @@ trait Implicits { } } catch { + // TODO: once refactoring of type errors is done we should only + // catch here cyclic references. case ex: TypeError => fail(ex.getMessage()) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 354eb52913..26e4da5d79 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -189,7 +189,7 @@ trait Infer { private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ - class Inferencer(context: Context) { + class Inferencer(context: Context) extends InferencerErrorTrees { /* -- Error Messages --------------------------------------------------- */ def setError[T <: Tree](tree: T): T = { def name = newTermName("<error: " + tree.symbol + ">") @@ -203,19 +203,20 @@ trait Infer { tree setType ErrorType } + protected def getContext = context + def error(pos: Position, msg: String) { context.error(pos, msg) } - def errorTree(tree: Tree, msg: String): Tree = { +/* def makeErrorTree(tree: Tree, msg: String): Tree = { if (!tree.isErroneous) error(tree.pos, msg) setError(tree) - } + }*/ def typeError(pos: Position, found: Type, req: Type) { if (!found.isErroneous && !req.isErroneous) { error(pos, withAddendum(pos)(typeErrorMsg(found, req))) - if (settings.explaintypes.value) explainTypes(found, req) } @@ -228,6 +229,8 @@ trait Infer { "type mismatch" + foundReqMsg(found, req) + missingArgsMsg } + // TODO: replace with standard Error tree + // currently only used in adapt in Typers def typeErrorTree(tree: Tree, found: Type, req: Type): Tree = { // If the expected type is a refinement type, and the found type is a refinement or an anon // class, we can greatly improve the error message by retyping the tree to recover the actual @@ -287,7 +290,7 @@ trait Infer { Console.println(tree) Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType)) } - new AccessError(tree, sym, pre, + AccessError(tree, sym, pre, context.enclClass.owner, if (settings.check.isDefault) analyzer.lastAccessCheckDetails else @@ -314,11 +317,17 @@ trait Infer { if (settings.debug.value) ex.printStackTrace val sym2 = underlying(sym1) val itype = pre.memberType(sym2) - new AccessError(tree, sym, pre, + return AccessError(tree, sym, pre, context.enclClass.owner, "\n because its instance type "+itype+ (if ("malformed type: "+itype.toString==ex.msg) " is malformed" - else " contains a "+ex.msg)).emit() - ErrorType + else " contains a "+ex.msg)) + +// enabling the following TypeError case +// crashes a few examples because there are situations (like in NamesDefaults) +// where CyclicReference is expected +// case ex: TypeError => +// return TypeErrorTree(tree, ex)(context) + } if (pre.isInstanceOf[SuperType]) owntype = owntype.substSuper(pre, site.symbol.thisType) @@ -911,7 +920,9 @@ trait Infer { false } - /** Todo: Try to make isApplicable always safe (i.e. not cause TypeErrors). + /** + * Todo: Try to make isApplicable always safe (i.e. not cause TypeErrors). + * The chance of TypeErrors should be reduced through error trees */ private[typechecker] def isApplicableSafe(undetparams: List[Symbol], ftpe: Type, argtpes0: List[Type], pt: Type): Boolean = { @@ -1092,37 +1103,20 @@ trait Infer { */ /** error if arguments not within bounds. */ def checkBounds(pos: Position, pre: Type, owner: Symbol, - tparams: List[Symbol], targs: List[Type], prefix: String) = { + tparams: List[Symbol], targs: List[Type], prefix: String): Option[ErrorTree] = { //@M validate variances & bounds of targs wrt variances & bounds of tparams //@M TODO: better place to check this? //@M TODO: errors for getters & setters are reported separately val kindErrors = checkKindBounds(tparams, targs, pre, owner) - if (!kindErrors.isEmpty) { - if (targs contains WildcardType) () - else error(pos, - prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") + - " do not conform to the expected kinds of the type parameters "+ - tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." + - kindErrors.toList.mkString("\n", ", ", "")) - } - else if (!isWithinBounds(pre, owner, tparams, targs)) { - if (!(targs exists (_.isErroneous)) && !(tparams exists (_.isErroneous))) { - //val bounds = instantiatedBounds(pre, owner, tparams, targs)//DEBUG - //println("bounds = "+bounds+", targs = "+targs+", targclasses = "+(targs map (_.getClass))+", parents = "+(targs map (_.parents))) - //println(List.map2(bounds, targs)((bound, targ) => bound containsType targ)) - error(pos, - prefix + "type arguments " + targs.mkString("[", ",", "]") + - " do not conform to " + tparams.head.owner + "'s type parameter bounds " + - (tparams map (_.defString)).mkString("[", ",", "]")) - if (settings.explaintypes.value) { - val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) - (targs, bounds).zipped foreach ((targ, bound) => explainTypes(bound.lo, targ)) - (targs, bounds).zipped foreach ((targ, bound) => explainTypes(targ, bound.hi)) - () - } - } - } + if(!kindErrors.isEmpty) { + if (targs contains WildcardType) None + else Some(KindBoundErrors(pos, prefix, targs, tparams, kindErrors)) + } else if (!isWithinBounds(pre, owner, tparams, targs)) { + if (!(targs exists (_.isErroneous)) && !(tparams exists (_.isErroneous))) + Some(NotWithinBounds(pos, prefix, targs, tparams, kindErrors)) + else None + } else None } @@ -1166,7 +1160,7 @@ trait Infer { * first to `strictPt` and then, if this fails, to `lenientPt`. If both * attempts fail, an error is produced. */ - def inferArgumentInstance(tree: Tree, undetparams: List[Symbol], strictPt: Type, lenientPt: Type) { + def inferArgumentInstance(tree: Tree, undetparams: List[Symbol], strictPt: Type, lenientPt: Type) = { printInference( ptBlock("inferArgumentInstance", "tree" -> tree, @@ -1180,8 +1174,8 @@ trait Infer { if ((targs eq null) || !(tree.tpe.subst(undetparams, targs) <:< strictPt)) { targs = exprTypeArgs(undetparams, tree.tpe, lenientPt) } - substExpr(tree, undetparams, targs, lenientPt) printInference("[inferArgumentInstance] finished, targs = " + targs) + substExpr(tree, undetparams, targs, lenientPt) } /** Infer type arguments `targs` for `tparams` of polymorphic expression in `tree`, given prototype `pt`. @@ -1189,7 +1183,7 @@ trait Infer { * Substitute `tparams` to `targs` in `tree`, after adjustment by `adjustTypeArgs`, returning the type parameters that were not determined * If passed, infers against specified type `treeTp` instead of `tree.tp`. */ - def inferExprInstance(tree: Tree, tparams: List[Symbol], pt: Type = WildcardType, treeTp0: Type = null, keepNothings: Boolean = true, useWeaklyCompatible: Boolean = false): List[Symbol] = { + def inferExprInstance(tree: Tree, tparams: List[Symbol], pt: Type = WildcardType, treeTp0: Type = null, keepNothings: Boolean = true, useWeaklyCompatible: Boolean = false): (Option[ErrorTree], List[Symbol]) = { val treeTp = if(treeTp0 eq null) tree.tpe else treeTp0 // can't refer to tree in default for treeTp0 printInference( ptBlock("inferExprInstance", @@ -1202,8 +1196,7 @@ trait Infer { val targs = exprTypeArgs(tparams, treeTp, pt, useWeaklyCompatible) if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226 - substExpr(tree, tparams, targs, pt) - List() + (substExpr(tree, tparams, targs, pt), List()) } else { val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, targs) printInference( @@ -1213,8 +1206,7 @@ trait Infer { "leftUndet" -> leftUndet ) ) - substExpr(tree, okParams, okArgs, pt) - leftUndet + (substExpr(tree, okParams, okArgs, pt), leftUndet) } } @@ -1227,13 +1219,15 @@ trait Infer { * @param pt ... */ private def substExpr(tree: Tree, undetparams: List[Symbol], - targs: List[Type], pt: Type) { + targs: List[Type], pt: Type): Option[ErrorTree] = { if (targs eq null) { if (!tree.tpe.isErroneous && !pt.isErroneous) - error(tree.pos, "polymorphic expression cannot be instantiated to expected type" + - foundReqMsg(polyType(undetparams, skipImplicit(tree.tpe)), pt)) + Some(PolymorphicExpressionInstantiationError(tree, undetparams, pt)) + else + None } else { new TreeTypeSubstituter(undetparams, targs).traverse(tree) + None } } @@ -1248,7 +1242,7 @@ trait Infer { * and that thus have not been substituted. */ def inferMethodInstance(fn: Tree, undetparams: List[Symbol], - args: List[Tree], pt0: Type): List[Symbol] = fn.tpe match { + args: List[Tree], pt0: Type): Either[ErrorTree, List[Symbol]] = fn.tpe match { case MethodType(params0, _) => printInference( ptBlock("inferMethodInstance", @@ -1268,31 +1262,32 @@ trait Infer { val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt) - checkBounds(fn.pos, NoPrefix, NoSymbol, undetparams, allargs, "inferred ") - val treeSubst = new TreeTypeSubstituter(okparams, okargs) - treeSubst traverseTrees fn :: args - - val result = leftUndet match { - case Nil => Nil - case xs => - // #3890 - val xs1 = treeSubst.typeSubst mapOver xs - if (xs ne xs1) - new TreeSymSubstTraverser(xs, xs1) traverseTrees fn :: args + checkBounds(fn.pos, NoPrefix, NoSymbol, undetparams, allargs, "inferred ") match { + case Some(err) => + Left(err) + case _ => + // bounds are ok + val treeSubst = new TreeTypeSubstituter(okparams, okargs) + treeSubst traverseTrees fn :: args + + val result = leftUndet match { + case Nil => Nil + case xs => + // #3890 + val xs1 = treeSubst.typeSubst mapOver xs + if (xs ne xs1) + new TreeSymSubstTraverser(xs, xs1) traverseTrees fn :: args + + xs1 + } + if (result.nonEmpty) + printInference("inferMethodInstance, still undetermined: " + result) - xs1 + Right(result) } - if (result.nonEmpty) - printInference("inferMethodInstance, still undetermined: " + result) - - result } catch ifNoInstance { msg => - errorTree(fn, "no type parameters for " + - applyErrorMsg(fn, " exist so that it can be applied to arguments ", args map (_.tpe.widen), WildcardType) + - "\n --- because ---\n" + msg - ) - Nil + Left(NoMethodInstanceError(fn, args, msg)) } } @@ -1317,7 +1312,7 @@ trait Infer { * @param undetparams the undetermined type parameters * @param pt the expected result type of the instance */ - def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) { + def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type): Option[ErrorTree] = { val pt = widen(pt0) //println("infer constr inst "+tree+"/"+undetparams+"/"+pt0) var restpe = tree.tpe.finalResultType @@ -1332,17 +1327,14 @@ trait Infer { // checkBounds(tree.pos, NoPrefix, NoSymbol, undetparams, targs, "inferred ") // no checkBounds here. If we enable it, test bug602 fails. new TreeTypeSubstituter(undetparams, targs).traverse(tree) - } catch { - case ex: NoInstance => - errorTree(tree, "constructor of type " + restpe + - " cannot be uniquely instantiated to expected type " + pt + - "\n --- because ---\n" + ex.getMessage()) + None + } catch ifNoInstance{ msg => + Some(NoConstructorInstanceError(tree, restpe, pt, msg)) } def instError = { if (settings.debug.value) Console.println("ici " + tree + " " + undetparams + " " + pt) if (settings.explaintypes.value) explainTypes(restpe.instantiateTypeParams(undetparams, tvars), pt) - errorTree(tree, "constructor cannot be instantiated to expected type" + - foundReqMsg(restpe, pt)) + Some(ConstrInstantiationError(tree, restpe, pt)) } if (restpe.instantiateTypeParams(undetparams, tvars) <:< pt) { computeArgs @@ -1360,6 +1352,7 @@ trait Infer { val pt1 = pt.instantiateTypeParams(ptparams, ptvars) if (isPopulated(restpe, pt1)) { ptvars foreach instantiateTypeVar + None } else { if (settings.debug.value) Console.println("no instance: "); instError } } else { if (settings.debug.value) Console.println("not a subtype " + restpe.instantiateTypeParams(undetparams, tvars) + " of " + ptWithWildcards); instError } } else { if (settings.debug.value) Console.println("not fully defined: " + pt); instError } @@ -1412,11 +1405,12 @@ trait Infer { } } - def checkCheckable(pos: Position, tp: Type, kind: String) { + def checkCheckable(pos: Position, tp: Type, kind: String): Option[ErrorTree] = { def patternWarning(tp0: Type, prefix: String) = { context.unit.uncheckedWarning(pos, prefix+tp0+" in type "+kind+tp+" is unchecked since it is eliminated by erasure") } - def check(tp: Type, bound: List[Symbol]) { + def check(tp: Type, bound: List[Symbol]): Option[ErrorTree] = { + implicit def listErrorsToPending(l: List[ErrorTree]): Option[ErrorTree] = if (l.isEmpty) None else Some(PendingErrors(l)) def isLocalBinding(sym: Symbol) = sym.isAbstractType && ((bound contains sym) || @@ -1428,36 +1422,38 @@ trait Infer { case SingleType(pre, _) => check(pre, bound) case TypeRef(pre, sym, args) => - if (sym.isAbstractType) { + val checkForSymAndArgs: Option[ErrorTree] = if (sym.isAbstractType) { if (!isLocalBinding(sym)) patternWarning(tp, "abstract type ") + None } else if (sym.isAliasType) { check(tp.normalize, bound) } else if (sym == NothingClass || sym == NullClass || sym == AnyValClass) { - error(pos, "type "+tp+" cannot be used in a type pattern or isInstanceOf test") + Some(TypePatternOrIsInstanceTestError(pos, tp)) } else { - for (arg <- args) { + args.map( arg => { if (sym == ArrayClass) check(arg, bound) - else if (arg.typeArgs.nonEmpty) () // avoid spurious warnings with higher-kinded types - else arg match { + else if (arg.typeArgs.nonEmpty) None // avoid spurious warnings with higher-kinded types + else {arg match { case TypeRef(_, sym, _) if isLocalBinding(sym) => - ; + case _ => patternWarning(arg, "non variable type-argument ") - } - } + }; None} + }).flatten } - check(pre, bound) + List(checkForSymAndArgs, check(pre, bound)).flatten case RefinedType(parents, decls) => - if (decls.isEmpty) for (p <- parents) check(p, bound) - else patternWarning(tp, "refinement ") + if (decls.isEmpty) parents.map(p => check(p, bound)).flatten + else { patternWarning(tp, "refinement "); None } case ExistentialType(quantified, tp1) => check(tp1, bound ::: quantified) case ThisType(_) => - ; + None case NoPrefix => - ; + None case _ => patternWarning(tp, "type ") + None } } check(tp, List()) @@ -1480,7 +1476,7 @@ trait Infer { } } - def inferTypedPattern(pos: Position, pattp: Type, pt0: Type): Type = { + def inferTypedPattern(pos: Position, pattp: Type, pt0: Type): Either[ErrorTree, Type] = { val pt = widen(pt0) val ptparams = freeTypeParamsOfTerms.collect(pt) val tpparams = freeTypeParamsOfTerms.collect(pattp) @@ -1493,9 +1489,13 @@ trait Infer { * and is a "final type", meaning final + invariant in all type parameters. */ if (pt.isFinalType && ptparams.isEmpty && !ptMatchesPattp) - error(pos, "scrutinee is incompatible with pattern type" + foundReqMsg(pattp, pt)) + return Left(IncompatibleScrutineeTypeError(pos, pattp, pt)) - checkCheckable(pos, pattp, "pattern ") + checkCheckable(pos, pattp, "pattern ") match { + case Some(err) => + return Left(err) + case _ => () + } if (pattp <:< pt) () else { debuglog("free type params (1) = " + tpparams) @@ -1517,10 +1517,8 @@ trait Infer { // fail if we didn't allow for pattpMatchesPt. if (isPopulated(tp, pt1) && isInstantiatable(tvars ++ ptvars) || pattpMatchesPt) ptvars foreach instantiateTypeVar - else { - error(pos, "pattern type is incompatible with expected type" + foundReqMsg(pattp, pt)) - return pattp - } + else + return Left(IncompletePatternTypeError(pos, pattp, pt)) } tvars foreach instantiateTypeVar } @@ -1528,43 +1526,22 @@ trait Infer { * we have to flip the arguments so the expected type is treated as more * general when calculating the intersection. See run/bug2755.scala. */ - if (tpparams.isEmpty && ptparams.nonEmpty) intersect(pattp, pt) - else intersect(pt, pattp) + if (tpparams.isEmpty && ptparams.nonEmpty) Right(intersect(pattp, pt)) + else Right(intersect(pt, pattp)) } - def inferModulePattern(pat: Tree, pt: Type) = + def inferModulePattern(pat: Tree, pt: Type): Option[ErrorTree] = if (!(pat.tpe <:< pt)) { val ptparams = freeTypeParamsOfTerms.collect(pt) debuglog("free type params (2) = " + ptparams) val ptvars = ptparams map freshVar val pt1 = pt.instantiateTypeParams(ptparams, ptvars) - if (pat.tpe <:< pt1) + if (pat.tpe <:< pt1) { ptvars foreach instantiateTypeVar - else { - val sym = pat.tpe.typeSymbol - val clazz = sym.companionClass - val addendum = ( - if (sym.isModuleClass && clazz.isCaseClass && (clazz isSubClass pt1.typeSymbol)) { - // TODO: move these somewhere reusable. - val typeString = clazz.typeParams match { - case Nil => "" + clazz.name - case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") - } - val caseString = ( - clazz.caseFieldAccessors - map (_ => "_") // could use the actual param names here - mkString (clazz.name + "(", ",", ")") - ) - ( - "\nNote: if you intended to match against the class, try `case _: " + - typeString + "` or `case " + caseString + "`" - ) - } - else "" - ) - error(pat.pos, "pattern type is incompatible with expected type"+foundReqMsg(pat.tpe, pt) + addendum) - } - } + None + } else + Some(PatternTypeIncompatibleWithPtError(pat, pt1, pt)) + } else None object toOrigin extends TypeMap { def apply(tp: Type): Type = tp match { @@ -1631,7 +1608,7 @@ trait Infer { * If several alternatives match `pt`, take parameterless one. * If no alternative matches `pt`, take the parameterless one anyway. */ - def inferExprAlternative(tree: Tree, pt: Type): Unit = tree.tpe match { + def inferExprAlternative(tree: Tree, pt: Type): Option[ErrorTree] = tree.tpe match { case OverloadedType(pre, alts) => tryTwice { val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) val secondTry = alts0.isEmpty @@ -1662,24 +1639,27 @@ trait Infer { case _ => } } - typeErrorTree(tree, tree.symbol.tpe, pt) + Some(NoBestExprAlternativeError(tree, pt)) } else if (!competing.isEmpty) { if (secondTry) { - typeErrorTree(tree, tree.symbol.tpe, pt) + Some(NoBestExprAlternativeError(tree, pt)) } else { if (!pt.isErroneous) - context.ambiguousError(tree.pos, pre, best, competing.head, "expected type " + pt) - setError(tree) + Some(AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt)) + else + Some(NullErrorTree) // already reported } } else { // val applicable = alts1 filter (alt => // global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) // checkNotShadowed(tree.pos, pre, best, applicable) tree.setSymbol(best).setType(pre.memberType(best)) + None } } } + // TODO: remove once error tree refactoring is done @inline private def wrapTypeError(expr: => Boolean): Boolean = try expr catch { case _: TypeError => false } @@ -1743,7 +1723,7 @@ trait Infer { * assignment expression. */ def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], - argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false): Unit = tree.tpe match { + argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false): Option[ErrorTree] = tree.tpe match { case OverloadedType(pre, alts) => val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 tryTwice { @@ -1770,24 +1750,22 @@ trait Infer { if (improves(alt, best)) alt else best) val competing = applicable.dropWhile(alt => best == alt || improves(best, alt)) if (best == NoSymbol) { - if (pt == WildcardType) { - errorTree(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) - } else { + if (pt == WildcardType) + Some(NoBestMethodAlternativeError(tree, argtpes, pt)) + else inferMethodAlternative(tree, undetparams, argtpes, WildcardType) - } } else if (!competing.isEmpty) { if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) - context.ambiguousError(tree.pos, pre, best, competing.head, - "argument types " + argtpes.mkString("(", ",", ")") + - (if (pt == WildcardType) "" else " and expected result type " + pt)) - setError(tree) - () + Some(AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt)) + else + Some(NullErrorTree) } else { // checkNotShadowed(tree.pos, pre, best, applicable) tree.setSymbol(best).setType(pre.memberType(best)) + None } } - case _ => + case _ => None } /** Try inference twice, once without views and once with views, @@ -1795,18 +1773,25 @@ trait Infer { * * @param infer ... */ - def tryTwice(infer: => Unit) { + def tryTwice(infer: => Option[ErrorTree]): Option[ErrorTree] = { if (context.implicitsEnabled) { val reportGeneralErrors = context.reportGeneralErrors context.reportGeneralErrors = false - try context.withImplicitsDisabled(infer) - catch { + val res = try { + context.withImplicitsDisabled(infer) match { + case Some(err) => + context.reportGeneralErrors = reportGeneralErrors + infer + case ok => ok + } + } catch { case ex: CyclicReference => throw ex case ex: TypeError => context.reportGeneralErrors = reportGeneralErrors infer } context.reportGeneralErrors = reportGeneralErrors + res } else infer } @@ -1818,10 +1803,10 @@ trait Infer { * @param tree ... * @param nparams ... */ - def inferPolyAlternatives(tree: Tree, argtypes: List[Type]): Unit = { + def inferPolyAlternatives(tree: Tree, argtypes: List[Type]): Option[ErrorTree] = { val OverloadedType(pre, alts) = tree.tpe val sym0 = tree.symbol filter (alt => sameLength(alt.typeParams, argtypes)) - def fail(msg: String): Unit = error(tree.pos, msg) + def fail(msg: String) = Some(PolyAlternativeError(tree, msg)) if (sym0 == NoSymbol) return fail( if (alts exists (_.typeParams.nonEmpty)) @@ -1835,12 +1820,13 @@ trait Infer { else { val sym = sym0 filter (alt => isWithinBounds(pre, alt.owner, alt.typeParams, argtypes)) if (sym == NoSymbol) { - if (argtypes forall (x => !x.isErroneous)) fail( + if (argtypes forall (x => !x.isErroneous)) + return fail( "type arguments " + argtypes.mkString("[", ",", "]") + " conform to the bounds of none of the overloaded alternatives of\n "+sym0+ ": "+sym0.info ) - return + return None } else if (sym.isOverloaded) { val xs = sym.alternatives @@ -1855,24 +1841,7 @@ trait Infer { } // Side effects tree with symbol and type tree setSymbol resSym setType resTpe - } - - abstract class TreeForwarder(forwardTo: Tree) extends Tree { - override def pos = forwardTo.pos - override def hasSymbol = forwardTo.hasSymbol - override def symbol = forwardTo.symbol - override def symbol_=(x: Symbol) = forwardTo.symbol = x - } - - case class AccessError(tree: Tree, sym: Symbol, pre: Type, explanation: String) extends TreeForwarder(tree) { - setError(this) - - // @PP: It is improbable this logic shouldn't be in use elsewhere as well. - private def location = if (sym.isClassConstructor) context.enclClass.owner else pre.widen - def emit(): Tree = { - val realsym = underlying(sym) - errorTree(tree, realsym.fullLocationString + " cannot be accessed in " + location + explanation) - } + None } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 56651f9af9..5a336d9027 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -65,13 +65,26 @@ trait Namers { self: Analyzer => classAndNamerOfModule.clear } - abstract class Namer(val context: Context) { + abstract class Namer(val context: Context) extends NamerErrorTrees { val typer = newTyper(context) def setPrivateWithin[Sym <: Symbol](tree: Tree, sym: Sym, mods: Modifiers): Sym = { - if (mods.hasAccessBoundary) - sym.privateWithin = typer.qualifyingClass(tree, mods.privateWithin, true) + if (mods.hasAccessBoundary) { + val symOrError = typer.qualifyingClass(tree, mods.privateWithin, true) match { + case Left(err) => + try { + err.emit(context) + } catch { + case _: TypeError => + assert(false, "qualifying class info can fail but cannot throw type errors") + } + NoSymbol + case Right(sym) => + sym + } + sym.privateWithin = symOrError + } sym } @@ -151,6 +164,7 @@ trait Namers { self: Analyzer => private def setInfo[Sym <: Symbol](sym : Sym)(tpe : LazyType) : Sym = sym.setInfo(tpe) + // TODO: make it into error trees private def doubleDefError(pos: Position, sym: Symbol) { val s1 = if (sym.isModule) "case class companion " else "" val s2 = if (sym.isSynthetic) "(compiler-generated) " + s1 else "" @@ -634,10 +648,18 @@ trait Namers { self: Analyzer => case _ => } sym.setInfo(if (sym.isJavaDefined) RestrictJavaArraysMap(tp) else tp) - if ((sym.isAliasType || sym.isAbstractType) && !sym.isParameter && - !typer.checkNonCyclic(tree.pos, tp)) - sym.setInfo(ErrorType) // this early test is there to avoid infinite baseTypes when - // adding setters and getters --> bug798 + if ((sym.isAliasType || sym.isAbstractType) && !sym.isParameter) { + val check = typer.checkNonCyclic(tree.pos, tp) + if (check.isDefined) { + sym.setInfo(ErrorType) // this early test is there to avoid infinite baseTypes when + // adding setters and getters --> bug798 + try { + check.get.emit(context) + } catch { + case _: TypeError => assert(false, "Type errors cannot be thrown in type completers") + } + } + } debuglog("defined " + sym); validate(sym) } @@ -803,7 +825,14 @@ trait Namers { self: Analyzer => } */ - var parents = typer.parentTypes(templ) map checkParent + var parents0 = typer.parentTypes(templ) + try { + parents0.foreach(p => if (p.containsError()) typer.emitAllErrorTrees(p, context)) + } catch { + case _: TypeError => + assert(false, "parent types cannot throw type errors") + } + val parents = parents0 map checkParent enterSelf(templ.self) val decls = new Scope // for (etdef <- earlyTypes) decls enter etdef.symbol @@ -1287,7 +1316,10 @@ trait Namers { self: Analyzer => case Import(expr, selectors) => val expr1 = typer.typedQualifier(expr) - typer checkStable expr1 + val stable = typer.checkStable(expr1) + if (stable.containsError()) + typer.emitAllErrorTrees(stable, context) + if (expr1.symbol != null && expr1.symbol.isRootPackage) context.error(tree.pos, "_root_ cannot be imported") @@ -1303,7 +1335,10 @@ trait Namers { self: Analyzer => } catch { case ex: TypeError => //Console.println("caught " + ex + " in typeSig") - typer.reportTypeError(tree.pos, ex) + // TODO: once ErrorTrees are implemented we should be able + // to get rid of this catch and simply report the error + // (maybe apart from cyclic errors) + TypeSigError(tree, ex).emit(context) ErrorType } result match { diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index bf7cc72fab..cef099e371 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -304,7 +304,9 @@ trait NamesDefaults { self: Analyzer => // `fun` is typed. `namelessArgs` might be typed or not, if they are types are kept. case Apply(fun, namelessArgs) => val transformedFun = transformNamedApplication(typer, mode, pt)(fun, x => x) - if (transformedFun.isErroneous) setError(tree) + // Is it safe to replace containsError() with containsErrorOrIsErrorTyped()? + if (transformedFun.containsError()) transformedFun + else if (transformedFun.isErroneous) NullErrorTree else { assert(isNamedApplyBlock(transformedFun), transformedFun) val NamedApplyInfo(qual, targs, vargss, blockTyper) = @@ -313,8 +315,7 @@ trait NamesDefaults { self: Analyzer => // type the application without names; put the arguments in definition-site order val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt) - - if (typedApp.tpe.isError) setError(tree) + if (typedApp.containsErrorOrIsErrorTyped()) typedApp else typedApp match { // Extract the typed arguments, restore the call-site evaluation order (using // ValDef's in the block), change the arguments to these local values. @@ -439,7 +440,6 @@ trait NamesDefaults { self: Analyzer => * after named ones. */ def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = { - import typer.infer.errorTree // maps indicies from (order written by user) to (order of definition) val argPos = (new Array[Int](args.length)) map (x => -1) @@ -457,10 +457,10 @@ trait NamesDefaults { self: Analyzer => // treat the arg as an assignment of type Unit Assign(a.lhs, rhs).setPos(arg.pos) } else { - errorTree(arg, "unknown parameter name: "+ name) + UnknwonParameterNameNamesDefaultError(arg, name) } } else if (argPos contains pos) { - errorTree(arg, "parameter specified twice: "+ name) + DoubleParamNamesDefaultError(arg, name) } else { // for named arguments, check whether the assignment expression would // typecheck. if it does, report an ambiguous error. @@ -481,26 +481,20 @@ trait NamesDefaults { self: Analyzer => val reportAmbiguousErrors = typer.context.reportAmbiguousErrors typer.context.reportAmbiguousErrors = false - var variableNameClash = false val typedAssign = try { typer.silent(_.typed(arg, subst(paramtpe))) } catch { // `silent` only catches and returns TypeErrors which are not // CyclicReferences. Fix for #3685 + + // Returning CyclicReference error trees is problematic + // so we stay with throwing exceptions case cr @ CyclicReference(sym, info) if sym.name == param.name => if (sym.isVariable || sym.isGetter && sym.accessed.isVariable) { // named arg not allowed - variableNameClash = true - typer.context.error(sym.pos, - "%s definition needs %s because '%s' is used as a named argument in its body.".format( - "variable", // "method" - "type", // "result type" - sym.name - ) - ) - typer.infer.setError(arg) + NameClashError(sym, arg) } - else cr + else NullErrorTree } def applyNamedArg = { @@ -513,28 +507,27 @@ trait NamesDefaults { self: Analyzer => } val res = typedAssign match { - case _: TypeError => applyNamedArg - + case err: NameClashError => + err + case _: TypeError => + // TODO: is should be safe to remove this case after error trees are fully implemented + applyNamedArg + case t: Tree if t.containsErrorOrIsErrorTyped() => + // containsErrorOrIsErrorTyped() needed because of for instance #4041 + applyNamedArg case t: Tree => - if (t.isErroneous && !variableNameClash) { - applyNamedArg - } else if (t.isErroneous) { - t // name clash with variable. error was already reported above. - } else { - // This throws an exception which is caught in `tryTypedApply` (as it - // uses `silent`) - unfortunately, tryTypedApply recovers from the - // exception if you use errorTree(arg, ...) and conforms is allowed as - // a view (see tryImplicit in Implicits) because it tries to produce a - // new qualifier (if the old one was P, the new one will be - // conforms.apply(P)), and if that works, it pretends nothing happened. - // - // To make sure tryTypedApply fails, we would like to pass EmptyTree - // instead of arg, but can't do that because eventually setType(ErrorType) - // is called, and EmptyTree can only be typed NoType. Thus we need to - // disable conforms as a view... - errorTree(arg, "reference to "+ name +" is ambiguous; it is both, a parameter\n"+ - "name of the method and the name of a variable currently in scope.") - } + // This throws an exception which is caught in `tryTypedApply` (as it + // uses `silent`) - unfortunately, tryTypedApply recovers from the + // exception if you use errorTree(arg, ...) and conforms is allowed as + // a view (see tryImplicit in Implicits) because it tries to produce a + // new qualifier (if the old one was P, the new one will be + // conforms.apply(P)), and if that works, it pretends nothing happened. + // + // To make sure tryTypedApply fails, we would like to pass EmptyTree + // instead of arg, but can't do that because eventually setType(ErrorType) + // is called, and EmptyTree can only be typed NoType. Thus we need to + // disable conforms as a view... + AmbiguousReferenceInNamesDefaultError(arg, name) } typer.context.reportAmbiguousErrors = reportAmbiguousErrors @@ -546,8 +539,9 @@ trait NamesDefaults { self: Analyzer => case _ => argPos(index) = index if (positionalAllowed) arg - else errorTree(arg, "positional after named argument.") + else PositionalAfterNamedNamesDefaultError(arg) } + (namelessArgs, argPos) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 10918ecffa..c05a5e721e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1150,10 +1150,15 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R /* Check whether argument types conform to bounds of type parameters */ private def checkBounds(pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type], pos: Position): Unit = - try typer.infer.checkBounds(pos, pre, owner, tparams, argtps, "") + try typer.infer.checkBounds(pos, pre, owner, tparams, argtps, "") match { + case Some(err) => err.emit(typer.context) + case _ => () + } catch { case ex: TypeError => - unit.error(pos, ex.getMessage()); + // checkBounds no longer throws errors (apart from Cyclic ones) + // so maybe it is safe to remove/simplify this catch? + unit.error(pos, ex.getMessage()) if (settings.explaintypes.value) { val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, argtps).bounds) (argtps, bounds).zipped map ((targ, bound) => explainTypes(bound.lo, targ)) @@ -1310,7 +1315,13 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case tpt@TypeTree() => if(tpt.original != null) { tpt.original foreach { - case dc@TypeTreeWithDeferredRefCheck() => applyRefchecksToAnnotations(dc.check()) // #2416 + case dc@TypeTreeWithDeferredRefCheck() => + dc.check() match { + case Left(err) => + err.emit() + case Right(t) => + applyRefchecksToAnnotations(t) // #2416 + } case _ => } } @@ -1474,8 +1485,11 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R if(tpt.original != null) { tpt.original foreach { case dc@TypeTreeWithDeferredRefCheck() => - transform(dc.check()) // #2416 -- only call transform to do refchecks, but discard results - // tpt has the right type if the deferred checks are ok + dc.check() match { + case Left(err) => err.emit() + case Right(tree) => transform(tree) // #2416 -- only call transform to do refchecks, but discard results + // tpt has the right type if the deferred checks are ok + } case _ => } } @@ -1551,7 +1565,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R result } catch { case ex: TypeError => - if (settings.debug.value) ex.printStackTrace(); + if (settings.debug.value) ex.printStackTrace() unit.error(tree.pos, ex.getMessage()) tree } finally { diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 03d1c66a36..35709ac40c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -52,10 +52,11 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT private def checkPackedConforms(tree: Tree, pt: Type): Tree = { if (tree.tpe exists (_.typeSymbol.isExistentialSkolem)) { val packed = localTyper.packedType(tree, NoSymbol) - if (!(packed <:< pt)) { + packed._2.foreach(_.emit()) + if (!(packed._1 <:< pt)) { val errorContext = localTyper.context.make(localTyper.context.tree) errorContext.reportGeneralErrors = true - analyzer.newTyper(errorContext).infer.typeError(tree.pos, packed, pt) + analyzer.newTyper(errorContext).infer.typeError(tree.pos, packed._1, pt) } } tree diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 9a8bcca2ba..6eaa65ce9b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -41,7 +41,7 @@ trait SyntheticMethods extends ast.TreeDSL { import CODE._ val localTyper = newTyper( - if (reporter.hasErrors) context makeSilent false else context + if (reporter.hasErrors || templ.containsError()) context makeSilent false else context ) def accessorTypes = clazz.caseFieldAccessors map (_.tpe.finalResultType) def accessorLub = global.weakLub(accessorTypes)._1 diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 6e0e78e8e2..0b84e67447 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -135,21 +135,6 @@ trait TypeDiagnostics { def alternativesString(tree: Tree) = alternatives(tree) map (x => " " + methodTypeErrorString(x)) mkString ("", " <and>\n", "\n") - def missingParameterTypeMsg(fun: Tree, vparam: ValDef, pt: Type) = { - def anonMessage = ( - "\nThe argument types of an anonymous function must be fully known. (SLS 8.5)" + - "\nExpected type was: " + pt.toLongString - ) - val suffix = - if (!vparam.mods.isSynthetic) "" - else " for expanded function" + (fun match { - case Function(_, Match(_, _)) => anonMessage - case _ => " " + fun - }) - - "missing parameter type" + suffix - } - def treeSymTypeMsg(tree: Tree): String = { val sym = tree.symbol def hasParams = tree.tpe.paramSectionCount > 0 @@ -168,34 +153,6 @@ trait TypeDiagnostics { else defaultMessage } - def notEnoughArgumentsMsg(fun: Tree, missing: List[Symbol]): String = { - val suffix = { - if (missing.isEmpty) "" - else { - val keep = missing take 3 map (_.name) - ".\nUnspecified value parameter%s %s".format( - if (missing.tail.isEmpty) "" else "s", - if (missing drop 3 nonEmpty) (keep :+ "...").mkString(", ") - else keep.mkString("", ", ", ".") - ) - } - } - - "not enough arguments for " + treeSymTypeMsg(fun) + suffix - } - - def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) = { - def asParams(xs: List[Any]) = xs.mkString("(", ", ", ")") - - def resType = if (pt isWildcard) "" else " with expected result type " + pt - def allTypes = (alternatives(tree) flatMap (_.paramTypes)) ++ argtpes :+ pt - def locals = alternatives(tree) flatMap (_.typeParams) - - withDisambiguation(locals, allTypes: _*) { - treeSymTypeMsg(tree) + msg + asParams(argtpes) + resType - } - } - def disambiguate(ss: List[String]) = ss match { case Nil => Nil case s :: ss => s :: (ss map { case `s` => "(some other)"+s ; case x => x }) @@ -405,8 +362,10 @@ trait TypeDiagnostics { trait TyperDiagnostics { self: Typer => - private def contextError(pos: Position, msg: String) = context.error(pos, msg) - private def contextError(pos: Position, err: Throwable) = context.error(pos, err) + private def contextError(pos: Position, msg: String) { contextError(context, pos, msg) } + private def contextError(context0: Analyzer#Context, pos: Position, msg: String) { context0.error(pos, msg) } + private def contextError(pos: Position, err: Throwable) { contextError(context, pos, err) } + private def contextError(context0: Analyzer#Context, pos: Position, err: Throwable) { context0.error(pos, err) } object checkDead { private var expr: Symbol = NoSymbol @@ -440,36 +399,46 @@ trait TypeDiagnostics { } } - def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded - def cyclicAdjective(sym: Symbol) = if (symWasOverloaded(sym)) "overloaded" else "recursive" + private def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded + private def cyclicAdjective(sym: Symbol) = if (symWasOverloaded(sym)) "overloaded" else "recursive" /** Returns Some(msg) if the given tree is untyped apparently due * to a cyclic reference, and None otherwise. */ - def cyclicReferenceMessage(sym: Symbol, tree: Tree) = condOpt(tree) { + private def cyclicReferenceMessage(sym: Symbol, tree: Tree) = condOpt(tree) { case ValDef(_, _, tpt, _) if tpt.tpe == null => "recursive "+sym+" needs type" case DefDef(_, _, _, _, tpt, _) if tpt.tpe == null => List(cyclicAdjective(sym), sym, "needs result type") mkString " " } + def reportTypeError(pos: Position, ex: TypeError) { + reportTypeError(context, pos, ex) + } + /** Report a type error. * * @param pos0 The position where to report the error * @param ex The exception that caused the error */ - def reportTypeError(pos: Position, ex: TypeError) { + def reportTypeError(context0: Analyzer#Context, pos: Position, ex: TypeError) { if (ex.pos == NoPosition) ex.pos = pos - if (!context.reportGeneralErrors) throw ex + if (!context0.reportGeneralErrors) throw ex if (settings.debug.value) ex.printStackTrace() ex match { case CyclicReference(sym, info: TypeCompleter) => - contextError(ex.pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) + contextError(context0, ex.pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) if (sym == ObjectClass) throw new FatalError("cannot redefine root "+sym) case _ => - contextError(ex.pos, ex) + contextError(context0, ex.pos, ex) } } + + def emitAllErrorTrees(tree: Tree, context: Context) = + errorTreesFinder(tree).foreach(_.emit(context)) + + def findAllNestedErrors(trees: List[Tree]): List[ErrorTree] = + trees.map(errorTreesFinder(_)).flatten } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index eb5a575596..e6b23d6097 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -12,13 +12,13 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } -import scala.tools.nsc.util.BatchSourceFile +import scala.collection.mutable import mutable.ListBuffer + +import scala.tools.nsc.util.BatchSourceFile import symtab.Flags._ -import util.Statistics import util.Statistics._ -import scala.tools.util.StringOps.{ countAsString, countElementsAsString } +import util.Statistics // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -59,7 +59,7 @@ trait Typers extends Modes with Adaptations { super.traverse(tree) } } -/* needed for experimental version where eraly types can be type arguments +/* needed for experimental version where early types can be type arguments class EarlyMap(clazz: Symbol) extends TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(NoPrefix, sym, List()) if (sym hasFlag PRESUPER) => @@ -77,7 +77,7 @@ trait Typers extends Modes with Adaptations { // that are turned private by typedBlock private final val SYNTHETIC_PRIVATE = TRANS_FLAG - abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation { + abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with TyperErrorTrees { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine } @@ -99,15 +99,6 @@ trait Typers extends Modes with Adaptations { def mkNamedArg(argTree: Tree, paramName: Name) = atPos(argTree.pos)(new AssignOrNamedArg(Ident(paramName), (argTree))) var mkArg: (Tree, Name) => Tree = mkPositionalArg - def errorMessage(paramName: Name, paramTp: Type) = - paramTp.typeSymbol match { - case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) - case _ => - "could not find implicit value for "+ - (if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type " - else "parameter "+paramName+": ")+paramTp - } - // DEPMETTODO: instantiate type vars that depend on earlier implicit args (see adapt (4.1)) // // apply the substitutions (undet type param -> type) that were determined @@ -124,8 +115,9 @@ trait Typers extends Modes with Adaptations { argBuff += mkArg(res.tree, param.name) } else { mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args - if (!param.hasDefault) - context.error(fun.pos, errorMessage(param.name, param.tpe)) + if (!param.hasDefault) { + argBuff += NoImplicitFoundError(fun, param) + } /* else { TODO: alternative (to expose implicit search failure more) --> resolve argument, do type inference, keep emitting positional args, infer type params based on default value for arg @@ -186,6 +178,20 @@ trait Typers extends Modes with Adaptations { var context = context0 def context1 = context + @inline + private def logErrorTree(t: Tree, buffer: List[ErrorTree]) = { + t match { + case et: ErrorTree => + et::buffer + case _ => + buffer + } + } + + @inline + private def wrapInBlock(errTree: Tree, original: Tree): Tree = Block(errTree, original) setType ErrorType + + def dropExistential(tp: Type): Type = tp match { case ExistentialType(tparams, tpe) => new SubstWildcardMap(tparams).apply(tp) @@ -202,22 +208,12 @@ trait Typers extends Modes with Adaptations { * @return ... */ def checkStable(tree: Tree): Tree = - if (treeInfo.isPureExpr(tree)) tree - else errorTree( - tree, - "stable identifier required, but "+tree+" found."+ - (if (isStableExceptVolatile(tree)) { - val tpe = tree.symbol.tpe match { - case PolyType(_, rtpe) => rtpe - case t => t - } - "\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile." - } else "")) + if (treeInfo.isPureExpr(tree)) tree else UnstableTreeError(tree) /** Would tree be a stable (i.e. a pure expression) if the type * of its symbol was not volatile? */ - private def isStableExceptVolatile(tree: Tree) = { + protected def isStableExceptVolatile(tree: Tree) = { tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile && { val savedTpe = tree.symbol.info val savedSTABLE = tree.symbol getFlag STABLE @@ -231,21 +227,21 @@ trait Typers extends Modes with Adaptations { } /** Check that `tpt` refers to a non-refinement class type */ - def checkClassType(tpt: Tree, existentialOK: Boolean, stablePrefix: Boolean) { - def errorNotClass(found: AnyRef) = error(tpt.pos, "class type required but "+found+" found") - def check(tpe: Type): Unit = tpe.normalize match { + def checkClassType(tpt: Tree, existentialOK: Boolean, stablePrefix: Boolean) = { + def errorNotClass(found: AnyRef) = Some(ClassTypeRequiredError(tpt, found)) + def check(tpe: Type): Option[ErrorTree] = tpe.normalize match { case TypeRef(pre, sym, _) if sym.isClass && !sym.isRefinementClass => - if (stablePrefix && phase.id <= currentRun.typerPhase.id) { + if (stablePrefix && phase.id <= currentRun.typerPhase.id) if (!pre.isStable) - error(tpt.pos, "type "+pre+" is not a stable prefix") - // A type projection like X#Y can get by the stable check if the - // prefix is singleton-bounded, so peek at the tree too. - else tpt match { + Some(TypeNotAStablePrefixError(pre, tpt.pos)) + else + tpt match { case SelectFromTypeTree(qual, _) if !isSingleType(qual.tpe) => errorNotClass(tpt) - case _ => ; - } - } - case ErrorType => ; + case _ => None + } + else + None + case ErrorType => None case PolyType(_, restpe) => check(restpe) case ExistentialType(_, restpe) if existentialOK => check(restpe) case AnnotatedType(_, underlying, _) => check(underlying) @@ -260,18 +256,19 @@ trait Typers extends Modes with Adaptations { * @param tp ... * @return <code>true</code> if <code>tp</code> is not a subtype of itself. */ - def checkNonCyclic(pos: Position, tp: Type): Boolean = { - def checkNotLocked(sym: Symbol): Boolean = { + def checkNonCyclic(pos: Position, tp: Type): Option[ErrorTree] = { + def checkNotLocked(sym: Symbol): Option[ErrorTree] = { sym.initialize - sym.lockOK || {error(pos, "cyclic aliasing or subtyping involving "+sym); false} + if (sym.lockOK) None + else Some(CyclicAliasingOrSubtypingError(pos, sym)) } tp match { case TypeRef(pre, sym, args) => - (checkNotLocked(sym)) && ( - !sym.isNonClassType || - checkNonCyclic(pos, appliedType(pre.memberInfo(sym), args), sym) // @M! info for a type ref to a type parameter now returns a polytype - // @M was: checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym) - ) + val check = checkNotLocked(sym) + if (check.isDefined) check + else if (!sym.isNonClassType) None + else checkNonCyclic(pos, appliedType(pre.memberInfo(sym), args), sym) + case SingleType(pre, sym) => checkNotLocked(sym) /* @@ -283,51 +280,70 @@ trait Typers extends Modes with Adaptations { case st: SubType => checkNonCyclic(pos, st.supertype) case ct: CompoundType => - ct.parents forall (x => checkNonCyclic(pos, x)) + val checkRes = ct.parents map (x => checkNonCyclic(pos, x)) flatten + + if (checkRes.isEmpty) None + else Some(PendingErrors(checkRes)) case _ => - true + None } } - def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = try { + // TODO in order to return CyclicReferenceError we need to change the signature of lock + // let's leave it for now + def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Option[ErrorTree] = try { lockedSym.lock { throw new TypeError("illegal cyclic reference involving " + lockedSym) } checkNonCyclic(pos, tp) + } catch { + case _ :TypeError => + Some(CyclicReferenceError(pos, lockedSym)) } finally { lockedSym.unlock() } - def checkNonCyclic(sym: Symbol) { - if (!checkNonCyclic(sym.pos, sym.tpe)) sym.setInfo(ErrorType) + def checkNonCyclic(sym: Symbol): Option[ErrorTree] = { + val check = checkNonCyclic(sym.pos, sym.tpe) + if (check.isDefined) + sym.setInfo(ErrorType) + check } - def checkNonCyclic(defn: Tree, tpt: Tree) { - if (!checkNonCyclic(defn.pos, tpt.tpe, defn.symbol)) { + def checkNonCyclic(defn: Tree, tpt: Tree): Option[ErrorTree] = { + val check = checkNonCyclic(defn.pos, tpt.tpe, defn.symbol) + if (check.isDefined) { tpt.tpe = ErrorType defn.symbol.setInfo(ErrorType) } + check } - def checkParamsConvertible(pos: Position, tpe: Type) { - tpe match { - case MethodType(formals, restpe) => - /* - if (formals.exists(_.typeSymbol == ByNameParamClass) && formals.length != 1) - error(pos, "methods with `=>`-parameter can be converted to function values only if they take no other parameters") - if (formals exists (isRepeatedParamType(_))) - error(pos, "methods with `*`-parameters cannot be converted to function values"); - */ - if (restpe.isDependent) - error(pos, "method with dependent type "+tpe+" cannot be converted to function value") - checkParamsConvertible(pos, restpe) - case _ => - } + def checkParamsConvertible(pos: Position, tpe0: Type): List[ErrorTree] = { + var pending: List[ErrorTree] = List() + def checkParamsConvertible0(tpe: Type) = + tpe match { + case MethodType(formals, restpe) => + /* + if (formals.exists(_.typeSymbol == ByNameParamClass) && formals.length != 1) + error(pos, "methods with `=>`-parameter can be converted to function values only if they take no other parameters") + if (formals exists (isRepeatedParamType(_))) + error(pos, "methods with `*`-parameters cannot be converted to function values"); + */ + if (restpe.isDependent) + pending = logErrorTree(DependentMethodTpeConversionToFunctionError(pos, tpe), pending) + checkParamsConvertible(pos, restpe) + case _ => + } + checkParamsConvertible0(tpe0) + pending } - def checkStarPatOK(pos: Position, mode: Int) = + @inline + private def checkStarPatOK(pos: Position, mode: Int):Option[ErrorTree] = if ((mode & STARmode) == 0 && phase.id <= currentRun.typerPhase.id) - error(pos, "star patterns must correspond with varargs parameters") + Some(StarPatternWithVarargParametersError(pos)) + else None /** Check that type of given tree does not contain local or private * components. @@ -345,7 +361,7 @@ trait Typers extends Modes with Adaptations { * @param tree ... * @return ... */ - def privates[T <: Tree](owner: Symbol, tree: T): T = + def privates(owner: Symbol, tree: Tree): Tree = check(owner, EmptyScope, WildcardType, tree) /** Check that type <code>tree</code> does not refer to entities @@ -356,16 +372,16 @@ trait Typers extends Modes with Adaptations { * @param tree ... * @return ... */ - def locals[T <: Tree](scope: Scope, pt: Type, tree: T): T = + def locals(scope: Scope, pt: Type, tree: Tree): Tree = check(NoSymbol, scope, pt, tree) - def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = { + private def check(owner: Symbol, scope: Scope, pt: Type, tree: Tree): Tree = { this.owner = owner this.scope = scope hiddenSymbols = List() val tp1 = apply(tree.tpe) if (hiddenSymbols.isEmpty) tree setType tp1 - else if (hiddenSymbols exists (_.isErroneous)) setError(tree) + else if (hiddenSymbols exists (_.isErroneous)) HiddenSymbolWithError(tree) //setError(tree) else if (isFullyDefined(pt)) tree setType pt //todo: eliminate else if (tp1.typeSymbol.isAnonymousClass) // todo: eliminate check(owner, scope, pt, tree setType tp1.typeSymbol.classBound) @@ -373,10 +389,7 @@ trait Typers extends Modes with Adaptations { tree setType packSymbols(hiddenSymbols.reverse, tp1) else if (!phase.erasedTypes) { // privates val badSymbol = hiddenSymbols.head - error(tree.pos, - (if (badSymbol.isPrivate) "private " else "") + badSymbol + - " escapes its defining scope as part of type "+tree.tpe) - setError(tree) + SymbolEscapesScopeError(tree, badSymbol) } else tree } @@ -439,16 +452,12 @@ trait Typers extends Modes with Adaptations { /** The qualifying class * of a this or super with prefix <code>qual</code>. */ - def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean): Symbol = + def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean): Either[ErrorTree, Symbol] = context.enclClass.owner.ownerChain.find(o => qual.isEmpty || o.isClass && o.name == qual) match { case Some(c) if packageOK || !c.isPackageClass => - c + Right(c) case _ => - error( - tree.pos, - if (qual.isEmpty) tree+" can be used only in a class, object, or template" - else qual+" is not an enclosing class") - NoSymbol + Left(QualifyingClassError(tree, qual)) } /** The typer for an expression, depending on where we are. If we are before a superclass @@ -565,14 +574,19 @@ trait Typers extends Modes with Adaptations { * 2. Check that packages and static modules are not used as values * 3. Turn tree type into stable type if possible and required by context. */ - private def stabilize(tree: Tree, pre: Type, mode: Int, pt: Type): Tree = { - if (tree.symbol.isOverloaded && !inFunMode(mode)) - inferExprAlternative(tree, pt) + private def stabilize(tree0: Tree, pre: Type, mode: Int, pt: Type): Tree = { + val tree = + if (tree0.symbol.isOverloaded && !inFunMode(mode)) + inferExprAlternative(tree0, pt) match { + case Some(err) => err + case _ => tree0 + } + else tree0 val sym = tree.symbol - def fail() = errorTree(tree, sym.kindString + " " + sym.fullName + " is not a value") + def fail() = NotAValueError(tree, sym) - if (tree.tpe.isError) tree + if (tree.containsErrorOrIsErrorTyped()) tree else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1) if (sym.isValue) checkStable(tree) else fail() @@ -652,6 +666,12 @@ trait Typers extends Modes with Adaptations { } catch { case ex: CyclicReference => throw ex case ex: TypeError => + // TODO: when move to error trees is complete we should + // be able to just drop this case + + // probably not, since we use it for Unit/List[Tree] as well. + // This method in theory will be eliminated/simplified once we are done + // with the transformation stopCounter(rawTypeFailed, rawTypeStart) stopCounter(findMemberFailed, findMemberStart) stopCounter(subtypeFailed, subtypeStart) @@ -660,23 +680,6 @@ trait Typers extends Modes with Adaptations { } } - /** Utility method: Try op1 on tree. If that gives an error try op2 instead. - */ - def tryBoth(tree: Tree)(op1: (Typer, Tree) => Tree)(op2: (Typer, Tree) => Tree): Tree = - silent(op1(_, tree)) match { - case result1: Tree => - result1 - case ex1: TypeError => - silent(op2(_, resetAllAttrs(tree))) match { - case result2: Tree => -// println("snd succeeded: "+result2) - result2 - case ex2: TypeError => - reportTypeError(tree.pos, ex1) - setError(tree) - } - } - def isCodeType(tpe: Type) = tpe.typeSymbol isNonBottomSubClass CodeClass /** Perform the following adaptations of expression, pattern or type `tree` wrt to @@ -722,7 +725,6 @@ trait Typers extends Modes with Adaptations { if (context.undetparams nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0` // dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed // needed for implicits in 2.8 collection library -- maybe once #3346 is fixed, we can reinstate the condition? - context.undetparams = inferExprInstance(tree, context.extractUndetparams(), pt, // approximate types that depend on arguments since dependency on implicit argument is like dependency on type parameter if (settings.YdepMethTpes.value) mt.approximate else mt, @@ -731,14 +733,21 @@ trait Typers extends Modes with Adaptations { // Looking for a manifest of Nil: This has many potential types, // so we need to instantiate to minimal type List[Nothing]. keepNothings = false, // retract Nothing's that indicate failure, ambiguities in manifests are dealt with in manifestOfType - useWeaklyCompatible = true) // #3808 + useWeaklyCompatible = true) match {// #3808 + case (Some(err), undetparams1) => + context.undetparams = undetparams1 + return err + case (_, undetparams1) => + context.undetparams = undetparams1 + } } val typer1 = constrTyperIf(treeInfo.isSelfOrSuperConstrCall(tree)) if (original != EmptyTree && pt != WildcardType) typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match { - case result: Tree => result - case ex: TypeError => + case result: Tree if !result.containsError() => + result + case _ => debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) val tree1 = typed(resetAllAttrs(original), mode, WildcardType) tree1.tpe = addAnnotations(tree1, tree1.tpe) @@ -756,7 +765,10 @@ trait Typers extends Modes with Adaptations { } if (!meth.isConstructor && isFunctionType(pt)) { // (4.2) debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) - checkParamsConvertible(tree.pos, tree.tpe) + val errs = checkParamsConvertible(tree.pos, tree.tpe) + if (!errs.isEmpty) { + return PendingErrors(errs.reverse) + } val tree0 = etaExpand(context.unit, tree) // println("eta "+tree+" ---> "+tree0+":"+tree0.tpe+" undet: "+context.undetparams+ " mode: "+Integer.toHexString(mode)) @@ -773,11 +785,10 @@ trait Typers extends Modes with Adaptations { } else if (!meth.isConstructor && mt.params.isEmpty) { // (4.3) adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt, original) } else if (context.implicitsEnabled) { - errorTree(tree, "missing arguments for " + meth + meth.locationString + - (if (meth.isConstructor) "" - else ";\nfollow this method with `_' if you want to treat it as a partially applied function")) + MissingArgsForMethodTpeError(tree, meth) } else { - setError(tree) + // Relevant only for #4425 ? + Bug4425Error(tree) } } @@ -790,8 +801,7 @@ trait Typers extends Modes with Adaptations { // or raw type (tree.symbol.isJavaDefined && context.unit.isJava), types must be of kind *, // and thus parameterized types must be applied to their type arguments // @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't? - errorTree(tree, tree.symbol + " takes type parameters") - tree setType tree.tpe + MissingTypeParametersError(tree) } else if ( // (7.1) @M: check kind-arity // @M: removed check for tree.hasSymbol and replace tree.symbol by tree.tpe.symbol (TypeTree's must also be checked here, and they don't directly have a symbol) (inHKMode(mode)) && @@ -806,9 +816,7 @@ trait Typers extends Modes with Adaptations { // Note that we treat Any and Nothing as kind-polymorphic. // We can't perform this check when typing type arguments to an overloaded method before the overload is resolved // (or in the case of an error type) -- this is indicated by pt == WildcardType (see case TypeApply in typed1). - errorTree(tree, tree.tpe + " takes " + countElementsAsString(tree.tpe.typeParams.length, "type parameter") + - ", expected: " + countAsString(pt.typeParams.length)) - tree setType tree.tpe + KindArityMismatchError(tree, pt) } else tree match { // (6) case TypeTree() => tree case _ => TypeTree(tree.tpe) setOriginal (tree) @@ -828,13 +836,18 @@ trait Typers extends Modes with Adaptations { val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) .setOriginal(tree) - inferConstructorInstance(tree1, clazz.typeParams, pt) - tree1 + inferConstructorInstance(tree1, clazz.typeParams, pt) match { + case Some(err) => + if (tree1.containsError()) + Block(List(err), tree1) + else err + case _ => tree1 + } } else { tree } } else { - errorTree(tree, tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method") + CaseClassConstructorError(tree) } } @@ -852,7 +865,8 @@ trait Typers extends Modes with Adaptations { case other => other } - typed(atPos(tree.pos)(Select(qual, nme.apply)), mode, pt) + if (qual.containsError()) qual // Fail quickly + else typed(atPos(tree.pos)(Select(qual, nme.apply)), mode, pt) } // begin adapt @@ -867,8 +881,10 @@ trait Typers extends Modes with Adaptations { } treeCopy.Literal(tree, value) case OverloadedType(pre, alts) if !inFunMode(mode) => // (1) - inferExprAlternative(tree, pt) - adapt(tree, mode, pt, original) + inferExprAlternative(tree, pt) match { + case Some(err) => err + case _ => adapt(tree, mode, pt, original) + } case NullaryMethodType(restpe) => // (2) adapt(tree setType restpe, mode, pt, original) case TypeRef(_, ByNameParamClass, List(arg)) if ((mode & EXPRmode) != 0) => // (2) @@ -927,7 +943,10 @@ trait Typers extends Modes with Adaptations { } else { if (inPatternMode(mode)) { if ((tree.symbol ne null) && tree.symbol.isModule) - inferModulePattern(tree, pt) + inferModulePattern(tree, pt) match { + case Some(err) => return err + case _ => + } if (isPopulated(tree.tpe, approximateAbstracts(pt))) return tree } @@ -955,7 +974,8 @@ trait Typers extends Modes with Adaptations { if (!context.undetparams.isEmpty) { return instantiate(tree, mode, pt) } - if (context.implicitsEnabled && !tree.tpe.isError && !pt.isError) { + val validTree = !tree.containsErrorOrIsErrorTyped() + if (context.implicitsEnabled && !pt.isError && validTree) { // (14); the condition prevents chains of views debuglog("inferring view from " + tree.tpe + " to " + pt) val coercion = inferView(tree, tree.tpe, pt, true) @@ -972,7 +992,7 @@ trait Typers extends Modes with Adaptations { new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) } } - if (isCodeType(pt) && !isCodeType(tree.tpe) && !tree.tpe.isError) + if (isCodeType(pt) && !isCodeType(tree.tpe) && validTree) return adapt(lifted(tree), mode, pt, original) } if (settings.debug.value) { @@ -1003,7 +1023,7 @@ trait Typers extends Modes with Adaptations { context.unit.warning(tree.pos, "recovering from existential Skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) adapt(tree, mode, pt.subst(pt.existentialSkolems, pt.existentialSkolems map (_ => WildcardType))) } else - throw ex + TypeErrorTree(tree, pt, ex) } } } @@ -1011,8 +1031,10 @@ trait Typers extends Modes with Adaptations { } def instantiate(tree: Tree, mode: Int, pt: Type): Tree = { - inferExprInstance(tree, context.extractUndetparams(), pt) - adapt(tree, mode, pt) + inferExprInstance(tree, context.extractUndetparams(), pt) match { + case (Some(err), _) => err + case _ => adapt(tree, mode, pt) + } } /** If the expected type is Unit: try instantiating type arguments * with expected type Unit, but if that fails, try again with pt = WildcardType @@ -1020,8 +1042,8 @@ trait Typers extends Modes with Adaptations { */ def instantiateExpectingUnit(tree: Tree, mode: Int): Tree = { val savedUndetparams = context.undetparams - silent(_.instantiate(tree, mode, UnitClass.tpe)) match { - case t: Tree => t + instantiate(tree, mode, UnitClass.tpe) match { + case t: Tree if !t.containsError() => t case _ => context.undetparams = savedUndetparams val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) @@ -1069,40 +1091,44 @@ trait Typers extends Modes with Adaptations { adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe)) if (pt != WildcardType) { silent(_ => doAdapt(pt)) match { - case result: Tree if result != qual => + case result: Tree if result != qual && !result.containsError() => result case _ => debuglog("fallback on implicits in adaptToArguments: "+qual+" . "+name) doAdapt(WildcardType) } - } else + } else { doAdapt(pt) + } } - /** Try o apply an implicit conversion to `qual` to that it contains - * a method `name`. If that's ambiguous try taking arguments into account using `adaptToArguments`. + /** Try to apply an implicit conversion to `qual` so that it contains + * a method `name`. If that's ambiguous try taking arguments into + * account using `adaptToArguments`. */ def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Int): Tree = { - try { - adaptToMember(qual, HasMember(name)) - } catch { - case ex: TypeError => - // this happens if implicits are ambiguous; try again with more context info. - // println("last ditch effort: "+qual+" . "+name) + def onError(reportError: => Tree): Tree = { + // last change effort context.tree match { case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => // try handling the arguments // println("typing args: "+args) silent(_.typedArgs(args, mode)) match { - case args: List[_] => + case args: List[Tree] if args.forall(!_.containsError()) => adaptToArguments(qual, name, args.asInstanceOf[List[Tree]], WildcardType) case _ => - throw ex + reportError } case _ => - // println("not in an apply: "+context.tree+"/"+tree) - throw ex + reportError } } + try { + val res = adaptToMember(qual, HasMember(name)) + if (res.containsError()) onError(res) else res + } catch { + case ex: TypeError => + onError(AdaptToMemberWithArgsError(tree, ex)) + } } /** Try to apply an implicit conversion to `qual` to that it contains a @@ -1135,6 +1161,7 @@ trait Typers extends Modes with Adaptations { if (templ.parents.isEmpty) List() else try { val clazz = context.owner + var pending: List[ErrorTree] = List() // Normalize supertype and mixins so that supertype is always a class, not a trait. var supertpt = typedTypeConstructor(templ.parents.head) @@ -1143,7 +1170,13 @@ trait Typers extends Modes with Adaptations { // If first parent is a trait, make it first mixin and add its superclass as first parent while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) { val supertpt1 = typedType(supertpt) - if (!supertpt1.tpe.isError) { + if (supertpt1.containsError()) { + // we assume here that all errors in supertpt1 have already been reported + supertpt setType ErrorType + } else if (supertpt1.tpe.isError) { + // Should not happen with ErrorTree + assert(false, "Erroneous SuperType contains Error tree") + } else { mixins = supertpt1 :: mixins supertpt = TypeTree(supertpt1.tpe.parents.head) setPos supertpt.pos.focus } @@ -1192,21 +1225,28 @@ trait Typers extends Modes with Adaptations { .typePrimaryConstrBody(clazz, cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate))) + if (cbody2.containsError()) { + val allErrors = errorTreesFinder(cbody2) + pending = allErrors.toList:::pending + } + scall match { case Apply(_, _) => val sarg = treeInfo.firstArgument(scall) if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) - error(sarg.pos, firstParent+" is a trait; does not take constructor arguments") + pending = logErrorTree(ConstrArgsInTraitParentTpeError(sarg, firstParent), pending) if (!supertparams.isEmpty) supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus case _ => - if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments") + if (!supertparams.isEmpty) + pending = logErrorTree(MissingTypeArgumentsParentTpeError(supertpt), pending) } (cstats1, treeInfo.preSuperFields(templ.body)).zipped map { (ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe } case _ => - if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments") + if (!supertparams.isEmpty) + pending = logErrorTree(MissingTypeArgumentsParentTpeError(supertpt), pending) } /* experimental: early types as type arguments val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef) @@ -1218,12 +1258,11 @@ trait Typers extends Modes with Adaptations { */ //Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG - supertpt :: mixins mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) + supertpt :: pending ::: mixins mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) } catch { case ex: TypeError => - templ.tpe = null - reportTypeError(templ.pos, ex) - List(TypeTree(AnyRefClass.tpe)) + // TODO: remove once error trees refactoring is done + List(ParentTypesError(templ, ex)) } /** <p>Check that</p> @@ -1240,32 +1279,35 @@ trait Typers extends Modes with Adaptations { * <li>no two parents define same symbol.</li> * </ul> */ - def validateParentClasses(parents: List[Tree], selfType: Type) { + def validateParentClasses(parents: List[Tree], selfType: Type): Option[PendingErrors] = { + var pending: List[ErrorTree] = List() def validateParentClass(parent: Tree, superclazz: Symbol) { - if (!parent.tpe.isError) { + if (!parent.containsErrorOrIsErrorTyped()) { val psym = parent.tpe.typeSymbol.initialize - checkClassType(parent, false, true) + checkClassType(parent, false, true) match { + case t@Some(err) => + pending = logErrorTree(err, pending) + case _ => + } if (psym != superclazz) { if (psym.isTrait) { val ps = psym.info.parents if (!ps.isEmpty && !superclazz.isSubClass(ps.head.typeSymbol)) - error(parent.pos, "illegal inheritance; super"+superclazz+ - "\n is not a subclass of the super"+ps.head.typeSymbol+ - "\n of the mixin " + psym) + pending = logErrorTree(ParentSuperSubclassError(parent.pos, superclazz, ps.head.typeSymbol, psym), pending) } else { - error(parent.pos, psym+" needs to be a trait to be mixed in") + pending = logErrorTree(ParentNotATraitMixinError(parent.pos, psym), pending) } } - if (psym.isFinal) { - error(parent.pos, "illegal inheritance from final "+psym) - } + if (psym.isFinal) + pending = logErrorTree(ParentFinalInheritanceError(parent.pos, psym), pending) + if (psym.isSealed && !phase.erasedTypes) { // AnyVal is sealed, but we have to let the value classes through manually if (context.unit.source.file == psym.sourceFile || isValueClass(context.owner)) psym addChild context.owner else - error(parent.pos, "illegal inheritance from sealed "+psym) + pending = logErrorTree(ParentSealedInheritanceError(parent.pos, psym), pending) } if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes && @@ -1277,18 +1319,19 @@ trait Typers extends Modes with Adaptations { //Console.println(context.owner);//DEBUG //Console.println(context.owner.unsafeTypeParams);//DEBUG //Console.println(List.fromArray(context.owner.info.closure));//DEBUG - error(parent.pos, "illegal inheritance;\n self-type "+ - selfType+" does not conform to "+parent + - "'s selftype "+parent.tpe.typeOfThis) + pending = logErrorTree(ParentSelfTypeConformanceError(parent.pos, selfType, parent), pending) if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis) } if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError)) - error(parent.pos, psym+" is inherited twice") + pending = logErrorTree(ParentInheritedTwiceError(parent.pos, psym), pending) } } - - if (!parents.isEmpty && !parents.head.tpe.isError) - for (p <- parents) validateParentClass(p, parents.head.tpe.typeSymbol) + if (parents.forall(!_.containsError())) { + // proceed as normal, we know that head.tpe is not error? + if (!parents.isEmpty && !parents.head.tpe.isError) + for (p <- parents) validateParentClass(p, parents.head.tpe.typeSymbol) + } + if (pending.isEmpty) None else Some(PendingErrors(pending.reverse)) /* if (settings.Xshowcls.value != "" && @@ -1299,11 +1342,11 @@ trait Typers extends Modes with Adaptations { */ } - def checkFinitary(classinfo: ClassInfoType) { + def checkFinitary(classinfo: ClassInfoType): List[ErrorTree] = { val clazz = classinfo.typeSymbol - for (tparam <- clazz.typeParams) { + + clazz.typeParams.map(tparam => if (classinfo.expansiveRefs(tparam) contains tparam) { - error(tparam.pos, "class graph is not finitary because type parameter "+tparam.name+" is expansively recursive") val newinfo = ClassInfoType( classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefClass.tpe))), classinfo.decls, @@ -1314,8 +1357,9 @@ trait Typers extends Modes with Adaptations { case _ => newinfo } } - } - } + Some(FinitiaryError(tparam)) + } else None + ).flatten } /** @@ -1402,15 +1446,16 @@ trait Typers extends Modes with Adaptations { val getter = if (isDeferred) value else value.getter(value.owner) assert(getter != NoSymbol, stat) + val gs = new ListBuffer[Tree] if (getter.isOverloaded) - error(getter.pos, getter+" is defined twice") + gs.append(GetterDefinedTwiceError(getter)) getter.setAnnotations(memberAnnots(allAnnots, GetterTargetClass)) if (value.isLazy) List(stat) else { val vdef = treeCopy.ValDef(stat, mods | PRIVATE | LOCAL, nme.getterToLocal(name), tpt, rhs) - val getterDef: DefDef = atPos(vdef.pos.focus) { + val getterDef = atPos(vdef.pos.focus) { if (isDeferred) { val r = DefDef(getter, EmptyTree) r.tpt.asInstanceOf[TypeTree].setOriginal(tpt) // keep type tree of original abstract field @@ -1426,18 +1471,21 @@ trait Typers extends Modes with Adaptations { r } } - checkNoEscaping.privates(getter, getterDef.tpt) - def setterDef(setter: Symbol, isBean: Boolean = false): DefDef = { + + def setterDef(setter: Symbol, isBean: Boolean = false): Tree = { setter setAnnotations memberAnnots(allAnnots, if (isBean) BeanSetterTargetClass else SetterTargetClass) val defTree = if ((mods hasFlag DEFERRED) || (setter hasFlag OVERLOADED)) EmptyTree else Assign(Select(This(value.owner), value), Ident(setter.paramss.head.head)) - typedPos(vdef.pos.focus)(DefDef(setter, defTree)).asInstanceOf[DefDef] + typedPos(vdef.pos.focus)(DefDef(setter, defTree)) } - val gs = new ListBuffer[DefDef] + val privateErrors = checkNoEscaping.privates(getter, getterDef.tpt) + if (privateErrors.containsError()) + privateErrors.foreach(err => gs.append(err)) + gs.append(getterDef) if (mods.isMutable) { val setter = getter.setter(value.owner) @@ -1453,7 +1501,7 @@ trait Typers extends Modes with Adaptations { if (beanGetter == NoSymbol) { // the namer decides wether to generate these symbols or not. at that point, we don't // have symbolic information yet, so we only look for annotations named "BeanProperty". - unit.error(stat.pos, "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import") + gs.append(BeanPropertyAnnotationLimitationError(stat)) } beanGetter.setAnnotations(memberAnnots(allAnnots, BeanGetterTargetClass)) if (mods.isMutable && beanGetter != NoSymbol) { @@ -1552,20 +1600,32 @@ trait Typers extends Modes with Adaptations { // the following is necessary for templates generated later assert(clazz.info.decls != EmptyScope) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body) - validateParentClasses(parents1, selfType) + + var doNotTypeBody = false + val parents2 = validateParentClasses(parents1, selfType) match { + case Some(err) => + doNotTypeBody = err.pending0.exists(_.isInstanceOf[BlockingError]) + List(err) + case _ => parents1 + } if (clazz.isCase) validateNoCaseAncestor(clazz) if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass) unit.error(clazz.pos, "inner classes cannot be classfile annotations") - if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members - checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) + + val finitiaryErrs = + if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members + checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) + else List() val body = - if (phase.id <= currentRun.typerPhase.id && !reporter.hasErrors) + if (phase.id <= currentRun.typerPhase.id && !reporter.hasErrors && !doNotTypeBody) templ.body flatMap addGetterSetter else templ.body - val body1 = typedStats(body, templ.symbol) - treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe + + val body1 = if (doNotTypeBody) body else typedStats(body, templ.symbol) + val body2 = finitiaryErrs ++ body1 + treeCopy.Template(templ, parents2, self1, body2) setType clazz.tpe } /** Remove definition annotations from modifiers (they have been saved @@ -1583,22 +1643,29 @@ trait Typers extends Modes with Adaptations { val sym = vdef.symbol val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor) val typedMods = removeAnnotations(vdef.mods) + var pending: List[ErrorTree] = List() // complete lazy annotations val annots = sym.annotations var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt)) - checkNonCyclic(vdef, tpt1) + checkNonCyclic(vdef, tpt1) match { + case Some(cyclic) => + pending = logErrorTree(cyclic, pending) + case _ => + } if (sym.hasAnnotation(definitions.VolatileAttr)) { if (!sym.isMutable) - error(vdef.pos, "values cannot be volatile") + pending = logErrorTree(VolatileValueError(vdef), pending) else if (sym.isFinal) - error(vdef.pos, "final vars cannot be volatile") + pending = logErrorTree(FinalVolatileVarError(vdef), pending) } val rhs1 = - if (vdef.rhs.isEmpty) { + if (tpt1.containsError()) { + vdef.rhs + } else if (vdef.rhs.isEmpty) { if (sym.isVariable && sym.owner.isTerm && phase.id <= currentRun.typerPhase.id) - error(vdef.pos, "local variables must be initialized") + pending = logErrorTree(LocalVarUninitializedError(vdef), pending) vdef.rhs } else { val tpt2 = if (sym.hasDefault) { @@ -1621,7 +1688,11 @@ trait Typers extends Modes with Adaptations { } else tpt1.tpe newTyper(typer1.context.make(vdef, sym)).transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) } - treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType + // flush all pending erros + val rhs2 = if (pending.nonEmpty) { + PendingErrors(pending.reverse) + } else rhs1 + treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs2)) setType NoType } /** Enter all aliases of local parameter accessors. @@ -1630,7 +1701,7 @@ trait Typers extends Modes with Adaptations { * @param vparamss ... * @param rhs ... */ - def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) { + def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree): List[ErrorTree] = { debuglog("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug def decompose(call: Tree): (Tree, List[Tree]) = call match { case Apply(fn, args) => @@ -1648,16 +1719,17 @@ trait Typers extends Modes with Adaptations { val (superConstr, superArgs) = decompose(rhs) assert(superConstr.symbol ne null)//debug + var pending: List[ErrorTree] = List() // an object cannot be allowed to pass a reference to itself to a superconstructor // because of initialization issues; bug #473 for (arg <- superArgs ; tree <- arg) { val sym = tree.symbol if (sym != null && (sym.info.baseClasses contains clazz)) { if (sym.isModule) - error(tree.pos, "super constructor cannot be passed a self reference unless parameter is declared by-name") + pending = SuperConstrReferenceError(tree)::pending tree match { case This(qual) => - error(tree.pos, "super constructor arguments cannot reference unconstructed `this`") + pending = SuperConstrArgsThisReferenceError(tree)::pending case _ => () } } @@ -1695,6 +1767,8 @@ trait Typers extends Modes with Adaptations { } } } + + pending } /** Check if a structurally defined method violates implementation restrictions. @@ -1743,7 +1817,7 @@ trait Typers extends Modes with Adaptations { lookupVariable(name.toString.substring(1), enclClass) match { case Some(repl) => silent(_.typedTypeConstructor(stringParser(repl).typ())) match { - case tpt: Tree => + case tpt: Tree if !tpt.containsError()=> val alias = enclClass.newAliasType(useCase.pos, name.toTypeName) val tparams = cloneSymbols(tpt.tpe.typeSymbol.typeParams, alias) alias setInfo typeFun(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) @@ -1772,6 +1846,7 @@ trait Typers extends Modes with Adaptations { */ def typedDefDef(ddef: DefDef): DefDef = { val meth = ddef.symbol + var pending: List[ErrorTree] = List() reenterTypeParams(ddef.tparams) reenterValueParams(ddef.vparamss) @@ -1793,17 +1868,22 @@ trait Typers extends Modes with Adaptations { val annots = meth.annotations for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1) - if (isRepeatedParamType(vparam1.symbol.tpe)) - error(vparam1.pos, "*-parameter must come last") + if (isRepeatedParamType(vparam1.symbol.tpe)) { + pending = logErrorTree(StarParamNotLastError(vparam1), pending) + } var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) if (!settings.YdepMethTpes.value) { for (vparams <- vparamss1; vparam <- vparams) { - checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () + pending = logErrorTree(checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt), pending) } - checkNoEscaping.locals(context.scope, WildcardType, tpt1) + pending = logErrorTree(checkNoEscaping.locals(context.scope, WildcardType, tpt1), pending) + } + checkNonCyclic(ddef, tpt1) match { + case Some(cylic) => + pending = logErrorTree(cylic, pending) + case _ => } - checkNonCyclic(ddef, tpt1) ddef.tpt.setType(tpt1.tpe) val typedMods = removeAnnotations(ddef.mods) var rhs1 = @@ -1812,35 +1892,42 @@ trait Typers extends Modes with Adaptations { (!meth.owner.isClass || meth.owner.isModuleClass || meth.owner.isAnonOrRefinementClass)) - error(ddef.pos, "constructor definition not allowed here") + pending = logErrorTree(InvalidConstructorDefError(ddef), pending) typed(ddef.rhs) } else { transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe) } if (meth.isPrimaryConstructor && meth.isClassConstructor && - phase.id <= currentRun.typerPhase.id && !reporter.hasErrors) - computeParamAliases(meth.owner, vparamss1, rhs1) + phase.id <= currentRun.typerPhase.id && !reporter.hasErrors) { + // Handle new error trees + val pending0 = computeParamAliases(meth.owner, vparamss1, rhs1) + + if (pending0.nonEmpty) { + pending = pending0 ::: pending + } + } if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass) rhs1 = checkDead(rhs1) if (phase.id <= currentRun.typerPhase.id && meth.owner.isClass && meth.paramss.exists(ps => ps.exists(_.hasDefaultFlag) && isRepeatedParamType(ps.last.tpe))) - error(meth.pos, "a parameter section with a `*'-parameter is not allowed to have default arguments") + pending = logErrorTree(StarWithDefaultError(meth), pending) if (phase.id <= currentRun.typerPhase.id) { val allParams = meth.paramss.flatten for (p <- allParams) { for (n <- p.deprecatedParamName) { if (allParams.exists(p1 => p1.name == n || (p != p1 && p1.deprecatedParamName.exists(_ == n)))) - error(p.pos, "deprecated parameter name "+ n +" has to be distinct from any other parameter name (deprecated or not).") + pending = logErrorTree(DeprecatedParamNameError(p, n), pending) } } } if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(meth) - + if (pending.nonEmpty) + rhs1 = wrapInBlock(PendingErrors(pending.reverse), rhs1) treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } @@ -1869,15 +1956,22 @@ trait Typers extends Modes with Adaptations { } val rhs1 = checkNoEscaping.privates(tdef.symbol, typedType(tdef.rhs)) - checkNonCyclic(tdef.symbol) - if (tdef.symbol.owner.isType) - rhs1.tpe match { - case TypeBounds(lo1, hi1) => - if (!(lo1 <:< hi1)) - error(tdef.pos, "lower bound "+lo1+" does not conform to upper bound "+hi1) - case _ => - } - treeCopy.TypeDef(tdef, typedMods, tdef.name, tparams1, rhs1) setType NoType + val rhs2 = checkNonCyclic(tdef.symbol) match { + case Some(cyclic) => + wrapInBlock(cyclic, rhs1) + case None => + if (tdef.symbol.owner.isType) + rhs1.tpe match { + case TypeBounds(lo1, hi1) if (!(lo1 <:< hi1)) => + wrapInBlock( + LowerBoundError(tdef, lo1, hi1), + rhs1) + case _ => + rhs1 + } + else rhs1 + } + treeCopy.TypeDef(tdef, typedMods, tdef.name, tparams1, rhs2) setType NoType } private def enterLabelDef(stat: Tree) { @@ -1994,10 +2088,17 @@ trait Typers extends Modes with Adaptations { */ def typedCase(cdef: CaseDef, pattpe: Type, pt: Type): CaseDef = { // verify no _* except in last position + var pending: List[ErrorTree] = List() for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x) - error(x.pos, "_* may only come last") + pending = logErrorTree(StarPositionInPatternError(x.pos), pending) - val pat1: Tree = typedPattern(cdef.pat, pattpe) + val pat0 = typedPattern(cdef.pat, pattpe) + val pat1: Tree = pending match { + case List() => + pat0 + case _ => + wrapInBlock(PendingErrors(pending.reverse), pat0) + } // When case classes have more than two parameter lists, the pattern ends // up typed as a method. We only pattern match on the first parameter // list, so substitute the final result type of the method, i.e. the type @@ -2013,16 +2114,17 @@ trait Typers extends Modes with Adaptations { val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree else typed(cdef.guard, BooleanClass.tpe) + var body1: Tree = typed(cdef.body, pt) if (!context.savedTypeBounds.isEmpty) { body1.tpe = context.restoreTypeBounds(body1.tpe) if (isFullyDefined(pt) && !(body1.tpe <:< pt)) { - body1 = - typed { - atPos(body1.pos) { - TypeApply(Select(body1, Any_asInstanceOf), List(TypeTree(pt))) // @M no need for pt.normalize here, is done in erasure - } + body1 = typed { + atPos(body1.pos) { + // @M no need for pt.normalize here, is done in erasure + TypeApply(Select(body1, Any_asInstanceOf), List(TypeTree(pt))) } + } } } // body1 = checkNoEscaping.locals(context.scope, pt, body1) @@ -2045,7 +2147,7 @@ trait Typers extends Modes with Adaptations { val codeExpected = !forMSIL && (pt.typeSymbol isNonBottomSubClass CodeClass) if (numVparams > definitions.MaxFunctionArity) - return errorTree(fun, "implementation restricts functions to " + definitions.MaxFunctionArity + " parameters") + return MaxFunctionArityError(fun) def decompose(pt: Type): (Symbol, List[Type], Type) = if ((isFunctionType(pt) @@ -2061,9 +2163,10 @@ trait Typers extends Modes with Adaptations { (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) val (clazz, argpts, respt) = decompose(if (codeExpected) pt.normalize.typeArgs.head else pt) + val vparamErrors = new mutable.HashMap[ValDef, ErrorTree]() if (argpts.lengthCompare(numVparams) != 0) - errorTree(fun, "wrong number of parameters; expected = " + argpts.length) + WrongNumberOfParametersError(fun, argpts) else { val vparamSyms = (fun.vparams, argpts).zipped map { (vparam, argpt) => if (vparam.tpt.isEmpty) { @@ -2073,7 +2176,7 @@ trait Typers extends Modes with Adaptations { fun match { case etaExpansion(vparams, fn, args) if !codeExpected => silent(_.typed(fn, forFunMode(mode), pt)) match { - case fn1: Tree if context.undetparams.isEmpty => + case fn1: Tree if context.undetparams.isEmpty && !fn1.containsError() => // if context,undetparams is not empty, the function was polymorphic, // so we need the missing arguments to infer its type. See #871 //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) @@ -2084,7 +2187,7 @@ trait Typers extends Modes with Adaptations { } case _ => } - error(vparam.pos, missingParameterTypeMsg(fun, vparam, pt)) + vparamErrors += ((vparam, MissingParameterTypeError(fun, vparam, pt))) ErrorType } if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus @@ -2094,14 +2197,21 @@ trait Typers extends Modes with Adaptations { vparam.symbol } - val vparams = fun.vparams mapConserve (typedValDef) + val vparams = fun.vparams mapConserve (p => + { val p0 = typedValDef(p) + if (vparamErrors.contains(p)) { + treeCopy.ValDef(p0, p0.mods, p0.name, vparamErrors(p), p0.rhs) + } + else p0 + }) // for (vparam <- vparams) { // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () // } - var body = typed(fun.body, respt) + val body0 = typed(fun.body, respt) val formals = vparamSyms map (_.tpe) - val restpe = packedType(body, fun.symbol).deconst - val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) + val (restpe0, errs) = packedType(body0, fun.symbol)//.deconst + val body = if (errs.isEmpty) body0 else Block(errs, body0) + val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe0.deconst) // body = checkNoEscaping.locals(context.scope, restpe, body) val fun1 = treeCopy.Function(fun, vparams, body).setType(funtpe) if (codeExpected) lifted(fun1) else fun1 @@ -2142,7 +2252,7 @@ trait Typers extends Modes with Adaptations { val localTarget = stats exists includesTargetPos def typedStat(stat: Tree): Tree = { if (context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(stat)) - errorTree(stat, "only declarations allowed here") + OnlyDeclarationsError(stat) else stat match { case imp @ Import(_, _) => @@ -2160,26 +2270,30 @@ trait Typers extends Modes with Adaptations { // XXX this creates a spurious dead code warning if an exception is thrown // in a constructor, even if it is the only thing in the constructor. val result = checkDead(localTyper.typed(stat, EXPRmode | BYVALmode, WildcardType)) - if (treeInfo.isSelfOrSuperConstrCall(result)) { - context.inConstructorSuffix = true - if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) - error(stat.pos, "called constructor's definition must precede calling constructor's definition") - } - result match { - case EmptyTree | Literal(Constant(())) => () - case _ => - if (treeInfo isPureExpr result) { - val sym = result.symbol + + val result1 = if (treeInfo.isSelfOrSuperConstrCall(result)) { + context.inConstructorSuffix = true + if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) + wrapInBlock(ConstructorsOrderError(stat), result) + else result + } else result + + result1 match { + case EmptyTree | Literal(Constant(())) => result1 + case tree if tree.containsError() => result1 + case tree => + if (treeInfo isPureExpr result1) { + val sym = result1.symbol if (sym != null && (sym.isModule || sym.isLazy)) { - debuglog("'Pure' but side-effecting expression in statement position: " + result) + debuglog("'Pure' but side-effecting expression in statement position: " + result1) } else context.warning(stat.pos, "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) } + result1 } - result } } } @@ -2211,8 +2325,7 @@ trait Typers extends Modes with Adaptations { // error for this is issued in RefChecks.checkDefaultsInOverloaded if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag && !e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) { - error(e.sym.pos, e1.sym+" is defined twice"+ - {if(!settings.debug.value) "" else " in "+unit.toString}) + newStats += DefDefinedTwiceError(e.sym, e1.sym) scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779 } e1 = scope.lookupNextEntry(e1) @@ -2379,8 +2492,15 @@ trait Typers extends Modes with Adaptations { arg1 } context.undetparams = undetparams - inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) - doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + + inferMethodAlternative(fun, undetparams, argtpes.toList, pt, + varArgsOnly = treeInfo.isWildcardStarArgList(args)) match { + case Some(err) => + err + case _ => + doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + } + case mt @ MethodType(params, _) => val paramTypes = mt.paramTypes @@ -2400,7 +2520,7 @@ trait Typers extends Modes with Adaptations { // the inner "doTypedApply" does "extractUndetparams" => restore when it fails val savedUndetparams = context.undetparams silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) match { - case t: Tree => + case t: Tree if !t.containsError() => // Depending on user options, may warn or error here if // a Unit or tuple was inserted. Some(t) filter (tupledTree => @@ -2408,7 +2528,7 @@ trait Typers extends Modes with Adaptations { || tupledTree.symbol == null || checkValidAdaptation(tupledTree, args) ) - case ex => + case _ => context.undetparams = savedUndetparams None } @@ -2426,19 +2546,24 @@ trait Typers extends Modes with Adaptations { if (mt.isErroneous) setError(tree) else if (inPatternMode(mode)) // #2064 - errorTree(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun)) + WrongNumberOfArgsError(tree, fun) else if (lencmp > 0) { - tryTupleApply getOrElse errorTree(tree, "too many arguments for "+treeSymTypeMsg(fun)) + tryTupleApply getOrElse TooManyArgsNamesDefaultsError(tree, fun) } else if (lencmp == 0) { // we don't need defaults. names were used, so this application is transformed // into a block (@see transformNamedApplication in NamesDefaults) + val (namelessArgs, argPos) = removeNames(Typer.this)(args, params) - if (namelessArgs exists (_.isErroneous)) { + if (namelessArgs exists (_.containsError())) { + // This should maybe be special handled + val tree1 = treeCopy.Apply(tree, fun, namelessArgs) + setError(tree1) + } else if (namelessArgs exists (_.isErroneous)) { + assert(false, "removeNames for NamesDefaults returns only ErrorTrees in case of error. We got " + namelessArgs) setError(tree) } else if (!isIdentity(argPos) && !sameLength(formals, params)) // !isIdentity indicates that named arguments are used to re-order arguments - errorTree(tree, "when using named arguments, the vararg parameter "+ - "has to be specified exactly once") + MultipleVarargError(tree) else if (isIdentity(argPos) && !isNamedApplyBlock(fun)) { // if there's no re-ordering, and fun is not transformed, no need to transform // more than an optimization, e.g. important in "synchronized { x = update-x }" @@ -2452,7 +2577,8 @@ trait Typers extends Modes with Adaptations { // calls to the default getters. Example: // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a)) val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x) - if (fun1.isErroneous) setError(tree) + if (fun1.containsError()) fun1 + else if (fun1.isErroneous) setError(tree) else { assert(isNamedApplyBlock(fun1), fun1) val NamedApplyInfo(qual, targs, previousArgss, _) = context.namedApplyBlockInfo.get._2 @@ -2469,17 +2595,20 @@ trait Typers extends Modes with Adaptations { val lencmp2 = compareLengths(allArgs, formals) if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) { - errorTree(tree, "module extending its companion class cannot use default constructor arguments") + ModuleUsingCompanionClassDefaultArgsErrror(tree) } else if (lencmp2 > 0) { - removeNames(Typer.this)(allArgs, params) // #3818 - setError(tree) + val errors = removeNames(Typer.this)(allArgs, params)._1.filter(_.containsError()) // #3818 + if (!errors.isEmpty) + wrapInBlock(PendingErrors(findAllNestedErrors(errors)), tree) + else + setError(tree) } else if (lencmp2 == 0) { // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]() val note = "Error occurred in an application involving default arguments." if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { - tryTupleApply getOrElse errorTree(tree, notEnoughArgumentsMsg(fun, missing)) + tryTupleApply getOrElse NotEnoughArgsError(tree, fun, missing) } } } @@ -2503,6 +2632,7 @@ trait Typers extends Modes with Adaptations { // instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case: // val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo) // precise(foo) : foo.type => foo.type + val restpe = mt.resultType(args1 map (arg => gen.stableTypeFor(arg) getOrElse arg.tpe)) def ifPatternSkipFormals(tp: Type) = tp match { case MethodType(_, rtp) if (inPatternMode(mode)) => rtp @@ -2538,11 +2668,12 @@ trait Typers extends Modes with Adaptations { atPos(tree.pos)(gen.mkNil setType restpe) else constfold(treeCopy.Apply(tree, fun, args1) setType ifPatternSkipFormals(restpe)) - } else if (needsInstantiation(tparams, formals, args)) { //println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info))) - inferExprInstance(fun, tparams) - doTypedApply(tree, fun, args, mode, pt) + inferExprInstance(fun, tparams) match { + case (Some(err), _) => err + case _ => doTypedApply(tree, fun, args, mode, pt) + } } else { assert(!inPatternMode(mode)) // this case cannot arise for patterns val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt) @@ -2559,20 +2690,31 @@ trait Typers extends Modes with Adaptations { val argtparams = context.extractUndetparams() if (!argtparams.isEmpty) { val strictPt = formal.instantiateTypeParams(tparams, strictTargs) - inferArgumentInstance(arg1, argtparams, strictPt, lenientPt) - } - arg1 + inferArgumentInstance(arg1, argtparams, strictPt, lenientPt) match { + case Some(err) => + err + case _ => + arg1 + } + } else arg1 } val args1 = (args, formals).zipped map typedArgToPoly - if (args1 exists (_.tpe.isError)) setError(tree) - else { + if (args1 exists {_.containsErrorOrIsErrorTyped()}) { + val allNestedErrors = findAllNestedErrors(args1.filter(_.containsError())) + if (allNestedErrors.isEmpty) SetErrorTree(tree) else PendingErrors(allNestedErrors) + } else { debuglog("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info)) //debug // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun" // returns those undetparams which have not been instantiated. - val undetparams = inferMethodInstance(fun, tparams, args1, pt) - val result = doTypedApply(tree, fun, args1, mode, pt) - context.undetparams = undetparams - result + val undetparamsOrError = inferMethodInstance(fun, tparams, args1, pt) + if (undetparamsOrError.isLeft) { + context.undetparams = Nil + undetparamsOrError.left.get + } else { + val result = doTypedApply(tree, fun, args1, mode, pt) + context.undetparams = undetparamsOrError.right.get + result + } } } } @@ -2581,12 +2723,15 @@ trait Typers extends Modes with Adaptations { doTypedApply(tree, fun setType fun.tpe.widen, args, mode, pt) case ErrorType => - setError(treeCopy.Apply(tree, fun, args)) + if (tree.containsError()) + tree + else + ErroneousFunInTypeApplyError(fun, args) /* --- begin unapply --- */ case otpe if inPatternMode(mode) && unapplyMember(otpe).exists => if (args.length > MaxTupleArity) - error(fun.pos, "too many arguments for unapply pattern, maximum = "+MaxTupleArity) + return TooManyArgsPatternError(fun) def freshArgType(tp: Type): (Type, List[Symbol]) = (tp: @unchecked) match { case MethodType(param :: _, _) => @@ -2594,9 +2739,10 @@ trait Typers extends Modes with Adaptations { case PolyType(tparams, restype) => val tparams1 = cloneSymbols(tparams) (freshArgType(restype)._1.substSym(tparams, tparams1), tparams1) - case OverloadedType(_, _) => - error(fun.pos, "cannot resolve overloaded unapply") - (ErrorType, Nil) + // No longer used, see test case neg/t960.scala (#960 has nothing to do with it) +// case OverloadedType(_, _) => +// error(fun.pos, "cannot resolve overloaded unapply") +// (ErrorType, Nil) } val unapp = unapplyMember(otpe) @@ -2611,7 +2757,11 @@ trait Typers extends Modes with Adaptations { freeVars foreach context1.scope.enter val typer1 = newTyper(context1) - val pattp = typer1.infer.inferTypedPattern(tree.pos, unappFormal, arg.tpe) + val pattp0 = typer1.infer.inferTypedPattern(tree.pos, unappFormal, arg.tpe) + val pattp = pattp0 match { + case Left(err) => return err // Fail quickly + case Right(tp) => tp + } // turn any unresolved type variables in freevars into existential skolems val skolems = freeVars map { fv => @@ -2627,7 +2777,14 @@ trait Typers extends Modes with Adaptations { // setType null is necessary so that ref will be stabilized; see bug 881 val fun1 = typedPos(fun.pos)(Apply(Select(fun setType null, unapp), List(arg))) - if (fun1.tpe.isErroneous) setError(tree) + // Set error tree + if (fun1.containsError()) + ErroneousFunInTypeApplyError(fun1, args) + else if (fun1.tpe.isErroneous) { + assert(false, "Bug #4425 workaround is invalid") + //Bug4425Error(tree) + setError(tree) + } else { val formals0 = unapplyTypeList(fun1.symbol, fun1.tpe) val formals1 = formalTypes(formals0, args.length) @@ -2642,14 +2799,13 @@ trait Typers extends Modes with Adaptations { arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) UnApply(fun1, args1) setPos tree.pos setType itype } - else { - errorTree(tree, "wrong number of arguments for "+treeSymTypeMsg(fun)) - } + else + WrongNumberArgsPatternError(tree, fun) } /* --- end unapply --- */ case _ => - errorTree(tree, fun.tpe+" does not take parameters") + ApplyWithoutArgsError(tree, fun) } } @@ -2661,8 +2817,11 @@ trait Typers extends Modes with Adaptations { def typedAnnotation(ann: Tree, mode: Int = EXPRmode, selfsym: Symbol = NoSymbol, annClass: Symbol = AnnotationClass, requireJava: Boolean = false): AnnotationInfo = { lazy val annotationError = AnnotationInfo(ErrorType, Nil, Nil) var hasError: Boolean = false - def error(pos: Position, msg: String) = { - context.error(pos, msg) + var pending: List[ErrorTree] = List() + + @inline + def reportAnnotationError(errTree: ErrorTree) = { + pending = logErrorTree(errTree, pending) hasError = true annotationError } @@ -2670,7 +2829,7 @@ trait Typers extends Modes with Adaptations { /** Calling constfold right here is necessary because some trees (negated * floats and literals in particular) are not yet folded. */ - def tryConst(tr: Tree, pt: Type): Option[LiteralAnnotArg] = { + def tryConst(tr: Tree, pt: Type): Either[ErrorTree, LiteralAnnotArg] = { val const: Constant = typed(constfold(tr), EXPRmode, pt) match { case l @ Literal(c) if !l.isErroneous => c case tree => tree.tpe match { @@ -2678,37 +2837,37 @@ trait Typers extends Modes with Adaptations { case tpe => null } } - def fail(msg: String) = { error(tr.pos, msg) ; None } - if (const == null) - fail("annotation argument needs to be a constant; found: " + tr) - else if (const.value == null) - fail("annotation argument cannot be null") + if (const == null) { + Left(AnnotationNotAConstantError(tr)) + } else if (const.value == null) + Left(AnnotationArgNulError(tr)) else - Some(LiteralAnnotArg(const)) + Right(LiteralAnnotArg(const)) } /** Converts an untyped tree to a ClassfileAnnotArg. If the conversion fails, * an error message is reported and None is returned. */ - def tree2ConstArg(tree: Tree, pt: Type): Option[ClassfileAnnotArg] = tree match { + def tree2ConstArg(tree: Tree, pt: Type): Either[Tree, ClassfileAnnotArg] = tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) if (pt.typeSymbol == ArrayClass) => - error(tree.pos, "Array constants have to be specified using the `Array(...)' factory method") - None + Left(ArrayConstantsError(tree)) case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => val annInfo = typedAnnotation(ann, mode, NoSymbol, pt.typeSymbol, true) if (annInfo.atp.isErroneous) { - // recursive typedAnnotation call already printed an error, so don't call "error" hasError = true - None - } else Some(NestedAnnotArg(annInfo)) + Left(NullErrorTree) + } else + Right(NestedAnnotArg(annInfo)) // use of Array.apply[T: ClassManifest](xs: T*): Array[T] // and Array.apply(x: Int, xs: Int*): Array[Int] (and similar) case Apply(fun, args) => val typedFun = typed(fun, forFunMode(mode), WildcardType) - if (typedFun.symbol.owner == ArrayModule.moduleClass && typedFun.symbol.name == nme.apply) + if (typedFun.containsError()) { + Left(typedFun) + } else if (typedFun.symbol.owner == ArrayModule.moduleClass && typedFun.symbol.name == nme.apply) pt match { case TypeRef(_, ArrayClass, targ :: _) => trees2ConstArg(args, targ) @@ -2717,21 +2876,24 @@ trait Typers extends Modes with Adaptations { // BT = Int, .., String, Class[_], JavaAnnotClass // T = BT | Array[BT] // So an array literal as argument can only be valid if pt is Array[_] - error(tree.pos, "found array constant, expected argument of type "+ pt) - None + Left(ArrayConstantsTypeMismatchError(tree, pt)) } - else - tryConst(tree, pt) + else tryConst(tree, pt) - case Typed(t, _) => tree2ConstArg(t, pt) + case Typed(t, _) => + tree2ConstArg(t, pt) case tree => tryConst(tree, pt) } - def trees2ConstArg(trees: List[Tree], pt: Type): Option[ArrayAnnotArg] = { + + def trees2ConstArg(trees: List[Tree], pt: Type): Either[ErrorTree, ArrayAnnotArg] = { val args = trees.map(tree2ConstArg(_, pt)) - if (args.exists(_.isEmpty)) None - else Some(ArrayAnnotArg(args.flatten.toArray)) + val par = args.filter(_.isLeft) + if (!par.isEmpty) + Left(PendingErrors(findAllNestedErrors(par.map(_.left.get)))) + else + Right(ArrayAnnotArg(args.map(_.right.get).toArray)) } // begin typedAnnotation @@ -2743,14 +2905,18 @@ trait Typers extends Modes with Adaptations { case Select(New(tpt), nme.CONSTRUCTOR) => (fun, outerArgss) case _ => - error(fun.pos, "unexpected tree in annotation: "+ fun) - (setError(fun), outerArgss) + (UnexpectedTreeAnnotation(fun), outerArgss) } extract(ann, List()) } - if (fun.isErroneous) annotationError - else { + + // It seems that fun.isErroneous was used due to check for deep error within fun? + // We don't need it now because deep error within fun + // is now detected by containsError() + val res = if (fun.containsErrorOrIsErrorTyped()) { + annotationError + } else { val typedFun @ Select(New(tpt), _) = typed(fun, forFunMode(mode), WildcardType) val annType = tpt.tpe @@ -2759,9 +2925,9 @@ trait Typers extends Modes with Adaptations { // annotation to be saved as java classfile annotation val isJava = typedFun.symbol.owner.isJavaDefined if (!annType.typeSymbol.isNonBottomSubClass(annClass)) { - error(tpt.pos, "expected annotation of type "+ annClass.tpe +", found "+ annType) + reportAnnotationError(AnnotationTypeMismatchError(tpt, annClass.tpe, annType)) } else if (argss.length > 1) { - error(ann.pos, "multiple argument lists on classfile annotation") + reportAnnotationError(MultipleArgumentListForAnnotationError(ann)) } else { val args = if (argss.head.length == 1 && !isNamed(argss.head.head)) @@ -2777,33 +2943,37 @@ trait Typers extends Modes with Adaptations { val sym = if (isJava) annScope.lookup(name) else typedFun.tpe.params.find(p => p.name == name).getOrElse(NoSymbol) if (sym == NoSymbol) { - error(arg.pos, "unknown annotation argument name: " + name) + reportAnnotationError(UnknownAnnotationNameError(arg, name)) (nme.ERROR, None) } else if (!names.contains(sym)) { - error(arg.pos, "duplicate value for annotation argument " + name) + reportAnnotationError(DuplicateValueAnnotationError(arg, name)) (nme.ERROR, None) } else { names -= sym if (isJava) sym.cookJavaRawInfo() // #3429 val annArg = tree2ConstArg(rhs, sym.tpe.resultType) - (sym.name, annArg) + if (annArg.isLeft) { + errorTreesFinder(annArg.left.get).foreach(reportAnnotationError) + (nme.ERROR, None) + } else + (sym.name, Some(annArg.right.get)) } case arg => - error(arg.pos, "classfile annotation arguments have to be supplied as named arguments") + reportAnnotationError(ClassfileAnnotationsAsNamedArgsError(arg)) (nme.ERROR, None) } for (name <- names) { if (!name.annotations.contains(AnnotationInfo(AnnotationDefaultAttr.tpe, List(), List())) && !name.hasDefaultFlag) - error(ann.pos, "annotation " + annType.typeSymbol.fullName + " is missing argument " + name.name) + reportAnnotationError(AnnotationMissingArgError(ann, annType, name)) } if (hasError) annotationError else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setPos(ann.pos) } } else if (requireJava) { - error(ann.pos, "nested classfile annotations must be defined in java; found: "+ annType) + reportAnnotationError(NestedAnnotationError(ann, annType)) } else { val typedAnn = if (selfsym == NoSymbol) { typed(ann, mode, annClass.tpe) @@ -2831,6 +3001,9 @@ trait Typers extends Modes with Adaptations { List(selfsym.info, annClass.tpe)) (typed(func, mode, funcType): @unchecked) match { + case t if t.containsError() => + reportAnnotationError(NullErrorTree) + t case t @ Function(List(arg), rhs) => val subs = new TreeSymSubstituter(List(arg.symbol),List(selfsym)) @@ -2855,16 +3028,27 @@ trait Typers extends Modes with Adaptations { annInfo(fun) case _ => - error(t.pos, "unexpected tree after typing annotation: "+ typedAnn) + reportAnnotationError(UnexpectedTreeAnnotationError(t, typedAnn)) } if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2) unit.deprecationWarning(ann.pos, "@deprecated now takes two arguments; see the scaladoc.") - if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) annotationError + if (typedAnn.containsError() || (typedAnn.tpe == null) || typedAnn.tpe.isErroneous) annotationError else annInfo(typedAnn) } } + + if (hasError) { + // We got errors, so now is the last chance to report them + try { + pending.foreach(_.emit(context)) + } catch { + case _: TypeError => + assert(false, "Cannot throw type errors when creating AnnotationInfo") + } + annotationError + } else res } def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? @@ -2923,7 +3107,8 @@ trait Typers extends Modes with Adaptations { } /** convert skolems to existentials */ - def packedType(tree: Tree, owner: Symbol): Type = { + def packedType(tree: Tree, owner: Symbol): (Type, List[ErrorTree]) = { + var pending: List[ErrorTree] = List() def defines(tree: Tree, sym: Symbol) = sym.isExistentialSkolem && sym.unpackLocation == tree || tree.isDef && tree.symbol == sym @@ -2949,7 +3134,7 @@ trait Typers extends Modes with Adaptations { if (sym.isAliasType && containsLocal(tp)) apply(tp.normalize) else { if (pre.isVolatile) - context.error(tree.pos, "Inferred type "+tree.tpe+" contains type selection from volatile type "+pre) + pending = logErrorTree(InferTypeWithVolatileTypeSelectionError(tree, tp), pending) mapOver(tp) } case _ => @@ -2966,8 +3151,7 @@ trait Typers extends Modes with Adaptations { localSyms += sym remainingSyms += sym } else { - unit.error(tree.pos, - "can't existentially abstract over parameterized type " + tp) + pending = logErrorTree(AbstractExistentiallyOverParamerizedTpeError(tree, tp), pending) } } } @@ -2996,10 +3180,10 @@ trait Typers extends Modes with Adaptations { } for (sym <- remainingSyms) addLocals(sym.existentialBound) } - val normalizedTpe = normalizeLocals(tree.tpe) addLocals(normalizedTpe) - packSymbols(localSyms.toList, normalizedTpe) + val res = packSymbols(localSyms.toList, normalizedTpe) + (res, pending.reverse) } protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = { @@ -3007,19 +3191,23 @@ trait Typers extends Modes with Adaptations { if (wc.symbol == NoSymbol) { namer.enterSym(wc); wc.symbol setFlag EXISTENTIAL } else context.scope enter wc.symbol val whereClauses1 = typedStats(tree.whereClauses, context.owner) + var pending: List[ErrorTree] = List() for (vd @ ValDef(_, _, _, _) <- tree.whereClauses) if (vd.symbol.tpe.isVolatile) - error(vd.pos, "illegal abstraction from value with volatile type "+vd.symbol.tpe) + pending = logErrorTree(AbstractionFromVolatileTypeError(vd), pending) val tpt1 = typedType(tree.tpt, mode) val (typeParams, tpe) = existentialTransform(tree.whereClauses map (_.symbol), tpt1.tpe) - //println(tpe + ": " + tpe.getClass ) - TypeTree(ExistentialType(typeParams, tpe)) setOriginal tree + val tt = TypeTree(ExistentialType(typeParams, tpe)) setOriginal tree + if (pending.isEmpty) tt else tt.setErrorCause(PendingErrors(pending.reverse)) } // lifted out of typed1 because it's needed in typedImplicit0 protected def typedTypeApply(tree: Tree, mode: Int, fun: Tree, args: List[Tree]): Tree = fun.tpe match { case OverloadedType(pre, alts) => - inferPolyAlternatives(fun, args map (_.tpe)) + inferPolyAlternatives(fun, args map (_.tpe)) match { + case Some(err) => return err + case _ => + } val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree) val args1 = if (sameLength(args, tparams)) { //@M: in case TypeApply we can't check the kind-arities of the type arguments, @@ -3032,7 +3220,7 @@ trait Typers extends Modes with Adaptations { // Martin, I'm using fake trees, because, if you use args or arg.map(typedType), // inferPolyAlternatives loops... -- I have no idea why :-( // ...actually this was looping anyway, see bug #278. - return errorTree(fun, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + return TypedApplyWrongNumberOfTpeParametersError(fun, fun) typedTypeApply(tree, mode, fun, args1) case SingleType(_, _) => @@ -3040,30 +3228,36 @@ trait Typers extends Modes with Adaptations { case PolyType(tparams, restpe) if tparams.nonEmpty => if (sameLength(tparams, args)) { val targs = args map (_.tpe) - checkBounds(tree.pos, NoPrefix, NoSymbol, tparams, targs, "") - if (fun.symbol == Predef_classOf) { - checkClassType(args.head, true, false) - atPos(tree.pos) { gen.mkClassOf(targs.head) } - } else { - if (phase.id <= currentRun.typerPhase.id && - fun.symbol == Any_isInstanceOf && !targs.isEmpty) - checkCheckable(tree.pos, targs.head, "") - val resultpe = restpe.instantiateTypeParams(tparams, targs) - //@M substitution in instantiateParams needs to be careful! - //@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int] - //@M --> first, m[a] gets changed to m[Int], then m gets substituted for List, - // this must preserve m's type argument, so that we end up with List[Int], and not List[a] - //@M related bug: #1438 - //println("instantiating type params "+restpe+" "+tparams+" "+targs+" = "+resultpe) - treeCopy.TypeApply(tree, fun, args) setType resultpe + checkBounds(tree.pos, NoPrefix, NoSymbol, tparams, targs, "") match { + case Some(err) => + err + case None => + if (fun.symbol == Predef_classOf) { + checkClassType(args.head, true, false) match { + case Some(err) => err + case _ => atPos(tree.pos) { gen.mkClassOf(targs.head) } + } + } else { + if (phase.id <= currentRun.typerPhase.id && + fun.symbol == Any_isInstanceOf && !targs.isEmpty) + checkCheckable(tree.pos, targs.head, "") + val resultpe = restpe.instantiateTypeParams(tparams, targs) + //@M substitution in instantiateParams needs to be careful! + //@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int] + //@M --> first, m[a] gets changed to m[Int], then m gets substituted for List, + // this must preserve m's type argument, so that we end up with List[Int], and not List[a] + //@M related bug: #1438 + //println("instantiating type params "+restpe+" "+tparams+" "+targs+" = "+resultpe) + treeCopy.TypeApply(tree, fun, args) setType resultpe + } } } else { - errorTree(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + TypedApplyWrongNumberOfTpeParametersError(tree, fun) } case ErrorType => setError(tree) case _ => - errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.") + TypedApplyDoesNotTakeTpeParametersError(tree, fun) } @inline final def deindentTyping() = context.typingIndentLevel -= 2 @@ -3133,9 +3327,10 @@ trait Typers extends Modes with Adaptations { atype0 // do not record selfsym if // this annotation did not need it - if (ainfo.isErroneous) + if (ainfo.isErroneous) { + // Erroneous annotations were already reported in typedAnnotation arg1 // simply drop erroneous annotations - else { + } else { ann.tpe = atype TypeTree(atype) setOriginal tree } @@ -3169,18 +3364,24 @@ trait Typers extends Modes with Adaptations { tree setSymbol vble setType vble.tpe } def typedBindTerm(name: TermName) = { + def typedBindTerm0() = { + val body1 = typed(body, mode, pt) + vble.setInfo( + if (treeInfo.isSequenceValued(body)) seqType(body1.tpe) + else body1.tpe) + treeCopy.Bind(tree, name, body1) setSymbol vble setType body1.tpe // burak, was: pt + } if (vble == NoSymbol) vble = context.owner.newValue(tree.pos, name) if (vble.name.toTermName != nme.WILDCARD) { - if ((mode & ALTmode) != 0) - error(tree.pos, "illegal variable in pattern alternative") vble = namer.enterInScope(vble) - } - val body1 = typed(body, mode, pt) - vble.setInfo( - if (treeInfo.isSequenceValued(body)) seqType(body1.tpe) - else body1.tpe) - treeCopy.Bind(tree, name, body1) setSymbol vble setType body1.tpe // burak, was: pt + + if ((mode & ALTmode) != 0) + VariableInPatternAlternativeError(tree) + else { + typedBindTerm0() + } + } else typedBindTerm0() } name match { case x: TypeName => typedBindType(x) @@ -3200,15 +3401,12 @@ trait Typers extends Modes with Adaptations { def typedAssign(lhs: Tree, rhs: Tree): Tree = { val lhs1 = typed(lhs, EXPRmode | LHSmode, WildcardType) val varsym = lhs1.symbol - def failMsg = - if (varsym != null && varsym.isValue) "reassignment to val" - else "assignment to non variable" def fail = { - if (!lhs1.tpe.isError) - error(tree.pos, failMsg) - - setError(tree) + if (lhs1.containsErrorOrIsErrorTyped()) + lhs1 + else // see #2494 for double error message example + AssignmentError(tree, varsym) } if (varsym == null) return fail @@ -3253,12 +3451,12 @@ trait Typers extends Modes with Adaptations { enclMethod.owner.isConstructor || context.enclClass.enclMethod == enclMethod // i.e., we are in a constructor of a local class ) { - errorTree(tree, "return outside method definition") + ReturnOutsideOfDefError(tree) } else { val DefDef(_, name, _, _, restpt, _) = enclMethod.tree - if (restpt.tpe eq null) - errorTree(tree, enclMethod.owner + " has return statement; needs result type") - else { + if (restpt.tpe eq null) { + ReturnWithoutTypeError(tree, enclMethod.owner) + } else { context.enclMethod.returnsSeen = true val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe) // Warn about returning a value if no value can be returned. @@ -3277,12 +3475,15 @@ trait Typers extends Modes with Adaptations { def typedNew(tpt: Tree) = { val tpt1 = { val tpt0 = typedTypeConstructor(tpt) - checkClassType(tpt0, false, true) - if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { - context.undetparams = cloneSymbols(tpt0.symbol.typeParams) - TypeTree().setOriginal(tpt0) - .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347. - } else tpt0 + checkClassType(tpt0, false, true) match { + case Some(err) => err + case _ => + if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { + context.undetparams = cloneSymbols(tpt0.symbol.typeParams) + TypeTree().setOriginal(tpt0) + .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347. + } else tpt0 + } } /** If current tree <tree> appears in <val x(: T)? = <tree>> @@ -3301,17 +3502,15 @@ trait Typers extends Modes with Adaptations { val tp = tpt1.tpe val sym = tp.typeSymbol if (sym.isAbstractType || sym.hasAbstractFlag) - error(tree.pos, sym + " is abstract; cannot be instantiated") + IsAbstractError(tree, sym) else if (!( tp == sym.initialize.thisSym.tpe // when there's no explicit self type -- with (#3612) or without self variable // sym.thisSym.tpe == tp.typeOfThis (except for objects) || narrowRhs(tp) <:< tp.typeOfThis || phase.erasedTypes )) { - error(tree.pos, sym + - " cannot be instantiated because it does not conform to its self-type "+ - tp.typeOfThis) - } - treeCopy.New(tree, tpt1).setType(tp) + DoesNotConformToSelfTypeError(tree, sym, tp.typeOfThis) + } else + treeCopy.New(tree, tpt1).setType(tp) } def typedEta(expr1: Tree): Tree = expr1.tpe match { @@ -3349,22 +3548,23 @@ trait Typers extends Modes with Adaptations { case ErrorType => expr1 case _ => - errorTree(expr1, "_ must follow method; cannot follow " + expr1.tpe) + UnderscoreEtaError(expr1) } /** * @param args ... * @return ... */ - def tryTypedArgs(args: List[Tree], mode: Int, other: TypeError): List[Tree] = { + def tryTypedArgs(args: List[Tree], mode: Int): List[Tree] = { val c = context.makeSilent(false) c.retyping = true try { newTyper(c).typedArgs(args, mode) } catch { - case ex: CyclicReference => throw ex - case ex: TypeError => - null + case ex: CyclicReference => + throw ex + case _: TypeError => + List(NullErrorTree) } } @@ -3373,10 +3573,8 @@ trait Typers extends Modes with Adaptations { */ def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { val start = startTimer(failedApplyNanos) - silent(_.doTypedApply(tree, fun, args, mode, pt)) match { - case t: Tree => - t - case ex: TypeError => + + def onError(treeWithError: Tree): Tree = { stopTimer(failedApplyNanos, start) // If the problem is with raw types, copnvert to existentials and try again. @@ -3399,26 +3597,44 @@ trait Typers extends Modes with Adaptations { case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) case _ => Nil }) - def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos) - val retry = fun :: tree :: args exists errorInResult + // Get correct posiition for the error + val (ePos, firstToReport) = { + val firstError = quickErrorTreeFinder(treeWithError) + (firstError.pos, firstError) + } + + def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ePos) + val retry = (ePos != null) && (fun :: tree :: args exists errorInResult) + if (settings.errortrees.value) + println("[ErrorTree retry] " + retry + " with " + treeWithError + " " + firstToReport.exception) printTyping { val funStr = ptTree(fun) + " and " + (args map ptTree mkString ", ") if (retry) "second try: " + funStr - else "no second try: " + funStr + " because error not in result: " + ex.pos+"!="+tree.pos + else "no second try: " + funStr + " because error not in result: " + ePos+"!="+tree.pos } if (retry) { val Select(qual, name) = fun - val args1 = tryTypedArgs(args, forArgMode(fun, mode), ex) + val args1 = tryTypedArgs(args, forArgMode(fun, mode)) + val invalidArgs = args1.length == 1 && (args1.head.containsError() || args1.head.tpe.isErroneous) val qual1 = - if ((args1 ne null) && !pt.isError) adaptToArguments(qual, name, args1, pt) + if (!invalidArgs && !pt.isError) adaptToArguments(qual, name, args1, pt) else qual if (qual1 ne qual) { val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos return typed1(tree1, mode | SNDTRYmode, pt) } } - reportTypeError(tree.pos, ex) - setError(tree) + + firstToReport + } + + silent(_.doTypedApply(tree, fun, args, mode, pt)) match { + case t: Tree if !t.containsError() => + t + case t: Tree => + onError(t) + case ex: TypeError => + onError(TypedApplyError(fun, ex)) } } @@ -3431,10 +3647,32 @@ trait Typers extends Modes with Adaptations { val funpt = if (isPatternMode) pt else WildcardType val appStart = startTimer(failedApplyNanos) val opeqStart = startTimer(failedOpEqNanos) + + def onError(reportError: => ErrorTree): Tree = { + fun match { + case Select(qual, name) + if !isPatternMode && nme.isOpAssignmentName(name.decode) => + val qual1 = typedQualifier(qual) + if (treeInfo.isVariableOrGetter(qual1)) { + stopTimer(failedOpEqNanos, opeqStart) + convertToAssignment(fun, qual1, name, args) + } else { + stopTimer(failedApplyNanos, appStart) + if ((qual1.symbol ne null) && qual1.symbol.isValue) + AssignmentTypedApplyError(tree) + else + reportError + } + case _ => + stopTimer(failedApplyNanos, appStart) + reportError + } + } + silent(_.typed(fun, forFunMode(mode), funpt), if ((mode & EXPRmode) != 0) false else context.reportAmbiguousErrors, if ((mode & EXPRmode) != 0) tree else context.tree) match { - case fun1: Tree => + case fun1: Tree if !fun1.containsError() => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 incCounter(typedApplyCount) def isImplicitMethod(tpe: Type) = tpe match { @@ -3460,39 +3698,30 @@ trait Typers extends Modes with Adaptations { //if (fun2.hasSymbol && fun2.symbol.name == nme.apply && fun2.symbol.owner == ArrayClass) { // But this causes cyclic reference for Array class in Cleanup. It is easy to overcome this // by calling ArrayClass.info here (or some other place before specialize). - if (fun2.symbol == Array_apply) { + if (fun2.symbol == Array_apply && !res.containsError()) { val checked = gen.mkCheckInit(res) // this check is needed to avoid infinite recursion in Duplicators // (calling typed1 more than once for the same tree) if (checked ne res) typed { atPos(tree.pos)(checked) } else res } else res + + case eTree: Tree => + if (settings.errortrees.value) + println("[ErrorTree silent] Encounter error in silent typing of apply") + + val ex = quickErrorTreeFinder(eTree) + onError(if (ex.exception == null) ex else TypedApplyError(fun, ex.exception)) + case ex: TypeError => - fun match { - case Select(qual, name) - if !isPatternMode && nme.isOpAssignmentName(name.decode) => - val qual1 = typedQualifier(qual) - if (treeInfo.isVariableOrGetter(qual1)) { - stopTimer(failedOpEqNanos, opeqStart) - convertToAssignment(fun, qual1, name, args, ex) - } else { - stopTimer(failedApplyNanos, appStart) - if ((qual1.symbol ne null) && qual1.symbol.isValue) - error(tree.pos, "reassignment to val") - else - reportTypeError(fun.pos, ex) - setError(tree) - } - case _ => - stopTimer(failedApplyNanos, appStart) - reportTypeError(fun.pos, ex) - setError(tree) - } + onError(TypedApplyError(fun, ex)) + + } } } - def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree], ex: TypeError): Tree = { + def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree]): Tree = { val prefix = name.subName(0, name.length - nme.EQL.length) def mkAssign(vble: Tree): Tree = Assign( @@ -3533,10 +3762,11 @@ trait Typers extends Modes with Adaptations { case Apply(fn, indices) => treeInfo.methPart(fn) match { case Select(table, nme.apply) => mkUpdate(table, indices) - case _ => errorTree(qual, "Unexpected tree during assignment conversion.") + case _ => UnexpectedTreeAssignmentConversionError(qual) } } typed1(tree1, mode, pt) + /* debuglog("retry assign: "+tree1) silent(_.typed1(tree1, mode, pt)) match { @@ -3549,8 +3779,8 @@ trait Typers extends Modes with Adaptations { */ } - def qualifyingClassSym(qual: Name): Symbol = - if (tree.symbol != NoSymbol) tree.symbol else qualifyingClass(tree, qual, false) + def qualifyingClassSym(qual: Name): Either[ErrorTree, Symbol] = + if (tree.symbol != NoSymbol) Right(tree.symbol) else qualifyingClass(tree, qual, false) def typedSuper(qual: Tree, mix: TypeName) = { val qual1 = typed(qual) @@ -3561,7 +3791,7 @@ trait Typers extends Modes with Adaptations { } //println(clazz+"/"+qual1.tpe.typeSymbol+"/"+qual1) - def findMixinSuper(site: Type): Type = { + def findMixinSuper(site: Type): Either[ErrorTree, Type] = { var ps = site.parents filter (_.typeSymbol.name == mix) if (ps.isEmpty) ps = site.parents filter (_.typeSymbol.toInterface.name == mix) @@ -3576,35 +3806,48 @@ trait Typers extends Modes with Adaptations { // println(mix) // the reference to super class got lost during erasure restrictionError(tree.pos, unit, "traits may not select fields or methods from super[C] where C is a class") + Left(NullErrorTree) } else { - error(tree.pos, mix+" does not name a parent class of "+clazz) + Left(MixinMissingParentClassNameError(tree, mix, clazz)) } - ErrorType } else if (!ps.tail.isEmpty) { - error(tree.pos, "ambiguous parent class qualifier") - ErrorType + Left(AmbiguousParentClassError(tree)) } else { - ps.head + Right(ps.head) } } val owntype = if (mix.isEmpty) { if ((mode & SUPERCONSTRmode) != 0) - if (clazz.info.parents.isEmpty) AnyRefClass.tpe // can happen due to cyclic references ==> #1036 - else clazz.info.parents.head - else intersectionType(clazz.info.parents) + if (clazz.info.parents.isEmpty) Right(AnyRefClass.tpe) // can happen due to cyclic references ==> #1036 + else Right(clazz.info.parents.head) + else Right(intersectionType(clazz.info.parents)) } else { findMixinSuper(clazz.tpe) } - treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) + val owntype1 = if (owntype.isLeft) { + // Is there any way we can push the error tree into super? + // Report here for the moment + try { + owntype.left.get.emit(context) + } catch { + case _: TypeError => + assert(false, "Invalid type error when typing Super") + } + ErrorType + } else owntype.right.get + + treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype1) } def typedThis(qual: Name) = { - val clazz = qualifyingClassSym(qual) - if (clazz == NoSymbol) setError(tree) + val clazzOrError = qualifyingClassSym(qual) + if (clazzOrError.isLeft) clazzOrError.left.get + else if (clazzOrError.right.get == NoSymbol) setError(tree) else { + val clazz = clazzOrError.right.get tree setSymbol clazz setType clazz.thisType.underlying if (isStableContext(tree, mode, pt)) tree setType clazz.thisType tree @@ -3638,9 +3881,14 @@ trait Typers extends Modes with Adaptations { } if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & EXPRmode) != 0) { val qual1 = - if (member(qual, name) != NoSymbol) qual + if (member(qual, name) != NoSymbol) NullErrorTree else adaptToMemberWithArgs(tree, qual, name, mode) - if (qual1 ne qual) return typed(treeCopy.Select(tree, qual1, name), mode, pt) + if (!qual1.containsError()) { + if (qual1 ne qual) + return typed(treeCopy.Select(tree, qual1, name), mode, pt) + } else { + return qual1 + } } if (!reallyExists(sym)) { @@ -3671,24 +3919,14 @@ trait Typers extends Modes with Adaptations { ) } - def makeErrorTree = { - val tree1 = tree match { - case Select(_, _) => treeCopy.Select(tree, qual, name) - case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) - } - setError(tree1) - } - - if (name == nme.ERROR && forInteractive) - return makeErrorTree - - if (!qual.tpe.widen.isErroneous) { + if (forInteractive) + NotAMemberInteractive(tree) + else if (!qual.tpe.widen.isErroneous) { val lastTry = missingHook(qual.tpe.typeSymbol, name) if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) - notAMemberError(tree.pos, qual, name) - } - - if (forInteractive) makeErrorTree else setError(tree) + NotAMemberError(tree, qual, name) + } else + NotAMemberErroneous(tree) } else { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) @@ -3714,16 +3952,32 @@ trait Typers extends Modes with Adaptations { result, (TypeTreeWithDeferredRefCheck(){ () => val tp = qual.tpe; val sym = tp.typeSymbolDirect // will execute during refchecks -- TODO: make private checkTypeRef in refchecks public and call that one? - checkBounds(qual.pos, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "") - qual // you only get to see the wrapped tree after running this check :-p + checkBounds(qual.pos, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "") match { + case Some(err) => Left(err) + case _ => Right(qual) // you only get to see the wrapped tree after running this check :-p + } }) setType qual.tpe, name) - case accErr: Inferencer#AccessError => - val qual1 = - try adaptToMemberWithArgs(tree, qual, name, mode) - catch { case _: TypeError => qual } - if (qual1 ne qual) typed(Select(qual1, name) setPos tree.pos, mode, pt) - else accErr.emit() + case tt if tt.containsError() => + val errTree = quickErrorTreeFinder(tt) + errTree match { + case accError: AccessError => + val qual1 = + try adaptToMemberWithArgs(tree, qual, name, mode) + catch { + case _: TypeError => + // Ambigues implicits throw TypeError + // and then they are reported here. + NullErrorTree + } + if (!qual1.containsError() && (qual1 ne qual)) { + typed(Select(qual1, name) setPos tree.pos, mode, pt) + } else { + accError + } + case _ => + tt + } case _ => result } @@ -3731,7 +3985,8 @@ trait Typers extends Modes with Adaptations { // getClass, we have to catch it immediately so expressions // like x.getClass().newInstance() are typed with the type of x. val isRefinableGetClass = ( - selection.symbol.name == nme.getClass_ + !selection.containsError() + && selection.symbol.name == nme.getClass_ && selection.tpe.params.isEmpty // TODO: If the type of the qualifier is inaccessible, we can cause private types // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this @@ -3739,7 +3994,7 @@ trait Typers extends Modes with Adaptations { && qual.tpe.typeSymbol.isPublic ) if (isRefinableGetClass) - selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe)) + selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe)) else selection } @@ -3753,8 +4008,17 @@ trait Typers extends Modes with Adaptations { * (2) Change imported symbols to selections */ def typedIdent(name: Name): Tree = { - def ambiguousError(msg: String) = - error(tree.pos, "reference to " + name + " is ambiguous;\n" + msg) + var errorContainer: Tree = null + @inline + def ambiguousError(msg: String) = { + assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") + errorContainer = AmbiguousIdentError(tree, name, msg) + } + @inline + def identError(tree: ErrorTree) = { + assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") + errorContainer = tree + } var defSym: Symbol = tree.symbol // the directly found symbol var pre: Type = NoPrefix // the prefix type of defSym, if a class member @@ -3856,7 +4120,7 @@ trait Typers extends Modes with Adaptations { ambiguousError( "it is imported twice in the same scope by\n"+imports.head + "\nand "+imports1.head) } - while (!imports1.isEmpty && + while (errorContainer == null && !imports1.isEmpty && (!imports.head.isExplicitImport(name) || imports1.head.depth == imports.head.depth)) { var impSym1 = imports1.head.importedSymbol(name) @@ -3871,11 +4135,17 @@ trait Typers extends Modes with Adaptations { } imports1 = imports1.tail } - defSym = impSym - val qual0 = imports.head.qual - if (!(shortenImports && qual0.symbol.isPackage)) // optimization: don't write out package prefixes - qual = atPos(tree.pos.focusStart)(resetPos(qual0.duplicate)) - pre = qual.tpe + if (errorContainer == null) { + if (imports.head.qual.containsError()) { + defSym = context.owner.newErrorSymbol(name) + } else { + defSym = impSym + val qual0 = imports.head.qual + if (!(shortenImports && qual0.symbol.isPackage)) // optimization: don't write out package prefixes + qual = atPos(tree.pos.focusStart)(resetPos(qual0.duplicate)) + pre = qual.tpe + } + } } else if (settings.exposeEmptyPackage.value && checkEmptyPackage()) log("Allowing empty package member " + name + " due to settings.") @@ -3889,50 +4159,53 @@ trait Typers extends Modes with Adaptations { if (inaccessibleSym eq NoSymbol) { // Avoiding some spurious error messages: see SI-2388. if (reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME)) () - else error(tree.pos, "not found: "+decodeWithKind(name, context.owner)) + else identError(SymbolNotFound(tree, name, context.owner)) + } else { + identError(AccessError( + tree, inaccessibleSym, context.enclClass.owner.thisType, context.enclClass.owner, + inaccessibleExplanation + )) } - else new AccessError( - tree, inaccessibleSym, context.enclClass.owner.thisType, - inaccessibleExplanation - ).emit() defSym = context.owner.newErrorSymbol(name) } } } - if (defSym.owner.isPackageClass) pre = defSym.owner.thisType - if (defSym.isThisSym) { - typed1(This(defSym.owner) setPos tree.pos, mode, pt) + + if (errorContainer != null) { + errorContainer } else { - val tree1 = if (qual == EmptyTree) tree - else atPos(tree.pos)(Select(qual, name)) - // atPos necessary because qualifier might come from startContext - val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) - // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? - stabilize(tree2, pre2, mode, pt) match { - case accErr: Inferencer#AccessError => accErr.emit() - case result => result + if (defSym.owner.isPackageClass) pre = defSym.owner.thisType + if (defSym.isThisSym) { + typed1(This(defSym.owner) setPos tree.pos, mode, pt) + } else { + val tree1 = if (qual == EmptyTree) tree + else atPos(tree.pos)(Select(qual, name)) + // atPos necessary because qualifier might come from startContext + val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) + // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? + stabilize(tree2, pre2, mode, pt) } } } def typedCompoundTypeTree(templ: Template) = { val parents1 = templ.parents mapConserve (typedType(_, mode)) - if (parents1 exists (_.tpe.isError)) tree setType ErrorType + if (parents1 exists (_.containsErrorOrIsErrorTyped())) tree setType ErrorType else { val decls = new Scope //Console.println("Owner: " + context.enclClass.owner + " " + context.enclClass.owner.id) val self = refinedType(parents1 map (_.tpe), context.enclClass.owner, decls, templ.pos) newTyper(context.make(templ, self.typeSymbol, decls)).typedRefinement(templ.body) - tree setType self + tree.setType(self) } } def typedAppliedTypeTree(tpt: Tree, args: List[Tree]) = { val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) - if (tpt1.tpe.isError) { - setError(tree) + if (tpt1.containsErrorOrIsErrorTyped()) { + setError(treeCopy.AppliedTypeTree(tree, tpt1, args)) } else if (!tpt1.hasSymbol) { - errorTree(tree, tpt1.tpe+" does not take type parameters") + AppliedTypeNoParametersError(tree, tpt1.tpe) } else { val tparams = tpt1.symbol.typeParams if (sameLength(tparams, args)) { @@ -3965,16 +4238,18 @@ trait Typers extends Modes with Adaptations { // wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap // we can't simply use original in refchecks because it does not contains types // (and the only typed trees we have have been mangled so they're not quite the original tree anymore) - checkBounds(result.pos, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "") - result // you only get to see the wrapped tree after running this check :-p + checkBounds(result.pos, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "") match { + case Some(err) => Left(err) + case _ => Right(result) // you only get to see the wrapped tree after running this check :-p + } }).setType(result.tpe) else result } else if (tparams.isEmpty) { - errorTree(tree, tpt1.tpe+" does not take type parameters") + AppliedTypeNoParametersError(tree, tpt1.tpe) } else { //Console.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}") if (settings.debug.value) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info)//debug - errorTree(tree, "wrong number of type arguments for "+tpt1.tpe+", should be "+tparams.length) + AppliedTypeWrongNumberOfArgsError(tree, "wrong number of type arguments for "+tpt1.tpe+", should be "+tparams.length) } } } @@ -3988,7 +4263,12 @@ trait Typers extends Modes with Adaptations { //if (settings.debug.value && tree.isDef) log("typing definition of "+sym);//DEBUG tree match { case PackageDef(pid, stats) => - val pid1 = typedQualifier(pid).asInstanceOf[RefTree] + val pid1 = typedQualifier(pid) match { + case e: ErrorTree => + RefTreeError(e, pid.name) + case t: RefTree => + t + } assert(sym.moduleClass ne NoSymbol, sym) // complete lazy annotations val annots = sym.annotations @@ -4023,6 +4303,9 @@ trait Typers extends Modes with Adaptations { typer1.silent(_.typedUseCase(useCase)) match { case ex: TypeError => unit.warning(useCase.pos, ex.msg) + case tree: Tree if tree.containsError() => + val ex = quickErrorTreeFinder(tree) + unit.warning(useCase.pos, ex.exception.msg) case _ => } for (useCaseSym <- useCase.defined) { @@ -4045,9 +4328,14 @@ trait Typers extends Modes with Adaptations { treeCopy.Alternative(tree, alts1) setType pt case Star(elem) => - checkStarPatOK(tree.pos, mode) + val err0 = checkStarPatOK(tree.pos, mode) val elem1 = typed(elem, mode, pt) - treeCopy.Star(tree, elem1) setType makeFullyDefined(pt) + val elem2 = err0 match { + case Some(err) => + wrapInBlock(err, elem1) + case _ => elem1 + } + treeCopy.Star(tree, elem2) setType makeFullyDefined(pt) case Bind(name, body) => typedBind(name, body) @@ -4131,25 +4419,32 @@ trait Typers extends Modes with Adaptations { val tparam = context.owner freshExistential "" setInfo TypeBounds.upper(pt) ExistentialType(List(tparam), arrayType(tparam.tpe)) } - val (expr1, baseClass) = expr0.tpe.typeSymbol match { - case ArrayClass => (adapt(expr0, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) - case _ => (adapt(expr0, onlyStickyModes(mode), seqType(pt)), SeqClass) - } - expr1.tpe.baseType(baseClass) match { - case TypeRef(_, _, List(elemtp)) => - treeCopy.Typed(tree, expr1, tpt setType elemtp) setType elemtp - case _ => - setError(tree) + + if (expr0.containsError()) + treeCopy.Typed(tree, expr0, tpt setType ErrorType) setType ErrorType + else { + val (expr1, baseClass) = expr0.tpe.typeSymbol match { + case ArrayClass => (adapt(expr0, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) + case _ => (adapt(expr0, onlyStickyModes(mode), seqType(pt)), SeqClass) + } + expr1.tpe.baseType(baseClass) match { + case TypeRef(_, _, List(elemtp)) => + treeCopy.Typed(tree, expr1, tpt setType elemtp) setType elemtp + case _ => + setError(tree) + } } case Typed(expr, tpt) => val tpt1 = typedType(tpt, mode) val expr1 = typed(expr, onlyStickyModes(mode), tpt1.tpe.deconst) - val owntype = - if (isPatternMode) inferTypedPattern(tpt1.pos, tpt1.tpe, pt) - else tpt1.tpe - //Console.println(typed pattern: "+tree+":"+", tp = "+tpt1.tpe+", pt = "+pt+" ==> "+owntype)//DEBUG - treeCopy.Typed(tree, expr1, tpt1) setType owntype + val (expr2, ownType) = if (isPatternMode) + inferTypedPattern(tpt1.pos, tpt1.tpe, pt) match { + case Left(err) => + (wrapInBlock(expr1, err), ErrorType) + case Right(tp) => (expr1, tp) + } else (expr1, tpt1.tpe) + treeCopy.Typed(tree, expr1, tpt1) setType ownType case TypeApply(fun, args) => // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) @@ -4192,6 +4487,8 @@ trait Typers extends Modes with Adaptations { case Apply(fun, args) => typedApply(fun, args) match { + case tree1 if tree1.containsError() => + tree1 case Apply(Select(New(tpt), name), args) if (tpt.tpe != null && tpt.tpe.typeSymbol == ArrayClass && @@ -4201,12 +4498,14 @@ trait Typers extends Modes with Adaptations { // convert new Array^N[T](len) for N > 1 to evidence[ClassManifest[T]].newArrayN(len) val Some((level, manifType)) = erasure.GenericArray.unapply(tpt.tpe) if (level > MaxArrayDims) - error(tree.pos, "cannot create a generic multi-dimensional array of more than "+MaxArrayDims+" dimensions") - val newArrayApp = atPos(tree.pos) { - val manif = getManifestTree(tree.pos, manifType, false) - new ApplyToImplicitArgs(Select(manif, if (level == 1) "newArray" else "newArray"+level), args) + MultiDimensionalArrayError(tree) + else { + val newArrayApp = atPos(tree.pos) { + val manif = getManifestTree(tree.pos, manifType, false) + new ApplyToImplicitArgs(Select(manif, if (level == 1) "newArray" else "newArray"+level), args) + } + typed(newArrayApp, mode, pt) } - typed(newArrayApp, mode, pt) case tree1 => tree1 } @@ -4235,20 +4534,26 @@ trait Typers extends Modes with Adaptations { if (name.isTypeName) qual1 = checkStable(qual1) val tree1 = // temporarily use `filter` and an alternative for `withFilter` - if (name == nme.withFilter) + if (qual1.containsError()) + treeCopy.Select(tree, qual1, name) setType ErrorType + else if (name == nme.withFilter) silent(_ => typedSelect(qual1, name)) match { - case result1: Tree => + case result1: Tree if !result1.containsError() => result1 - case ex1: TypeError => + case ex1 => silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { case result2: Tree => unit.deprecationWarning( tree.pos, "`withFilter' method does not yet exist on "+qual1.tpe.widen+ ", using `filter' method instead") result2 - case ex2: TypeError => - reportTypeError(tree.pos, ex1) - setError(tree) + case _: TypeError => + val ex2 = ex1 match { + case te: TypeError => te + case t: Tree => + quickErrorTreeFinder(t).exception + } + WithFilterError(tree, ex2) } } else @@ -4273,12 +4578,17 @@ trait Typers extends Modes with Adaptations { case SingletonTypeTree(ref) => val ref1 = checkStable( typed(ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe)) - tree setType ref1.tpe.resultType + if (ref1.containsError()) + treeCopy.SingletonTypeTree(tree, ref1) setType ErrorType + else + tree setType ref1.tpe.resultType case SelectFromTypeTree(qual, selector) => val qual1 = typedType(qual, mode) - if (qual1.tpe.isVolatile) error(tree.pos, "illegal type selection from volatile type "+qual.tpe) - typedSelect(qual1, selector) + if (qual1.tpe.isVolatile) + TypeSelectionFromVolatileTypeError(tree, qual) + else + typedSelect(qual1, selector) case CompoundTypeTree(templ) => typedCompoundTypeTree(templ) @@ -4311,6 +4621,39 @@ trait Typers extends Modes with Adaptations { } } + def handleErrorTree(t: Tree) { + val allErrors = errorTreesFinder(t) + + // Report any errors if possible in the current context + // If an error tree throws type error (we are in silent mode), then we will not mark + // tree as visited and it will be reported later if necessary + allErrors.foreach { t0 => + try { + if (settings.errortrees.value) { + println("[ErrorTree emit] " + t0) + if (t0.exception != null) { + println(t0.exception.getClass) + } + } + t0.emit(context) + t0.reported = true + } catch { + case te: TypeError => + // Catch all type errors if we are in typer phase. + // All typer errors will be reported at some point anyway. + if (settings.errortrees.value) + println("[ErrorTree TypeError] Throws " + te) + + // After typer all bets are off + // and we do not try to hide TypeError within error tree + if (phase.id > currentRun.typerPhase.id) { + t0.reported = true + throw te + } + } + } + } + /** * @param tree ... * @param mode ... @@ -4354,22 +4697,36 @@ trait Typers extends Modes with Adaptations { tree1 } - tree1.tpe = addAnnotations(tree1, tree1.tpe) - val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree) + if (tree1.containsError()) { + handleErrorTree(tree1) + if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, tree1) + tree1 + } else { + tree1.tpe = addAnnotations(tree1, tree1.tpe) + val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree) - if (!alreadyTyped) { - printTyping("adapted %s: %s to %s, %s".format( - tree1, tree1.tpe.widen, pt, context.undetparamsString) - ) //DEBUG + if (!alreadyTyped) { + printTyping("adapted %s: %s to %s, %s".format( + tree1, tree1.tpe.widen, pt, context.undetparamsString) + ) //DEBUG + } + if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result) + if (result.containsError()) handleErrorTree(result) + result } - -// for (t <- tree1.tpe) assert(t != WildcardType) -// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe) - if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result) - result } catch { case ex: TypeError => tree.tpe = null + // At some point we might want to get rid of the code below + // because everything will be handled by error trees. + // The only problematic case are Cyclic errors which can pop up almost anywhere + if (settings.errortrees.value) { + println("[ErrorTree CAUGHT] %s: while typing %s".format(ex, tree)) + println("TODO with error trees shouldn't throw non-cyclic errors") + ex.printStackTrace() + println("----------") + } + printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG reportTypeError(tree.pos, ex) setError(tree) @@ -4473,12 +4830,12 @@ trait Typers extends Modes with Adaptations { // to see are those in the signatures. These do not need a unique object as a prefix. // The situation is different for new's and super's, but scalac does not look deep // enough to see those. See #3938 - error(tree.pos, restpe.prefix+" is not a legal prefix for a constructor") + return ConstructorPrefixError(tree, restpe) } //@M fix for #2208 // if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef - if(result.tpe.typeArgs.isEmpty) { + if (result.tpe.typeArgs.isEmpty && !result.containsError()) { // minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) { // must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not // designed to deal with the cycles in the scala package (ScalaObject extends @@ -4494,10 +4851,25 @@ trait Typers extends Modes with Adaptations { def typedTypeConstructor(tree: Tree): Tree = typedTypeConstructor(tree, NOmode) + // Because this is called from Namers and pattern matcher, we + // have to report missing errors (if any) def computeType(tree: Tree, pt: Type): Type = { val tree1 = typed(tree, pt) - transformed(tree) = tree1 - packedType(tree1, context.owner) + if (tree1.containsError()) { + assert(errorTreesFinder(tree1).isEmpty, "All type errors have been reported during computation of type") + ErrorType + } else { + transformed(tree) = tree1 + val (tpe, errs) = packedType(tree1, context.owner) + try { + errs.foreach(_.emit(context)) + tpe + } catch { + case _: TypeError => + assert(false, "No type errors can be thrown after type was computed") + ErrorType + } + } } def transformedOrTyped(tree: Tree, mode: Int, pt: Type): Tree = transformed.get(tree) match { @@ -4515,8 +4887,7 @@ trait Typers extends Modes with Adaptations { def getManifestTree(pos: Position, tp: Type, full: Boolean): Tree = { val manifestOpt = findManifest(tp, full) if (manifestOpt.tree.isEmpty) { - error(pos, "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp) - Literal(Constant(null)) + MissingManifestError(pos, full, tp) } else { manifestOpt.tree } diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index cf9d301e95..7185ea73e2 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -217,6 +217,25 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => override def toString: String = show(this) override def hashCode(): Int = System.identityHashCode(this) override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + + // TODO: implementation of more aggresive caching still needs + // more testing + //protected var hasErrorTree: List[ErrorTree] = null + protected var hasErrorTree: Option[Boolean] = None + protected def initErrorCheck: Unit + + def containsError(): Boolean = { + if (hasErrorTree.isEmpty) + initErrorCheck + hasErrorTree.get + } + } + + @inline def containsErrorCheck(ts: List[Tree]): Some[Boolean] = Some(ts.exists(_.containsError())) + @inline def containsErrorCheck(t: Tree): Some[Boolean] = Some(t.containsError()) + + trait AbsErrorTree extends Tree { + def emit(): Unit } /** A tree for a term. Not all terms are TermTrees; use isTerm @@ -259,6 +278,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => override def tpe_=(t: Type) = if (t != NoType) throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") override def isEmpty = true + + protected def initErrorCheck { + hasErrorTree = Some(false) + } } /** Common base class for all member definitions: types, classes, @@ -284,6 +307,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => extends MemberDef { def name = pid.name def mods = Modifiers() + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(stats) + } } /** A common base class for class and object definitions. @@ -295,13 +322,21 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => /** A class definition. */ case class ClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], impl: Template) - extends ImplDef + extends ImplDef { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(tparams ++ List(impl)) + } + } /** An object definition, e.g. `object Foo`. Internally, objects are * quite frequently called modules to reduce ambiguity. */ case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) - extends ImplDef + extends ImplDef { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(impl) + } + } /** A common base class for ValDefs and DefDefs. */ @@ -314,17 +349,29 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => /** A value definition (this includes vars as well, which differ from * vals only in having the MUTABLE flag set in their Modifiers.) */ - case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef + case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(tpt, rhs)) + } + } /** A method definition. */ case class DefDef(mods: Modifiers, name: TermName, tparams: List[TypeDef], - vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) extends ValOrDefDef + vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) extends ValOrDefDef { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(tparams ++ vparamss.flatten ++ List(tpt, rhs)) + } + } /** An abstract type, a type parameter, or a type alias. */ case class TypeDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) - extends MemberDef + extends MemberDef { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(tparams ++ List(rhs)) + } + } /** A labelled expression. Not expressible in language syntax, but * generated by the compiler to simulate while/do-while loops, and @@ -341,7 +388,11 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => * Forward jumps within a block are allowed. */ case class LabelDef(name: TermName, params: List[Ident], rhs: Tree) - extends DefTree with TermTree + extends DefTree with TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(params ++ List(rhs)) + } + } /** Import selector * @@ -360,7 +411,11 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => * @param selectors */ case class Import(expr: Tree, selectors: List[ImportSelector]) - extends SymTree + extends SymTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(expr) + } + } // The symbol of an Import is an import symbol @see Symbol.newImport // It's used primarily as a marker to check that the import has been typechecked. @@ -382,27 +437,46 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => // def bar // owner is local dummy // } // System.err.println("TEMPLATE: " + parents) + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(self::parents:::body) + } } /** Block of expressions (semicolon separated expressions) */ case class Block(stats: List[Tree], expr: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(stats ++ List(expr)) + } + } /** Case clause in a pattern match, eliminated during explicitouter * (except for occurrences in switch statements) */ case class CaseDef(pat: Tree, guard: Tree, body: Tree) - extends Tree + extends Tree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(pat, guard, body)) + } + } /** Alternatives of patterns, eliminated by explicitouter, except for * occurrences in encoded Switch stmt (=remaining Match(CaseDef(...)) */ case class Alternative(trees: List[Tree]) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(trees) + } + } /** Repetition of pattern, eliminated by explicitouter */ case class Star(elem: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(elem) + } + } /** Bind of a variable to a rhs pattern, eliminated by explicitouter * @@ -410,29 +484,53 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => * @param body */ case class Bind(name: Name, body: Tree) - extends DefTree + extends DefTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(body) + } + } case class UnApply(fun: Tree, args: List[Tree]) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(fun::args) + } + } /** Array of expressions, needs to be translated in backend, */ case class ArrayValue(elemtpt: Tree, elems: List[Tree]) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(elemtpt::elems) + } + } /** Anonymous function, eliminated by analyzer */ case class Function(vparams: List[ValDef], body: Tree) - extends TermTree with SymTree + extends TermTree with SymTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(vparams ++ List(body)) + } + } // The symbol of a Function is a synthetic value of name nme.ANON_FUN_NAME // It is the owner of the function's parameters. /** Assignment */ case class Assign(lhs: Tree, rhs: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(lhs, rhs)) + } + } /** Conditional expression */ case class If(cond: Tree, thenp: Tree, elsep: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(cond, thenp, elsep)) + } + } /** - Pattern matching expression (before explicitouter) * - Switch statements (after explicitouter) @@ -446,30 +544,54 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => * `Ident(nme.WILDCARD)` */ case class Match(selector: Tree, cases: List[CaseDef]) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(selector::cases) + } + } /** Return expression */ case class Return(expr: Tree) - extends TermTree with SymTree + extends TermTree with SymTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(expr) + } + } // The symbol of a Return node is the enclosing method. case class Try(block: Tree, catches: List[CaseDef], finalizer: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(block::catches ++ List(finalizer)) + } + } /** Throw expression */ case class Throw(expr: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(expr) + } + } /** Object instantiation * One should always use factory method below to build a user level new. * * @param tpt a class type */ - case class New(tpt: Tree) extends TermTree + case class New(tpt: Tree) extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(tpt) + } + } /** Type annotation, eliminated by explicit outer */ case class Typed(expr: Tree, tpt: Tree) - extends TermTree + extends TermTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(expr, tpt)) + } + } /** Common base class for Apply and TypeApply. This could in principle * be a SymTree, but whether or not a Tree is a SymTree isn't used @@ -488,6 +610,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => extends GenericApply { override def symbol: Symbol = fun.symbol override def symbol_=(sym: Symbol) { fun.symbol = sym } + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(fun::args) + } } /** Value application */ @@ -495,6 +621,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => extends GenericApply { override def symbol: Symbol = fun.symbol override def symbol_=(sym: Symbol) { fun.symbol = sym } + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(fun::args) + } } class ApplyToImplicitArgs(fun: Tree, args: List[Tree]) extends Apply(fun, args) @@ -508,7 +638,11 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => * - f is stored as the node's symbol field. */ case class ApplyDynamic(qual: Tree, args: List[Tree]) - extends TermTree with SymTree + extends TermTree with SymTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(qual::args) + } + } // The symbol of an ApplyDynamic is the function symbol of `qual`, or NoSymbol, if there is none. /** Super reference, qual = corresponding this reference */ @@ -517,20 +651,37 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => // For instance in C.super(...), it would be C. override def symbol: Symbol = qual.symbol override def symbol_=(sym: Symbol) { qual.symbol = sym } + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(qual) + } } /** Self reference */ case class This(qual: TypeName) - extends TermTree with SymTree + extends TermTree with SymTree { + protected def initErrorCheck { + // TODO should check qual name, symbol? + hasErrorTree = Some(false) + } + } // The symbol of a This is the class to which the this refers. // For instance in C.this, it would be C. /** Designator <qualifier> . <name> */ case class Select(qualifier: Tree, name: Name) - extends RefTree + extends RefTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(qualifier) + } + } /** Identifier <name> */ - case class Ident(name: Name) extends RefTree { } + case class Ident(name: Name) extends RefTree { + protected def initErrorCheck { + hasErrorTree = Some(false) + } + } class BackQuotedIdent(name: Name) extends Ident(name) @@ -538,6 +689,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => case class Literal(value: Constant) extends TermTree { assert(value ne null) + + protected def initErrorCheck { + hasErrorTree = Some(false) + } } // @deprecated("will be removed and then be re-introduced with changed semantics, use Literal(Constant(x)) instead") @@ -548,45 +703,81 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => * Eliminated by typechecker (typedAnnotated), the annotations are then stored in * an AnnotatedType. */ - case class Annotated(annot: Tree, arg: Tree) extends Tree + case class Annotated(annot: Tree, arg: Tree) extends Tree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(annot, arg)) + } + } /** Singleton type, eliminated by RefCheck */ case class SingletonTypeTree(ref: Tree) - extends TypTree + extends TypTree { + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(ref) + } + } /** Type selection <qualifier> # <name>, eliminated by RefCheck */ case class SelectFromTypeTree(qualifier: Tree, name: TypeName) - extends TypTree with RefTree + extends TypTree with RefTree { + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(qualifier) + } + } /** Intersection type <parent1> with ... with <parentN> { <decls> }, eliminated by RefCheck */ case class CompoundTypeTree(templ: Template) - extends TypTree + extends TypTree { + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(templ) + } + } /** Applied type <tpt> [ <args> ], eliminated by RefCheck */ case class AppliedTypeTree(tpt: Tree, args: List[Tree]) extends TypTree { override def symbol: Symbol = tpt.symbol override def symbol_=(sym: Symbol) { tpt.symbol = sym } + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(tpt::args) + } } case class TypeBoundsTree(lo: Tree, hi: Tree) - extends TypTree + extends TypTree { + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(List(lo, hi)) + } + } case class ExistentialTypeTree(tpt: Tree, whereClauses: List[Tree]) - extends TypTree + extends TypTree { + + protected def initErrorCheck { + hasErrorTree = containsErrorCheck(tpt::whereClauses) + } + } /** A synthetic tree holding an arbitrary type. Not to be confused with * with TypTree, the trait for trees that are only used for type trees. * TypeTree's are inserted in several places, but most notably in * `RefCheck`, where the arbitrary type trees are all replaced by - * TypeTree's. */ + * TypeTree's. + * + */ case class TypeTree() extends TypTree { private var orig: Tree = null + private var errorCause: Tree = null private[scala] var wasEmpty: Boolean = false override def symbol = if (tpe == null) null else tpe.typeSymbol override def isEmpty = (tpe eq null) || tpe == NoType + def original: Tree = orig def setOriginal(tree: Tree): this.type = { def followOriginal(t: Tree): Tree = t match { @@ -598,10 +789,20 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => this } + def setErrorCause(tree: Tree): this.type = { + assert(tree != null) + errorCause = tree + this + } + override def defineType(tp: Type): this.type = { wasEmpty = isEmpty setType(tp) } + + protected def initErrorCheck { + hasErrorTree = Some(errorCause != null) + } } def TypeTree(tp: Type): TypeTree = TypeTree() setType tp @@ -715,6 +916,7 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => traverse(lo); traverse(hi) case ExistentialTypeTree(tpt, whereClauses) => traverse(tpt); traverseTrees(whereClauses) + case _: AbsErrorTree => case _ => xtraverse(this, tree) } diff --git a/test/files/neg/patternalts.check b/test/files/neg/patternalts.check index 9bec9a001a..63bd244345 100644 --- a/test/files/neg/patternalts.check +++ b/test/files/neg/patternalts.check @@ -1,4 +1,7 @@ patternalts.scala:3: error: illegal variable in pattern alternative case List(x) | List() => Console.println(x) ^ -one error found +patternalts.scala:3: error: not found: value x + case List(x) | List() => Console.println(x) + ^ +two errors found diff --git a/test/files/neg/t1878.check b/test/files/neg/t1878.check index 4b9cfebde1..de59b023ab 100644 --- a/test/files/neg/t1878.check +++ b/test/files/neg/t1878.check @@ -1,15 +1,18 @@ -t1878.scala:3: error: _* may only come last - val err1 = "" match { case Seq(f @ _*, ',') => f } - ^ t1878.scala:3: error: scrutinee is incompatible with pattern type; found : Seq[A] required: java.lang.String val err1 = "" match { case Seq(f @ _*, ',') => f } ^ +t1878.scala:3: error: not found: value f + val err1 = "" match { case Seq(f @ _*, ',') => f } + ^ +t1878.scala:3: error: _* may only come last + val err1 = "" match { case Seq(f @ _*, ',') => f } + ^ t1878.scala:9: error: _* may only come last val List(List(_*, arg2), _) = List(List(1,2,3), List(4,5,6)) ^ t1878.scala:13: error: _* may only come last case <p> { _* } </p> => ^ -four errors found +5 errors found diff --git a/test/files/neg/t2641.check b/test/files/neg/t2641.check index 2056a1b9ab..595dd46b11 100644 --- a/test/files/neg/t2641.check +++ b/test/files/neg/t2641.check @@ -1,24 +1,15 @@ t2641.scala:18: error: illegal cyclic reference involving trait ManagedSeq with TraversableViewLike[A, ManagedSeqStrict[A], ManagedSeq[A]] ^ -t2641.scala:16: error: illegal inheritance; - self-type ManagedSeq does not conform to ManagedSeqStrict[A]'s selftype ManagedSeqStrict[A] - extends ManagedSeqStrict[A] - ^ -t2641.scala:17: error: illegal inheritance; - self-type ManagedSeq does not conform to scala.collection.TraversableView[A,ManagedSeqStrict[A]]'s selftype scala.collection.TraversableView[A,ManagedSeqStrict[A]] - with TraversableView[A, ManagedSeqStrict[A]] - ^ -t2641.scala:16: error: illegal inheritance; - self-type ManagedSeq does not conform to ScalaObject's selftype ScalaObject - extends ManagedSeqStrict[A] - ^ t2641.scala:24: error: something is wrong (wrong class file?): trait ManagedSeq with type parameters [A,Coll] gets applied to arguments [], phase = typer trait Transformed[+B] extends ManagedSeq[B, Coll] with super.Transformed[B] ^ t2641.scala:26: error: something is wrong (wrong class file?): trait ManagedSeq with type parameters [A,Coll] gets applied to arguments [], phase = namer trait Sliced extends Transformed[A] with super.Sliced { ^ +t2641.scala:27: error: value managedIterator is not a member of ManagedSeq + override def managedIterator = self.managedIterator slice (from, until) + ^ t2641.scala:26: error: illegal inheritance; superclass Any is not a subclass of the superclass ManagedSeqStrict of the mixin trait Transformed @@ -29,7 +20,16 @@ t2641.scala:26: error: illegal inheritance; superclass Any of the mixin trait Sliced trait Sliced extends Transformed[A] with super.Sliced { ^ -t2641.scala:27: error: value managedIterator is not a member of ManagedSeq - override def managedIterator = self.managedIterator slice (from, until) - ^ +t2641.scala:16: error: illegal inheritance; + self-type ManagedSeq does not conform to ManagedSeqStrict[A]'s selftype ManagedSeqStrict[A] + extends ManagedSeqStrict[A] + ^ +t2641.scala:17: error: illegal inheritance; + self-type ManagedSeq does not conform to scala.collection.TraversableView[A,ManagedSeqStrict[A]]'s selftype scala.collection.TraversableView[A,ManagedSeqStrict[A]] + with TraversableView[A, ManagedSeqStrict[A]] + ^ +t2641.scala:16: error: illegal inheritance; + self-type ManagedSeq does not conform to ScalaObject's selftype ScalaObject + extends ManagedSeqStrict[A] + ^ 9 errors found diff --git a/test/files/neg/t2918.check b/test/files/neg/t2918.check index e67f24ec57..d125895463 100644 --- a/test/files/neg/t2918.check +++ b/test/files/neg/t2918.check @@ -1,7 +1,7 @@ -t2918.scala:2: error: cyclic aliasing or subtyping involving type A - def g[X, A[X] <: A[X]](x: A[X]) = x - ^ t2918.scala:2: error: A does not take type parameters - def g[X, A[X] <: A[X]](x: A[X]) = x + def g[X, A[X] <: A[X]](x: A[X]) = x ^ +t2918.scala:2: error: cyclic aliasing or subtyping involving type A + def g[X, A[X] <: A[X]](x: A[X]) = x + ^ two errors found diff --git a/test/files/neg/t3015.check b/test/files/neg/t3015.check index 32809b0669..fec54b087b 100644 --- a/test/files/neg/t3015.check +++ b/test/files/neg/t3015.check @@ -3,9 +3,4 @@ t3015.scala:7: error: scrutinee is incompatible with pattern type; required: java.lang.String val b(foo) = "foo" ^ -t3015.scala:7: error: type mismatch; - found : _$1(in value foo) where type _$1(in value foo) <: java.lang.String - required: (some other)_$1(in value foo) where type (some other)_$1(in value foo) - val b(foo) = "foo" - ^ -two errors found +one error found diff --git a/test/files/neg/t997.check b/test/files/neg/t997.check index c9fe0de756..166d465971 100644 --- a/test/files/neg/t997.check +++ b/test/files/neg/t997.check @@ -10,4 +10,10 @@ t997.scala:13: error: wrong number of arguments for object Foo t997.scala:13: error: not found: value a "x" match { case Foo(a, b, c) => Console.println((a,b,c)) } ^ -four errors found +t997.scala:13: error: not found: value b +"x" match { case Foo(a, b, c) => Console.println((a,b,c)) } + ^ +t997.scala:13: error: not found: value c +"x" match { case Foo(a, b, c) => Console.println((a,b,c)) } + ^ +6 errors found diff --git a/test/files/neg/volatile.check b/test/files/neg/volatile.check index b904284125..da27c68e6c 100644 --- a/test/files/neg/volatile.check +++ b/test/files/neg/volatile.check @@ -1,7 +1,4 @@ -volatile.scala:11: error: Inferred type C.this.D with C.this.E#T contains type selection from volatile type C.this.D with C.this.E +volatile.scala:11: error: Inferred type C.this.D with C.this.E#T contains type selection from volatile type C.this.D with C.this.E#T var sneak = { () => y.x } ^ -volatile.scala:11: error: Inferred type () => C.this.D with C.this.E#T contains type selection from volatile type C.this.D with C.this.E - var sneak = { () => y.x } - ^ -two errors found +one error found |