diff options
author | Hubert Plociniczak <hubert.plociniczak@gmail.com> | 2012-01-25 11:33:53 +0100 |
---|---|---|
committer | Hubert Plociniczak <hubert.plociniczak@gmail.com> | 2012-01-25 11:33:53 +0100 |
commit | c800d1fec5241ed8c29e5af30465856f9b583246 (patch) | |
tree | a37c194bb2d632adf9adeda17a107913f63864ba /src | |
parent | de2b0c68785afc0f801fbe8d2750366e90c9fa70 (diff) | |
download | scala-c800d1fec5241ed8c29e5af30465856f9b583246.tar.gz scala-c800d1fec5241ed8c29e5af30465856f9b583246.tar.bz2 scala-c800d1fec5241ed8c29e5af30465856f9b583246.zip |
Use context for buffering errors that cannot/shouldn't be reported in the given moment (instead of throwing type errors). This avoids previous problems where we were creating fake error trees in some incorrect places like in type completers in Namers etc. Implicits relied heavily on type errors being thrown but performance should stay the same due to some explicit checks/returns.
Some of the problems involved how ambiguous error messages were collected/reported because it was very random (similarly for divergent implicits). This should be more explicit now. Reduced the number of unnecessary cyclic references being thrown (apart from those in Symbols/Types which don't have a context and need to stay for now as is).
Review by @paulp, @odersky.
Diffstat (limited to 'src')
25 files changed, 2119 insertions, 1167 deletions
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index a943b6fe24..6b4080c6ad 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -345,22 +345,26 @@ trait Symbols extends api.Symbols { self: SymbolTable => } // Lock a symbol, using the handler if the recursion depth becomes too great. - def lock(handler: => Unit) = { + def lock(handler: => Unit): Boolean = { if ((rawflags & LOCKED) != 0L) { if (settings.Yrecursion.value != 0) { recursionTable get this match { case Some(n) => if (n > settings.Yrecursion.value) { handler + false } else { recursionTable += (this -> (n + 1)) + true } case None => recursionTable += (this -> 1) + true } - } else { handler } + } else { handler; false } } else { rawflags |= LOCKED + true // activeLocks += 1 // lockedSyms += this } @@ -963,7 +967,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => phase = phaseOf(infos.validFrom) tp.complete(this) } finally { - // if (id == 431) println("completer ran "+tp.getClass+" for "+fullName) unlock() phase = current } diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index dcc395ddd2..3a0717d344 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -397,7 +397,6 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => // case SelectFromArray(qualifier, name, _) => // print(qualifier); print(".<arr>"); print(symName(tree, name)) - case tree => xprintTree(this, tree) } diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index a2c55a89d6..5bb0c98bfb 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -129,6 +129,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 isErrorTyped = (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. */ diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 1df60f32d9..8ca00dd5c5 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -1388,7 +1388,7 @@ trait Types extends api.Types { self: SymbolTable => //Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG } if (baseTypeSeqCache eq undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + typeSymbol) + throw new RecoverableCyclicReference(typeSymbol) baseTypeSeqCache } @@ -1430,7 +1430,7 @@ trait Types extends api.Types { self: SymbolTable => } } if (baseClassesCache eq null) - throw new TypeError("illegal cyclic reference involving " + typeSymbol) + throw new RecoverableCyclicReference(typeSymbol) baseClassesCache } @@ -1946,7 +1946,7 @@ trait Types extends api.Types { self: SymbolTable => // If a subtyping cycle is not detected here, we'll likely enter an infinite // loop before a sensible error can be issued. SI-5093 is one example. case x: SubType if x.supertype eq this => - throw new TypeError("illegal cyclic reference involving " + sym) + throw new RecoverableCyclicReference(sym) case tp => tp } } @@ -2064,7 +2064,7 @@ trait Types extends api.Types { self: SymbolTable => } } if (baseTypeSeqCache == undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + sym) + throw new RecoverableCyclicReference(sym) baseTypeSeqCache } @@ -2074,11 +2074,11 @@ trait Types extends api.Types { self: SymbolTable => else pre.prefixString ) private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]") - private def refinementString = ( + def refinementString = ( if (sym.isStructuralRefinement) ( decls filter (sym => sym.isPossibleInRefinement && sym.isPublic) map (_.defString) - mkString(" {", "; ", "}") + mkString("{", "; ", "}") ) else "" ) @@ -2498,7 +2498,7 @@ trait Types extends api.Types { self: SymbolTable => if (args.isEmpty && params.isEmpty) new TypeVar(origin, constr) else if (args.size == params.size) new AppliedTypeVar(origin, constr, params zip args) else if (args.isEmpty) new HKTypeVar(origin, constr, params) - else throw new TypeError("Invalid TypeVar construction: " + ((origin, constr, args, params))) + else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) ) trace("create", "In " + tv.originLocation)(tv) @@ -2599,7 +2599,7 @@ trait Types extends api.Types { self: SymbolTable => TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv) } else - throw new TypeError("Invalid type application in TypeVar: " + params + ", " + newArgs) + throw new Error("Invalid type application in TypeVar: " + params + ", " + newArgs) ) // newArgs.length may differ from args.length (could've been empty before) // @@ -3079,7 +3079,7 @@ trait Types extends api.Types { self: SymbolTable => // don't expand cyclical type alias // we require that object is initialized, thus info.typeParams instead of typeParams. if (sym1.isAliasType && sameLength(sym1.info.typeParams, args) && !sym1.lockOK) - throw new TypeError("illegal cyclic reference involving " + sym1) + throw new RecoverableCyclicReference(sym1) val pre1 = pre match { case x: SuperType if sym1.isEffectivelyFinal || sym1.isDeferred => @@ -3101,7 +3101,7 @@ trait Types extends api.Types { self: SymbolTable => def copyTypeRef(tp: Type, pre: Type, sym: Symbol, args: List[Type]): Type = tp match { case TypeRef(pre0, sym0, _) if pre == pre0 && sym0.name == sym.name => if (sym.isAliasType && sameLength(sym.info.typeParams, args) && !sym.lockOK) - throw new TypeError("illegal cyclic reference involving " + sym) + throw new RecoverableCyclicReference(sym) TypeRef(pre, sym, args) case _ => @@ -3985,15 +3985,17 @@ trait Types extends api.Types { self: SymbolTable => else instParamRelaxed(ps.tail, as.tail) //Console.println("instantiating " + sym + " from " + basesym + " with " + basesym.typeParams + " and " + baseargs+", pre = "+pre+", symclazz = "+symclazz);//DEBUG - if (sameLength(basesym.typeParams, baseargs)) { + if (sameLength(basesym.typeParams, baseargs)) instParam(basesym.typeParams, baseargs) - } else { - throw new TypeError( - "something is wrong (wrong class file?): "+basesym+ - " with type parameters "+ - basesym.typeParams.map(_.name).mkString("[",",","]")+ - " gets applied to arguments "+baseargs.mkString("[",",","]")+", phase = "+phase) - } + else + if (symclazz.tpe.parents.exists(_.isErroneous)) + ErrorType // don't be to overzealous with throwing exceptions, see #2641 + else + throw new Error( + "something is wrong (wrong class file?): "+basesym+ + " with type parameters "+ + basesym.typeParams.map(_.name).mkString("[",",","]")+ + " gets applied to arguments "+baseargs.mkString("[",",","]")+", phase = "+phase) case ExistentialType(tparams, qtpe) => capturedSkolems = capturedSkolems union tparams toInstance(qtpe, clazz) @@ -6278,6 +6280,12 @@ trait Types extends api.Types { self: SymbolTable => def this(msg: String) = this(NoPosition, msg) } + // TODO: RecoverableCyclicReference should be separated from TypeError, + // but that would be a big change. Left for further refactoring. + /** An exception for cyclic references from which we can recover */ + case class RecoverableCyclicReference(sym: Symbol) + extends TypeError("illegal cyclic reference involving " + sym) + class NoCommonType(tps: List[Type]) extends Throwable( "lub/glb of incompatible types: " + tps.mkString("", " and ", "")) with ControlThrowable @@ -6286,9 +6294,6 @@ trait Types extends api.Types { self: SymbolTable => def this(pre: Type, tp: String) = this("malformed type: " + pre + "#" + tp) } - /** An exception signalling a variance annotation/usage conflict */ - class VarianceError(msg: String) extends TypeError(msg) - /** The current indentation string for traces */ private var indent: String = "" diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index 7b5de1f3dd..c1d6c1a4d4 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -33,17 +33,16 @@ abstract class TreeBrowsers { val borderSize = 10 - def create(): SwingBrowser = new SwingBrowser(); /** Pseudo tree class, so that all JTree nodes are treated uniformly */ case class ProgramTree(units: List[UnitTree]) extends Tree { - override def toString(): String = "Program" + override def toString: String = "Program" } /** Pseudo tree class, so that all JTree nodes are treated uniformly */ case class UnitTree(unit: CompilationUnit) extends Tree { - override def toString(): String = unit.toString() + override def toString: String = unit.toString } /** diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 88a9b5e18b..3a2c5f61b2 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -17,7 +17,6 @@ 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 @@ -31,7 +30,6 @@ trait Trees extends reflect.internal.Trees { self: Global => override def isType = definition.isType } - /** Either an assignment or a named argument. Only appears in argument lists, * eliminated by typecheck (doTypedApply) */ @@ -40,7 +38,7 @@ trait Trees extends reflect.internal.Trees { self: Global => /** 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 /** emitted by typer, eliminated by refchecks */ case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree @@ -163,7 +161,7 @@ trait Trees extends reflect.internal.Trees { self: Global => traverser.traverse(qualifier) case ReferenceToBoxed(idt) => traverser.traverse(idt) - case TypeTreeWithDeferredRefCheck() => // TODO: should we traverse the wrapped tree? + case TypeTreeWithDeferredRefCheck() => // (and rewrap the result? how to update the deferred check? would need to store wrapped tree instead of returning it from check) case _ => super.xtraverse(traverser, tree) } diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 0fea0a2d92..477cec8c8e 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -1060,6 +1060,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") implicit def addOnTypeError[T](x: => T): OnTypeError[T] = new OnTypeError(x) + // OnTypeError should still catch TypeError because of cyclic references, + // but DivergentImplicit shouldn't leak anymore here class OnTypeError[T](op: => T) { def onTypeError(alt: => T) = try { op diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 36227c1052..da913a1601 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -28,7 +28,7 @@ trait Plugins { val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map Path.apply val classes = Plugin.loadAllFrom(jars, dirs, settings.disable.value) - // Lach plugin must only be instantiated once. A common pattern + // Each plugin must only be instantiated once. A common pattern // is to register annotation checkers during object construction, so // creating multiple plugin instances will leave behind stale checkers. classes map (Plugin.instantiate(_, this)) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 2eddd36db0..25ae6f33d2 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/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 56d9658377..bdd6a73b79 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -422,7 +422,7 @@ abstract class UnCurry extends InfoTransform if (tp.typeSymbol.isBottomClass) getManifest(AnyClass.tpe) else if (!manifestOpt.tree.isEmpty) manifestOpt.tree else if (tp.bounds.hi ne tp) getManifest(tp.bounds.hi) - else localTyper.getManifestTree(tree.pos, tp, false) + else localTyper.getManifestTree(tree, tp, false) } atPhase(phase.next) { localTyper.typedPos(pos) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 16d55c26ca..18c7635b1e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -23,6 +23,7 @@ trait Analyzer extends AnyRef with Macros with NamesDefaults with TypeDiagnostics + with ContextErrors { val global : Global import global._ diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala new file mode 100644 index 0000000000..a762e44bda --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -0,0 +1,1052 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package typechecker + +import scala.collection.{ mutable, immutable } +import scala.tools.util.StringOps.{ countElementsAsString, countAsString } +import symtab.Flags.{ PRIVATE, PROTECTED } +import scala.tools.util.EditDistance.similarString + +trait ContextErrors { + self: Analyzer => + + import global._ + + object ErrorKinds extends Enumeration { + type ErrorKind = Value + val Normal, Access, Ambiguous, Divergent = Value + } + + import ErrorKinds.ErrorKind + + trait AbsTypeError extends Throwable { + def errPos: Position + def errMsg: String + def kind: ErrorKind + } + + case class NormalTypeError(underlyingTree: Tree, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + + def errPos:Position = underlyingTree.pos + override def toString() = "[Type error at:" + underlyingTree.pos + "] " + errMsg + } + + case class SymbolTypeError(underlyingSym: Symbol, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + + def errPos = underlyingSym.pos + } + + case class TypeErrorWrapper(ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + def errMsg = ex.msg + def errPos = ex.pos + } + + case class TypeErrorWithUnderlyingTree(tree: Tree, ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + def errMsg = ex.msg + def errPos = tree.pos + } + + case class AmbiguousTypeError(underlyingTree: Tree, errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Ambiguous) extends AbsTypeError + + case class PosAndMsgTypeError(errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) extends AbsTypeError + + object ErrorUtils { + def issueNormalTypeError(tree: Tree, msg: String)(implicit context: Context) { + issueTypeError(NormalTypeError(tree, msg)) + } + + def issueSymbolTypeError(sym: Symbol, msg: String)(implicit context: Context) { + issueTypeError(SymbolTypeError(sym, msg)) + } + + def issueDivergentImplicitsError(tree: Tree, msg: String)(implicit context: Context) { + issueTypeError(NormalTypeError(tree, msg, ErrorKinds.Divergent)) + } + + def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) { + context.issueAmbiguousError(pre, sym1, sym2, err) + } + + def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) } + + def typeErrorMsg(found: Type, req: Type, possiblyMissingArgs: Boolean) = { + def missingArgsMsg = if (possiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else "" + "type mismatch" + foundReqMsg(found, req) + missingArgsMsg + } + } + + import ErrorUtils._ + + trait TyperContextErrors { + self: Typer => + + import infer.setError + + object TyperErrorGen { + implicit val context0: Context = infer.getContext + + def UnstableTreeError(tree: Tree) = { + def addendum = { + "\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile." + } + issueNormalTypeError(tree, + "stable identifier required, but "+tree+" found." + ( + if (isStableExceptVolatile(tree)) addendum else "")) + setError(tree) + } + + def NoImplicitFoundError(tree: Tree, param: Symbol) = { + 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 + } + } + issueNormalTypeError(tree, errMsg) + } + + def AdaptTypeError(tree: Tree, found: Type, req: Type) = { + // 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 + // members present, then display along with the expected members. This is done here because + // this is the last point where we still have access to the original tree, rather than just + // the found/req types. + val foundType: Type = req.normalize match { + case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass => + val retyped = typed (tree.duplicate setType null) + val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) + + if (foundDecls.isEmpty) found + else { + // The members arrive marked private, presumably because there was no + // expected type and so they're considered members of an anon class. + foundDecls foreach (_ resetFlag (PRIVATE | PROTECTED)) + // TODO: if any of the found parents match up with required parents after normalization, + // print the error so that they match. The major beneficiary there would be + // java.lang.Object vs. AnyRef. + refinedType(found.parents, found.typeSymbol.owner, foundDecls, tree.pos) + } + case _ => + found + } + assert(!found.isErroneous && !req.isErroneous) + + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req))) ) + if (settings.explaintypes.value) + explainTypes(found, req) + } + + def WithFilterError(tree: Tree, ex: AbsTypeError) = { + issueTypeError(ex) + setError(tree) + } + + def ParentTypesError(templ: Template, ex: TypeError) = { + templ.tpe = null + issueNormalTypeError(templ, ex.getMessage()) + } + + // additional parentTypes errors + def ConstrArgsInTraitParentTpeError(arg: Tree, parent: Symbol) = + issueNormalTypeError(arg, parent + " is a trait; does not take constructor arguments") + + def MissingTypeArgumentsParentTpeError(supertpt: Tree) = + issueNormalTypeError(supertpt, "missing type arguments") + + // typedIdent + def AmbiguousIdentError(tree: Tree, name: Name, msg: String) = + NormalTypeError(tree, "reference to " + name + " is ambiguous;\n" + msg) + + def SymbolNotFoundError(tree: Tree, name: Name, owner: Symbol, startingIdentCx: Context) = { + // This laborious determination arrived at to keep the tests working. + val calcSimilar = ( + name.length > 2 && ( + startingIdentCx.reportErrors + || startingIdentCx.enclClassOrMethod.reportErrors + ) + ) + // avoid calculating if we're in "silent" mode. + // name length check to limit unhelpful suggestions for e.g. "x" and "b1" + val similar = { + if (!calcSimilar) "" + else { + val allowed = ( + startingIdentCx.enclosingContextChain + flatMap (ctx => ctx.scope.toList ++ ctx.imports.flatMap(_.allImportedSymbols)) + filter (sym => sym.isTerm == name.isTermName) + filterNot (sym => sym.isPackage || sym.isSynthetic || sym.hasMeaninglessName) + ) + val allowedStrings = ( + allowed.map("" + _.name).distinct.sorted + filterNot (s => (s contains '$') || (s contains ' ')) + ) + similarString("" + name, allowedStrings) + } + } + NormalTypeError(tree, "not found: "+decodeWithKind(name, owner) + similar) + } + + // typedAppliedTypeTree + def AppliedTypeNoParametersError(tree: Tree, errTpe: Type) = { + issueNormalTypeError(tree, errTpe + " does not take type parameters") + setError(tree) + } + + def AppliedTypeWrongNumberOfArgsError(tree: Tree, tpt: Tree, tparams: List[Symbol]) = { + val tptSafeString: String = try { + tpt.tpe.toString() + } catch { + case _: CyclicReference => + tpt.toString() + } + val msg = "wrong number of type arguments for "+tptSafeString+", should be "+tparams.length + issueNormalTypeError(tree, msg) + setError(tree) + } + + // typedTypeDef + def LowerBoundError(tree: TypeDef, lowB: Type, highB: Type) = + issueNormalTypeError(tree, "lower bound "+lowB+" does not conform to upper bound "+highB) + + def HiddenSymbolWithError[T <: Tree](tree: T): T = + setError(tree) + + def SymbolEscapesScopeError[T <: Tree](tree: T, badSymbol: Symbol): T = { + val modifierString = if (badSymbol.isPrivate) "private " else "" + issueNormalTypeError(tree, modifierString + badSymbol + " escapes its defining scope as part of type "+tree.tpe) + setError(tree) + } + + // typedDefDef + def StarParamNotLastError(param: Tree) = + issueNormalTypeError(param, "*-parameter must come last") + + def StarWithDefaultError(meth: Symbol) = + issueSymbolTypeError(meth, "a parameter section with a `*'-parameter is not allowed to have default arguments") + + def InvalidConstructorDefError(ddef: Tree) = + issueNormalTypeError(ddef, "constructor definition not allowed here") + + def DeprecatedParamNameError(param: Symbol, name: Name) = + issueSymbolTypeError(param, "deprecated parameter name "+ name +" has to be distinct from any other parameter name (deprecated or not).") + + // computeParamAliases + def SuperConstrReferenceError(tree: Tree) = + NormalTypeError(tree, "super constructor cannot be passed a self reference unless parameter is declared by-name") + + def SuperConstrArgsThisReferenceError(tree: Tree) = + NormalTypeError(tree, "super constructor arguments cannot reference unconstructed `this`") + + // typedValDef + def VolatileValueError(vdef: Tree) = + issueNormalTypeError(vdef, "values cannot be volatile") + + def FinalVolatileVarError(vdef: Tree) = + issueNormalTypeError(vdef, "final vars cannot be volatile") + + def LocalVarUninitializedError(vdef: Tree) = + issueNormalTypeError(vdef, "local variables must be initialized") + + //typedAssign + def AssignmentError(tree: Tree, varSym: Symbol) = { + issueNormalTypeError(tree, + if (varSym != null && varSym.isValue) "reassignment to val" + else "assignment to non variable") + setError(tree) + } + + def UnexpectedTreeAssignmentConversionError(tree: Tree) = { + issueNormalTypeError(tree, "Unexpected tree during assignment conversion.") + setError(tree) + } + + def MultiDimensionalArrayError(tree: Tree) = { + issueNormalTypeError(tree, "cannot create a generic multi-dimensional array of more than "+ definitions.MaxArrayDims+" dimensions") + setError(tree) + } + + //typedSuper + def MixinMissingParentClassNameError(tree: Tree, mix: Name, clazz: Symbol) = + issueNormalTypeError(tree, mix+" does not name a parent class of "+clazz) + + def AmbiguousParentClassError(tree: Tree) = + issueNormalTypeError(tree, "ambiguous parent class qualifier") + + //typedSelect + def NotAMemberError(sel: Tree, qual: Tree, name: Name) = { + 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 + ) + } + issueNormalTypeError(sel, errMsg) + // the error has to be set for the copied tree, otherwise + // the error remains persistent acros multiple compilations + // and causes problems + //setError(sel) + } + + //typedNew + def IsAbstractError(tree: Tree, sym: Symbol) = { + issueNormalTypeError(tree, sym + " is abstract; cannot be instantiated") + setError(tree) + } + + def DoesNotConformToSelfTypeError(tree: Tree, sym: Symbol, tpe0: Type) = { + issueNormalTypeError(tree, sym + " cannot be instantiated because it does not conform to its self-type " + tpe0) + setError(tree) + } + + //typedEta + def UnderscoreEtaError(tree: Tree) = { + issueNormalTypeError(tree, "_ must follow method; cannot follow " + tree.tpe) + setError(tree) + } + + //typedReturn + def ReturnOutsideOfDefError(tree: Tree) = { + issueNormalTypeError(tree, "return outside method definition") + setError(tree) + } + + def ReturnWithoutTypeError(tree: Tree, owner: Symbol) = { + issueNormalTypeError(tree, owner + " has return statement; needs result type") + setError(tree) + } + + //typedBind + def VariableInPatternAlternativeError(tree: Tree) = { + issueNormalTypeError(tree, "illegal variable in pattern alternative") + //setError(tree) + } + + //typedCase + def StarPositionInPatternError(tree: Tree) = + issueNormalTypeError(tree, "_* may only come last") + + //typedFunction + def MaxFunctionArityError(fun: Tree) = { + issueNormalTypeError(fun, "implementation restricts functions to " + definitions.MaxFunctionArity + " parameters") + setError(fun) + } + + def WrongNumberOfParametersError(tree: Tree, argpts: List[Type]) = { + issueNormalTypeError(tree, "wrong number of parameters; expected = " + argpts.length) + setError(tree) + } + + def MissingParameterTypeError(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 + }) + + issueNormalTypeError(vparam, "missing parameter type" + suffix) + } + + def ConstructorsOrderError(tree: Tree) = { + issueNormalTypeError(tree, "called constructor's definition must precede calling constructor's definition") + setError(tree) + } + + def OnlyDeclarationsError(tree: Tree) = { + issueNormalTypeError(tree, "only declarations allowed here") + setError(tree) + } + + // typedAnnotation + def AnnotationNotAConstantError(tree: Tree) = + NormalTypeError(tree, "annotation argument needs to be a constant; found: " + tree) + + def AnnotationArgNullError(tree: Tree) = + NormalTypeError(tree, "annotation argument cannot be null") + + def ArrayConstantsError(tree: Tree) = + NormalTypeError(tree, "Array constants have to be specified using the `Array(...)' factory method") + + def ArrayConstantsTypeMismatchError(tree: Tree, pt: Type) = + NormalTypeError(tree, "found array constant, expected argument of type " + pt) + + def UnexpectedTreeAnnotation(tree: Tree) = + NormalTypeError(tree, "unexpected tree in annotation: "+ tree) + + def AnnotationTypeMismatchError(tree: Tree, expected: Type, found: Type) = + NormalTypeError(tree, "expected annotation of type " + expected + ", found " + found) + + def MultipleArgumentListForAnnotationError(tree: Tree) = + NormalTypeError(tree, "multiple argument lists on classfile annotation") + + def UnknownAnnotationNameError(tree: Tree, name: Name) = + NormalTypeError(tree, "unknown annotation argument name: " + name) + + def DuplicateValueAnnotationError(tree: Tree, name: Name) = + NormalTypeError(tree, "duplicate value for annotation argument " + name) + + def ClassfileAnnotationsAsNamedArgsError(tree: Tree) = + NormalTypeError(tree, "classfile annotation arguments have to be supplied as named arguments") + + def AnnotationMissingArgError(tree: Tree, annType: Type, sym: Symbol) = + NormalTypeError(tree, "annotation " + annType.typeSymbol.fullName + " is missing argument " + sym.name) + + def NestedAnnotationError(tree: Tree, annType: Type) = + NormalTypeError(tree, "nested classfile annotations must be defined in java; found: "+ annType) + + def UnexpectedTreeAnnotationError(tree: Tree, unexpected: Tree) = + NormalTypeError(tree, "unexpected tree after typing annotation: "+ unexpected) + + // TODO no test case + //typedExistentialTypeTree + def AbstractionFromVolatileTypeError(vd: ValDef) = + issueNormalTypeError(vd, "illegal abstraction from value with volatile type "+vd.symbol.tpe) + + def TypedApplyWrongNumberOfTpeParametersError(tree: Tree, fun: Tree) = { + issueNormalTypeError(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + setError(tree) + } + + def TypedApplyDoesNotTakeTpeParametersError(tree: Tree, fun: Tree) = { + issueNormalTypeError(tree, treeSymTypeMsg(fun)+" does not take type parameters.") + setError(tree) + } + + // doTypeApply + //tryNamesDefaults + def WrongNumberOfArgsError(tree: Tree, fun: Tree) = + NormalTypeError(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun)) + + def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) = + NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun)) + + // can it still happen? see test case neg/t960.scala + // TODO no test case + def OverloadedUnapplyError(tree: Tree) = + issueNormalTypeError(tree, "cannot resolve overloaded unapply") + + def UnapplyWithSingleArgError(tree: Tree) = + issueNormalTypeError(tree, "an unapply method must accept a single argument.") + + def MultipleVarargError(tree: Tree) = + NormalTypeError(tree, "when using named arguments, the vararg parameter has to be specified exactly once") + + def ModuleUsingCompanionClassDefaultArgsErrror(tree: Tree) = + NormalTypeError(tree, "module extending its companion class cannot use default constructor arguments") + + def NotEnoughArgsError(tree: Tree, fun0: Tree, missing0: List[Symbol]) = { + def notEnoughArgumentsMsg(fun: Tree, missing: List[Symbol]) = { + 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 + } + NormalTypeError(tree, notEnoughArgumentsMsg(fun0, missing0)) + } + + //doTypedApply - patternMode + // TODO: missing test case + def TooManyArgsPatternError(fun: Tree) = + NormalTypeError(fun, "too many arguments for unapply pattern, maximum = "+definitions.MaxTupleArity) + + def WrongNumberArgsPatternError(tree: Tree, fun: Tree) = + NormalTypeError(tree, "wrong number of arguments for "+treeSymTypeMsg(fun)) + + def ApplyWithoutArgsError(tree: Tree, fun: Tree) = + NormalTypeError(tree, fun.tpe+" does not take parameters") + + //checkClassType + def TypeNotAStablePrefixError(tpt: Tree, pre: Type) = { + issueNormalTypeError(tpt, "type "+pre+" is not a stable prefix") + setError(tpt) + } + + def ClassTypeRequiredError(tree: Tree, found: AnyRef) = { + issueNormalTypeError(tree, "class type required but "+found+" found") + setError(tree) + } + + // validateParentClasses + def ParentSuperSubclassError(parent: Tree, superclazz: Symbol, + parentSym: Symbol, mixin: Symbol) = + NormalTypeError(parent, "illegal inheritance; super"+superclazz+ + "\n is not a subclass of the super"+parentSym+ + "\n of the mixin " + mixin) + + def ParentNotATraitMixinError(parent: Tree, mixin: Symbol) = + NormalTypeError(parent, mixin+" needs to be a trait to be mixed in") + + def ParentFinalInheritanceError(parent: Tree, mixin: Symbol) = + NormalTypeError(parent, "illegal inheritance from final "+mixin) + + def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = + NormalTypeError(parent, "illegal inheritance from sealed " + psym + ": " + context.unit.source.file.canonicalPath + " != " + psym.sourceFile.canonicalPath) + + def ParentSelfTypeConformanceError(parent: Tree, selfType: Type) = + NormalTypeError(parent, + "illegal inheritance;\n self-type "+selfType+" does not conform to "+ + parent +"'s selftype "+parent.tpe.typeOfThis) + + // TODO: missing test case + def ParentInheritedTwiceError(parent: Tree, parentSym: Symbol) = + NormalTypeError(parent, parentSym+" is inherited twice") + + //adapt + def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { + issueNormalTypeError(tree, + "missing arguments for " + meth.fullLocationString + ( + if (meth.isConstructor) "" + else ";\nfollow this method with `_' if you want to treat it as a partially applied function" + )) + setError(tree) + } + + def MissingTypeParametersError(tree: Tree) = { + issueNormalTypeError(tree, tree.symbol+" takes type parameters") + setError(tree) + } + + def KindArityMismatchError(tree: Tree, pt: Type) = { + issueNormalTypeError(tree, + tree.tpe+" takes "+countElementsAsString(tree.tpe.typeParams.length, "type parameter")+ + ", expected: "+countAsString(pt.typeParams.length)) + setError(tree) + } + + def CaseClassConstructorError(tree: Tree) = { + issueNormalTypeError(tree, tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method") + setError(tree) + } + + //TODO Needs test case + def ConstructorPrefixError(tree: Tree, restpe: Type) = { + issueNormalTypeError(tree, restpe.prefix+" is not a legal prefix for a constructor") + setError(tree) + } + + // SelectFromTypeTree + def TypeSelectionFromVolatileTypeError(tree: Tree, qual: Tree) = { + issueNormalTypeError(tree, "illegal type selection from volatile type "+qual.tpe) + setError(tree) + } + + // packedType + def InferTypeWithVolatileTypeSelectionError(tree: Tree, pre: Type) = + issueNormalTypeError(tree, "Inferred type "+tree.tpe+" contains type selection from volatile type "+pre) + + def AbstractExistentiallyOverParamerizedTpeError(tree: Tree, tp: Type) = + issueNormalTypeError(tree, "can't existentially abstract over parameterized type " + tp) + + //manifestTreee + def MissingManifestError(tree: Tree, full: Boolean, tp: Type) = { + issueNormalTypeError(tree, "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp) + setError(tree) + } + + // TODO needs test case + // cases where we do not necessarily return trees + def DependentMethodTpeConversionToFunctionError(tree: Tree, tp: Type) = + issueNormalTypeError(tree, "method with dependent type "+tp+" cannot be converted to function value") + + //checkStarPatOK + def StarPatternWithVarargParametersError(tree: Tree) = + issueNormalTypeError(tree, "star patterns must correspond with varargs parameters") + + // TODO missing test case + def FinitaryError(tparam: Symbol) = + issueSymbolTypeError(tparam, "class graph is not finitary because type parameter "+tparam.name+" is expansively recursive") + + // TODO missing test case for a second case + def QualifyingClassError(tree: Tree, qual: Name) = { + issueNormalTypeError(tree, + if (qual.isEmpty) tree + " can be used only in a class, object, or template" + else qual + " is not an enclosing class") + setError(tree) + } + + // def stabilize + def NotAValueError(tree: Tree, sym: Symbol) = { + issueNormalTypeError(tree, sym.kindString + " " + sym.fullName + " is not a value") + setError(tree) + } + + // checkNoDoubleDefs... + def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = + issueSymbolTypeError(sym0, sym1+" is defined twice"+ + {if(!settings.debug.value) "" else " in "+context0.unit}+ + {if (sym0.isMacro && sym1.isMacro) " \n(note that macros cannot be overloaded)" else ""}) + + // cyclic errors + def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, "cyclic aliasing or subtyping involving "+sym0)) + + def CyclicReferenceError(errPos: Position, lockedSym: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym)) + + def MacroExpandError(tree: Tree, t: Any) = { + issueNormalTypeError(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass) + setError(tree) + } + } + } + + trait InferencerContextErrors { + 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 + } + } + + object InferErrorGen { + + implicit val context0 = getContext + + object PolyAlternativeErrorKind extends Enumeration { + type ErrorType = Value + val WrongNumber, NoParams, ArgsDoNotConform = Value + } + + private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) = + if (sym1.hasDefaultFlag && sym2.hasDefaultFlag && sym1.enclClass == sym2.enclClass) { + val methodName = nme.defaultGetterToMethod(sym1.name) + (sym1.enclClass.pos, + "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + + " define default arguments") + } else { + (pos, + ("ambiguous reference to overloaded definition,\n" + + "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + + "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + + "\nmatch " + rest) + ) + } + + def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String) = { + def errMsg = { + val location = if (sym.isClassConstructor) owner0 else pre.widen + + underlying(sym).fullLocationString + " cannot be accessed in " + + location + explanation + } + NormalTypeError(tree, errMsg, ErrorKinds.Access) + } + + def NoMethodInstanceError(fn: Tree, args: List[Tree], msg: String) = + issueNormalTypeError(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) + + // TODO: no test case + def NoConstructorInstanceError(tree: Tree, restpe: Type, pt: Type, msg: String) = { + issueNormalTypeError(tree, + "constructor of type " + restpe + + " cannot be uniquely instantiated to expected type " + pt + + "\n --- because ---\n" + msg) + setError(tree) + } + + def ConstrInstantiationError(tree: Tree, restpe: Type, pt: Type) = { + issueNormalTypeError(tree, + "constructor cannot be instantiated to expected type" + foundReqMsg(restpe, pt)) + setError(tree) + } + + def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = + issueNormalTypeError(tree, + applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) + + def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, + firstCompeting: Symbol, argtpes: List[Type], pt: Type) = { + val msg0 = + "argument types " + argtpes.mkString("(", ",", ")") + + (if (pt == WildcardType) "" else " and expected result type " + pt) + val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + } + + def NoBestExprAlternativeError(tree: Tree, pt: Type) = + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(tree.symbol.tpe, pt, isPossiblyMissingArgs(tree.symbol.tpe, pt)))) + + def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type) = { + val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) + setError(tree) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + } + + // checkBounds + def KindBoundErrors(tree: Tree, prefix: String, targs: List[Type], + tparams: List[Symbol], kindErrors: List[String]) = { + issueNormalTypeError(tree, + 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 NotWithinBounds(tree: Tree, prefix: String, targs: List[Type], + tparams: List[Symbol], kindErrors: List[String]) = { + 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)) + () + } + + issueNormalTypeError(tree, + prefix + "type arguments " + targs.mkString("[", ",", "]") + + " do not conform to " + tparams.head.owner + "'s type parameter bounds " + + (tparams map (_.defString)).mkString("[", ",", "]")) + } + + //substExpr + def PolymorphicExpressionInstantiationError(tree: Tree, undetparams: List[Symbol], pt: Type) = + issueNormalTypeError(tree, + "polymorphic expression cannot be instantiated to expected type" + + foundReqMsg(polyType(undetparams, skipImplicit(tree.tpe)), pt)) + + //checkCheckable + def TypePatternOrIsInstanceTestError(tree: Tree, tp: Type) = + issueNormalTypeError(tree, "type "+tp+" cannot be used in a type pattern or isInstanceOf test") + + def PatternTypeIncompatibleWithPtError1(tree: Tree, pattp: Type, pt: Type) = + issueNormalTypeError(tree, "pattern type is incompatible with expected type" + foundReqMsg(pattp, pt)) + + def IncompatibleScrutineeTypeError(tree: Tree, pattp: Type, pt: Type) = + issueNormalTypeError(tree, "scrutinee is incompatible with pattern type" + foundReqMsg(pattp, pt)) + + def PatternTypeIncompatibleWithPtError2(pat: Tree, pt1: Type, pt: Type) = { + 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 + } + issueNormalTypeError(pat, errMsg) + } + + def PolyAlternativeError(tree: Tree, argtypes: List[Type], sym: Symbol, err: PolyAlternativeErrorKind.ErrorType) = { + import PolyAlternativeErrorKind._ + val msg = + err match { + case WrongNumber => + "wrong number of type parameters for " + treeSymTypeMsg(tree) + case NoParams => + treeSymTypeMsg(tree) + " does not take type parameters" + case ArgsDoNotConform => + "type arguments " + argtypes.mkString("[", ",", "]") + + " conform to the bounds of none of the overloaded alternatives of\n "+sym+ + ": "+sym.info + } + issueNormalTypeError(tree, msg) + () + } + } + } + + trait NamerContextErrors { + self: Namer => + + object NamerErrorGen { + + implicit val context0 = context + + object SymValidateErrors extends Enumeration { + val ImplicitConstr, ImplicitNotTerm, ImplicitTopObject, + OverrideClass, SealedNonClass, AbstractNonClass, + OverrideConstr, AbstractOverride, LazyAndEarlyInit, + ByNameParameter, AbstractVar = Value + } + + object DuplicatesErrorKinds extends Enumeration { + val RenamedTwice, AppearsTwice = Value + } + + import SymValidateErrors._ + import DuplicatesErrorKinds._ + import symtab.Flags + + def TypeSigError(tree: Tree, ex: TypeError) = { + ex match { + case CyclicReference(sym, info: TypeCompleter) => + issueNormalTypeError(tree, typer.cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) + case _ => + context0.issue(TypeErrorWithUnderlyingTree(tree, ex)) + } + } + + def GetterDefinedTwiceError(getter: Symbol) = + issueSymbolTypeError(getter, getter+" is defined twice") + + def ValOrValWithSetterSuffixError(tree: Tree) = + issueNormalTypeError(tree, "Names of vals or vars may not end in `_='") + + def PrivateThisCaseClassParameterError(tree: Tree) = + issueNormalTypeError(tree, "private[this] not allowed for case class parameters") + + def BeanPropertyAnnotationLimitationError(tree: Tree) = + issueNormalTypeError(tree, "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import") + + def BeanPropertyAnnotationFieldWithoutLetterError(tree: Tree) = + issueNormalTypeError(tree, "`BeanProperty' annotation can be applied only to fields that start with a letter") + + def BeanPropertyAnnotationPrivateFieldError(tree: Tree) = + issueNormalTypeError(tree, "`BeanProperty' annotation can be applied only to non-private fields") + + def DoubleDefError(currentSym: Symbol, prevSym: Symbol) = { + val s1 = if (prevSym.isModule) "case class companion " else "" + val s2 = if (prevSym.isSynthetic) "(compiler-generated) " + s1 else "" + val s3 = if (prevSym.isCase) "case class " + prevSym.name else "" + prevSym + + issueSymbolTypeError(currentSym, prevSym.name + " is already defined as " + s2 + s3) + } + + def MaxParametersCaseClassError(tree: Tree) = + issueNormalTypeError(tree, "Implementation restriction: case classes cannot have more than " + definitions.MaxFunctionArity + " parameters.") + + def InheritsItselfError(tree: Tree) = + issueNormalTypeError(tree, tree.tpe.typeSymbol+" inherits itself") + + def MissingParameterOrValTypeError(vparam: Tree) = + issueNormalTypeError(vparam, "missing parameter type") + + def RootImportError(tree: Tree) = + issueNormalTypeError(tree, "_root_ cannot be imported") + + def SymbolValidationError(sym: Symbol, errKind: SymValidateErrors.Value) { + val msg = errKind match { + case ImplicitConstr => + "`implicit' modifier not allowed for constructors" + + case ImplicitNotTerm => + "`implicit' modifier can be used only for values, variables and methods" + + case ImplicitTopObject => + "`implicit' modifier cannot be used for top-level objects" + + case OverrideClass => + "`override' modifier not allowed for classes" + + case SealedNonClass => + "`sealed' modifier can be used only for classes" + + case AbstractNonClass => + "`abstract' modifier can be used only for classes; it should be omitted for abstract members" + + case OverrideConstr => + "`override' modifier not allowed for constructors" + + case AbstractOverride => + "`abstract override' modifier only allowed for members of traits" + + case LazyAndEarlyInit => + "`lazy' definitions may not be initialized early" + + case ByNameParameter => + "pass-by-name arguments not allowed for case class parameters" + + case AbstractVar => + "only classes can have declared but undefined members" + abstractVarMessage(sym) + + } + issueSymbolTypeError(sym, msg) + } + + + def AbstractMemberWithModiferError(sym: Symbol, flag: Int) = + issueSymbolTypeError(sym, "abstract member may not have " + Flags.flagsToString(flag) + " modifier") + + def IllegalModifierCombination(sym: Symbol, flag1: Int, flag2: Int) = + issueSymbolTypeError(sym, "illegal combination of modifiers: %s and %s for: %s".format( + Flags.flagsToString(flag1), Flags.flagsToString(flag2), sym)) + + def IllegalDependentMethTpeError(sym: Symbol)(context: Context) = { + val errorAddendum = + ": parameter appears in the type of another parameter in the same section or an earlier one" + issueSymbolTypeError(sym, "illegal dependent method type" + errorAddendum)(context) + } + + def DuplicatesError(tree: Tree, name: Name, kind: DuplicatesErrorKinds.Value) = { + val msg = kind match { + case RenamedTwice => + "is renamed twice" + case AppearsTwice => + "appears twice as a target of a renaming" + } + + issueNormalTypeError(tree, name.decode + " " + msg) + } + } + } + + trait ImplicitsContextErrors { + self: ImplicitSearch => + + import definitions._ + + def AmbiguousImplicitError(info1: ImplicitInfo, info2: ImplicitInfo, + pre1: String, pre2: String, trailer: String) + (isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = { + if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { + val coreMsg = + pre1+" "+info1.sym.fullLocationString+" of type "+info1.tpe+"\n "+ + pre2+" "+info2.sym.fullLocationString+" of type "+info2.tpe+"\n "+ + trailer + val errMsg = + if (isView) { + val found = pt.typeArgs(0) + val req = pt.typeArgs(1) + def defaultExplanation = + "Note that implicit conversions are not applicable because they are ambiguous:\n "+ + coreMsg+"are possible conversion functions from "+ found+" to "+req + + def explanation = { + val sym = found.typeSymbol + // Explain some common situations a bit more clearly. + if (AnyRefClass.tpe <:< req) { + if (sym == AnyClass || sym == UnitClass) { + "Note: " + sym.name + " is not implicitly converted to AnyRef. You can safely\n" + + "pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so." + } + else boxedClass get sym match { + case Some(boxed) => + "Note: an implicit exists from " + sym.fullName + " => " + boxed.fullName + ", but\n" + + "methods inherited from Object are rendered ambiguous. This is to avoid\n" + + "a blanket implicit which would convert any " + sym.fullName + " to any AnyRef.\n" + + "You may wish to use a type ascription: `x: " + boxed.fullName + "`." + case _ => + defaultExplanation + } + } + else defaultExplanation + } + + typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + "\n" + explanation + } else { + "ambiguous implicit values:\n "+coreMsg + "match expected type "+pt + } + context.issueAmbiguousError(AmbiguousTypeError(tree, tree.pos, errMsg)) + } + } + + def DivergingImplicitExpansionError(tree: Tree, pt: Type, sym: Symbol)(implicit context0: Context) = + issueDivergentImplicitsError(tree, + "diverging implicit expansion for type "+pt+"\nstarting with "+ + sym.fullLocationString) + } + + object NamesDefaultsErrorsGen { + import typer.infer.setError + + def NameClashError(sym: Symbol, arg: Tree)(implicit context: Context) = { + setError(arg) // to distinguish it from ambiguous reference error + + 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) + issueSymbolTypeError(sym, errMsg) + } + + def AmbiguousReferenceInNamesDefaultError(arg: Tree, name: Name)(implicit context: Context) = { + if (!arg.isErroneous) { // check if name clash wasn't reported already + issueNormalTypeError(arg, + "reference to "+ name +" is ambiguous; it is both a method parameter "+ + "and a variable in scope.") + setError(arg) + } else arg + } + + def UnknownParameterNameNamesDefaultError(arg: Tree, name: Name)(implicit context: Context) = { + issueNormalTypeError(arg, "unknown parameter name: " + name) + setError(arg) + } + + def DoubleParamNamesDefaultError(arg: Tree, name: Name)(implicit context: Context) = { + issueNormalTypeError(arg, "parameter specified twice: "+ name) + setError(arg) + } + + def PositionalAfterNamedNamesDefaultError(arg: Tree)(implicit context: Context) = { + issueNormalTypeError(arg, "positional after named argument.") + setError(arg) + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index faff4ccab2..d828b019f9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package typechecker import symtab.Flags._ -import scala.collection.mutable.ListBuffer +import scala.collection.mutable.{LinkedHashSet, Set} import annotation.tailrec /** @@ -66,8 +66,7 @@ trait Contexts { self: Analyzer => sc.depth += 1 } val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports) - c.reportAmbiguousErrors = !erasedTypes - c.reportGeneralErrors = !erasedTypes + if (erasedTypes) c.setThrowErrors() else c.setReportErrors() c.implicitsEnabled = !erasedTypes c } @@ -83,7 +82,17 @@ trait Contexts { self: Analyzer => } } + private object Errors { + final val ReportErrors = 1 << 0 + final val BufferErrors = 1 << 1 + final val AmbiguousErrors = 1 << 2 + final val notThrowMask = ReportErrors | BufferErrors + final val AllMask = ReportErrors | BufferErrors | AmbiguousErrors + } + class Context private[typechecker] { + import Errors._ + var unit: CompilationUnit = NoCompilationUnit var tree: Tree = _ // Tree associated with this context var owner: Symbol = NoSymbol // The current owner @@ -109,8 +118,6 @@ trait Contexts { self: Analyzer => // (the call to the super or self constructor in the first line of a constructor) // in this context the object's fields should not be in scope - var reportAmbiguousErrors = false - var reportGeneralErrors = false var diagnostic: List[String] = Nil // these messages are printed when issuing an error var implicitsEnabled = false var checking = false @@ -138,12 +145,41 @@ trait Contexts { self: Analyzer => tparams } - def withoutReportingErrors[T](op: => T): T = { - val saved = reportGeneralErrors - reportGeneralErrors = false - try op - finally reportGeneralErrors = saved + private[this] var mode = 0 + private[this] val buffer = LinkedHashSet[AbsTypeError]() + + def errBuffer = buffer + def hasErrors = buffer.nonEmpty + + def state: Int = mode + def restoreState(state0: Int) = mode = state0 + + def reportErrors = (state & ReportErrors) != 0 + def bufferErrors = (state & BufferErrors) != 0 + def ambiguousErrors = (state & AmbiguousErrors) != 0 + def throwErrors = (state & notThrowMask) == 0 + + def setReportErrors() = mode = (ReportErrors | AmbiguousErrors) + def setBufferErrors() = { + assert(bufferErrors || !hasErrors, "When entering the buffer state, context has to be clean. Current buffer: " + buffer) + mode = BufferErrors + } + def setThrowErrors() = mode &= (~AllMask) + def setAmbiguousErrors(report: Boolean) = if (report) mode |= AmbiguousErrors else mode &= notThrowMask + + def updateBuffer(errors: Set[AbsTypeError]) = buffer ++= errors + def condBufferFlush(removeP: AbsTypeError => Boolean) { + val elems = buffer.filter(removeP) + buffer --= elems } + def flushBuffer() { buffer.clear() } + def flushAndReturnBuffer(): Set[AbsTypeError] = { + val current = buffer.clone() + buffer.clear() + current + } + + def logError(err: AbsTypeError) = buffer += err def withImplicitsDisabled[T](op: => T): T = { val saved = implicitsEnabled @@ -183,8 +219,7 @@ trait Contexts { self: Analyzer => c.depth = if (scope == this.scope) this.depth else this.depth + 1 c.imports = imports c.inSelfSuperCall = inSelfSuperCall - c.reportAmbiguousErrors = this.reportAmbiguousErrors - c.reportGeneralErrors = this.reportGeneralErrors + c.restoreState(this.state) c.diagnostic = this.diagnostic c.typingIndentLevel = typingIndentLevel c.implicitsEnabled = this.implicitsEnabled @@ -196,10 +231,10 @@ trait Contexts { self: Analyzer => c } + // TODO: remove? Doesn't seem to be used def make(unit: CompilationUnit): Context = { val c = make(unit, EmptyTree, owner, scope, imports) - c.reportAmbiguousErrors = true - c.reportGeneralErrors = true + c.setReportErrors() c.implicitsEnabled = true c } @@ -229,8 +264,8 @@ trait Contexts { self: Analyzer => def makeSilent(reportAmbiguousErrors: Boolean, newtree: Tree = tree): Context = { val c = make(newtree) - c.reportGeneralErrors = false - c.reportAmbiguousErrors = reportAmbiguousErrors + c.setBufferErrors() + c.setAmbiguousErrors(reportAmbiguousErrors) c } @@ -242,13 +277,11 @@ trait Contexts { self: Analyzer => def makeConstructorContext = { var baseContext = enclClass.outer - //todo: find out why we need next line while (baseContext.tree.isInstanceOf[Template]) baseContext = baseContext.outer val argContext = baseContext.makeNewScope(tree, owner) argContext.inSelfSuperCall = true - argContext.reportGeneralErrors = this.reportGeneralErrors - argContext.reportAmbiguousErrors = this.reportAmbiguousErrors + argContext.restoreState(this.state) def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { if (e != null && e.owner == c.scope) { @@ -275,41 +308,41 @@ trait Contexts { self: Analyzer => private def unitError(pos: Position, msg: String) = unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) + def issue(err: AbsTypeError) { + if (reportErrors) unitError(err.errPos, addDiagString(err.errMsg)) + else if (bufferErrors) { buffer += err } + else throw new TypeError(err.errPos, err.errMsg) + } + + def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { + if (ambiguousErrors) { + if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) + unitError(err.errPos, err.errMsg) + } else if (bufferErrors) { buffer += err } + else throw new TypeError(err.errPos, err.errMsg) + } + + def issueAmbiguousError(err: AbsTypeError) { + if (ambiguousErrors) + unitError(err.errPos, addDiagString(err.errMsg)) + else if (bufferErrors) { buffer += err } + else throw new TypeError(err.errPos, err.errMsg) + } + + // TODO remove def error(pos: Position, err: Throwable) = - if (reportGeneralErrors) unitError(pos, addDiagString(err.getMessage())) + if (reportErrors) unitError(pos, addDiagString(err.getMessage())) else throw err def error(pos: Position, msg: String) = { val msg1 = addDiagString(msg) - if (reportGeneralErrors) unitError(pos, msg1) + if (reportErrors) unitError(pos, msg1) else throw new TypeError(pos, msg1) } - def warning(pos: Position, msg: String) = { - if (reportGeneralErrors) unit.warning(pos, msg) - } - - def ambiguousError(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) { - val (reportPos, msg) = ( - if (sym1.hasDefaultFlag && sym2.hasDefaultFlag && sym1.enclClass == sym2.enclClass) { - val methodName = nme.defaultGetterToMethod(sym1.name) - (sym1.enclClass.pos, - "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + - " define default arguments") - } - else { - (pos, - ("ambiguous reference to overloaded definition,\n" + - "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + - "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + - "\nmatch " + rest) - ) - } - ) - if (reportAmbiguousErrors) { - if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) - unit.error(reportPos, msg) - } else throw new TypeError(pos, msg) + def warning(pos: Position, msg: String): Unit = warning(pos, msg, false) + def warning(pos: Position, msg: String, force: Boolean) { + if (reportErrors || force) unit.warning(pos, msg) } def isLocal(): Boolean = tree match { @@ -343,8 +376,8 @@ trait Contexts { self: Analyzer => def enclosingContextChain: List[Context] = this :: outer.enclosingContextChain - override def toString = "Context(%s@%s unit=%s scope=%s)".format( - owner.fullName, tree.shortClass, unit, scope.## + override def toString = "Context(%s@%s unit=%s scope=%s errors=%b)".format( + owner.fullName, tree.shortClass, unit, scope.##, hasErrors ) /** Is `sub` a subclass of `base` or a companion object of such a subclass? */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index e14f0bcd87..6cb1d562ce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -31,20 +31,26 @@ trait Implicits { import typeDebug.{ ptTree, ptBlock, ptLine } import global.typer.{ printTyping, deindentTyping, indentTyping, printInference } + def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = + inferImplicit(tree, pt, reportAmbiguous, isView, context, true) + /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch` * for more info how the search is conducted. - * @param tree The tree for which the implicit needs to be inserted. - * (the inference might instantiate some of the undetermined - * type parameters of that tree. - * @param pt The expected type of the implicit. - * @param reportAmbiguous Should ambiguous implicit errors be reported? - * False iff we search for a view to find out - * whether one type is coercible to another. - * @param isView We are looking for a view - * @param context The current context - * @return A search result + * @param tree The tree for which the implicit needs to be inserted. + * (the inference might instantiate some of the undetermined + * type parameters of that tree. + * @param pt The expected type of the implicit. + * @param reportAmbiguous Should ambiguous implicit errors be reported? + * False iff we search for a view to find out + * whether one type is coercible to another. + * @param isView We are looking for a view + * @param context The current context + * @param saveAmbiguousDivergent False if any divergent/ambiguous errors should be ignored after + * implicits search, + * true if they should be reported (used in further typechecking). + * @return A search result */ - def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = { + def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = { printInference("[infer %s] %s with pt=%s in %s".format( if (isView) "view" else "implicit", tree, pt, context.owner.enclClass) @@ -64,8 +70,10 @@ trait Implicits { val start = startTimer(implicitNanos) if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty) printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) - - val result = new ImplicitSearch(tree, pt, isView, context.makeImplicit(reportAmbiguous)).bestImplicit + val implicitSearchContext = context.makeImplicit(reportAmbiguous) + val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext).bestImplicit + if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) + context.updateBuffer(implicitSearchContext.errBuffer.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent)) printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains @@ -244,7 +252,8 @@ trait Implicits { * @param isView We are looking for a view * @param context0 The context used for the implicit search */ - class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context) extends Typer(context0) { + class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context) + extends Typer(context0) with ImplicitsContextErrors { printTyping( ptBlock("new ImplicitSearch", "tree" -> tree, @@ -327,50 +336,6 @@ trait Implicits { incCounter(implicitSearchCount) - /** Issues an error signalling ambiguous implicits */ - private def ambiguousImplicitError(info1: ImplicitInfo, info2: ImplicitInfo, - pre1: String, pre2: String, trailer: String) = - if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { - val coreMsg = - pre1+" "+info1.sym.fullLocationString+" of type "+info1.tpe+"\n "+ - pre2+" "+info2.sym.fullLocationString+" of type "+info2.tpe+"\n "+ - trailer - error(tree.pos, - if (isView) { - val found = pt.typeArgs(0) - val req = pt.typeArgs(1) - def defaultExplanation = - "Note that implicit conversions are not applicable because they are ambiguous:\n "+ - coreMsg+"are possible conversion functions from "+ found+" to "+req - - def explanation = { - val sym = found.typeSymbol - // Explain some common situations a bit more clearly. - if (AnyRefClass.tpe <:< req) { - if (sym == AnyClass || sym == UnitClass) { - "Note: " + sym.name + " is not implicitly converted to AnyRef. You can safely\n" + - "pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so." - } - else boxedClass get sym match { - case Some(boxed) => - "Note: an implicit exists from " + sym.fullName + " => " + boxed.fullName + ", but\n" + - "methods inherited from Object are rendered ambiguous. This is to avoid\n" + - "a blanket implicit which would convert any " + sym.fullName + " to any AnyRef.\n" + - "You may wish to use a type ascription: `x: " + boxed.fullName + "`." - case _ => - defaultExplanation - } - } - else defaultExplanation - } - - typeErrorMsg(found, req) + "\n" + explanation - } - else { - "ambiguous implicit values:\n "+coreMsg + "match expected type "+pt - }) - } - /** The type parameters to instantiate */ val undetParams = if (isView) List() else context.outer.undetparams @@ -400,9 +365,7 @@ trait Implicits { // println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG if (context.openImplicits.tail.isEmpty) { if (!(pt.isErroneous)) - context.unit.error( - tree.pos, "diverging implicit expansion for type "+pt+"\nstarting with "+ - info.sym.fullLocationString) + DivergingImplicitExpansionError(tree, pt, info.sym)(context) SearchFailure } else { throw DivergentImplicit @@ -578,6 +541,9 @@ trait Implicits { else typed1(itree, EXPRmode, wildPt) + if (context.hasErrors) + return fail("typed implicit %s has errors".format(info.sym.fullLocationString)) + incCounter(typedImplicits) printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt)) @@ -597,8 +563,8 @@ trait Implicits { } } - if (itree2.tpe.isError) - SearchFailure + if (context.hasErrors) + fail("hasMatchingSymbol reported threw error(s)") else if (!hasMatchingSymbol(itree1)) fail("candidate implicit %s is shadowed by other implicit %s".format( info.sym.fullLocationString, itree1.symbol.fullLocationString)) @@ -620,7 +586,9 @@ 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 ") + checkBounds(itree2, NoPrefix, NoSymbol, undetParams, targs, "inferred ") + if (context.hasErrors) + return fail("type parameters weren't correctly instantiated outside of the implicit tree") // filter out failures from type inference, don't want to remove them from undetParams! // we must be conservative in leaving type params in undetparams @@ -646,21 +614,29 @@ trait Implicits { // re-typecheck) // TODO: the return tree is ignored. This seems to make // no difference, but it's bad practice regardless. - itree2 match { + + + 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) - incCounter(foundImplicits) - printInference("[success] found %s for pt %s".format(result, ptInstantiated)) - result + + if (context.hasErrors) + fail("typing TypeApply reported errors for the implicit tree") + else { + val result = new SearchResult(checked, subst) + incCounter(foundImplicits) + printInference("[success] found %s for pt %s".format(result, ptInstantiated)) + result + } } else fail("incompatible: %s does not match expected type %s".format(itree2.tpe, ptInstantiated)) } } catch { - case ex: TypeError => fail(ex.getMessage()) + case ex: TypeError => + fail(ex.getMessage()) } } @@ -794,7 +770,11 @@ trait Implicits { catch divergenceHandler tryImplicitInfo(i) match { - case SearchFailure => rankImplicits(is, acc) + case SearchFailure => + // We don't want errors that occur during checking implicit info + // to influence the check of further infos. + context.condBufferFlush(_.kind != ErrorKinds.Divergent) + rankImplicits(is, acc) case newBest => best = newBest val newPending = undoLog undo { @@ -829,7 +809,8 @@ trait Implicits { case chosen :: rest => rest find (alt => !improves(chosen, alt)) match { case Some(competing) => - ambiguousImplicitError(chosen, competing, "both", "and", "") + AmbiguousImplicitError(chosen, competing, "both", "and", "")(isView, pt, tree)(context) + return SearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala case _ => if (isView) chosen.useCountView += 1 else chosen.useCountArg += 1 @@ -1230,12 +1211,14 @@ trait Implicits { incCounter(inscopeImplicitHits) } if (result == SearchFailure) { + val previousErrs = context.flushAndReturnBuffer() val failstart = startTimer(oftypeFailNanos) val succstart = startTimer(oftypeSucceedNanos) result = implicitManifestOrOfExpectedType(pt) if (result == SearchFailure) { + context.updateBuffer(previousErrs) stopTimer(oftypeFailNanos, failstart) } else { stopTimer(oftypeSucceedNanos, succstart) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 9db291a306..eac657da19 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -191,12 +191,14 @@ trait Infer { private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ - class Inferencer(context: Context) { + class Inferencer(context: Context) extends InferencerContextErrors { + import InferErrorGen._ + /* -- Error Messages --------------------------------------------------- */ def setError[T <: Tree](tree: T): T = { def name = newTermName("<error: " + tree.symbol + ">") - def errorClass = if (context.reportGeneralErrors) context.owner.newErrorClass(name.toTypeName) else stdErrorClass - def errorValue = if (context.reportGeneralErrors) context.owner.newErrorValue(name) else stdErrorValue + def errorClass = if (context.reportErrors) context.owner.newErrorClass(name.toTypeName) else stdErrorClass + def errorValue = if (context.reportErrors) context.owner.newErrorValue(name) else stdErrorValue def errorSym = if (tree.isType) errorClass else errorValue if (tree.hasSymbol) @@ -205,59 +207,12 @@ trait Infer { tree setType ErrorType } - def error(pos: Position, msg: String) { - context.error(pos, msg) - } - - def errorTree(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) - } - } - - def typeErrorMsg(found: Type, req: Type) = { - def isPossiblyMissingArgs = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) - def missingArgsMsg = if (isPossiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else "" - - "type mismatch" + foundReqMsg(found, req) + missingArgsMsg - } - - 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 - // members present, then display along with the expected members. This is done here because - // this is the last point where we still have access to the original tree, rather than just - // the found/req types. - val foundType: Type = req.normalize match { - case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass => - val retyped = typer typed (tree.duplicate setType null) - val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) - - if (foundDecls.isEmpty) found - else { - // The members arrive marked private, presumably because there was no - // expected type and so they're considered members of an anon class. - foundDecls foreach (_ resetFlag (PRIVATE | PROTECTED)) - // TODO: if any of the found parents match up with required parents after normalization, - // print the error so that they match. The major beneficiary there would be - // java.lang.Object vs. AnyRef. - refinedType(found.parents, found.typeSymbol.owner, foundDecls, tree.pos) - } - case _ => - found - } - typeError(tree.pos, foundType, req) - setError(tree) - } + def getContext = context + def issue(err: AbsTypeError): Unit = context.issue(err) + + def isPossiblyMissingArgs(found: Type, req: Type) = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) + def explainTypes(tp1: Type, tp2: Type) = withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) @@ -279,7 +234,6 @@ trait Infer { var sym1 = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) // Console.println("check acc " + (sym, sym1) + ":" + (sym.tpe, sym1.tpe) + " from " + pre);//DEBUG - if (sym1 == NoSymbol && sym.isJavaDefined && context.unit.isJava) // don't try to second guess Java; see #4402 sym1 = sym @@ -289,7 +243,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, + ErrorUtils.issueTypeError(AccessError(tree, sym, pre, context.enclClass.owner, if (settings.check.isDefault) analyzer.lastAccessCheckDetails else @@ -303,7 +257,8 @@ trait Infer { "context.owner" -> context.owner, "context.outer.enclClass.owner" -> context.outer.enclClass.owner ) - ) + ))(context) + setError(tree) } else { if (sym1.isTerm) @@ -316,10 +271,11 @@ trait Infer { if (settings.debug.value) ex.printStackTrace val sym2 = underlyingSymbol(sym1) val itype = pre.memberType(sym2) - new AccessError(tree, sym, pre, - "\n because its instance type "+itype+ - (if ("malformed type: "+itype.toString==ex.msg) " is malformed" - else " contains a "+ex.msg)).emit() + ErrorUtils.issueTypeError( + 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)))(context) ErrorType } } @@ -762,25 +718,20 @@ 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 context errors */ private[typechecker] def isApplicableSafe(undetparams: List[Symbol], ftpe: Type, argtpes0: List[Type], pt: Type): Boolean = { - val reportAmbiguousErrors = context.reportAmbiguousErrors - context.reportAmbiguousErrors = false - try { - isApplicable(undetparams, ftpe, argtpes0, pt) - } catch { - case ex: TypeError => - try { - isApplicable(undetparams, ftpe, argtpes0, WildcardType) - } catch { - case ex: TypeError => - false - } - } finally { - context.reportAmbiguousErrors = reportAmbiguousErrors - } + val silentContext = context.makeSilent(false) + val typer0 = newTyper(silentContext) + val res1 = typer0.infer.isApplicable(undetparams, ftpe, argtpes0, pt) + if (pt != WildcardType && silentContext.hasErrors) { + silentContext.flushBuffer() + val res2 = typer0.infer.isApplicable(undetparams, ftpe, argtpes0, WildcardType) + if (silentContext.hasErrors) false else res2 + } else res1 } /** Is type <code>ftpe1</code> strictly more specific than type <code>ftpe2</code> @@ -942,38 +893,22 @@ trait Infer { */ /** error if arguments not within bounds. */ - def checkBounds(pos: Position, pre: Type, owner: Symbol, - tparams: List[Symbol], targs: List[Type], prefix: String) = { + def checkBounds(tree: Tree, pre: Type, owner: Symbol, + tparams: List[Symbol], targs: List[Type], prefix: String): Boolean = { //@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(!kindErrors.isEmpty) { + if (targs contains WildcardType) true + else { KindBoundErrors(tree, prefix, targs, tparams, kindErrors); false } + } 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)) - () - } - } - } + NotWithinBounds(tree, prefix, targs, tparams, kindErrors) + false + } else true + } else true } def checkKindBounds(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol): List[String] = { @@ -1055,8 +990,7 @@ trait Infer { targs: List[Type], pt: Type) { 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)) + PolymorphicExpressionInstantiationError(tree, undetparams, pt) } else { new TreeTypeSubstituter(undetparams, targs).traverse(tree) } @@ -1092,27 +1026,24 @@ trait Infer { (okparams map (_.name), okargs).zipped.map(_ + "=" + _).mkString("solved: ", ", ", "") )) - checkBounds(fn.pos, NoPrefix, NoSymbol, undetparams, allargs, "inferred ") - val treeSubst = new TreeTypeSubstituter(okparams, okargs) - treeSubst traverseTrees fn :: args + if (checkBounds(fn, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")) { + val treeSubst = new TreeTypeSubstituter(okparams, okargs) + treeSubst traverseTrees fn :: args - leftUndet match { - case Nil => Nil - case xs => - // #3890 - val xs1 = treeSubst.typeMap mapOver xs - if (xs ne xs1) - new TreeSymSubstTraverser(xs, xs1) traverseTrees fn :: args + leftUndet match { + case Nil => Nil + case xs => + // #3890 + val xs1 = treeSubst.typeMap mapOver xs + if (xs ne xs1) + new TreeSymSubstTraverser(xs, xs1) traverseTrees fn :: args - xs1 - } + xs1 + } + } else Nil } 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 + NoMethodInstanceError(fn, args, msg); List() } } @@ -1137,20 +1068,16 @@ trait Infer { try { val targs = solvedTypes(tvars, undetparams, undetparams map varianceInType(restpe), true, lubDepth(List(restpe, pt))) -// checkBounds(tree.pos, NoPrefix, NoSymbol, undetparams, targs, "inferred ") +// checkBounds(tree, 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()) + } catch ifNoInstance{ msg => + 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)) + ConstrInstantiationError(tree, restpe, pt) } if (restpe.instantiateTypeParams(undetparams, tvars) <:< pt) { computeArgs @@ -1220,9 +1147,9 @@ trait Infer { } } - def checkCheckable(pos: Position, tp: Type, kind: String) { + def checkCheckable(tree: Tree, tp: Type, kind: String) { def patternWarning(tp0: Type, prefix: String) = { - context.unit.uncheckedWarning(pos, prefix+tp0+" in type "+kind+tp+" is unchecked since it is eliminated by erasure") + context.unit.uncheckedWarning(tree.pos, prefix+tp0+" in type "+kind+tp+" is unchecked since it is eliminated by erasure") } def check(tp: Type, bound: List[Symbol]) { def isLocalBinding(sym: Symbol) = @@ -1241,7 +1168,7 @@ trait Infer { } 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") + TypePatternOrIsInstanceTestError(tree, tp) } else { for (arg <- args) { if (sym == ArrayClass) check(arg, bound) @@ -1265,11 +1192,12 @@ trait Infer { case ExistentialType(quantified, tp1) => check(tp1, bound ::: quantified) case ThisType(_) => - ; + () case NoPrefix => - ; + () case _ => patternWarning(tp, "type ") + () } } check(tp, List()) @@ -1292,7 +1220,7 @@ trait Infer { } } - def inferTypedPattern(pos: Position, pattp: Type, pt0: Type): Type = { + def inferTypedPattern(tree0: Tree, pattp: Type, pt0: Type): Type = { val pt = widen(pt0) val ptparams = freeTypeParamsOfTerms.collect(pt) val tpparams = freeTypeParamsOfTerms.collect(pattp) @@ -1304,10 +1232,12 @@ trait Infer { * This is the case if the scrutinee has no unresolved type arguments * 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)) + if (pt.isFinalType && ptparams.isEmpty && !ptMatchesPattp) { + IncompatibleScrutineeTypeError(tree0, pattp, pt) + return ErrorType + } - checkCheckable(pos, pattp, "pattern ") + checkCheckable(tree0, pattp, "pattern ") if (pattp <:< pt) () else { debuglog("free type params (1) = " + tpparams) @@ -1330,8 +1260,8 @@ trait Infer { 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 + PatternTypeIncompatibleWithPtError1(tree0, pattp, pt) + return ErrorType } } tvars foreach instantiateTypeVar @@ -1352,30 +1282,8 @@ trait Infer { val pt1 = pt.instantiateTypeParams(ptparams, ptvars) 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) - } + else + PatternTypeIncompatibleWithPtError2(pat, pt1, pt) } object toOrigin extends TypeMap { @@ -1452,7 +1360,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) = tree.tpe match { case OverloadedType(pre, alts) => tryTwice { val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) val secondTry = alts0.isEmpty @@ -1483,15 +1391,10 @@ trait Infer { case _ => } } - typeErrorTree(tree, tree.symbol.tpe, pt) + NoBestExprAlternativeError(tree, pt) } else if (!competing.isEmpty) { - if (secondTry) { - typeErrorTree(tree, tree.symbol.tpe, pt) - } else { - if (!pt.isErroneous) - context.ambiguousError(tree.pos, pre, best, competing.head, "expected type " + pt) - setError(tree) - } + if (secondTry) NoBestExprAlternativeError(tree, pt) + else { if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) } } else { // val applicable = alts1 filter (alt => // global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) @@ -1501,9 +1404,11 @@ trait Infer { } } - @inline private def wrapTypeError(expr: => Boolean): Boolean = - try expr - catch { case _: TypeError => false } + @inline private def inSilentMode(expr: Typer => Boolean): Boolean = { + val silentContext = context.makeSilent(context.ambiguousErrors) + val res = expr(newTyper(silentContext)) + if (silentContext.hasErrors) false else res + } // Checks against the name of the parameter and also any @deprecatedName. private def paramMatchesName(param: Symbol, name: Name) = @@ -1573,9 +1478,7 @@ trait Infer { val applicable = resolveOverloadedMethod(argtpes, { alts filter { alt => - // TODO: this will need to be re-written once we substitute throwing exceptions - // with generating error trees. We wrap this applicability in try/catch because of #4457. - wrapTypeError(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && + inSilentMode(typer0 => typer0.infer.isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && (!varArgsOnly || isVarArgsList(alt.tpe.params)) } }) @@ -1591,16 +1494,13 @@ 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) + 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)) + AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt) setError(tree) () } else { @@ -1616,18 +1516,27 @@ trait Infer { * * @param infer ... */ - def tryTwice(infer: => Unit) { + def tryTwice(infer: => Unit): Unit = { if (context.implicitsEnabled) { - val reportGeneralErrors = context.reportGeneralErrors - context.reportGeneralErrors = false - try context.withImplicitsDisabled(infer) - catch { - case ex: CyclicReference => throw ex - case ex: TypeError => - context.reportGeneralErrors = reportGeneralErrors + val saved = context.state + var fallback = false + context.setBufferErrors() + val res = try { + context.withImplicitsDisabled(infer) + if (context.hasErrors) { + fallback = true + context.restoreState(saved) + context.flushBuffer() infer + } + } catch { + case ex: CyclicReference => throw ex + case ex: TypeError => // recoverable cyclic references + context.restoreState(saved) + if (!fallback) infer else () } - context.reportGeneralErrors = reportGeneralErrors + context.restoreState(saved) + res } else infer } @@ -1642,13 +1551,13 @@ trait Infer { def inferPolyAlternatives(tree: Tree, argtypes: List[Type]): Unit = { 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(kind: PolyAlternativeErrorKind.ErrorType) = + PolyAlternativeError(tree, argtypes, sym0, kind) - if (sym0 == NoSymbol) return fail( + if (sym0 == NoSymbol) return ( if (alts exists (_.typeParams.nonEmpty)) - "wrong number of type parameters for " + treeSymTypeMsg(tree) - else treeSymTypeMsg(tree) + " does not take type parameters" - ) + fail(PolyAlternativeErrorKind.WrongNumber) + else fail(PolyAlternativeErrorKind.NoParams)) val (resSym, resTpe) = { if (!sym0.isOverloaded) @@ -1656,11 +1565,8 @@ 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( - "type arguments " + argtypes.mkString("[", ",", "]") + - " conform to the bounds of none of the overloaded alternatives of\n "+sym0+ - ": "+sym0.info - ) + if (argtypes forall (x => !x.isErroneous)) + fail(PolyAlternativeErrorKind.ArgsDoNotConform) return } else if (sym.isOverloaded) { @@ -1677,24 +1583,6 @@ 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 = underlyingSymbol(sym) - errorTree(tree, realsym.fullLocationString + " cannot be accessed in " + location + explanation) - } - } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 7f9e56a926..c63ae90ef6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -97,8 +97,6 @@ trait Macros { self: Analyzer => override def defaultReflectiveClassLoader() = libraryClassLoader } - class MacroExpandError(val msg: String) extends Exception(msg) - /** Return optionally address of companion object and implementation method symbol * of given macro; or None if implementation classfile cannot be loaded or does * not contain the macro implementation. @@ -127,7 +125,7 @@ trait Macros { self: Analyzer => * Or, if that fails, and the macro overrides a method return * tree that calls this method instead of the macro. */ - def macroExpand(tree: Tree): Any = { + def macroExpand(tree: Tree, context: Context): Option[Any] = { val macroDef = tree.symbol macroImpl(macroDef) match { case Some((receiver, rmeth)) => @@ -139,41 +137,55 @@ trait Macros { self: Analyzer => } val rawArgs: Seq[Any] = rawArgss.flatten try { - mirror.invoke(receiver, rmeth, rawArgs: _*) + Some(mirror.invoke(receiver, rmeth, rawArgs: _*)) } catch { case ex => val realex = ReflectionUtils.unwrapThrowable(ex) val stacktrace = new java.io.StringWriter() realex.printStackTrace(new java.io.PrintWriter(stacktrace)) val msg = System.getProperty("line.separator") + stacktrace - throw new MacroExpandError("exception during macro expansion: " + msg) + context.unit.error(tree.pos, "exception during macro expansion: " + msg) + None } case None => val trace = scala.tools.nsc.util.trace when settings.debug.value - def notFound() = throw new MacroExpandError("macro implementation not found: " + macroDef.name) - def fallBackToOverridden(tree: Tree): Tree = { + def notFound() = { + context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name) + None + } + def fallBackToOverridden(tree: Tree): Option[Tree] = { tree match { case Select(qual, name) if (macroDef.isMacro) => macroDef.allOverriddenSymbols match { - case first :: others => - return Select(qual, name) setPos tree.pos setSymbol first + case first :: _ => + Some(Select(qual, name) setPos tree.pos setSymbol first) case _ => trace("macro is not overridden: ")(tree) notFound() } case Apply(fn, args) => - Apply(fallBackToOverridden(fn), args) setPos tree.pos + fallBackToOverridden(fn) match { + case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos) + case _ => None + } case TypeApply(fn, args) => - TypeApply(fallBackToOverridden(fn), args) setPos tree.pos + fallBackToOverridden(fn) match { + case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos) + case _ => None + } case _ => trace("unexpected tree in fallback: ")(tree) notFound() } } - val tree1 = fallBackToOverridden(tree) - trace("falling back to ")(tree1) - currentRun.macroExpansionFailed = true - tree1 + fallBackToOverridden(tree) match { + case Some(tree1) => + trace("falling back to ")(tree1) + currentRun.macroExpansionFailed = true + Some(tree1) + case None => + None + } } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 29dffd99d6..c6ca9870c3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -32,10 +32,12 @@ trait MethodSynthesis { trait MethodSynth { self: Namer => + import NamerErrorGen._ + def enterGetterSetter(tree: ValDef) { val ValDef(mods, name, _, _) = tree if (nme.isSetterName(name)) - context.error(tree.pos, "Names of vals or vars may not end in `_='") + ValOrValWithSetterSuffixError(tree) val getter = Getter(tree).createAndEnterSymbol() @@ -43,7 +45,7 @@ trait MethodSynthesis { if (mods.isLazy) enterLazyVal(tree, getter) else { if (mods.isPrivateLocal) - context.error(tree.pos, "private[this] not allowed for case class parameters") + PrivateThisCaseClassParameterError(tree) // Create the setter if necessary. if (mods.isMutable) Setter(tree).createAndEnterSymbol() @@ -187,7 +189,7 @@ trait MethodSynthesis { override def validate() { assert(derivedSym != NoSymbol, tree) if (derivedSym.isOverloaded) - context.error(derivedSym.pos, derivedSym+" is defined twice") + GetterDefinedTwiceError(derivedSym) super.validate() } @@ -255,8 +257,7 @@ trait MethodSynthesis { if (derivedSym == NoSymbol) { // the namer decides whether to generate these symbols or not. at that point, we don't // have symbolic information yet, so we only look for annotations named "BeanProperty". - context.error(tree.pos, - "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import") + BeanPropertyAnnotationLimitationError(tree) } super.validate() } @@ -304,9 +305,9 @@ trait MethodSynthesis { val beans = beanAccessorsFromNames(tree) if (beans.nonEmpty) { if (!name(0).isLetter) - context.error(tree.pos, "`BeanProperty' annotation can be applied only to fields that start with a letter") + BeanPropertyAnnotationFieldWithoutLetterError(tree) else if (mods.isPrivate) // avoids name clashes with private fields in traits - context.error(tree.pos, "`BeanProperty' annotation can be applied only to non-private fields") + BeanPropertyAnnotationPrivateFieldError(tree) // Create and enter the symbols here, add the trees in finishGetterSetter. beans foreach (_.createAndEnterSymbol()) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index e04d89047b..701c69a4bb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -73,7 +73,9 @@ trait Namers extends MethodSynthesis { classAndNamerOfModule.clear() } - abstract class Namer(val context: Context) extends MethodSynth { + abstract class Namer(val context: Context) extends MethodSynth with NamerContextErrors { + + import NamerErrorGen._ val typer = newTyper(context) private lazy val innerNamer = @@ -109,9 +111,10 @@ trait Namers extends MethodSynthesis { protected def owner = context.owner private def contextFile = context.unit.source.file - private def typeErrorHandler[T](pos: Position, alt: T): PartialFunction[Throwable, T] = { + private def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = { case ex: TypeError => - typer.reportTypeError(pos, ex) + // H@ need to ensure that we handle only cyclic references + TypeSigError(tree, ex) alt } // PRIVATE | LOCAL are fields generated for primary constructor arguments @@ -129,10 +132,17 @@ trait Namers extends MethodSynthesis { || vd.symbol.isLazy ) - def setPrivateWithin[Sym <: Symbol](tree: Tree, sym: Sym, mods: Modifiers): Sym = { + def setPrivateWithin[Sym <: Symbol](tree: Tree, sym: Sym, mods: Modifiers): Sym = if (sym.isPrivateLocal || !mods.hasAccessBoundary) sym - else sym setPrivateWithin typer.qualifyingClass(tree, mods.privateWithin, true) - } + else sym setPrivateWithin ( + typer.qualifyingClass(tree, mods.privateWithin, true) match { + case None => + NoSymbol + case Some(sym) => + sym + } + ) + def setPrivateWithin(tree: MemberDef, sym: Symbol): Symbol = setPrivateWithin(tree, sym, tree.mods) @@ -195,14 +205,6 @@ trait Namers extends MethodSynthesis { ) ) - 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 "" - val s3 = if (sym.isCase) "case class " + sym.name else "" + sym - - context.error(pos, sym.name + " is already defined as " + s2 + s3) - } - private def allowsOverload(sym: Symbol) = ( sym.isSourceMethod && sym.owner.isClass && !sym.owner.isPackageClass ) @@ -221,7 +223,7 @@ trait Namers extends MethodSynthesis { if (!allowsOverload(sym)) { val prev = scope.lookupEntry(sym.name) if ((prev ne null) && prev.owner == scope && conflict(sym, prev.sym)) { - doubleDefError(sym.pos, prev.sym) + DoubleDefError(sym, prev.sym) sym setInfo ErrorType scope unlink prev.sym // let them co-exist... // FIXME: The comment "let them co-exist" is confusing given that the @@ -250,7 +252,7 @@ trait Namers extends MethodSynthesis { returnContext } tree.symbol match { - case NoSymbol => try dispatch() catch typeErrorHandler(tree.pos, this.context) + case NoSymbol => try dispatch() catch typeErrorHandler(tree, this.context) case sym => enterExistingSym(sym) } } @@ -447,6 +449,7 @@ trait Namers extends MethodSynthesis { } private def checkSelectors(tree: Import): Unit = { + import DuplicatesErrorKinds._ val Import(expr, selectors) = tree val base = expr.tpe @@ -483,8 +486,10 @@ trait Namers extends MethodSynthesis { typeSig(tree) } // for Java code importing Scala objects - else if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) - notAMemberError(tree.pos, expr, from) + else if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) { + typer.TyperErrorGen.NotAMemberError(tree, expr, from) + typer.infer.setError(tree) + } } // Setting the position at the import means that if there is // more than one hidden name, the second will not be warned. @@ -492,20 +497,21 @@ trait Namers extends MethodSynthesis { checkNotRedundant(tree.pos withPoint fromPos, from, to) } } - def noDuplicates(names: List[Name], message: String) { + + def noDuplicates(names: List[Name], check: DuplicatesErrorKinds.Value) { def loop(xs: List[Name]): Unit = xs match { case Nil => () case hd :: tl => if (hd == nme.WILDCARD || !(tl contains hd)) loop(tl) - else context.error(tree.pos, hd.decode + " " + message) + else DuplicatesError(tree, hd, check) } loop(names filterNot (x => x == null || x == nme.WILDCARD)) } selectors foreach checkSelector // checks on the whole set - noDuplicates(selectors map (_.name), "is renamed twice") - noDuplicates(selectors map (_.rename), "appears twice as a target of a renaming") + noDuplicates(selectors map (_.name), RenamedTwice) + noDuplicates(selectors map (_.rename), AppearsTwice) } def enterCopyMethodOrGetter(tree: Tree, tparams: List[TypeDef]): Symbol = { @@ -620,7 +626,7 @@ trait Namers extends MethodSynthesis { if (mods.isCase) { if (treeInfo.firstConstructorArgs(impl.body).size > MaxFunctionArity) - context.error(tree.pos, "Implementation restriction: case classes cannot have more than " + MaxFunctionArity + " parameters.") + MaxParametersCaseClassError(tree) val m = ensureCompanionObject(tree, caseModuleDef) classOfModuleClass(m.moduleClass) = new WeakReference(tree) @@ -823,7 +829,7 @@ trait Namers extends MethodSynthesis { val tp = tpt.tpe val inheritsSelf = tp.typeSymbol == owner if (inheritsSelf) - context.error(tpt.pos, ""+tp.typeSymbol+" inherits itself") + InheritsItselfError(tpt) if (inheritsSelf || tp.isError) AnyRefClass.tpe else tp @@ -918,7 +924,7 @@ trait Namers extends MethodSynthesis { } def thisMethodType(restpe: Type) = { - val checkDependencies = new DependentTypeChecker(context) + val checkDependencies = new DependentTypeChecker(context)(this) checkDependencies check vparamSymss // DEPMETTODO: check not needed when they become on by default checkDependencies(restpe) @@ -994,7 +1000,7 @@ trait Namers extends MethodSynthesis { } mforeach(vparamss) { vparam => if (vparam.tpt.isEmpty) { - context.error(vparam.pos, "missing parameter type") + MissingParameterOrValTypeError(vparam) vparam.tpt defineType ErrorType } } @@ -1262,7 +1268,7 @@ trait Namers extends MethodSynthesis { val typer1 = typer.constrTyperIf(isBeforeSupercall) if (tpt.isEmpty) { if (rhs.isEmpty) { - context.error(tpt.pos, "missing parameter type"); + MissingParameterOrValTypeError(tpt) ErrorType } else assignTypeToTree(vdef, newTyper(typer1.context.make(vdef, sym)), WildcardType) @@ -1276,7 +1282,7 @@ trait Namers extends MethodSynthesis { val expr1 = typer.typedQualifier(expr) typer checkStable expr1 if (expr1.symbol != null && expr1.symbol.isRootPackage) - context.error(tree.pos, "_root_ cannot be imported") + RootImportError(tree) val newImport = treeCopy.Import(tree, expr1, selectors).asInstanceOf[Import] checkSelectors(newImport) @@ -1290,7 +1296,7 @@ trait Namers extends MethodSynthesis { val result = try getSig - catch typeErrorHandler(tree.pos, ErrorType) + catch typeErrorHandler(tree, ErrorType) result match { case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => typer.deskolemizeTypeParams(tparams)(result) @@ -1337,43 +1343,43 @@ trait Namers extends MethodSynthesis { * - declarations only in mixins or abstract classes (when not @native) */ def validate(sym: Symbol) { - def fail(msg: String) = context.error(sym.pos, msg) + import SymValidateErrors._ + def fail(kind: SymValidateErrors.Value) = SymbolValidationError(sym, kind) + def checkWithDeferred(flag: Int) { if (sym hasFlag flag) - fail("abstract member may not have " + flagsToString(flag) + " modifier") + AbstractMemberWithModiferError(sym, flag) } def checkNoConflict(flag1: Int, flag2: Int) { if (sym hasAllFlags flag1 | flag2) - fail("illegal combination of modifiers: %s and %s for: %s".format( - flagsToString(flag1), flagsToString(flag2), sym)) + IllegalModifierCombination(sym, flag1, flag2) } if (sym.isImplicit) { if (sym.isConstructor) - fail("`implicit' modifier not allowed for constructors") + fail(ImplicitConstr) if (!sym.isTerm) - fail("`implicit' modifier can be used only for values, variables and methods") + fail(ImplicitNotTerm) if (sym.owner.isPackageClass) - fail("`implicit' modifier cannot be used for top-level objects") + fail(ImplicitTopObject) } if (sym.isClass) { if (sym.isAnyOverride && !sym.hasFlag(TRAIT)) - fail("`override' modifier not allowed for classes") - } - else { + fail(OverrideClass) + } else { if (sym.isSealed) - fail("`sealed' modifier can be used only for classes") + fail(SealedNonClass) if (sym.hasFlag(ABSTRACT)) - fail("`abstract' modifier can be used only for classes; it should be omitted for abstract members") + fail(AbstractNonClass) } if (sym.isConstructor && sym.isAnyOverride) - fail("`override' modifier not allowed for constructors") + fail(OverrideConstr) if (sym.isAbstractOverride && !sym.owner.isTrait) - fail("`abstract override' modifier only allowed for members of traits") + fail(AbstractOverride) if (sym.isLazy && sym.hasFlag(PRESUPER)) - fail("`lazy' definitions may not be initialized early") + fail(LazyAndEarlyInit) if (sym.info.typeSymbol == FunctionClass(0) && sym.isValueParameter && sym.owner.isCaseClass) - fail("pass-by-name arguments not allowed for case class parameters") + fail(ByNameParameter) if (sym.isDeferred) { // Is this symbol type always allowed the deferred flag? @@ -1391,7 +1397,7 @@ trait Namers extends MethodSynthesis { if (sym hasAnnotation NativeAttr) sym resetFlag DEFERRED else if (!symbolAllowsDeferred && ownerRequiresConcrete) - fail("only classes can have declared but undefined members" + abstractVarMessage(sym)) + fail(AbstractVar) checkWithDeferred(PRIVATE) checkWithDeferred(FINAL) @@ -1456,14 +1462,14 @@ trait Namers extends MethodSynthesis { // def foo[T, T2](a: T, x: T2)(implicit w: ComputeT2[T, T2]) // moreover, the latter is not an encoding of the former, which hides type // inference of T2, so you can specify T while T2 is purely computed - private class DependentTypeChecker(ctx: Context) extends TypeTraverser { + private class DependentTypeChecker(ctx: Context)(namer: Namer) extends TypeTraverser { private[this] val okParams = mutable.Set[Symbol]() private[this] val method = ctx.owner def traverse(tp: Type) = tp match { case SingleType(_, sym) => if (sym.owner == method && sym.isValueParameter && !okParams(sym)) - ctx.error(sym.pos, "illegal dependent method type" + errorAddendum) + namer.NamerErrorGen.IllegalDependentMethTpeError(sym)(ctx) case _ => mapOver(tp) } @@ -1476,8 +1482,6 @@ trait Namers extends MethodSynthesis { okParams ++= vps } } - private def errorAddendum = - ": parameter appears in the type of another parameter in the same section or an earlier one" } @deprecated("Use underlyingSymbol instead", "2.10.0") @@ -1506,7 +1510,7 @@ trait Namers extends MethodSynthesis { } catch { case e: InvalidCompanions => - ctx.error(original.pos, e.getMessage) + ctx.unit.error(original.pos, e.getMessage) NoSymbol } } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 79cb211215..3a3c244d1c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -17,6 +17,7 @@ trait NamesDefaults { self: Analyzer => import global._ import definitions._ + import NamesDefaultsErrorsGen._ val defaultParametersOfMethod = perRunCaches.newWeakMap[Symbol, Set[Symbol]]() withDefaultValue Set() @@ -312,8 +313,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.isErrorTyped) tree 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. @@ -384,6 +384,7 @@ trait NamesDefaults { self: Analyzer => if (missing forall (_.hasDefaultFlag)) { val defaultArgs = missing flatMap (p => { val defGetter = defaultGetter(p, context) + // TODO #3649 can create spurious errors when companion object is gone (because it becomes unlinked from scope) if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649 else { var default1 = qual match { @@ -434,12 +435,12 @@ trait NamesDefaults { self: Analyzer => private def savingUndeterminedTParams[T](context: Context)(fn: List[Symbol] => T): T = { val savedParams = context.extractUndetparams() - val savedReporting = context.reportAmbiguousErrors + val savedReporting = context.ambiguousErrors - context.reportAmbiguousErrors = false + context.setAmbiguousErrors(false) try fn(savedParams) finally { - context.reportAmbiguousErrors = savedReporting + context.setAmbiguousErrors(savedReporting) //@M note that we don't get here when an ambiguity was detected (during the computation of res), // as errorTree throws an exception context.undetparams = savedParams @@ -488,7 +489,7 @@ trait NamesDefaults { self: Analyzer => // is called, and EmptyTree can only be typed NoType. Thus we need to // disable conforms as a view... try typer.silent(_.typed(arg, subst(paramtpe))) match { - case t: Tree => !t.isErroneous + case SilentResultValue(t) => !t.isErroneous // #4041 case _ => false } catch { @@ -496,9 +497,7 @@ trait NamesDefaults { self: Analyzer => // CyclicReferences. Fix for #3685 case cr @ CyclicReference(sym, _) => (sym.name == param.name) && sym.accessedOrSelf.isVariable && { - context.error(sym.pos, - "variable definition needs type because '%s' is used as a named argument in its body.".format(sym.name)) - typer.infer.setError(arg) + NameClashError(sym, arg)(typer.context) true } } @@ -514,18 +513,17 @@ trait NamesDefaults { self: Analyzer => * after named ones. */ def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = { - import typer.context + implicit val context0 = typer.context // maps indices from (order written by user) to (order of definition) val argPos = Array.fill(args.length)(-1) var positionalAllowed = true val namelessArgs = mapWithIndex(args) { (arg, index) => - def fail(msg: String) = typer.infer.errorTree(arg, msg) arg match { case arg @ AssignOrNamedArg(Ident(name), rhs) => def matchesName(param: Symbol) = !param.isSynthetic && ( (param.name == name) || (param.deprecatedParamName match { case Some(`name`) => - context.unit.deprecationWarning(arg.pos, + context0.unit.deprecationWarning(arg.pos, "the parameter name "+ name +" has been deprecated. Use "+ param.name +" instead.") true case _ => false @@ -539,12 +537,12 @@ trait NamesDefaults { self: Analyzer => // treat the arg as an assignment of type Unit Assign(arg.lhs, rhs) setPos arg.pos } - else fail("unknown parameter name: " + name) + else UnknownParameterNameNamesDefaultError(arg, name) } else if (argPos contains pos) - fail("parameter specified twice: " + name) + DoubleParamNamesDefaultError(arg, name) else if (isAmbiguousAssignment(typer, params(pos), arg)) - fail("reference to " + name + " is ambiguous; it is both a method parameter and a variable in scope.") + AmbiguousReferenceInNamesDefaultError(arg, name) else { // if the named argument is on the original parameter // position, positional after named is allowed. @@ -556,7 +554,7 @@ trait NamesDefaults { self: Analyzer => case _ => argPos(index) = index if (positionalAllowed) arg - else fail("positional after named argument.") + else PositionalAfterNamedNamesDefaultError(arg) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index ed185c27d6..dc5e248631 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -315,7 +315,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val extractorCall = try { context.undetparams = Nil silent(_.typed(Apply(Select(orig, extractor), List(Ident(nme.SELECTOR_DUMMY) setType fun.tpe.finalResultType)), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { - case extractorCall: Tree => extractorCall // if !extractorCall.containsError() + case SilentResultValue(extractorCall) => extractorCall // if !extractorCall.containsError() case _ => // this fails to resolve overloading properly... // Apply(typedOperator(Select(orig, extractor)), List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 112aa47114..0405163ef3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1240,11 +1240,11 @@ 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, "") + private def checkBounds(tree0: Tree, pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type]): Unit = + try typer.infer.checkBounds(tree0, pre, owner, tparams, argtps, "") catch { case ex: TypeError => - unit.error(pos, ex.getMessage()); + unit.error(tree0.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)) @@ -1374,22 +1374,22 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R false } - private def checkTypeRef(tp: Type, pos: Position) = tp match { + private def checkTypeRef(tp: Type, tree: Tree) = tp match { case TypeRef(pre, sym, args) => - checkDeprecated(sym, pos) + checkDeprecated(sym, tree.pos) if(sym.isJavaDefined) sym.typeParams foreach (_.cookJavaRawInfo()) if (!tp.isHigherKinded) - checkBounds(pre, sym.owner, sym.typeParams, args, pos) + checkBounds(tree, pre, sym.owner, sym.typeParams, args) case _ => } - private def checkAnnotations(tpes: List[Type], pos: Position) = tpes foreach (tp => checkTypeRef(tp, pos)) + private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach (tp => checkTypeRef(tp, tree)) private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f private def applyRefchecksToAnnotations(tree: Tree): Unit = { def applyChecks(annots: List[AnnotationInfo]) = { - checkAnnotations(annots map (_.atp), tree.pos) + checkAnnotations(annots map (_.atp), tree) transformTrees(annots flatMap (_.args)) } @@ -1404,7 +1404,8 @@ 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() => + applyRefchecksToAnnotations(dc.check()) // #2416 case _ => } } @@ -1450,7 +1451,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R unit.error(tree.pos, "too many dimensions for array creation") Literal(Constant(null)) } else { - localTyper.getManifestTree(tree.pos, etpe, false) + localTyper.getManifestTree(tree, etpe, false) } } val newResult = localTyper.typedPos(tree.pos) { @@ -1578,13 +1579,13 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case ExistentialType(tparams, tpe) => existentialParams ++= tparams case t: TypeRef => - checkTypeRef(deriveTypeWithWildcards(existentialParams.toList)(t), tree.pos) + checkTypeRef(deriveTypeWithWildcards(existentialParams.toList)(t), tree) case _ => } tree case TypeApply(fn, args) => - checkBounds(NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe), tree.pos) + checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe)) transformCaseApply(tree, ()) case x @ Apply(_, _) => @@ -1641,7 +1642,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 4e4fbe35cb..b109d57554 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -51,12 +51,21 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } private def checkPackedConforms(tree: Tree, pt: Type): Tree = { + def typeError(typer: analyzer.Typer, pos: Position, found: Type, req: Type) { + if (!found.isErroneous && !req.isErroneous) { + val msg = analyzer.ErrorUtils.typeErrorMsg(found, req, typer.infer.isPossiblyMissingArgs(found, req)) + typer.context.error(pos, analyzer.withAddendum(pos)(msg)) + if (settings.explaintypes.value) + explainTypes(found, req) + } + } + if (tree.tpe exists (_.typeSymbol.isExistentialSkolem)) { val packed = localTyper.packedType(tree, NoSymbol) if (!(packed <:< pt)) { val errorContext = localTyper.context.make(localTyper.context.tree) - errorContext.reportGeneralErrors = true - analyzer.newTyper(errorContext).infer.typeError(tree.pos, packed, pt) + errorContext.setReportErrors() + typeError(analyzer.newTyper(errorContext), tree.pos, packed, pt) } } tree diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index fe3ceafa2d..8c434a8838 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -93,37 +93,6 @@ trait TypeDiagnostics { } } - def notAMemberMessage(pos: Position, qual: Tree, name: Name) = { - 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 (posPrecedes(qual.pos, pos)) - "\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 - ) - } - /** An explanatory note to be added to error messages * when there's a problem with abstract var defs */ def abstractVarMessage(sym: Symbol): String = @@ -131,9 +100,6 @@ trait TypeDiagnostics { "\n(Note that variables need to be initialized to be defined)" else "" - def notAMemberError(pos: Position, qual: Tree, name: Name) = - context.error(pos, notAMemberMessage(pos, qual, name)) - /** Only prints the parameter names if they're not synthetic, * since "x$1: Int" does not offer any more information than "Int". */ @@ -154,21 +120,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 - } - /** The symbol which the given accessor represents (possibly in part). * This is used for error messages, where we want to speak in terms * of the actual declaration or definition, not in terms of the generated setters @@ -202,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 }) @@ -446,8 +369,8 @@ 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(context0: Analyzer#Context, pos: Position, msg: String) = context0.error(pos, msg) + private def contextError(context0: Analyzer#Context, pos: Position, err: Throwable) = context0.error(pos, err) private def contextWarning(pos: Position, msg: String) = context.unit.warning(pos, msg) def permanentlyHiddenWarning(pos: Position, hidden: Name, defn: Symbol) = @@ -466,14 +389,8 @@ trait TypeDiagnostics { // Error suppression will squash some of these warnings unless we circumvent it. // It is presumed if you are using a -Y option you would really like to hear // the warnings you've requested. - if (settings.warnDeadCode.value && context.unit.exists && treeOK(tree) && exprOK) { - val saved = context.reportGeneralErrors - try { - context.reportGeneralErrors = true - context.warning(tree.pos, "dead code following this construct") - } - finally context.reportGeneralErrors = saved - } + if (settings.warnDeadCode.value && context.unit.exists && treeOK(tree) && exprOK) + context.warning(tree.pos, "dead code following this construct", true) tree } @@ -485,8 +402,8 @@ 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. @@ -500,15 +417,18 @@ trait TypeDiagnostics { "\nIf applicable, you may wish to try moving some members into another object." ) } - + /** 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: Context, pos: Position, ex: TypeError) { if (ex.pos == NoPosition) ex.pos = pos - if (!context.reportGeneralErrors) throw ex + // TODO: should be replaced by throwErrors + // but it seems that throwErrors excludes some of the errors that should actually be + // buffered, causing TypeErrors to fly around again. This needs some more investigation. + if (!context0.reportErrors) throw ex if (settings.debug.value) ex.printStackTrace() ex match { @@ -517,12 +437,12 @@ trait TypeDiagnostics { case Import(expr, _) => expr.pos case _ => ex.pos } - contextError(pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) + contextError(context0, 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) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 216ad6cd4c..da7e54af5c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -12,14 +12,12 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } +import scala.collection.mutable import scala.tools.nsc.util.BatchSourceFile import mutable.ListBuffer import symtab.Flags._ import util.Statistics import util.Statistics._ -import scala.tools.util.StringOps.{ countAsString, countElementsAsString } -import scala.tools.util.EditDistance.similarString // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -60,7 +58,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) => @@ -71,6 +69,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } */ + sealed abstract class SilentResult[+T] + case class SilentTypeError(err: AbsTypeError) extends SilentResult[Nothing] { } + case class SilentResultValue[+T](value: T) extends SilentResult[T] { } + def newTyper(context: Context): Typer = new NormalTyper(context) private class NormalTyper(context : Context) extends Typer(context) @@ -80,9 +82,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { private def isPastTyper = phase.id > currentRun.typerPhase.id - abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation { + abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with TyperContextErrors { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine } + import TyperErrorGen._ val infer = new Inferencer(context0) { override def isCoercible(tp: Type, pt: Type): Boolean = undoLog undo { // #3281 @@ -102,15 +105,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 @@ -127,8 +121,19 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) { + context.errBuffer.find(_.kind == ErrorKinds.Divergent) match { + case Some(divergentImplicit) => + // DivergentImplicit error has higher priority than "no implicit found" + // no need to issue the problem again if we are still in silent mode + if (context.reportErrors) { + context.issue(divergentImplicit) + context.condBufferFlush(_.kind == ErrorKinds.Divergent) + } + case None => + 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 @@ -149,6 +154,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case ErrorType => fun } + + def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = + inferView(tree, from, to, reportAmbiguous, true) /** Infer an implicit conversion (``view'') between two types. * @param tree The tree which needs to be converted. @@ -157,8 +165,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @param reportAmbiguous Should ambiguous implicit errors be reported? * False iff we search for a view to find out * whether one type is coercible to another. + * @param saveErrors Should ambiguous and divergent implicit errors that were buffered + * during the inference of a view be put into the original buffer. + * False iff we don't care about them. */ - def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = { + def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { debuglog("infer view from "+from+" to "+to)//debug if (isPastTyper) EmptyTree else from match { @@ -167,7 +178,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case PolyType(_, _) => EmptyTree case _ => def wrapImplicit(from: Type): Tree = { - val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context) + val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context, saveErrors) if (result.subst != EmptyTreeTypeSubstituter) result.subst traverse tree result.tree } @@ -205,22 +216,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @return ... */ def checkStable(tree: Tree): Tree = - if (treeInfo.isExprSafeToInline(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.isExprSafeToInline(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 @@ -234,21 +235,24 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } /** 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): Boolean = { + def errorNotClass(found: AnyRef) = { ClassTypeRequiredError(tpt, found); false } + def check(tpe: Type): Boolean = tpe.normalize match { case TypeRef(pre, sym, _) if sym.isClass && !sym.isRefinementClass => - if (stablePrefix && !isPastTyper) { - 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 { + if (stablePrefix && !isPastTyper) + if (!pre.isStable) { + TypeNotAStablePrefixError(tpt, pre) + false + } else + // A type projection like X#Y can get by the stable check if the + // prefix is singleton-bounded, so peek at the tree too. + tpt match { case SelectFromTypeTree(qual, _) if !isSingleType(qual.tpe) => errorNotClass(tpt) - case _ => ; - } - } - case ErrorType => ; + case _ => true + } + else + true + case ErrorType => true case PolyType(_, restpe) => check(restpe) case ExistentialType(_, restpe) if existentialOK => check(restpe) case AnnotatedType(_, underlying, _) => check(underlying) @@ -264,17 +268,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @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 checkNotLocked(sym: Symbol) = { sym.initialize - sym.lockOK || {error(pos, "cyclic aliasing or subtyping involving "+sym); false} + sym.lockOK || { CyclicAliasingOrSubtypingError(pos, sym); false } } 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) - ) + 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) + case SingleType(pre, sym) => checkNotLocked(sym) /* @@ -293,10 +297,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = try { - lockedSym.lock { - throw new TypeError("illegal cyclic reference involving " + lockedSym) - } - checkNonCyclic(pos, tp) + if (!lockedSym.lock(CyclicReferenceError(pos, lockedSym))) false + else checkNonCyclic(pos, tp) } finally { lockedSym.unlock() } @@ -312,26 +314,24 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - 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(tree: Tree, tpe0: Type) { + 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) + DependentMethodTpeConversionToFunctionError(tree, tpe) + checkParamsConvertible(tree, restpe) + case _ => + } + checkParamsConvertible0(tpe0) } - def checkStarPatOK(pos: Position, mode: Int) = - if ((mode & STARmode) == 0 && !isPastTyper) - error(pos, "star patterns must correspond with varargs parameters") - /** Check that type of given tree does not contain local or private * components. */ @@ -362,13 +362,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def locals[T <: Tree](scope: Scope, pt: Type, tree: T): T = check(NoSymbol, scope, pt, tree) - def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = { + private def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = { 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) else if (isFullyDefined(pt)) tree setType pt else if (tp1.typeSymbol.isAnonymousClass) check(owner, scope, pt, tree setType tp1.typeSymbol.classBound) @@ -376,10 +376,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 } @@ -441,28 +438,66 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { /** The qualifying class * of a this or super with prefix <code>qual</code>. + * packageOk is equal false when qualifying class symbol */ - def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean): Symbol = + def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean = false): Option[Symbol] = context.enclClass.owner.ownerChain.find(o => qual.isEmpty || o.isClass && o.name == qual) match { case Some(c) if packageOK || !c.isPackageClass => - c + Some(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 + QualifyingClassError(tree, qual) + None } /** The typer for an expression, depending on where we are. If we are before a superclass * call, this is a typer over a constructor context; otherwise it is the current typer. */ - def constrTyperIf(inConstr: Boolean): Typer = + @inline + final def constrTyperIf(inConstr: Boolean): Typer = if (inConstr) { assert(context.undetparams.isEmpty) newTyper(context.makeConstructorContext) } else this + @inline + final def withCondConstrTyper[T](inConstr: Boolean)(f: Typer => T): T = + if (inConstr) { + assert(context.undetparams.isEmpty) + val c = context.makeConstructorContext + typerWithLocalContext(c)(f) + } else { + f(this) + } + + @inline + final def typerWithCondLocalContext[T](c: => Context)(cond: Boolean)(f: Typer => T): T = + if (cond) typerWithLocalContext(c)(f) else f(this) + + @inline + final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = { + val res = f(newTyper(c)) + if (c.hasErrors) + context.updateBuffer(c.flushAndReturnBuffer()) + res + } + + @inline + final def typerReportAnyContextErrors[T](c: Context)(f: Typer => T): T = { + val res = f(newTyper(c)) + if (c.hasErrors) + context.issue(c.errBuffer.head) + res + } + + @inline + final def withSavedContext[T](c: Context)(f: => T) = { + val savedErrors = c.flushAndReturnBuffer() + val res = f + c.updateBuffer(savedErrors) + res + } + + /** The typer for a label definition. If this is part of a template we * first have to enter the label definition. */ @@ -573,9 +608,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { inferExprAlternative(tree, pt) 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.isErrorTyped) tree else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1) if (sym.isValue) checkStable(tree) else fail() @@ -629,15 +664,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def silent[T](op: Typer => T, - reportAmbiguousErrors: Boolean = context.reportAmbiguousErrors, - newtree: Tree = context.tree): Any /* in fact, TypeError or T */ = { + reportAmbiguousErrors: Boolean = context.ambiguousErrors, + newtree: Tree = context.tree): SilentResult[T] = { val rawTypeStart = startCounter(rawTypeFailed) val findMemberStart = startCounter(findMemberFailed) val subtypeStart = startCounter(subtypeFailed) val failedSilentStart = startTimer(failedSilentNanos) try { - if (context.reportGeneralErrors || - reportAmbiguousErrors != context.reportAmbiguousErrors || + if (context.reportErrors || + reportAmbiguousErrors != context.ambiguousErrors || newtree != context.tree) { val context1 = context.makeSilent(reportAmbiguousErrors, newtree) context1.undetparams = context.undetparams @@ -648,38 +683,29 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - result + if (context1.hasErrors) SilentTypeError(context1.errBuffer.head) + else SilentResultValue(result) } else { - op(this) + assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") + withSavedContext(context){ + val res = op(this) + val errorsToReport = context.flushAndReturnBuffer() + if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head) + } } } catch { case ex: CyclicReference => throw ex case ex: TypeError => + // fallback in case TypeError is still thrown + // @H this happens for example in cps annotation checker stopCounter(rawTypeFailed, rawTypeStart) stopCounter(findMemberFailed, findMemberStart) stopCounter(subtypeFailed, subtypeStart) stopTimer(failedSilentNanos, failedSilentStart) - ex + SilentTypeError(TypeErrorWrapper(ex)) } } - /** 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 @@ -724,8 +750,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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, + context.undetparams = inferExprInstance(tree, context.extractUndetparams(), pt, // approximate types that depend on arguments since dependency on implicit argument is like dependency on type parameter mt.approximate, // if we are looking for a manifest, instantiate type to Nothing anyway, @@ -736,18 +761,24 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { useWeaklyCompatible = true) // #3808 } - 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 => - debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) - val tree1 = typed(resetAllAttrs(original), mode, WildcardType) - tree1.tpe = addAnnotations(tree1, tree1.tpe) - if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) - } - else - typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + // avoid throwing spurious DivergentImplicit errors + if (context.hasErrors) + return setError(tree) + + withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree)){ typer1 => + if (original != EmptyTree && pt != WildcardType) + typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match { + case SilentResultValue(result) => + result + case _ => + debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) + val tree1 = typed(resetAllAttrs(original), mode, WildcardType) + tree1.tpe = addAnnotations(tree1, tree1.tpe) + if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) + } + else + typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + } } def instantiateToMethodType(mt: MethodType): Tree = { @@ -758,7 +789,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (!meth.isConstructor && !meth.isMacro && isFunctionType(pt)) { // (4.2) debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) - checkParamsConvertible(tree.pos, tree.tpe) + checkParamsConvertible(tree, tree.tpe) val tree0 = etaExpand(context.unit, tree) // println("eta "+tree+" ---> "+tree0+":"+tree0.tpe+" undet: "+context.undetparams+ " mode: "+Integer.toHexString(mode)) @@ -775,9 +806,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } 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) } @@ -792,8 +821,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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)) && @@ -808,9 +836,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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) setPos (tree.pos) @@ -836,7 +862,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tree } } else { - errorTree(tree, tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method") + CaseClassConstructorError(tree) } } @@ -960,7 +986,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!context.undetparams.isEmpty) { return instantiate(tree, mode, pt) } - if (context.implicitsEnabled && !tree.tpe.isError && !pt.isError) { + if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { // (14); the condition prevents chains of views debuglog("inferring view from " + tree.tpe + " to " + pt) val coercion = inferView(tree, tree.tpe, pt, true) @@ -977,8 +1003,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { unit.echo(tree.pos, msg) debuglog(msg) - return newTyper(context.makeImplicit(context.reportAmbiguousErrors)).typed( + val silentContext = context.makeImplicit(context.ambiguousErrors) + val res = newTyper(silentContext).typed( new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) + if (silentContext.hasErrors) context.issue(silentContext.errBuffer.head) else return res } } } @@ -986,31 +1014,34 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { log("error tree = " + tree) if (settings.explaintypes.value) explainTypes(tree.tpe, pt) } - try { - typeErrorTree(tree, tree.tpe, pt) - } catch { - case ex: TypeError => - if (isPastTyper && pt.existentialSkolems.nonEmpty) { - // Ignore type errors raised in later phases that are due to mismatching types with existential skolems - // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. - // Here's my hypothsis why this happens. The pattern matcher defines a variable of type - // - // val x: T = expr - // - // where T is the type of expr, but T contains existential skolems ts. - // In that case, this value definition does not typecheck. - // The value definition - // - // val x: T forSome { ts } = expr - // - // would typecheck. Or one can simply leave out the type of the `val`: - // - // val x = expr - 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, deriveTypeWithWildcards(pt.existentialSkolems)(pt)) - } else - throw ex + + val found = tree.tpe + val req = pt + if (!found.isErroneous && !req.isErroneous) { + if (!context.reportErrors && isPastTyper && req.existentialSkolems.nonEmpty) { + // Ignore type errors raised in later phases that are due to mismatching types with existential skolems + // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. + // Here's my hypothsis why this happens. The pattern matcher defines a variable of type + // + // val x: T = expr + // + // where T is the type of expr, but T contains existential skolems ts. + // In that case, this value definition does not typecheck. + // The value definition + // + // val x: T forSome { ts } = expr + // + // would typecheck. Or one can simply leave out the type of the `val`: + // + // val x = expr + 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, deriveTypeWithWildcards(pt.existentialSkolems)(pt)) + } else { + // create an actual error + AdaptTypeError(tree, found, req) + } } + setError(tree) } } } @@ -1027,7 +1058,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def instantiateExpectingUnit(tree: Tree, mode: Int): Tree = { val savedUndetparams = context.undetparams silent(_.instantiate(tree, mode, UnitClass.tpe)) match { - case t: Tree => t + case SilentResultValue(t) => t case _ => context.undetparams = savedUndetparams val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) @@ -1053,15 +1084,20 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // Note: implicit arguments are still inferred (this kind of "chaining" is allowed) ) } + + def adaptToMember(qual: Tree, searchTemplate: Type): Tree = + adaptToMember(qual, searchTemplate, true, true) + def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean): Tree = + adaptToMember(qual, searchTemplate, reportAmbiguous, true) - def adaptToMember(qual: Tree, searchTemplate: Type): Tree = { + def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { if (isAdaptableWithView(qual)) { qual.tpe.widen.normalize match { case et: ExistentialType => qual setType et.skolemizeExistential(context.owner, qual) // open the existential case _ => } - inferView(qual, qual.tpe, searchTemplate, true) match { + inferView(qual, qual.tpe, searchTemplate, reportAmbiguous, saveErrors) match { case EmptyTree => qual case coercion => if (settings.logImplicitConv.value) @@ -1083,13 +1119,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * If no conversion is found, return `qual` unchanged. * */ - def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type): Tree = { + def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { def doAdapt(restpe: Type) = //util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ") - adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe)) + adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe), reportAmbiguous, saveErrors) if (pt != WildcardType) { silent(_ => doAdapt(pt)) match { - case result: Tree if result != qual => + case SilentResultValue(result) if result != qual => result case _ => debuglog("fallback on implicits in adaptToArguments: "+qual+" . "+name) @@ -1099,30 +1135,32 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Int, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def onError(reportError: => Tree): Tree = { context.tree match { - case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => // try handling the arguments - // println("typing args: "+args) + case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => silent(_.typedArgs(args, mode)) match { - case args: List[_] => - adaptToArguments(qual, name, args.asInstanceOf[List[Tree]], WildcardType) - case _ => - throw ex + case SilentResultValue(xs) => + val args = xs.asInstanceOf[List[Tree]] + if (args exists (_.isErrorTyped)) + reportError + else + adaptToArguments(qual, name, args, WildcardType, reportAmbiguous, saveErrors) + case _ => + reportError } case _ => - // println("not in an apply: "+context.tree+"/"+tree) - throw ex + reportError } } + silent(_.adaptToMember(qual, HasMember(name), false)) match { + case SilentResultValue(res) => res + case SilentTypeError(err) => onError({if (reportAmbiguous) { context.issue(err) }; setError(tree)}) + } } /** Try to apply an implicit conversion to `qual` to that it contains a @@ -1162,7 +1200,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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.isErrorTyped) { mixins = supertpt1 :: mixins supertpt = TypeTree(supertpt1.tpe.parents.head) setPos supertpt.pos.focus } @@ -1206,14 +1244,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val cbody2 = newTyper(cscope) // called both during completion AND typing. .typePrimaryConstrBody(clazz, cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate))) + superCall match { case Apply(_, _) => val sarg = treeInfo.firstArgument(superCall) if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) - error(sarg.pos, firstParent+" is a trait; does not take constructor arguments") - if (!supertparams.isEmpty) supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus + ConstrArgsInTraitParentTpeError(sarg, firstParent) + if (!supertparams.isEmpty) + supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus case _ => - if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments") + if (!supertparams.isEmpty) + MissingTypeArgumentsParentTpeError(supertpt) } val preSuperVals = treeInfo.preSuperFields(templ.body) @@ -1223,7 +1264,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) case _ => - if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments") + if (!supertparams.isEmpty) + MissingTypeArgumentsParentTpeError(supertpt) } /* experimental: early types as type arguments val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef) @@ -1256,8 +1298,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } catch { case ex: TypeError => - templ.tpe = null - reportTypeError(templ.pos, ex) + // fallback in case of cyclic errors + // @H none of the tests enter here but I couldn't rule it out + ParentTypesError(templ, ex) List(TypeTree(AnyRefClass.tpe)) } @@ -1276,30 +1319,29 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * </ul> */ def validateParentClasses(parents: List[Tree], selfType: Type) { + val pending = ListBuffer[AbsTypeError]() def validateParentClass(parent: Tree, superclazz: Symbol) { - if (!parent.tpe.isError) { + if (!parent.isErrorTyped) { val psym = parent.tpe.typeSymbol.initialize checkClassType(parent, false, true) 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 += ParentSuperSubclassError(parent, superclazz, ps.head.typeSymbol, psym) } else { - error(parent.pos, psym+" needs to be a trait to be mixed in") + pending += ParentNotATraitMixinError(parent, psym) } } - if (psym.isFinal) { - error(parent.pos, "illegal inheritance from final "+psym) - } + if (psym.isFinal) + pending += ParentFinalInheritanceError(parent, psym) + 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+": " + context.unit.source.file.canonicalPath + " != " + psym.sourceFile.canonicalPath) + pending += ParentSealedInheritanceError(parent, psym) } if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes && @@ -1311,17 +1353,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { //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 += ParentSelfTypeConformanceError(parent, selfType) 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 += ParentInheritedTwiceError(parent, psym) } } - - if (!parents.isEmpty && !parents.head.tpe.isError) + if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) for (p <- parents) validateParentClass(p, parents.head.tpe.typeSymbol) /* @@ -1331,13 +1370,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ", baseclasses = "+(context.owner.info.baseClasses map (_.fullName))+ ", lin = "+(context.owner.info.baseClasses map (context.owner.thisType.baseType))) */ + pending.foreach(ErrorUtils.issueTypeError) } def checkFinitary(classinfo: ClassInfoType) { val clazz = classinfo.typeSymbol + for (tparam <- clazz.typeParams) { 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, @@ -1348,6 +1388,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case _ => newinfo } } + FinitaryError(tparam) } } } @@ -1363,8 +1404,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { assert(clazz != NoSymbol) reenterTypeParams(cdef.tparams) val tparams1 = cdef.tparams mapConserve (typedTypeDef) - val impl1 = newTyper(context.make(cdef.impl, clazz, new Scope)) - .typedTemplate(cdef.impl, parentTypes(cdef.impl)) + val impl1 = typerReportAnyContextErrors(context.make(cdef.impl, clazz, new Scope)){ + _.typedTemplate(cdef.impl, parentTypes(cdef.impl)) + } val impl2 = finishMethodSynthesis(impl1, clazz, context) if ((clazz != ClassfileAnnotationClass) && (clazz isNonBottomSubClass ClassfileAnnotationClass)) @@ -1397,17 +1439,16 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val clazz = mdef.symbol.moduleClass val typedMods = removeAnnotations(mdef.mods) assert(clazz != NoSymbol, mdef) - - val typer0 = newTyper(context.make(mdef.impl, clazz, new Scope)) - val impl1 = typer0.typedTemplate(mdef.impl, { - parentTypes(mdef.impl) ++ ( - if (linkedClass == NoSymbol || !linkedClass.isSerializable || clazz.isSerializable) Nil - else { - clazz.makeSerializable() - List(TypeTree(SerializableClass.tpe) setPos clazz.pos.focus) - } - ) - }) + val impl1 = typerReportAnyContextErrors(context.make(mdef.impl, clazz, new Scope)){ + _.typedTemplate(mdef.impl, { + parentTypes(mdef.impl) ++ ( + if (linkedClass == NoSymbol || !linkedClass.isSerializable || clazz.isSerializable) Nil + else { + clazz.makeSerializable() + List(TypeTree(SerializableClass.tpe) setPos clazz.pos.focus) + } + ) + })} val impl2 = finishMethodSynthesis(impl1, clazz, context) treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType @@ -1516,14 +1557,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (sym.hasAnnotation(definitions.VolatileAttr)) { if (!sym.isMutable) - error(vdef.pos, "values cannot be volatile") + VolatileValueError(vdef) else if (sym.isFinal) - error(vdef.pos, "final vars cannot be volatile") + FinalVolatileVarError(vdef) } val rhs1 = if (vdef.rhs.isEmpty) { if (sym.isVariable && sym.owner.isTerm && !isPastTyper) - error(vdef.pos, "local variables must be initialized") + LocalVarUninitializedError(vdef) vdef.rhs } else { val tpt2 = if (sym.hasDefault) { @@ -1571,16 +1612,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val (superConstr, superArgs) = decompose(rhs) assert(superConstr.symbol ne null)//debug + val pending = ListBuffer[AbsTypeError]() // 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) tree match { case This(qual) => - error(tree.pos, "super constructor arguments cannot reference unconstructed `this`") + pending += SuperConstrArgsThisReferenceError(tree) case _ => () } } @@ -1613,6 +1655,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } } + pending.foreach(ErrorUtils.issueTypeError) } /** Check if a structurally defined method violates implementation restrictions. @@ -1661,7 +1704,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { lookupVariable(name.toString.substring(1), enclClass) match { case Some(repl) => silent(_.typedTypeConstructor(stringParser(repl).typ())) match { - case tpt: Tree => + case SilentResultValue(tpt) => val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) alias setInfo typeFun(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) @@ -1711,7 +1754,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1) if (isRepeatedParamType(vparam1.symbol.tpe)) - error(vparam1.pos, "*-parameter must come last") + StarParamNotLastError(vparam1) var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) checkNonCyclic(ddef, tpt1) @@ -1723,7 +1766,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (!meth.owner.isClass || meth.owner.isModuleClass || meth.owner.isAnonOrRefinementClass)) - error(ddef.pos, "constructor definition not allowed here") + InvalidConstructorDefError(ddef) typed(ddef.rhs) } else if (meth.isMacro) { EmptyTree @@ -1738,30 +1781,26 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!isPastTyper && 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") + StarWithDefaultError(meth) if (!isPastTyper) { 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).") + DeprecatedParamNameError(p, n) } } } if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(meth) - treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } - def typedTypeDef(tdef: TypeDef): TypeDef = { - def typeDefTyper = { - if(tdef.tparams isEmpty) Typer.this - else newTyper(context.makeNewScope(tdef, tdef.symbol)) + def typedTypeDef(tdef: TypeDef): TypeDef = + typerWithCondLocalContext(context.makeNewScope(tdef, tdef.symbol))(tdef.tparams.nonEmpty){ + _.typedTypeDef0(tdef) } - typeDefTyper.typedTypeDef0(tdef) - } // call typedTypeDef instead // a TypeDef with type parameters must always be type checked in a new scope @@ -1783,10 +1822,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 _ => + case TypeBounds(lo1, hi1) if (!(lo1 <:< hi1)) => LowerBoundError(tdef, lo1, hi1) + case _ => () } treeCopy.TypeDef(tdef, typedMods, tdef.name, tparams1, rhs1) setType NoType } @@ -1906,9 +1943,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedCase(cdef: CaseDef, pattpe: Type, pt: Type): CaseDef = { // verify no _* except in last position for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x) - error(x.pos, "_* may only come last") + StarPositionInPatternError(x) - val pat1: Tree = typedPattern(cdef.pat, pattpe) + val pat1 = typedPattern(cdef.pat, pattpe) // 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 @@ -1952,7 +1989,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) @@ -1968,9 +2005,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) val (clazz, argpts, respt) = decompose(if (codeExpected) pt.normalize.typeArgs.head else pt) - if (argpts.lengthCompare(numVparams) != 0) - errorTree(fun, "wrong number of parameters; expected = " + argpts.length) + WrongNumberOfParametersError(fun, argpts) else { val vparamSyms = map2(fun.vparams, argpts) { (vparam, argpt) => if (vparam.tpt.isEmpty) { @@ -1980,7 +2016,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { fun match { case etaExpansion(vparams, fn, args) if !codeExpected => silent(_.typed(fn, forFunMode(mode), pt)) match { - case fn1: Tree if context.undetparams.isEmpty => + case SilentResultValue(fn1) if context.undetparams.isEmpty => // 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) @@ -1991,7 +2027,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } case _ => } - error(vparam.pos, missingParameterTypeMsg(fun, vparam, pt)) + MissingParameterTypeError(fun, vparam, pt) ErrorType } if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus @@ -2005,12 +2041,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // for (vparam <- vparams) { // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () // } - var body = typed(fun.body, respt) + val body1 = typed(fun.body, respt) val formals = vparamSyms map (_.tpe) - val restpe = packedType(body, fun.symbol).deconst.resultType + val restpe = packedType(body1, fun.symbol).deconst.resultType val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) // body = checkNoEscaping.locals(context.scope, restpe, body) - val fun1 = treeCopy.Function(fun, vparams, body).setType(funtpe) + val fun1 = treeCopy.Function(fun, vparams, body1).setType(funtpe) if (codeExpected) lifted(fun1) else fun1 } } @@ -2025,7 +2061,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { unit.toCheck += { () => // go to next outer context which is not silent, see #3614 var c = context - while (!c.reportGeneralErrors) c = c.outer + while (c.bufferErrors) c = c.outer val stats1 = newTyper(c).typedStats(stats, NoSymbol) for (stat <- stats1 if stat.isDef) { val member = stat.symbol @@ -2041,11 +2077,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Some(imp1: Import) => imp1 case None => log("unhandled import: "+imp+" in "+unit); imp } - private def isWarnablePureExpression(tree: Tree) = tree match { case EmptyTree | Literal(Constant(())) => false case _ => - (treeInfo isExprSafeToInline tree) && { + !tree.isErrorTyped && (treeInfo isExprSafeToInline tree) && { val sym = tree.symbol (sym == null) || !(sym.isModule || sym.isLazy) || { debuglog("'Pure' but side-effecting expression in statement position: " + tree) @@ -2059,9 +2094,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def includesTargetPos(tree: Tree) = tree.pos.isRange && context.unit.exists && (tree.pos includes context.unit.targetPos) val localTarget = stats exists includesTargetPos + val statsErrors = scala.collection.mutable.LinkedHashSet[AbsTypeError]() 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(_, _) => @@ -2074,20 +2110,25 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // the targetposition stat } else { - val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this - else newTyper(context.make(stat, exprOwner)) + val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) { + context.flushBuffer() + this + } else newTyper(context.make(stat, exprOwner)) // 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") + ConstructorsOrderError(stat) } + if (isWarnablePureExpression(result)) context.warning(stat.pos, "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) + statsErrors ++= localTyper.context.errBuffer result } } @@ -2121,9 +2162,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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}+ - {if (e.sym.isMacro && e1.sym.isMacro) " \n(note that macros cannot be overloaded)" else ""}) + DefDefinedTwiceError(e.sym, e1.sym) scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779 } e1 = scope.lookupNextEntry(e1) @@ -2168,14 +2207,20 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { }) ::: newStats.toList } } - val result = stats mapConserve typedStat - if (phase.erasedTypes) result - else checkNoDoubleDefsAndAddSynthetics(result) + + val stats1 = withSavedContext(context) { + val result = stats mapConserve typedStat + context.flushBuffer() + result + } + context.updateBuffer(statsErrors) + if (phase.erasedTypes) stats1 + else checkNoDoubleDefsAndAddSynthetics(stats1) } def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = { val typedMode = onlyStickyModes(mode) | newmode - val t = constrTyperIf((mode & SCCmode) != 0).typed(arg, typedMode, pt) + val t = withCondConstrTyper((mode & SCCmode) != 0)(_.typed(arg, typedMode, pt)) checkDead.inMode(typedMode, t) } @@ -2244,8 +2289,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { // TODO_NMT: check the assumption that args nonEmpty - def errTree = setError(treeCopy.Apply(tree, fun0, args)) - def errorTree(msg: String) = { error(tree.pos, msg); errTree } + def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) + def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } var fun = fun0 if (fun.hasSymbol && fun.symbol.isOverloaded) { @@ -2308,8 +2353,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { arg1 } context.undetparams = undetparams - inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) - doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + if (context.hasErrors) + setError(tree) + else { + inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) + doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + } case mt @ MethodType(params, _) => val paramTypes = mt.paramTypes @@ -2329,7 +2378,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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 SilentResultValue(t) => // Depending on user options, may warn or error here if // a Unit or tuple was inserted. Some(t) filter (tupledTree => @@ -2337,7 +2386,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { || tupledTree.symbol == null || checkValidAdaptation(tupledTree, args) ) - case ex => + case _ => context.undetparams = savedUndetparams None } @@ -2352,21 +2401,21 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def tryNamesDefaults: Tree = { val lencmp = compareLengths(args, formals) - if (mt.isErroneous) errTree - else if (inPatternMode(mode)) + if (mt.isErroneous) duplErrTree + else if (inPatternMode(mode)) { // #2064 - errorTree("wrong number of arguments for "+ treeSymTypeMsg(fun)) - else if (lencmp > 0) { - tryTupleApply getOrElse errorTree("too many arguments for "+treeSymTypeMsg(fun)) + duplErrorTree(WrongNumberOfArgsError(tree, fun)) + } else if (lencmp > 0) { + tryTupleApply getOrElse duplErrorTree(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)) { - errTree + duplErrTree } else if (!isIdentity(argPos) && !sameLength(formals, params)) // !isIdentity indicates that named arguments are used to re-order arguments - errorTree("when using named arguments, the vararg parameter has to be specified exactly once") + duplErrorTree(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 }" @@ -2380,7 +2429,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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) errTree + if (fun1.isErroneous) duplErrTree else { assert(isNamedApplyBlock(fun1), fun1) val NamedApplyInfo(qual, targs, previousArgss, _) = context.namedApplyBlockInfo.get._2 @@ -2397,17 +2446,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val lencmp2 = compareLengths(allArgs, formals) if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) { - errorTree("module extending its companion class cannot use default constructor arguments") + duplErrorTree(ModuleUsingCompanionClassDefaultArgsErrror(tree)) } else if (lencmp2 > 0) { removeNames(Typer.this)(allArgs, params) // #3818 - errTree + duplErrTree } 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(notEnoughArgumentsMsg(fun, missing)) + tryTupleApply getOrElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) } } } @@ -2466,7 +2515,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) @@ -2488,11 +2536,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!argtparams.isEmpty) { val strictPt = formal.instantiateTypeParams(tparams, strictTargs) inferArgumentInstance(arg1, argtparams, strictPt, lenientPt) - } - arg1 + arg1 + } else arg1 } val args1 = map2(args, formals)(typedArgToPoly) - if (args1 exists (_.tpe.isError)) errTree + if (args1 exists {_.isErrorTyped}) duplErrTree 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" @@ -2509,12 +2557,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { doTypedApply(tree, fun setType fun.tpe.widen, args, mode, pt) case ErrorType => - setError(treeCopy.Apply(tree, fun, args)) + if (!tree.isErrorTyped) setError(tree) else tree + // @H change to setError(treeCopy.Apply(tree, 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 duplErrorTree(TooManyArgsPatternError(fun)) // def freshArgType(tp: Type): (List[Symbol], Type) = tp match { @@ -2522,11 +2571,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (Nil, param.tpe) case PolyType(tparams, restpe) => createFromClonedSymbols(tparams, freshArgType(restpe)._2)((ps, t) => ((ps, t))) + // 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") + OverloadedUnapplyError(fun) (Nil, ErrorType) case _ => - error(fun.pos, "an unapply method must accept a single argument.") + UnapplyWithSingleArgError(fun) (Nil, ErrorType) } @@ -2542,7 +2592,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { freeVars foreach unapplyContext.scope.enter val typer1 = newTyper(unapplyContext) - val pattp = typer1.infer.inferTypedPattern(tree.pos, unappFormal, arg.tpe) + val pattp = typer1.infer.inferTypedPattern(tree, unappFormal, arg.tpe) // turn any unresolved type variables in freevars into existential skolems val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) @@ -2553,8 +2603,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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) errTree - else { + if (fun1.tpe.isErroneous) { + duplErrTree + } else { val formals0 = unapplyTypeList(fun1.symbol, fun1.tpe) val formals1 = formalTypes(formals0, args.length) if (sameLength(formals1, args)) { @@ -2567,15 +2618,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val itype = glb(List(pt1, arg.tpe)) 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("wrong number of arguments for "+treeSymTypeMsg(fun)) - } + } else + duplErrorTree(WrongNumberArgsPatternError(tree, fun)) } /* --- end unapply --- */ case _ => - errorTree(fun.tpe+" does not take parameters") + duplErrorTree(ApplyWithoutArgsError(tree, fun)) } } @@ -2587,8 +2636,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) + val pending = ListBuffer[AbsTypeError]() + + def reportAnnotationError(err: AbsTypeError) = { + pending += err hasError = true annotationError } @@ -2604,13 +2655,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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") - else + if (const == null) { + reportAnnotationError(AnnotationNotAConstantError(tr)); None + } else if (const.value == null) { + reportAnnotationError(AnnotationArgNullError(tr)); None + } else Some(LiteralAnnotArg(const)) } @@ -2619,16 +2669,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { */ def tree2ConstArg(tree: Tree, pt: Type): Option[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 + reportAnnotationError(ArrayConstantsError(tree)); None 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)) + if (annInfo.atp.isErroneous) { hasError = true; None } + else Some(NestedAnnotArg(annInfo)) // use of Array.apply[T: ClassManifest](xs: T*): Array[T] // and Array.apply(x: Int, xs: Int*): Array[Int] (and similar) @@ -2643,13 +2689,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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) + reportAnnotationError(ArrayConstantsTypeMismatchError(tree, pt)) None } - 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) @@ -2669,13 +2715,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Select(New(tpt), nme.CONSTRUCTOR) => (fun, outerArgss) case _ => - error(fun.pos, "unexpected tree in annotation: "+ fun) + reportAnnotationError(UnexpectedTreeAnnotation(fun)) (setError(fun), outerArgss) } extract(ann, List()) } - if (fun.isErroneous) annotationError + val res = if (fun.isErroneous) annotationError else { val typedFun @ Select(New(tpt), _) = typed(fun, forFunMode(mode), WildcardType) val annType = tpt.tpe @@ -2685,9 +2731,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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)) @@ -2703,10 +2749,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 @@ -2715,21 +2761,21 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (sym.name, annArg) } case arg => - error(arg.pos, "classfile annotation arguments have to be supplied as named arguments") + reportAnnotationError(ClassfileAnnotationsAsNamedArgsError(arg)) (nme.ERROR, None) } for (sym <- names) { // make sure the flags are up to date before erroring (jvm/t3415 fails otherwise) sym.initialize if (!sym.hasAnnotation(AnnotationDefaultAttr) && !sym.hasDefaultFlag) - error(ann.pos, "annotation " + annType.typeSymbol.fullName + " is missing argument " + sym.name) + reportAnnotationError(AnnotationMissingArgError(ann, annType, sym)) } 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) @@ -2781,7 +2827,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { annInfo(fun) case _ => - error(t.pos, "unexpected tree after typing annotation: "+ typedAnn) + reportAnnotationError(UnexpectedTreeAnnotationError(t, typedAnn)) } if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2) @@ -2791,6 +2837,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else annInfo(typedAnn) } } + + if (hasError) { + pending.foreach(ErrorUtils.issueTypeError) + annotationError + } else res } def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? @@ -2890,7 +2941,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) + InferTypeWithVolatileTypeSelectionError(tree, pre) mapOver(tp) } case _ => @@ -2907,8 +2958,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { localSyms += sym remainingSyms += sym } else { - unit.error(tree.pos, - "can't existentially abstract over parameterized type " + tp) + AbstractExistentiallyOverParamerizedTpeError(tree, tp) } } } @@ -2977,10 +3027,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { new DeSkolemizeMap mapOver tp } - def typedClassOf(tree: Tree, tpt: Tree) = { - checkClassType(tpt, true, false) - atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) - } + def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) = + if (!checkClassType(tpt, true, false) && noGen) tpt + else atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = { for (wc <- tree.whereClauses) @@ -2989,7 +3038,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val whereClauses1 = typedStats(tree.whereClauses, context.owner) for (vd @ ValDef(_, _, _, _) <- tree.whereClauses) if (vd.symbol.tpe.isVolatile) - error(vd.pos, "illegal abstraction from value with volatile type "+vd.symbol.tpe) + AbstractionFromVolatileTypeError(vd) val tpt1 = typedType(tree.tpt, mode) existentialTransform(tree.whereClauses map (_.symbol), tpt1.tpe)((tparams, tp) => TypeTree(newExistentialType(tparams, tp)) setOriginal tree @@ -3012,7 +3061,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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(_, _) => @@ -3020,12 +3069,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case PolyType(tparams, restpe) if tparams.nonEmpty => if (sameLength(tparams, args)) { val targs = args map (_.tpe) - checkBounds(tree.pos, NoPrefix, NoSymbol, tparams, targs, "") + checkBounds(tree, NoPrefix, NoSymbol, tparams, targs, "") if (fun.symbol == Predef_classOf) - typedClassOf(tree, args.head) + typedClassOf(tree, args.head, true) else { if (!isPastTyper && fun.symbol == Any_isInstanceOf && !targs.isEmpty) - checkCheckable(tree.pos, targs.head, "") + checkCheckable(tree, 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] @@ -3036,12 +3085,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { treeCopy.TypeApply(tree, fun, args) setType resultpe } } else { - errorTree(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + TypedApplyWrongNumberOfTpeParametersError(tree, fun) } case ErrorType => setError(treeCopy.TypeApply(tree, fun, args)) case _ => - errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.") + TypedApplyDoesNotTakeTpeParametersError(tree, fun) } @inline final def deindentTyping() = context.typingIndentLevel -= 2 @@ -3110,6 +3159,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // this annotation did not need it if (ainfo.isErroneous) + // Erroneous annotations were already reported in typedAnnotation arg1 // simply drop erroneous annotations else { ann.tpe = atype @@ -3149,7 +3199,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { vble = context.owner.newValue(name, tree.pos) if (vble.name.toTermName != nme.WILDCARD) { if ((mode & ALTmode) != 0) - error(tree.pos, "illegal variable in pattern alternative") + VariableInPatternAlternativeError(tree) vble = namer.enterInScope(vble) } val body1 = typed(body, mode, pt) @@ -3176,18 +3226,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) + // see #2494 for double error message example + def fail() = + if (lhs1.isErrorTyped) lhs1 + else AssignmentError(tree, varsym) - setError(tree) - } if (varsym == null) - return fail + return fail() if (treeInfo.mayBeVarGetter(varsym)) { treeInfo.methPart(lhs1) match { @@ -3203,7 +3249,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val rhs1 = typed(rhs, EXPRmode | BYVALmode, lhs1.tpe) treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitClass.tpe } - else fail + else fail() } def typedIf(cond: Tree, thenp: Tree, elsep: Tree) = { @@ -3273,12 +3319,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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. @@ -3297,12 +3343,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 + if (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 + else tpt0 } /** If current tree <tree> appears in <val x(: T)? = <tree>> @@ -3321,17 +3368,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val tp = tpt1.tpe val sym = tp.typeSymbol.initialize if (sym.isAbstractType || sym.hasAbstractFlag) - error(tree.pos, sym + " is abstract; cannot be instantiated") + IsAbstractError(tree, sym) else if (!( tp == sym.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 { @@ -3369,22 +3414,28 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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): Option[List[Tree]] = { val c = context.makeSilent(false) c.retyping = true try { - newTyper(c).typedArgs(args, mode) + val res = newTyper(c).typedArgs(args, mode) + if (c.hasErrors) None else Some(res) } catch { - case ex: CyclicReference => throw ex - case ex: TypeError => - null + case ex: CyclicReference => + throw ex + case te: TypeError => + // @H some of typer erros can still leak, + // for instance in continuations + None + } finally { + c.flushBuffer() } } @@ -3393,10 +3444,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { */ 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(typeError: AbsTypeError): Tree = { stopTimer(failedApplyNanos, start) // If the problem is with raw types, copnvert to existentials and try again. @@ -3419,27 +3468,39 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 + def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == typeError.errPos) + + val retry = (typeError.errPos != null) && (fun :: tree :: args exists errorInResult) 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: " + typeError.errPos+"!="+tree.pos } if (retry) { val Select(qual, name) = fun - val args1 = tryTypedArgs(args, forArgMode(fun, mode), ex) - val qual1 = - if ((args1 ne null) && !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) + tryTypedArgs(args, forArgMode(fun, mode)) match { + case Some(args1) => + assert((args1.length == 0) || !args1.head.tpe.isErroneous, "try typed args is ok") + val qual1 = + if (!pt.isError) adaptToArguments(qual, name, args1, pt, true, true) + 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) + } + case _ => () } } - reportTypeError(tree.pos, ex) + issue(typeError) setError(treeCopy.Apply(tree, fun, args)) } + + silent(_.doTypedApply(tree, fun, args, mode, pt)) match { + case SilentResultValue(t) => + t + case SilentTypeError(err) => + onError(err) + } } def typedApply(fun: Tree, args: List[Tree]) = { @@ -3451,10 +3512,28 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val funpt = if (isPatternMode) pt else WildcardType val appStart = startTimer(failedApplyNanos) val opeqStart = startTimer(failedOpEqNanos) + + def onError(reportError: => Tree): Tree = { + fun match { + case Select(qual, name) + if !isPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) => + val qual1 = typedQualifier(qual) + if (treeInfo.isVariableOrGetter(qual1)) { + stopTimer(failedOpEqNanos, opeqStart) + convertToAssignment(fun, qual1, name, args) + } else { + stopTimer(failedApplyNanos, appStart) + reportError + } + case _ => + stopTimer(failedApplyNanos, appStart) + reportError + } + } silent(_.typed(fun, forFunMode(mode), funpt), - if ((mode & EXPRmode) != 0) false else context.reportAmbiguousErrors, + if ((mode & EXPRmode) != 0) false else context.ambiguousErrors, if ((mode & EXPRmode) != 0) tree else context.tree) match { - case fun1: Tree => + case SilentResultValue(fun1) => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 incCounter(typedApplyCount) def isImplicitMethod(tpe: Type) = tpe match { @@ -3481,7 +3560,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { //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.isErrorTyped) { val checked = gen.mkCheckInit(res) // this check is needed to avoid infinite recursion in Duplicators // (calling typed1 more than once for the same tree) @@ -3489,30 +3568,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else res } else res - case ex: TypeError => - fun match { - case Select(qual, name) - if !isPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) => - val qual1 = typedQualifier(qual) - if (treeInfo.isVariableOrGetter(qual1)) { - stopTimer(failedOpEqNanos, opeqStart) - convertToAssignment(fun, qual1, name, args, ex) - } - else { - stopTimer(failedApplyNanos, appStart) - reportTypeError(fun.pos, ex) - setError(tree) - } - case _ => - stopTimer(failedApplyNanos, appStart) - reportTypeError(fun.pos, ex) - setError(tree) - } + case SilentTypeError(err) => + onError({issue(err); setError(tree)}) } } } - 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( @@ -3553,25 +3615,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 { - case t: Tree => - t - case _ => - reportTypeError(tree.pos, ex) - setError(tree) - } -*/ } - def qualifyingClassSym(qual: Name): Symbol = - if (tree.symbol != NoSymbol) tree.symbol else qualifyingClass(tree, qual, false) - def typedSuper(qual: Tree, mix: TypeName) = { val qual1 = typed(qual) @@ -3596,12 +3645,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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") + ErrorType } else { - error(tree.pos, mix+" does not name a parent class of "+clazz) + MixinMissingParentClassNameError(tree, mix, clazz) + ErrorType } - ErrorType } else if (!ps.tail.isEmpty) { - error(tree.pos, "ambiguous parent class qualifier") + AmbiguousParentClassError(tree) ErrorType } else { ps.head @@ -3618,16 +3668,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { findMixinSuper(clazz.tpe) } - treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) - } + treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) + } def typedThis(qual: Name) = { - val clazz = qualifyingClassSym(qual) - if (clazz == NoSymbol) setError(tree) - else { - tree setSymbol clazz setType clazz.thisType.underlying - if (isStableContext(tree, mode, pt)) tree setType clazz.thisType - tree + val qualifyingClassSym = if (tree.symbol != NoSymbol) Some(tree.symbol) else qualifyingClass(tree, qual) + qualifyingClassSym match { + case Some(clazz) => + tree setSymbol clazz setType clazz.thisType.underlying + if (isStableContext(tree, mode, pt)) tree setType clazz.thisType + tree + case None => tree } } @@ -3659,10 +3710,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & EXPRmode) != 0) { val qual1 = if (member(qual, name) != NoSymbol) qual - else adaptToMemberWithArgs(tree, qual, name, mode) - if (qual1 ne qual) return typed(treeCopy.Select(tree, qual1, name), mode, pt) - } + else adaptToMemberWithArgs(tree, qual, name, mode, true, true) + if (qual1 ne qual) + return typed(treeCopy.Select(tree, qual1, name), mode, pt) + } if (!reallyExists(sym)) { if (context.owner.toplevelClass.isJavaDefined && name.isTypeName) { val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } @@ -3691,7 +3743,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ) } - def makeErrorTree = { + def makeInteractiveErrorTree = { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) @@ -3700,24 +3752,31 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (name == nme.ERROR && forInteractive) - return makeErrorTree + return makeInteractiveErrorTree if (!qual.tpe.widen.isErroneous) { if ((mode & QUALmode) != 0) { val lastTry = missingHook(qual.tpe.typeSymbol, name) if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) } - notAMemberError(tree.pos, qual, name) + NotAMemberError(tree, qual, name) } - if (forInteractive) makeErrorTree else setError(tree) + if (forInteractive) makeInteractiveErrorTree else setError(tree) } else { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) } - val (tree2, pre2) = makeAccessible(tree1, sym, qual.tpe, qual) - val result = stabilize(tree2, pre2, mode, pt) + val (result, accessibleError) = silent(_.makeAccessible(tree1, sym, qual.tpe, qual)) match { + case SilentTypeError(err) => + if (err.kind != ErrorKinds.Access) { + context issue err + return setError(tree) + } else (tree1, Some(err)) + case SilentResultValue(treeAndPre) => + (stabilize(treeAndPre._1, treeAndPre._2, mode, pt), None) + } def isPotentialNullDeference() = { !isPastTyper && @@ -3736,16 +3795,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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, "") + checkBounds(qual, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "") qual // you only get to see the wrapped tree after running this check :-p }) setType qual.tpe setPos qual.pos, 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 _ if accessibleError.isDefined => + val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, false, false) + if (!qual1.isErrorTyped && (qual1 ne qual)) + typed(Select(qual1, name) setPos tree.pos, mode, pt) + else { + issue(accessibleError.get) + setError(tree) + } case _ => result } @@ -3753,7 +3814,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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.isErrorTyped + && 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 @@ -3761,7 +3823,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { && qual.tpe.typeSymbol.isPublic ) if (isRefinableGetClass) - selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe)) + selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe)) else selection } @@ -3775,8 +3837,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * (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: AbsTypeError = 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: AbsTypeError) = { + 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 @@ -3896,7 +3967,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) @@ -3930,72 +4001,47 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (inaccessibleSym eq NoSymbol) { // Avoiding some spurious error messages: see SI-2388. if (reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME)) () - else { - // This laborious determination arrived at to keep the tests working. - val calcSimilar = ( - name.length > 2 && ( - startingIdentContext.reportGeneralErrors - || startingIdentContext.enclClassOrMethod.reportGeneralErrors - ) - ) - // avoid calculating if we're in "silent" mode. - // name length check to limit unhelpful suggestions for e.g. "x" and "b1" - val similar = { - if (!calcSimilar) "" - else { - val allowed = ( - startingIdentContext.enclosingContextChain - flatMap (ctx => ctx.scope.toList ++ ctx.imports.flatMap(_.allImportedSymbols)) - filter (sym => sym.isTerm == name.isTermName) - filterNot (sym => sym.isPackage || sym.isSynthetic || sym.hasMeaninglessName) - ) - val allowedStrings = ( - allowed.map("" + _.name).distinct.sorted - filterNot (s => (s contains '$') || (s contains ' ')) - ) - similarString("" + name, allowedStrings) - } - } - error(tree.pos, "not found: "+decodeWithKind(name, context.owner) + similar) - } - } - else new AccessError( - tree, inaccessibleSym, context.enclClass.owner.thisType, - inaccessibleExplanation - ).emit() + else identError(SymbolNotFoundError(tree, name, context.owner, startingIdentContext)) + } else + identError(InferErrorGen.AccessError( + tree, inaccessibleSym, context.enclClass.owner.thisType, context.enclClass.owner, + inaccessibleExplanation + )) defSym = context.owner.newErrorSymbol(name) } } } - if (defSym.owner.isPackageClass) - pre = defSym.owner.thisType + if (errorContainer != null) { + ErrorUtils.issueTypeError(errorContainer) + setError(tree) + } else { + if (defSym.owner.isPackageClass) + pre = defSym.owner.thisType - // Inferring classOf type parameter from expected type. - if (defSym.isThisSym) { - typed1(This(defSym.owner) setPos tree.pos, mode, pt) - } - // Inferring classOf type parameter from expected type. Otherwise an - // actual call to the stubbed classOf method is generated, returning null. - else if (isPredefMemberNamed(defSym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) - typedClassOf(tree, TypeTree(pt.typeArgs.head)) - else { - val tree1 = ( - if (qual == EmptyTree) tree - // atPos necessary because qualifier might come from startContext - else atPos(tree.pos)(Select(qual, name)) - ) - 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 + // Inferring classOf type parameter from expected type. + if (defSym.isThisSym) { + typed1(This(defSym.owner) setPos tree.pos, mode, pt) + } + // Inferring classOf type parameter from expected type. Otherwise an + // actual call to the stubbed classOf method is generated, returning null. + else if (isPredefMemberNamed(defSym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) + typedClassOf(tree, TypeTree(pt.typeArgs.head)) + else { + val tree1 = ( + if (qual == EmptyTree) tree + // atPos necessary because qualifier might come from startContext + else atPos(tree.pos)(Select(qual, name)) + ) + 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 (_.isErrorTyped)) tree setType ErrorType else { val decls = new Scope //Console.println("Owner: " + context.enclClass.owner + " " + context.enclClass.owner.id) @@ -4007,10 +4053,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedAppliedTypeTree(tpt: Tree, args: List[Tree]) = { val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) - if (tpt1.tpe.isError) { - setError(tree) + if (tpt1.isErrorTyped) { + tpt1 } 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)) { @@ -4042,16 +4088,16 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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, "") + checkBounds(result, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "") result // you only get to see the wrapped tree after running this check :-p } setType (result.tpe) setPos(result.pos) 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, tpt1, tparams) } } } @@ -4098,8 +4144,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val typer1 = newTyper(context.makeNewScope(tree, context.owner)) for (useCase <- comment.useCases) { typer1.silent(_.typedUseCase(useCase)) match { - case ex: TypeError => - unit.warning(useCase.pos, ex.msg) + case SilentTypeError(err) => + unit.warning(useCase.pos, err.errMsg) case _ => } for (useCaseSym <- useCase.defined) { @@ -4114,17 +4160,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { typedAnnotated(constr, typed(arg, mode, pt)) case tree @ Block(_, _) => - newTyper(context.makeNewScope(tree, context.owner)) - .typedBlock(tree, mode, pt) + typerWithLocalContext(context.makeNewScope(tree, context.owner)){ + _.typedBlock(tree, mode, pt) + } case Alternative(alts) => val alts1 = alts mapConserve (alt => typed(alt, mode | ALTmode, pt)) treeCopy.Alternative(tree, alts1) setType pt case Star(elem) => - checkStarPatOK(tree.pos, mode) - val elem1 = typed(elem, mode, pt) - treeCopy.Star(tree, elem1) setType makeFullyDefined(pt) + if ((mode & STARmode) == 0 && !isPastTyper) + StarPatternWithVarargParametersError(tree) + treeCopy.Star(tree, typed(elem, mode, pt)) setType makeFullyDefined(pt) case Bind(name, body) => typedBind(name, body) @@ -4141,8 +4188,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case tree @ Function(_, _) => if (tree.symbol == NoSymbol) tree.symbol = context.owner.newAnonymousFunctionValue(tree.pos) - - newTyper(context.makeNewScope(tree, tree.symbol)).typedFunction(tree, mode, pt) + typerWithLocalContext(context.makeNewScope(tree, tree.symbol))(_.typedFunction(tree, mode, pt)) case Assign(lhs, rhs) => typedAssign(lhs, rhs) @@ -4181,17 +4227,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Typed(expr, Function(List(), EmptyTree)) => typedEta(checkDead(typed1(expr, mode, pt))) - case Typed(expr, tpt @ Ident(tpnme.WILDCARD_STAR)) => - val expr0 = typed(expr, onlyStickyModes(mode), WildcardType) + case Typed(expr0, tpt @ Ident(tpnme.WILDCARD_STAR)) => + val expr = typed(expr0, onlyStickyModes(mode), WildcardType) def subArrayType(pt: Type) = if (isValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt) else { val tparam = context.owner freshExistential "" setInfo TypeBounds.upper(pt) newExistentialType(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) + + val (expr1, baseClass) = expr.tpe.typeSymbol match { + case ArrayClass => (adapt(expr, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) + case _ => (adapt(expr, onlyStickyModes(mode), seqType(pt)), SeqClass) } expr1.tpe.baseType(baseClass) match { case TypeRef(_, _, List(elemtp)) => @@ -4203,11 +4250,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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 ownType = if (isPatternMode) inferTypedPattern(tpt1, tpt1.tpe, pt) else 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) @@ -4259,12 +4303,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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, 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 } @@ -4295,18 +4341,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val tree1 = // temporarily use `filter` and an alternative for `withFilter` if (name == nme.withFilter) silent(_ => typedSelect(qual1, name)) match { - case result1: Tree => - result1 - case ex1: TypeError => + case SilentResultValue(result) => + result + case _ => silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { - case result2: Tree => + case SilentResultValue(result2) => 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 SilentTypeError(err) => + WithFilterError(tree, err) } } else @@ -4339,8 +4384,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { 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) @@ -4354,7 +4399,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe) case etpt @ ExistentialTypeTree(_, _) => - newTyper(context.makeNewScope(tree, context.owner)).typedExistentialTypeTree(etpt, mode) + typerWithLocalContext(context.makeNewScope(tree, context.owner)){ + _.typedExistentialTypeTree(etpt, mode) + } case dc@TypeTreeWithDeferredRefCheck() => dc // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure) case tpt @ TypeTree() => @@ -4404,7 +4451,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ptLine("typing %s: pt = %s".format(ptTree(tree), pt), "undetparams" -> context.undetparams, "implicitsEnabled" -> context.implicitsEnabled, - "silent" -> !context.reportGeneralErrors, + "silent" -> context.bufferErrors, "context.owner" -> context.owner ) ) @@ -4424,16 +4471,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tree1, tree1.tpe.widen, pt, context.undetparamsString) ) //DEBUG } - -// for (t <- tree1.tpe) assert(t != WildcardType) -// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe) if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result) result } catch { case ex: TypeError => tree.tpe = null + // The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere. printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG - reportTypeError(tree.pos, ex) + + reportTypeError(context, tree.pos, ex) setError(tree) case ex: Exception => if (settings.debug.value) // @M causes cyclic reference error @@ -4455,14 +4501,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def expandMacro(tree: Tree): Tree = try { - macroExpand(tree) match { - case t: Tree => t - case t => errorTree(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass) + def expandMacro(tree: Tree): Tree = + macroExpand(tree, context) match { + case Some(t: Tree) => t + case Some(t) => MacroExpandError(tree, t) + case None => setError(tree) // error already reported } - } catch { - case ex: MacroExpandError => errorTree(tree, ex.msg) - } def atOwner(owner: Symbol): Typer = newTyper(context.make(context.tree, owner)) @@ -4547,22 +4591,22 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // 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") - } - - //@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) { - // 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 - // AnyRef, but the AnyRef type alias is entered after the scala package is - // loaded and completed, so that ScalaObject is unpickled while AnyRef is not - // yet defined ) - result setType(restpe) - } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 - // during uncurry (after refchecks), all types are normalized - result + ConstructorPrefixError(tree, restpe) + } else { + //@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) { + // 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 + // AnyRef, but the AnyRef type alias is entered after the scala package is + // loaded and completed, so that ScalaObject is unpickled while AnyRef is not + // yet defined ) + result setType(restpe) + } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 + // during uncurry (after refchecks), all types are normalized + result + } } } @@ -4586,11 +4630,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { true, false, context) } - def getManifestTree(pos: Position, tp: Type, full: Boolean): Tree = { + def getManifestTree(tree: Tree, 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(tree, full, tp) } else { manifestOpt.tree } diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index 2394925657..27f2ab1d08 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -8,8 +8,6 @@ package api import scala.collection.mutable.ListBuffer -//import scala.tools.nsc.util.{ FreshNameCreator, HashSet, SourceFile } - trait Trees /*extends reflect.generic.Trees*/ { self: Universe => private[scala] var nodeCount = 0 @@ -549,7 +547,7 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe => Select(qualifier, sym.name) setSymbol sym /** Identifier <name> */ - case class Ident(name: Name) extends RefTree { } + case class Ident(name: Name) extends RefTree def Ident(name: String): Ident = Ident(newTermName(name)) |