diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
28 files changed, 3751 insertions, 3499 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 62c584e97b..567d5d0ecd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -66,9 +66,9 @@ trait Adaptations { ) } - if (settings.noAdaptedArgs.value) + if (settings.noAdaptedArgs) adaptWarning("No automatic adaptation here: use explicit parentheses.") - else if (settings.warnAdaptedArgs.value) + else if (settings.warnAdaptedArgs) adaptWarning( if (args.isEmpty) "Adapting argument list by inserting (): " + ( if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous." @@ -77,7 +77,7 @@ trait Adaptations { else "Adapting argument list by creating a " + args.size + "-tuple: this may not be what you want." ) - !settings.noAdaptedArgs.value + !settings.noAdaptedArgs } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index b50486306d..02e1eb6f00 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -16,7 +16,6 @@ trait Analyzer extends AnyRef with Typers with Infer with Implicits - with Variances with EtaExpansion with SyntheticMethods with Unapplies @@ -88,22 +87,25 @@ trait Analyzer extends AnyRef override def run() { val start = if (Statistics.canEnable) Statistics.startTimer(typerNanos) else null global.echoPhaseSummary(this) - currentRun.units foreach applyPhase - undoLog.clear() - // need to clear it after as well or 10K+ accumulated entries are - // uncollectable the rest of the way. + for (unit <- currentRun.units) { + applyPhase(unit) + undoLog.clear() + } if (Statistics.canEnable) Statistics.stopTimer(typerNanos, start) } def apply(unit: CompilationUnit) { try { - unit.body = newTyper(rootContext(unit)).typed(unit.body) - if (global.settings.Yrangepos.value && !global.reporter.hasErrors) global.validatePositions(unit.body) + val typer = newTyper(rootContext(unit)) + unit.body = typer.typed(unit.body) + if (global.settings.Yrangepos && !global.reporter.hasErrors) global.validatePositions(unit.body) for (workItem <- unit.toCheck) workItem() - } finally { + if (settings.lint) + typer checkUnused unit + } + finally { unit.toCheck.clear() } } } } } - diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index 28f620dbb5..4210d0b9fb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -33,7 +33,7 @@ trait AnalyzerPlugins { self: Analyzer => /** * Let analyzer plugins change the expected type before type checking a tree. */ - def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = pt + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Mode): Type = pt /** * Let analyzer plugins modify the type that has been computed for a tree. @@ -44,7 +44,7 @@ trait AnalyzerPlugins { self: Analyzer => * @param mode Mode that was used for typing `tree` * @param pt Expected type that was used for typing `tree` */ - def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = tpe + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = tpe /** * Let analyzer plugins change the types assigned to definitions. For definitions that have @@ -133,7 +133,7 @@ trait AnalyzerPlugins { self: Analyzer => * Decide whether this analyzer plugin can adapt a tree that has an annotated type to the * given type tp, taking into account the given mode (see method adapt in trait Typers). */ - def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = false + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = false /** * Adapt a tree that has an annotated type to the given type tp, taking into account the given @@ -142,7 +142,7 @@ trait AnalyzerPlugins { self: Analyzer => * An implementation cannot rely on canAdaptAnnotations being called before. If the implementing * class cannot do the adapting, it should return the tree unchanged. */ - def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = tree + def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = tree /** * Modify the type of a return expression. By default, return expressions have type @@ -169,13 +169,13 @@ trait AnalyzerPlugins { self: Analyzer => /** @see AnalyzerPlugin.pluginsPt */ - def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Mode): Type = if (analyzerPlugins.isEmpty) pt else analyzerPlugins.foldLeft(pt)((pt, plugin) => if (!plugin.isActive()) pt else plugin.pluginsPt(pt, typer, tree, mode)) /** @see AnalyzerPlugin.pluginsTyped */ - def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = { + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = { // support deprecated methods in annotation checkers val annotCheckersTpe = addAnnotations(tree, tpe) if (analyzerPlugins.isEmpty) annotCheckersTpe @@ -196,7 +196,7 @@ trait AnalyzerPlugins { self: Analyzer => if (!plugin.isActive()) tpe else plugin.pluginsTypeSigAccessor(tpe, typer, tree, sym)) /** @see AnalyzerPlugin.canAdaptAnnotations */ - def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = { + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = { // support deprecated methods in annotation checkers val annotCheckersExists = global.canAdaptAnnotations(tree, mode, pt) annotCheckersExists || { @@ -207,7 +207,7 @@ trait AnalyzerPlugins { self: Analyzer => } /** @see AnalyzerPlugin.adaptAnnotations */ - def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = { + def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = { // support deprecated methods in annotation checkers val annotCheckersTree = global.adaptAnnotations(tree, mode, pt) if (analyzerPlugins.isEmpty) annotCheckersTree diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index d30b5c2601..0686b28079 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -6,12 +6,8 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.ListBuffer -import scala.util.control.ControlThrowable -import symtab.Flags._ -import scala.annotation.tailrec import Checkability._ +import scala.language.postfixOps /** On pattern matcher checkability: * @@ -66,6 +62,9 @@ trait Checkable { bases foreach { bc => val tps1 = (from baseType bc).typeArgs val tps2 = (tvarType baseType bc).typeArgs + if (tps1.size != tps2.size) + devWarning(s"Unequally sized type arg lists in propagateKnownTypes($from, $to): ($tps1, $tps2)") + (tps1, tps2).zipped foreach (_ =:= _) // Alternate, variance respecting formulation causes // neg/unchecked3.scala to fail (abstract types). TODO - @@ -82,7 +81,7 @@ trait Checkable { val resArgs = tparams zip tvars map { case (_, tvar) if tvar.instValid => tvar.constr.inst - case (tparam, _) => tparam.tpe + case (tparam, _) => tparam.tpeHK } appliedType(to, resArgs: _*) } @@ -112,7 +111,7 @@ trait Checkable { private class CheckabilityChecker(val X: Type, val P: Type) { def Xsym = X.typeSymbol def Psym = P.typeSymbol - def XR = propagateKnownTypes(X, Psym) + def XR = if (Xsym == AnyClass) classExistentialType(Psym) else propagateKnownTypes(X, Psym) // sadly the spec says (new java.lang.Boolean(true)).isInstanceOf[scala.Boolean] def P1 = X matchesPattern P def P2 = !Psym.isPrimitiveValueClass && isNeverSubType(X, P) @@ -134,7 +133,7 @@ trait Checkable { else if (P3) RuntimeCheckable else if (uncheckableType == NoType) { // Avoid warning (except ourselves) if we can't pinpoint the uncheckable type - debugwarn("Checkability checker says 'Uncheckable', but uncheckable type cannot be found:\n" + summaryString) + debuglog("Checkability checker says 'Uncheckable', but uncheckable type cannot be found:\n" + summaryString) CheckabilityError } else Uncheckable @@ -154,6 +153,7 @@ trait Checkable { def neverSubClass = isNeverSubClass(Xsym, Psym) def neverMatches = result == StaticallyFalse def isUncheckable = result == Uncheckable + def isCheckable = !isUncheckable def uncheckableMessage = uncheckableType match { case NoType => "something" case tp @ RefinedType(_, _) => "refinement " + tp @@ -195,19 +195,27 @@ trait Checkable { * so I will consult with moors about the optimal time to be doing this. */ def areIrreconcilableAsParents(sym1: Symbol, sym2: Symbol): Boolean = areUnrelatedClasses(sym1, sym2) && ( - sym1.initialize.isEffectivelyFinal // initialization important - || sym2.initialize.isEffectivelyFinal + isEffectivelyFinal(sym1) // initialization important + || isEffectivelyFinal(sym2) || !sym1.isTrait && !sym2.isTrait || sym1.isSealed && sym2.isSealed && allChildrenAreIrreconcilable(sym1, sym2) && !currentRun.compiles(sym1) && !currentRun.compiles(sym2) ) + private def isEffectivelyFinal(sym: Symbol): Boolean = ( + // initialization important + sym.initialize.isEffectivelyFinal || ( + settings.future && isTupleSymbol(sym) // SI-7294 step into the future and treat TupleN as final. + ) + ) + def isNeverSubClass(sym1: Symbol, sym2: Symbol) = areIrreconcilableAsParents(sym1, sym2) private def isNeverSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = /*logResult(s"isNeverSubArgs($tps1, $tps2, $tparams)")*/ { - def isNeverSubArg(t1: Type, t2: Type, variance: Int) = { - if (variance > 0) isNeverSubType(t2, t1) - else if (variance < 0) isNeverSubType(t1, t2) - else isNeverSameType(t1, t2) - } + def isNeverSubArg(t1: Type, t2: Type, variance: Variance) = ( + if (variance.isInvariant) isNeverSameType(t1, t2) + else if (variance.isCovariant) isNeverSubType(t2, t1) + else if (variance.isContravariant) isNeverSubType(t1, t2) + else false + ) exists3(tps1, tps2, tparams map (_.variance))(isNeverSubArg) } private def isNeverSameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { @@ -232,6 +240,17 @@ trait Checkable { trait InferCheckable { self: Inferencer => + def isUncheckable(P0: Type) = !isCheckable(P0) + + def isCheckable(P0: Type): Boolean = ( + uncheckedOk(P0) || (P0.widen match { + case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => false + case RefinedType(_, decls) if !decls.isEmpty => false + case p => + new CheckabilityChecker(AnyClass.tpe, p) isCheckable + }) + ) + /** TODO: much better error positions. * Kind of stuck right now because they just pass us the one tree. * TODO: Eliminate inPattern, canRemedy, which have no place here. diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala index 89e2ee44be..65bfd8e34e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package typechecker - import java.lang.ArithmeticException /** This class ... @@ -18,7 +17,6 @@ abstract class ConstantFolder { val global: Global import global._ - import definitions._ /** If tree is a constant operation, replace with result. */ def apply(tree: Tree): Tree = fold(tree, tree match { @@ -29,9 +27,6 @@ abstract class ConstantFolder { /** If tree is a constant value that can be converted to type `pt`, perform * the conversion. - * - * @param tree ... - * @param pt ... */ def apply(tree: Tree, pt: Type): Tree = fold(apply(tree), tree.tpe match { case ConstantType(x) => x convertTo pt diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 49049f110d..49f380086f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -6,21 +6,20 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.StringOps.{ countElementsAsString, countAsString } -import symtab.Flags.{ PRIVATE, PROTECTED, IS_ERROR } +import symtab.Flags.IS_ERROR import scala.compat.Platform.EOL import scala.reflect.runtime.ReflectionUtils import scala.reflect.macros.runtime.AbortMacroException import scala.util.control.NonFatal import scala.tools.nsc.util.stackTraceString +import scala.reflect.io.NoAbstractFile trait ContextErrors { self: Analyzer => import global._ import definitions._ - import treeInfo._ object ErrorKinds extends Enumeration { type ErrorKind = Value @@ -153,11 +152,10 @@ trait ContextErrors { // 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 { + val foundType: Type = req.dealiasWiden match { case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass => - val retyped = typed (tree.duplicate setType null) + val retyped = typed (tree.duplicate.clearType()) val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) - if (foundDecls.isEmpty || (found.typeSymbol eq NoSymbol)) found else { // The members arrive marked private, presumably because there was no @@ -171,11 +169,10 @@ trait ContextErrors { case _ => found } - assert(!found.isErroneous && !req.isErroneous, (found, req)) + assert(!foundType.isErroneous && !req.isErroneous, (foundType, req)) - issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req))) ) - if (settings.explaintypes.value) - explainTypes(found, req) + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(foundType, req, infer.isPossiblyMissingArgs(foundType, req))) ) + infer.explainTypes(foundType, req) } def WithFilterError(tree: Tree, ex: AbsTypeError) = { @@ -184,14 +181,18 @@ trait ContextErrors { } def ParentTypesError(templ: Template, ex: TypeError) = { - templ.tpe = null - issueNormalTypeError(templ, ex.getMessage()) + templ.clearType() + issueNormalTypeError(templ, ex.getMessage()) + setError(templ) } // additional parentTypes errors - def ConstrArgsInTraitParentTpeError(arg: Tree, parent: Symbol) = + def ConstrArgsInParentWhichIsTraitError(arg: Tree, parent: Symbol) = issueNormalTypeError(arg, parent + " is a trait; does not take constructor arguments") + def ConstrArgsInParentOfTraitError(arg: Tree, parent: Symbol) = + issueNormalTypeError(arg, "parents of traits may not have parameters") + def MissingTypeArgumentsParentTpeError(supertpt: Tree) = issueNormalTypeError(supertpt, "missing type arguments") @@ -299,7 +300,7 @@ trait ContextErrors { 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. */ + /* Illuminating some common situations and errors a bit further. */ def addendum = { val companion = { if (name.isTermName && owner.isPackageClass) { @@ -513,7 +514,7 @@ trait ContextErrors { NormalTypeError(tree, fun.tpe+" does not take parameters") // Dynamic - def DynamicVarArgUnsupported(tree: Tree, name: String) = + def DynamicVarArgUnsupported(tree: Tree, name: Name) = issueNormalTypeError(tree, name+ " does not support passing a vararg parameter") def DynamicRewriteError(tree: Tree, err: AbsTypeError) = { @@ -559,11 +560,13 @@ trait ContextErrors { //adapt def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { - issueNormalTypeError(tree, - "missing arguments for " + meth.fullLocationString + ( + val message = + if (meth.isMacro) MacroPartialApplicationErrorMessage + else "missing arguments for " + meth.fullLocationString + ( if (meth.isConstructor) "" else ";\nfollow this method with `_' if you want to treat it as a partially applied function" - )) + ) + issueNormalTypeError(tree, message) setError(tree) } @@ -644,7 +647,7 @@ trait ContextErrors { val addendums = List( if (sym0.associatedFile eq sym1.associatedFile) Some("conflicting symbols both originated in file '%s'".format(sym0.associatedFile.canonicalPath)) - else if ((sym0.associatedFile ne null) && (sym1.associatedFile ne null)) + else if ((sym0.associatedFile ne NoAbstractFile) && (sym1.associatedFile ne NoAbstractFile)) Some("conflicting symbols originated in files '%s' and '%s'".format(sym0.associatedFile.canonicalPath, sym1.associatedFile.canonicalPath)) else None , if (isBug) Some("Note: this may be due to a bug in the compiler involving wildcards in package objects") else None @@ -661,8 +664,8 @@ trait ContextErrors { 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 CyclicReferenceError(errPos: Position, tp: Type, lockedSym: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, s"illegal cyclic reference involving $tp and $lockedSym")) // macro-related errors (also see MacroErrors below) @@ -671,22 +674,30 @@ trait ContextErrors { setError(tree) } + def MacroTooManyArgumentListsError(expandee: Tree, fun: Symbol) = { + NormalTypeError(expandee, "too many argument lists for " + fun) + } + + def MacroInvalidExpansionError(expandee: Tree, role: String, allowedExpansions: String) = { + issueNormalTypeError(expandee, s"macro in $role role can only expand into $allowedExpansions") + } + // same reason as for MacroBodyTypecheckException case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable - private def macroExpansionError(expandee: Tree, msg: String = null, pos: Position = NoPosition) = { + protected def macroExpansionError(expandee: Tree, msg: String, pos: Position = NoPosition) = { def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg macroLogLite("macro expansion has failed: %s".format(msgForLog)) - val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition) if (msg != null) context.error(pos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions setError(expandee) throw MacroExpansionException } + def MacroPartialApplicationErrorMessage = "macros cannot be partially applied" def MacroPartialApplicationError(expandee: Tree) = { // macroExpansionError won't work => swallows positions, hence needed to do issueTypeError // kinda contradictory to the comment in `macroExpansionError`, but this is how it works - issueNormalTypeError(expandee, "macros cannot be partially applied") + issueNormalTypeError(expandee, MacroPartialApplicationErrorMessage) setError(expandee) throw MacroExpansionException } @@ -752,23 +763,26 @@ trait ContextErrors { macroExpansionError(expandee, template(sym.name.nameKind).format(sym.name + " " + sym.origin, forgotten)) } - def MacroExpansionIsNotExprError(expandee: Tree, expanded: Any) = + def MacroExpansionHasInvalidTypeError(expandee: Tree, expanded: Any) = { + val expected = "expr" + val isPathMismatch = expanded != null && expanded.isInstanceOf[scala.reflect.api.Exprs#Expr[_]] macroExpansionError(expandee, - "macro must return a compiler-specific expr; returned value is " + ( + s"macro must return a compiler-specific $expected; returned value is " + ( if (expanded == null) "null" - else if (expanded.isInstanceOf[Expr[_]]) " Expr, but it doesn't belong to this compiler's universe" + else if (isPathMismatch) s" $expected, but it doesn't belong to this compiler" else " of " + expanded.getClass )) - - def MacroImplementationNotFoundError(expandee: Tree) = { - val message = - "macro implementation not found: " + expandee.symbol.name + " " + - "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)" + - (if (forScaladoc) ". When generating scaladocs for multiple projects at once, consider using -Ymacro-no-expand to disable macro expansions altogether." - else "") - macroExpansionError(expandee, message) } + + def MacroImplementationNotFoundError(expandee: Tree) = + macroExpansionError(expandee, macroImplementationNotFoundMessage(expandee.symbol.name)) } + + /** This file will be the death of me. */ + protected def macroImplementationNotFoundMessage(name: Name): String = ( + s"""|macro implementation not found: $name + |(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)""".stripMargin + ) } trait InferencerContextErrors { @@ -810,7 +824,10 @@ trait ContextErrors { ) } - def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String) = { + def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError = + AccessError(tree, sym, ctx.enclClass.owner.thisType, ctx.enclClass.owner, explanation) + + def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String): AbsTypeError = { def errMsg = { val location = if (sym.isClassConstructor) owner0 else pre.widen.directObjectString @@ -906,7 +923,7 @@ trait ContextErrors { def NotWithinBounds(tree: Tree, prefix: String, targs: List[Type], tparams: List[Symbol], kindErrors: List[String]) = issueNormalTypeError(tree, - NotWithinBoundsErrorMessage(prefix, targs, tparams, settings.explaintypes.value)) + NotWithinBoundsErrorMessage(prefix, targs, tparams, settings.explaintypes)) //substExpr def PolymorphicExpressionInstantiationError(tree: Tree, undetparams: List[Symbol], pt: Type) = @@ -1014,20 +1031,14 @@ trait ContextErrors { 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 - val where = if (currentSym.owner.isPackageClass != prevSym.owner.isPackageClass) { - val inOrOut = if (prevSym.owner.isPackageClass) "outside of" else "in" + val where = if (currentSym.isTopLevel != prevSym.isTopLevel) { + val inOrOut = if (prevSym.isTopLevel) "outside of" else "in" " %s package object %s".format(inOrOut, ""+prevSym.effectiveOwner.name) } else "" issueSymbolTypeError(currentSym, prevSym.name + " is already defined as " + s2 + s3 + where) } - 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") @@ -1239,11 +1250,12 @@ trait ContextErrors { // not exactly an error generator, but very related // and I dearly wanted to push it away from Macros.scala private def checkSubType(slot: String, rtpe: Type, atpe: Type) = { - val ok = if (macroDebugVerbose || settings.explaintypes.value) { - if (rtpe eq atpe) println(rtpe + " <: " + atpe + "?" + EOL + "true") + val ok = if (macroDebugVerbose) { withTypesExplained(rtpe <:< atpe) } else rtpe <:< atpe if (!ok) { + if (!macroDebugVerbose) + explainTypes(rtpe, atpe) compatibilityError("type mismatch for %s: %s does not conform to %s".format(slot, abbreviateCoreAliases(rtpe.toString), abbreviateCoreAliases(atpe.toString))) } } @@ -1330,7 +1342,7 @@ trait ContextErrors { } def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) = - compatibilityError(typer.infer.InferErrorGen.NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value)) + compatibilityError(typer.infer.InferErrorGen.NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes)) def MacroImplTparamInstantiationError(atparams: List[Symbol], ex: NoInstance) = compatibilityError( diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6c2945cad3..538b3b3b6c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -6,9 +6,9 @@ package scala.tools.nsc package typechecker -import symtab.Flags._ -import scala.collection.mutable.{LinkedHashSet, Set} +import scala.collection.{ immutable, mutable } import scala.annotation.tailrec +import scala.reflect.internal.util.shortClassOfInstance /** * @author Martin Odersky @@ -16,25 +16,38 @@ import scala.annotation.tailrec */ trait Contexts { self: Analyzer => import global._ + import definitions.{ JavaLangPackage, ScalaPackage, PredefModule } + import ContextMode._ + + object NoContext + extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit, + outer = null /*We can't pass NoContext here, overriden below*/) { + + override val outer = this - object NoContext extends Context { - outer = this enclClass = this enclMethod = this + override val depth = 0 override def nextEnclosing(p: Context => Boolean): Context = this override def enclosingContextChain: List[Context] = Nil override def implicitss: List[List[ImplicitInfo]] = Nil + override def imports: List[ImportInfo] = Nil + override def firstImport: Option[ImportInfo] = None override def toString = "NoContext" } private object RootImports { - import definitions._ // Possible lists of root imports val javaList = JavaLangPackage :: Nil val javaAndScalaList = JavaLangPackage :: ScalaPackage :: Nil val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil } + def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) = + LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") + def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = + LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") + private lazy val startContext = { NoContext.make( Template(List(), emptyValDef, List()) setSymbol global.NoSymbol setType global.NoType, @@ -42,6 +55,25 @@ trait Contexts { self: Analyzer => rootMirror.RootClass.info.decls) } + private lazy val allUsedSelectors = + mutable.Map[ImportInfo, Set[ImportSelector]]() withDefaultValue Set() + private lazy val allImportInfos = + mutable.Map[CompilationUnit, List[ImportInfo]]() withDefaultValue Nil + + def warnUnusedImports(unit: CompilationUnit) = { + for (imps <- allImportInfos.remove(unit)) { + for (imp <- imps.reverse.distinct) { + val used = allUsedSelectors(imp) + def isMask(s: ImportSelector) = s.name != nme.WILDCARD && s.rename == nme.WILDCARD + + imp.tree.selectors filterNot (s => isMask(s) || used(s)) foreach { sel => + unit.warning(imp posOf sel, "Unused import") + } + } + allUsedSelectors --= imps + } + } + var lastAccessCheckDetails: String = "" /** List of symbols to import from in a root context. Typically that @@ -55,292 +87,356 @@ trait Contexts { self: Analyzer => protected def rootImports(unit: CompilationUnit): List[Symbol] = { assert(definitions.isDefinitionsInitialized, "definitions uninitialized") - if (settings.noimports.value) Nil + if (settings.noimports) Nil else if (unit.isJava) RootImports.javaList - else if (settings.nopredef.value || treeInfo.noPredefImportForUnit(unit.body)) RootImports.javaAndScalaList + else if (settings.nopredef || treeInfo.noPredefImportForUnit(unit.body)) { + debuglog("Omitted import of Predef._ for " + unit) + RootImports.javaAndScalaList + } else RootImports.completeList } - def rootContext(unit: CompilationUnit): Context = rootContext(unit, EmptyTree, false) - def rootContext(unit: CompilationUnit, tree: Tree): Context = rootContext(unit, tree, false) - def rootContext(unit: CompilationUnit, tree: Tree, erasedTypes: Boolean): Context = { - import definitions._ - var sc = startContext - for (sym <- rootImports(unit)) { - sc = sc.makeNewImport(sym) - sc.depth += 1 - } - val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports) + def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = { + val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym))) + val c = rootImportsContext.make(tree, unit = unit) if (erasedTypes) c.setThrowErrors() else c.setReportErrors() - c.implicitsEnabled = !erasedTypes - c.enrichmentEnabled = c.implicitsEnabled + c(EnrichmentEnabled | ImplicitsEnabled) = !erasedTypes c } def resetContexts() { - var sc = startContext - while (sc != NoContext) { - sc.tree match { - case Import(qual, _) => qual.tpe = singleType(qual.symbol.owner.thisType, qual.symbol) - case _ => + startContext.enclosingContextChain foreach { context => + context.tree match { + case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol) + case _ => } - sc.flushAndReturnBuffer() - sc.flushAndReturnWarningsBuffer() - sc = sc.outer + context.reportBuffer.clearAll() } } - 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 - } + /** + * A motley collection of the state and loosely associated behaviour of the type checker. + * Each `Typer` has an associated context, and as it descends into the tree new `(Typer, Context)` + * pairs are spawned. + * + * Meet the crew; first the state: + * + * - A tree, symbol, and scope representing the focus of the typechecker + * - An enclosing context, `outer`. + * - The current compilation unit. + * - A variety of bits that track the current error reporting policy (more on this later); + * whether or not implicits/macros are enabled, whether we are in a self or super call or + * in a constructor suffix. These are represented as bits in the mask `contextMode`. + * - Some odds and ends: undetermined type pararameters of the current line of type inference; + * contextual augmentation for error messages, tracking of the nesting depth. + * + * And behaviour: + * + * - The central point for issuing errors and warnings from the typechecker, with a means + * to buffer these for use in 'silent' type checking, when some recovery might be possible. + * - `Context` is something of a Zipper for the tree were are typechecking: it `enclosingContextChain` + * is the path back to the root. This is exactly what we need to resolve names (`lookupSymbol`) + * and to collect in-scope implicit defintions (`implicitss`) + * Supporting these are `imports`, which represents all `Import` trees in in the enclosing context chain. + * - In a similar vein, we can assess accessiblity (`isAccessible`.) + * + * More on error buffering: + * When are type errors recoverable? In quite a few places, it turns out. Some examples: + * trying to type an application with/without the expected type, or with/without implicit views + * enabled. This is usually mediated by `Typer.silent`, `Inferencer#tryTwice`. + * + * Intially, starting from the `typer` phase, the contexts either buffer or report errors; + * afterwards errors are thrown. This is configured in `rootContext`. Additionally, more + * fine grained control is needed based on the kind of error; ambiguity errors are often + * suppressed during exploraratory typing, such as determining whether `a == b` in an argument + * position is an assignment or a named argument, when `Infererencer#isApplicableSafe` type checks + * applications with and without an expected type, or whtn `Typer#tryTypedApply` tries to fit arguments to + * a function type with/without implicit views. + * + * When the error policies entails error/warning buffering, the mutable [[ReportBuffer]] records + * everything that is issued. It is important to note, that child Contexts created with `make` + * "inherit" the very same `ReportBuffer` instance, whereas children spawned through `makeSilent` + * receive an separate, fresh buffer. + * + * @param tree Tree associated with this context + * @param owner The current owner + * @param scope The current scope + * @param outer The next outer context. + */ + class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope, + val unit: CompilationUnit, val outer: Context) { + /** The next outer context whose tree is a template or package definition */ + var enclClass: Context = _ - 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 - var scope: Scope = _ // The current scope - var outer: Context = _ // The next outer context - var enclClass: Context = _ // The next outer context whose tree is a - // template or package definition - @inline final def savingEnclClass[A](c: Context)(a: => A): A = { + @inline private def savingEnclClass[A](c: Context)(a: => A): A = { val saved = enclClass enclClass = c try a finally enclClass = saved } - var enclMethod: Context = _ // The next outer context whose tree is a method - var variance: Int = _ // Variance relative to enclosing class - private var _undetparams: List[Symbol] = List() // Undetermined type parameters, - // not inherited to child contexts - var depth: Int = 0 - var imports: List[ImportInfo] = List() // currently visible imports - var openImplicits: List[(Type,Tree)] = List() // types for which implicit arguments - // are currently searched - // for a named application block (Tree) the corresponding NamedApplyInfo + /** A bitmask containing all the boolean flags in a context, e.g. are implicit views enabled */ + var contextMode: ContextMode = ContextMode.DefaultMode + + /** Update all modes in `mask` to `value` */ + def update(mask: ContextMode, value: Boolean) { + contextMode = contextMode.set(value, mask) + } + + /** Set all modes in the mask `enable` to true, and all in `disable` to false. */ + def set(enable: ContextMode = NOmode, disable: ContextMode = NOmode): this.type = { + contextMode = contextMode.set(true, enable).set(false, disable) + this + } + + /** Is this context in all modes in the given `mask`? */ + def apply(mask: ContextMode): Boolean = contextMode.inAll(mask) + + /** The next outer context whose tree is a method */ + var enclMethod: Context = _ + + /** Variance relative to enclosing class */ + var variance: Variance = Variance.Invariant + + private var _undetparams: List[Symbol] = List() + + val depth: Int = { + val increasesDepth = isRootImport || (outer eq null) || (outer.scope != scope) + def outerDepth = if (outer eq null) 0 else outer.depth + ( if (increasesDepth) 1 else 0 ) + outerDepth + } + + /** The currently visible imports */ + def imports: List[ImportInfo] = outer.imports + /** Equivalent to `imports.headOption`, but more efficient */ + def firstImport: Option[ImportInfo] = outer.firstImport + def isRootImport: Boolean = false + + /** Types for which implicit arguments are currently searched */ + var openImplicits: List[(Type,Tree)] = List() + + /* For a named application block (`Tree`) the corresponding `NamedApplyInfo`. */ var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None var prefix: Type = NoPrefix - var inConstructorSuffix = false // are we in a secondary constructor - // after the this constructor call? - var returnsSeen = false // for method context: were returns encountered? - var inSelfSuperCall = false // is this context (enclosed in) a constructor call? - // (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 diagnostic: List[String] = Nil // these messages are printed when issuing an error - var implicitsEnabled = false - var macrosEnabled = true - var enrichmentEnabled = false // to selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed - var checking = false - var retyping = false - - var savedTypeBounds: List[(Symbol, Type)] = List() // saved type bounds - // for type parameters which are narrowed in a GADT + def inConstructorSuffix_=(value: Boolean) = this(ConstructorSuffix) = value + def inConstructorSuffix = this(ConstructorSuffix) + def returnsSeen_=(value: Boolean) = this(ReturnsSeen) = value + def returnsSeen = this(ReturnsSeen) + def inSelfSuperCall_=(value: Boolean) = this(SelfSuperCall) = value + def inSelfSuperCall = this(SelfSuperCall) + def implicitsEnabled_=(value: Boolean) = this(ImplicitsEnabled) = value + def implicitsEnabled = this(ImplicitsEnabled) + def macrosEnabled_=(value: Boolean) = this(MacrosEnabled) = value + def macrosEnabled = this(MacrosEnabled) + def enrichmentEnabled_=(value: Boolean) = this(EnrichmentEnabled) = value + def enrichmentEnabled = this(EnrichmentEnabled) + def checking_=(value: Boolean) = this(Checking) = value + def checking = this(Checking) + def retyping_=(value: Boolean) = this(ReTyping) = value + def retyping = this(ReTyping) + + /** These messages are printed when issuing an error */ + var diagnostic: List[String] = Nil + + /** Saved type bounds for type parameters which are narrowed in a GADT. */ + var savedTypeBounds: List[(Symbol, Type)] = List() + + /** Indentation level, in columns, for output under -Ytyper-debug */ var typingIndentLevel: Int = 0 def typingIndent = " " * typingIndentLevel - var buffer: Set[AbsTypeError] = _ - var warningsBuffer: Set[(Position, String)] = _ - + /** The next enclosing context (potentially `this`) that is owned by a class or method */ def enclClassOrMethod: Context = if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this else outer.enclClassOrMethod + /** The next enclosing context (potentially `this`) that has a `CaseDef` as a tree */ + def enclosingCaseDef = nextEnclosing(_.tree.isInstanceOf[CaseDef]) + + /** ...or an Apply. */ + def enclosingApply = nextEnclosing(_.tree.isInstanceOf[Apply]) + + // + // Tracking undetermined type parameters for type argument inference. + // def undetparamsString = if (undetparams.isEmpty) "" else undetparams.mkString("undetparams=", ", ", "") - def undetparams = _undetparams + /** Undetermined type parameters. See `Infer#{inferExprInstance, adjustTypeArgs}`. Not inherited to child contexts */ + def undetparams: List[Symbol] = _undetparams def undetparams_=(ps: List[Symbol]) = { _undetparams = ps } - def extractUndetparams() = { + /** Return and clear the undetermined type parameters */ + def extractUndetparams(): List[Symbol] = { val tparams = undetparams undetparams = List() tparams } - private[this] var mode = 0 - - def errBuffer = buffer - def hasErrors = buffer.nonEmpty - def hasWarnings = warningsBuffer.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 + /** Run `body` with this context with no undetermined type parameters, restore the original + * the original list afterwards. + * @param reportAmbiguous Should ambiguous errors be reported during evaluation of `body`? + */ + def savingUndeterminedTypeParams[A](reportAmbiguous: Boolean = ambiguousErrors)(body: => A): A = { + withMode() { + this(AmbiguousErrors) = reportAmbiguous + val saved = extractUndetparams() + try body + finally undetparams = saved + } } - 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() + // + // Error reporting policies and buffer. + // + + private var _reportBuffer: ReportBuffer = new ReportBuffer + /** A buffer for errors and warnings, used with `this.bufferErrors == true` */ + def reportBuffer = _reportBuffer + /** Discard the current report buffer, and replace with an empty one */ + def useFreshReportBuffer() = _reportBuffer = new ReportBuffer + /** Discard the current report buffer, and replace with `other` */ + def restoreReportBuffer(other: ReportBuffer) = _reportBuffer = other + + /** The first error, if any, in the report buffer */ + def firstError: Option[AbsTypeError] = reportBuffer.firstError + /** Does the report buffer contain any errors? */ + def hasErrors = reportBuffer.hasErrors + + def reportErrors = this(ReportErrors) + def bufferErrors = this(BufferErrors) + def ambiguousErrors = this(AmbiguousErrors) + def throwErrors = contextMode.inNone(ReportErrors | BufferErrors) + + def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors) + def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors) + def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false + def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report + + /** Append the given errors to the report buffer */ + def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors + /** Clear all errors from the report buffer */ + def flushBuffer() { reportBuffer.clearAllErrors() } + /** Return and clear all errors from the report buffer */ + def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = { + val current = reportBuffer.errors + reportBuffer.clearAllErrors() current } - def flushAndReturnWarningsBuffer(): Set[(Position, String)] = { - val current = warningsBuffer.clone() - warningsBuffer.clear() - current + + /** Issue and clear all warnings from the report buffer */ + def flushAndIssueWarnings() { + reportBuffer.warnings foreach { + case (pos, msg) => unit.warning(pos, msg) + } + reportBuffer.clearAllWarnings() } - def logError(err: AbsTypeError) = buffer += err + // + // Temporary mode adjustment + // - def withImplicitsEnabled[T](op: => T): T = { - val saved = implicitsEnabled - implicitsEnabled = true + @inline def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = { + val saved = contextMode + set(enabled, disabled) try op - finally implicitsEnabled = saved + finally contextMode = saved } - def withImplicitsDisabled[T](op: => T): T = { - val saved = implicitsEnabled - implicitsEnabled = false - val savedP = enrichmentEnabled - enrichmentEnabled = false - try op - finally { - implicitsEnabled = saved - enrichmentEnabled = savedP + def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op) + def withImplicitsDisabled[T](op: => T): T = withMode(disabled = ImplicitsEnabled | EnrichmentEnabled)(op) + def withImplicitsDisabledAllowEnrichment[T](op: => T): T = withMode(enabled = EnrichmentEnabled, disabled = ImplicitsEnabled)(op) + def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op) + def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op) + + /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */ + @inline final def inSilentMode(expr: => Boolean): Boolean = { + withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`. + setBufferErrors() + try expr && !hasErrors + finally reportBuffer.clearAll() } } - def withImplicitsDisabledAllowEnrichment[T](op: => T): T = { - val saved = implicitsEnabled - implicitsEnabled = false - val savedP = enrichmentEnabled - enrichmentEnabled = true - try op - finally { - implicitsEnabled = saved - enrichmentEnabled = savedP - } - } + // + // Child Context Creation + // - def withMacrosEnabled[T](op: => T): T = { - val saved = macrosEnabled - macrosEnabled = true - try op - finally macrosEnabled = saved - } - - def withMacrosDisabled[T](op: => T): T = { - val saved = macrosEnabled - macrosEnabled = false - try op - finally macrosEnabled = saved - } - - def make(unit: CompilationUnit, tree: Tree, owner: Symbol, - scope: Scope, imports: List[ImportInfo]): Context = { - val c = new Context - c.unit = unit - c.tree = tree - c.owner = owner - c.scope = scope - c.outer = this - - tree match { - case Template(_, _, _) | PackageDef(_, _) => - c.enclClass = c - c.prefix = c.owner.thisType - c.inConstructorSuffix = false - case _ => - c.enclClass = this.enclClass - c.prefix = - if (c.owner != this.owner && c.owner.isTerm) NoPrefix - else this.prefix - c.inConstructorSuffix = this.inConstructorSuffix + /** + * Construct a child context. The parent and child will share the report buffer. + * Compare with `makeSilent`, in which the child has a fresh report buffer. + * + * If `tree` is an `Import`, that import will be avaiable at the head of + * `Context#imports`. + */ + def make(tree: Tree = tree, owner: Symbol = owner, + scope: Scope = scope, unit: CompilationUnit = unit): Context = { + val isTemplateOrPackage = tree match { + case _: Template | _: PackageDef => true + case _ => false } - tree match { - case DefDef(_, _, _, _, _, _) => - c.enclMethod = c - case _ => - c.enclMethod = this.enclMethod + val isDefDef = tree match { + case _: DefDef => true + case _ => false } - c.variance = this.variance - c.depth = if (scope == this.scope) this.depth else this.depth + 1 - c.imports = imports - c.inSelfSuperCall = inSelfSuperCall - c.restoreState(this.state) - c.diagnostic = this.diagnostic - c.typingIndentLevel = typingIndentLevel - c.implicitsEnabled = this.implicitsEnabled - c.macrosEnabled = this.macrosEnabled - c.enrichmentEnabled = this.enrichmentEnabled - c.checking = this.checking - c.retyping = this.retyping - c.openImplicits = this.openImplicits - c.buffer = if (this.buffer == null) LinkedHashSet[AbsTypeError]() else this.buffer // need to initialize - c.warningsBuffer = if (this.warningsBuffer == null) LinkedHashSet[(Position, String)]() else this.warningsBuffer + val isImport = tree match { + case _: Import => true + case _ => false + } + val sameOwner = owner == this.owner + val prefixInChild = + if (isTemplateOrPackage) owner.thisType + else if (!sameOwner && owner.isTerm) NoPrefix + else prefix + + // The blank canvas + val c = if (isImport) + new Context(tree, owner, scope, unit, this) with ImportContext + else + new Context(tree, owner, scope, unit, this) + + // Fields that are directly propagated + c.variance = variance + c.diagnostic = diagnostic + c.typingIndentLevel = typingIndentLevel + c.openImplicits = openImplicits + c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. + c._reportBuffer = reportBuffer + + // Fields that may take on a different value in the child + c.prefix = prefixInChild + c.enclClass = if (isTemplateOrPackage) c else enclClass + c(ConstructorSuffix) = !isTemplateOrPackage && c(ConstructorSuffix) + c.enclMethod = if (isDefDef) c else enclMethod + registerContext(c.asInstanceOf[analyzer.Context]) debuglog("[context] ++ " + c.unit + " / " + tree.summaryString) c } - // TODO: remove? Doesn't seem to be used - def make(unit: CompilationUnit): Context = { - val c = make(unit, EmptyTree, owner, scope, imports) - c.setReportErrors() - c.implicitsEnabled = true - c.macrosEnabled = true - c - } - - def makeNewImport(sym: Symbol): Context = - makeNewImport(gen.mkWildcardImport(sym)) - - def makeNewImport(imp: Import): Context = - make(unit, imp, owner, scope, new ImportInfo(imp, depth) :: imports) - def make(tree: Tree, owner: Symbol, scope: Scope): Context = + // TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail. + // even if it is extened to check that `unit == this.unit`. Why is this? if (tree == this.tree && owner == this.owner && scope == this.scope) this - else make0(tree, owner, scope) - - private def make0(tree: Tree, owner: Symbol, scope: Scope): Context = - make(unit, tree, owner, scope, imports) + else make(tree, owner, scope, unit) + /** Make a child context that represents a new nested scope */ def makeNewScope(tree: Tree, owner: Symbol): Context = make(tree, owner, newNestedScope(scope)) - // IDE stuff: distinguish between scopes created for typing and scopes created for naming. - - def make(tree: Tree, owner: Symbol): Context = - make0(tree, owner, scope) - def make(tree: Tree): Context = - make(tree, owner) - - def makeSilent(reportAmbiguousErrors: Boolean, newtree: Tree = tree): Context = { + /** Make a child context that buffers errors and warnings into a fresh report buffer. */ + def makeSilent(reportAmbiguousErrors: Boolean = ambiguousErrors, newtree: Tree = tree): Context = { val c = make(newtree) c.setBufferErrors() c.setAmbiguousErrors(reportAmbiguousErrors) - c.buffer = new LinkedHashSet[AbsTypeError]() + c._reportBuffer = new ReportBuffer // A fresh buffer so as not to leak errors/warnings into `this`. c } + /** Make a silent child context does not allow implicits. Used to prevent chaining of implicit views. */ def makeImplicit(reportAmbiguousErrors: Boolean) = { val c = makeSilent(reportAmbiguousErrors) - c.implicitsEnabled = false - c.enrichmentEnabled = false + c(ImplicitsEnabled | EnrichmentEnabled) = false c } @@ -355,12 +451,10 @@ trait Contexts { self: Analyzer => * accessible. */ def makeConstructorContext = { - var baseContext = enclClass.outer - while (baseContext.tree.isInstanceOf[Template]) - baseContext = baseContext.outer + val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template]) val argContext = baseContext.makeNewScope(tree, owner) + argContext.contextMode = contextMode argContext.inSelfSuperCall = true - argContext.restoreState(this.state) def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { if (e != null && e.owner == c.scope) { @@ -368,7 +462,7 @@ trait Contexts { self: Analyzer => argContext.scope enter e.sym } } - if (c.owner.isTerm && !c.owner.isLocalDummy) { + if (c.isLocal && !c.owner.isLocalDummy) { enterElems(c.outer) enterLocalElems(c.scope.elems) } @@ -379,6 +473,10 @@ trait Contexts { self: Analyzer => argContext } + // + // Error and warning issuance + // + private def addDiagString(msg: String) = { val ds = if (diagnostic.isEmpty) "" @@ -390,19 +488,23 @@ trait Contexts { self: Analyzer => unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) { - debugwarn("issue error: " + err.errMsg) - if (settings.Yissuedebug.value) (new Exception).printStackTrace() + if (settings.Yissuedebug) { + log("issue error: " + err.errMsg) + (new Exception).printStackTrace() + } if (pf isDefinedAt err) pf(err) - else if (bufferErrors) { buffer += err } + else if (bufferErrors) { reportBuffer += err } else throw new TypeError(err.errPos, err.errMsg) } + /** Issue/buffer/throw the given type error according to the current mode for error reporting. */ def issue(err: AbsTypeError) { issueCommon(err) { case _ if reportErrors => unitError(err.errPos, addDiagString(err.errMsg)) } } + /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { issueCommon(err) { case _ if ambiguousErrors => if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) @@ -410,44 +512,31 @@ trait Contexts { self: Analyzer => } } + /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ def issueAmbiguousError(err: AbsTypeError) { issueCommon(err) { case _ if ambiguousErrors => unitError(err.errPos, addDiagString(err.errMsg)) } } - // TODO remove + /** Issue/throw the given `err` according to the current mode for error reporting. */ def error(pos: Position, err: Throwable) = if (reportErrors) unitError(pos, addDiagString(err.getMessage())) else throw err + /** Issue/throw the given error message according to the current mode for error reporting. */ def error(pos: Position, msg: String) = { val msg1 = addDiagString(msg) if (reportErrors) unitError(pos, msg1) else throw new TypeError(pos, msg1) } - def warning(pos: Position, msg: String): Unit = warning(pos, msg, false) - def warning(pos: Position, msg: String, force: Boolean) { + /** Issue/throw the given error message according to the current mode for error reporting. */ + def warning(pos: Position, msg: String, force: Boolean = false) { if (reportErrors || force) unit.warning(pos, msg) - else if (bufferErrors) warningsBuffer += ((pos, msg)) + else if (bufferErrors) reportBuffer += (pos -> msg) } - def isLocal(): Boolean = tree match { - case Block(_,_) => true - case PackageDef(_, _) => false - case EmptyTree => false - case _ => outer.isLocal() - } - - /** Fast path for some slow checks (ambiguous assignment in Refchecks, and - * existence of __match for MatchTranslation in virtpatmat.) This logic probably - * needs improvement. - */ - def isNameInScope(name: Name) = ( - enclosingContextChain exists (ctx => - (ctx.scope.lookupEntry(name) != null) - || (ctx.owner.rawInfo.member(name) != NoSymbol) - ) - ) + /** Is the owning symbol of this context a term? */ + final def isLocal: Boolean = owner.isTerm // nextOuter determines which context is searched next for implicits // (after `this`, which contributes `newImplicits` below.) In @@ -473,26 +562,35 @@ trait Contexts { self: Analyzer => def enclosingContextChain: List[Context] = this :: outer.enclosingContextChain - override def toString = "Context(%s@%s unit=%s scope=%s errors=%b, reportErrors=%b, throwErrors=%b)".format( - owner.fullName, tree.shortClass, unit, scope.##, hasErrors, reportErrors, throwErrors - ) - /** Is `sub` a subclass of `base` or a companion object of such a subclass? - */ - def isSubClassOrCompanion(sub: Symbol, base: Symbol) = - sub.isNonBottomSubClass(base) || - sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base) - - /** Return closest enclosing context that defines a superclass of `clazz`, or a - * companion module of a superclass of `clazz`, or NoContext if none exists */ - def enclosingSuperClassContext(clazz: Symbol): Context = { - var c = this.enclClass - while (c != NoContext && - !clazz.isNonBottomSubClass(c.owner) && - !(c.owner.isModuleClass && clazz.isNonBottomSubClass(c.owner.companionClass))) - c = c.outer.enclClass - c + private def treeTruncated = tree.toString.replaceAll("\\s+", " ").lines.mkString("\\n").take(70) + private def treeIdString = if (settings.uniqid.value) "#" + System.identityHashCode(tree).toString.takeRight(3) else "" + private def treeString = tree match { + case x: Import => "" + x + case Template(parents, `emptyValDef`, body) => + val pstr = if ((parents eq null) || parents.isEmpty) "Nil" else parents mkString " " + val bstr = if (body eq null) "" else body.length + " stats" + s"""Template($pstr, _, $bstr)""" + case x => s"${tree.shortClass}${treeIdString}:${treeTruncated}" } + override def toString = + sm"""|Context($unit) { + | owner = $owner + | tree = $treeString + | scope = ${scope.size} decls + | contextMode = $contextMode + | outer.owner = ${outer.owner} + |}""" + + // + // Accessibility checking + // + + /** Is `sub` a subclass of `base` or a companion object of such a subclass? */ + private def isSubClassOrCompanion(sub: Symbol, base: Symbol) = + sub.isNonBottomSubClass(base) || + sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base) + /** Return the closest enclosing context that defines a subclass of `clazz` * or a companion object thereof, or `NoContext` if no such context exists. */ @@ -503,9 +601,7 @@ trait Contexts { self: Analyzer => c } - /** Is `sym` accessible as a member of tree `site` with type - * `pre` in current context? - */ + /** Is `sym` accessible as a member of `pre` in current context? */ def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean = false): Boolean = { lastAccessCheckDetails = "" // Console.println("isAccessible(%s, %s, %s)".format(sym, pre, superAccess)) @@ -518,7 +614,7 @@ trait Contexts { self: Analyzer => (linked ne NoSymbol) && accessWithin(linked) } - /** Are we inside definition of `ab`? */ + /* Are we inside definition of `ab`? */ def accessWithin(ab: Symbol) = { // #3663: we must disregard package nesting if sym isJavaDefined if (sym.isJavaDefined) { @@ -530,26 +626,12 @@ trait Contexts { self: Analyzer => } else (owner hasTransOwner ab) } -/* - var c = this - while (c != NoContext && c.owner != owner) { - if (c.outer eq null) abort("accessWithin(" + owner + ") " + c);//debug - if (c.outer.enclClass eq null) abort("accessWithin(" + owner + ") " + c);//debug - c = c.outer.enclClass - } - c != NoContext - } -*/ - /** Is `clazz` a subclass of an enclosing class? */ - def isSubClassOfEnclosing(clazz: Symbol): Boolean = - enclosingSuperClassContext(clazz) != NoContext - def isSubThisType(pre: Type, clazz: Symbol): Boolean = pre match { case ThisType(pclazz) => pclazz isNonBottomSubClass clazz case _ => false } - /** Is protected access to target symbol permitted */ + /* Is protected access to target symbol permitted */ def isProtectedAccessOK(target: Symbol) = { val c = enclosingSubClassContext(sym.owner) if (c == NoContext) @@ -589,8 +671,7 @@ trait Contexts { self: Analyzer => ( superAccess || pre.isInstanceOf[ThisType] || phase.erasedTypes - || isProtectedAccessOK(sym) - || (sym.allOverriddenSymbols exists isProtectedAccessOK) + || (sym.overrideChain exists isProtectedAccessOK) // that last condition makes protected access via self types work. ) ) @@ -600,26 +681,50 @@ trait Contexts { self: Analyzer => } } + // + // Type bound management + // + def pushTypeBounds(sym: Symbol) { + sym.info match { + case tb: TypeBounds => if (!tb.isEmptyBounds) log(s"Saving $sym info=$tb") + case info => devWarning(s"Something other than a TypeBounds seen in pushTypeBounds: $info is a ${shortClassOfInstance(info)}") + } savedTypeBounds ::= ((sym, sym.info)) } def restoreTypeBounds(tp: Type): Type = { - var current = tp - for ((sym, info) <- savedTypeBounds) { - debuglog("resetting " + sym + " to " + info); - sym.info match { - case TypeBounds(lo, hi) if (hi <:< lo && lo <:< hi) => - current = current.instantiateTypeParams(List(sym), List(lo)) -//@M TODO: when higher-kinded types are inferred, probably need a case PolyType(_, TypeBounds(...)) if ... => - case _ => - } - sym.setInfo(info) + def restore(): Type = savedTypeBounds.foldLeft(tp) { case (current, (sym, savedInfo)) => + def bounds_s(tb: TypeBounds) = if (tb.isEmptyBounds) "<empty bounds>" else s"TypeBounds(lo=${tb.lo}, hi=${tb.hi})" + //@M TODO: when higher-kinded types are inferred, probably need a case PolyType(_, TypeBounds(...)) if ... => + val TypeBounds(lo, hi) = sym.info.bounds + val isUnique = lo <:< hi && hi <:< lo + val isPresent = current contains sym + def saved_s = bounds_s(savedInfo.bounds) + def current_s = bounds_s(sym.info.bounds) + + if (isUnique && isPresent) + devWarningResult(s"Preserving inference: ${sym.nameString}=$hi in $current (based on $current_s) before restoring $sym to saved $saved_s")( + current.instantiateTypeParams(List(sym), List(hi)) + ) + else if (isPresent) + devWarningResult(s"Discarding inferred $current_s because it does not uniquely determine $sym in")(current) + else + logResult(s"Discarding inferred $current_s because $sym does not appear in")(current) + } + try restore() + finally { + for ((sym, savedInfo) <- savedTypeBounds) + sym setInfo debuglogResult(s"Discarding inferred $sym=${sym.info}, restoring saved info")(savedInfo) + + savedTypeBounds = Nil } - savedTypeBounds = List() - current } + // + // Implicit collection + // + private var implicitsCache: List[List[ImplicitInfo]] = null private var implicitsRunId = NoRunId @@ -662,7 +767,7 @@ trait Contexts { self: Analyzer => case ImportSelector(from, _, to, _) :: sels1 => var impls = collect(sels1) filter (info => info.name != from) if (to != nme.WILDCARD) { - for (sym <- imp.importedSymbol(to).alternatives) + for (sym <- importedAccessibleSymbol(imp, to).alternatives) if (isQualifyingImplicit(to, sym, pre, imported = true)) impls = new ImplicitInfo(to, pre, sym) :: impls } @@ -679,6 +784,8 @@ trait Contexts { self: Analyzer => * filtered out later by `eligibleInfos` (SI-4270 / 9129cfe9), as they don't type-check. */ def implicitss: List[List[ImplicitInfo]] = { + val imports = this.imports + val nextOuter = this.nextOuter if (implicitsRunId != currentRunId) { implicitsRunId = currentRunId implicitsCache = List() @@ -695,8 +802,8 @@ trait Contexts { self: Analyzer => } else if (scope != nextOuter.scope && !owner.isPackageClass) { debuglog("collect local implicits " + scope.toList)//DEBUG collectImplicits(scope, NoPrefix) - } else if (imports != nextOuter.imports) { - assert(imports.tail == nextOuter.imports, (imports, nextOuter.imports)) + } else if (firstImport != nextOuter.firstImport) { + assert(imports.tail.headOption == nextOuter.firstImport, (imports, nextOuter.imports)) collectImplicitImports(imports.head) } else if (owner.isPackageClass) { // the corresponding package object may contain implicit members. @@ -708,6 +815,304 @@ trait Contexts { self: Analyzer => implicitsCache } + // + // Imports and symbol lookup + // + + /** It's possible that seemingly conflicting identifiers are + * identifiably the same after type normalization. In such cases, + * allow compilation to proceed. A typical example is: + * package object foo { type InputStream = java.io.InputStream } + * import foo._, java.io._ + */ + private def resolveAmbiguousImport(name: Name, imp1: ImportInfo, imp2: ImportInfo): Option[ImportInfo] = { + val imp1Explicit = imp1 isExplicitImport name + val imp2Explicit = imp2 isExplicitImport name + val ambiguous = if (imp1.depth == imp2.depth) imp1Explicit == imp2Explicit else !imp1Explicit && imp2Explicit + val imp1Symbol = (imp1 importedSymbol name).initialize filter (s => isAccessible(s, imp1.qual.tpe, superAccess = false)) + val imp2Symbol = (imp2 importedSymbol name).initialize filter (s => isAccessible(s, imp2.qual.tpe, superAccess = false)) + + // The types of the qualifiers from which the ambiguous imports come. + // If the ambiguous name is a value, these must be the same. + def t1 = imp1.qual.tpe + def t2 = imp2.qual.tpe + // The types of the ambiguous symbols, seen as members of their qualifiers. + // If the ambiguous name is a monomorphic type, we can relax this far. + def mt1 = t1 memberType imp1Symbol + def mt2 = t2 memberType imp2Symbol + + def characterize = List( + s"types: $t1 =:= $t2 ${t1 =:= t2} members: ${mt1 =:= mt2}", + s"member type 1: $mt1", + s"member type 2: $mt2" + ).mkString("\n ") + + if (!ambiguous || !imp2Symbol.exists) Some(imp1) + else if (!imp1Symbol.exists) Some(imp2) + else ( + // The symbol names are checked rather than the symbols themselves because + // each time an overloaded member is looked up it receives a new symbol. + // So foo.member("x") != foo.member("x") if x is overloaded. This seems + // likely to be the cause of other bugs too... + if (t1 =:= t2 && imp1Symbol.name == imp2Symbol.name) { + log(s"Suppressing ambiguous import: $t1 =:= $t2 && $imp1Symbol == $imp2Symbol") + Some(imp1) + } + // Monomorphism restriction on types is in part because type aliases could have the + // same target type but attach different variance to the parameters. Maybe it can be + // relaxed, but doesn't seem worth it at present. + else if (mt1 =:= mt2 && name.isTypeName && imp1Symbol.isMonomorphicType && imp2Symbol.isMonomorphicType) { + log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $imp1Symbol and $imp2Symbol are equivalent") + Some(imp1) + } + else { + log(s"Import is genuinely ambiguous:\n " + characterize) + None + } + ) + } + + /** The symbol with name `name` imported via the import in `imp`, + * if any such symbol is accessible from this context. + */ + def importedAccessibleSymbol(imp: ImportInfo, name: Name): Symbol = + importedAccessibleSymbol(imp, name, requireExplicit = false) + + private def importedAccessibleSymbol(imp: ImportInfo, name: Name, requireExplicit: Boolean): Symbol = + imp.importedSymbol(name, requireExplicit) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false)) + + /** Is `sym` defined in package object of package `pkg`? + * Since sym may be defined in some parent of the package object, + * we cannot inspect its owner only; we have to go through the + * info of the package object. However to avoid cycles we'll check + * what other ways we can before pushing that way. + */ + def isInPackageObject(sym: Symbol, pkg: Symbol): Boolean = { + def uninitialized(what: String) = { + log(s"Cannot look for $sym in package object of $pkg; $what is not initialized.") + false + } + def pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg + def matchesInfo = ( + // need to be careful here to not get a cyclic reference during bootstrap + if (pkg.isInitialized) { + val module = pkg.info member nme.PACKAGEkw + if (module.isInitialized) + module.info.member(sym.name).alternatives contains sym + else + uninitialized("" + module) + } + else uninitialized("" + pkg) + ) + def inPackageObject(sym: Symbol) = ( + // To be in the package object, one of these must be true: + // 1) sym.owner is a package object class, and sym.owner.owner is the package class for `pkg` + // 2) sym.owner is inherited by the correct package object class + // We try to establish 1) by inspecting the owners directly, and then we try + // to rule out 2), and only if both those fail do we resort to looking in the info. + !sym.isPackage && (sym.owner ne NoSymbol) && ( + if (sym.owner.isPackageObjectClass) + sym.owner.owner == pkgClass + else + !sym.owner.isPackageClass && matchesInfo + ) + ) + + // An overloaded symbol might not have the expected owner! + // The alternatives must be inspected directly. + pkgClass.isPackageClass && ( + if (sym.isOverloaded) + sym.alternatives forall (isInPackageObject(_, pkg)) + else + inPackageObject(sym) + ) + } + + def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess + + /** Find the symbol of a simple name starting from this context. + * All names are filtered through the "qualifies" predicate, + * the search continuing as long as no qualifying name is found. + */ + def lookupSymbol(name: Name, qualifies: Symbol => Boolean): NameLookup = { + var lookupError: NameLookup = null // set to non-null if a definite error is encountered + var inaccessible: NameLookup = null // records inaccessible symbol for error reporting in case none is found + var defSym: Symbol = NoSymbol // the directly found symbol + var pre: Type = NoPrefix // the prefix type of defSym, if a class member + var cx: Context = this // the context under consideration + var symbolDepth: Int = -1 // the depth of the directly found symbol + + def finish(qual: Tree, sym: Symbol): NameLookup = ( + if (lookupError ne null) lookupError + else sym match { + case NoSymbol if inaccessible ne null => inaccessible + case NoSymbol => LookupNotFound + case _ => LookupSucceeded(qual, sym) + } + ) + def finishDefSym(sym: Symbol, pre0: Type): NameLookup = + if (requiresQualifier(sym)) + finish(gen.mkAttributedQualifier(pre0), sym) + else + finish(EmptyTree, sym) + + def isPackageOwnedInDifferentUnit(s: Symbol) = ( + s.isDefinedInPackage && ( + !currentRun.compiles(s) + || unit.exists && s.sourceFile != unit.source.file + ) + ) + def requiresQualifier(s: Symbol) = ( + s.owner.isClass + && !s.owner.isPackageClass + && !s.isTypeParameterOrSkolem + ) + def lookupInPrefix(name: Name) = pre member name filter qualifies + def accessibleInPrefix(s: Symbol) = isAccessible(s, pre, superAccess = false) + + def searchPrefix = { + cx = cx.enclClass + val found0 = lookupInPrefix(name) + val found1 = found0 filter accessibleInPrefix + if (found0.exists && !found1.exists && inaccessible == null) + inaccessible = LookupInaccessible(found0, analyzer.lastAccessCheckDetails) + + found1 + } + + def lookupInScope(scope: Scope) = + (scope lookupUnshadowedEntries name filter (e => qualifies(e.sym))).toList + + def newOverloaded(owner: Symbol, pre: Type, entries: List[ScopeEntry]) = + logResult(s"!!! lookup overloaded")(owner.newOverloaded(pre, entries map (_.sym))) + + // Constructor lookup should only look in the decls of the enclosing class + // not in the self-type, nor in the enclosing context, nor in imports (SI-4460, SI-6745) + if (name == nme.CONSTRUCTOR) return { + val enclClassSym = cx.enclClass.owner + val scope = cx.enclClass.prefix.baseType(enclClassSym).decls + val constructorSym = lookupInScope(scope) match { + case Nil => NoSymbol + case hd :: Nil => hd.sym + case entries => newOverloaded(enclClassSym, cx.enclClass.prefix, entries) + } + finishDefSym(constructorSym, cx.enclClass.prefix) + } + + // cx.scope eq null arises during FixInvalidSyms in Duplicators + while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) { + pre = cx.enclClass.prefix + defSym = lookupInScope(cx.scope) match { + case Nil => searchPrefix + case entries @ (hd :: tl) => + // we have a winner: record the symbol depth + symbolDepth = (cx.depth - cx.scope.nestingLevel) + hd.depth + if (tl.isEmpty) hd.sym + else newOverloaded(cx.owner, pre, entries) + } + if (!defSym.exists) + cx = cx.outer // push further outward + } + if (symbolDepth < 0) + symbolDepth = cx.depth + + var impSym: Symbol = NoSymbol + var imports = Context.this.imports + def imp1 = imports.head + def imp2 = imports.tail.head + def sameDepth = imp1.depth == imp2.depth + def imp1Explicit = imp1 isExplicitImport name + def imp2Explicit = imp2 isExplicitImport name + + def lookupImport(imp: ImportInfo, requireExplicit: Boolean) = + importedAccessibleSymbol(imp, name, requireExplicit) filter qualifies + + // Java: A single-type-import declaration d in a compilation unit c of package p + // that imports a type named n shadows, throughout c, the declarations of: + // + // 1) any top level type named n declared in another compilation unit of p + // + // A type-import-on-demand declaration never causes any other declaration to be shadowed. + // + // Scala: Bindings of different kinds have a precedence defined on them: + // + // 1) Definitions and declarations that are local, inherited, or made available by a + // package clause in the same compilation unit where the definition occurs have + // highest precedence. + // 2) Explicit imports have next highest precedence. + def depthOk(imp: ImportInfo) = ( + imp.depth > symbolDepth + || (unit.isJava && imp.isExplicitImport(name) && imp.depth == symbolDepth) + ) + + while (!impSym.exists && imports.nonEmpty && depthOk(imports.head)) { + impSym = lookupImport(imp1, requireExplicit = false) + if (!impSym.exists) + imports = imports.tail + } + + if (defSym.exists && impSym.exists) { + // imported symbols take precedence over package-owned symbols in different compilation units. + if (isPackageOwnedInDifferentUnit(defSym)) + defSym = NoSymbol + // Defined symbols take precedence over erroneous imports. + else if (impSym.isError || impSym.name == nme.CONSTRUCTOR) + impSym = NoSymbol + // Otherwise they are irreconcilably ambiguous + else + return ambiguousDefnAndImport(defSym.owner, imp1) + } + + // At this point only one or the other of defSym and impSym might be set. + if (defSym.exists) + finishDefSym(defSym, pre) + else if (impSym.exists) { + // We continue walking down the imports as long as the tail is non-empty, which gives us: + // imports == imp1 :: imp2 :: _ + // And at least one of the following is true: + // - imp1 and imp2 are at the same depth + // - imp1 is a wildcard import, so all explicit imports from outer scopes must be checked + def keepLooking = ( + lookupError == null + && imports.tail.nonEmpty + && (sameDepth || !imp1Explicit) + ) + // If we find a competitor imp2 which imports the same name, possible outcomes are: + // + // - same depth, imp1 wild, imp2 explicit: imp2 wins, drop imp1 + // - same depth, imp1 wild, imp2 wild: ambiguity check + // - same depth, imp1 explicit, imp2 explicit: ambiguity check + // - differing depth, imp1 wild, imp2 explicit: ambiguity check + // - all others: imp1 wins, drop imp2 + // + // The ambiguity check is: if we can verify that both imports refer to the same + // symbol (e.g. import foo.X followed by import foo._) then we discard imp2 + // and proceed. If we cannot, issue an ambiguity error. + while (keepLooking) { + // If not at the same depth, limit the lookup to explicit imports. + // This is desirable from a performance standpoint (compare to + // filtering after the fact) but also necessary to keep the unused + // import check from being misled by symbol lookups which are not + // actually used. + val other = lookupImport(imp2, requireExplicit = !sameDepth) + def imp1wins() = { imports = imp1 :: imports.tail.tail } + def imp2wins() = { impSym = other ; imports = imports.tail } + + if (!other.exists) // imp1 wins; drop imp2 and continue. + imp1wins() + else if (sameDepth && !imp1Explicit && imp2Explicit) // imp2 wins; drop imp1 and continue. + imp2wins() + else resolveAmbiguousImport(name, imp1, imp2) match { + case Some(imp) => if (imp eq imp1) imp1wins() else imp2wins() + case _ => lookupError = ambiguousImports(imp1, imp2) + } + } + // optimization: don't write out package prefixes + finish(resetPos(imp1.qual.duplicate), impSym) + } + else finish(EmptyTree, NoSymbol) + } + /** * Find a symbol in this context or one of its outers. * @@ -731,39 +1136,148 @@ trait Contexts { self: Analyzer => } } //class Context + /** A `Context` focussed on an `Import` tree */ + trait ImportContext extends Context { + private def makeImpInfo = { + val info = new ImportInfo(tree.asInstanceOf[Import], outer.depth) + if (settings.lint && !info.isRootImport) // excludes java.lang/scala/Predef imports + allImportInfos(unit) ::= info + info + } + + private var _impInfo: ImportInfo = null // hand rolled lazy val, we don't need/want synchronization. + private def impInfo: ImportInfo = { + if (_impInfo eq null) _impInfo = makeImpInfo + _impInfo + } + + override final def imports = impInfo :: super.imports + override final def firstImport = Some(impInfo) + override final def isRootImport = impInfo.isRootImport + override final def toString = s"ImportContext { $impInfo; outer.owner = ${outer.owner} }" + } + + /** A buffer for warnings and errors that are accumulated during speculative type checking. */ + final class ReportBuffer { + type Error = AbsTypeError + type Warning = (Position, String) + + private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results. + + // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This + // is replicated here out of conservatism. + private var _errorBuffer: mutable.LinkedHashSet[Error] = _ + private def errorBuffer = {if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer} + def errors: immutable.Seq[Error] = errorBuffer.toVector + + private var _warningBuffer: mutable.LinkedHashSet[Warning] = _ + private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer} + def warnings: immutable.Seq[Warning] = warningBuffer.toVector + + def +=(error: AbsTypeError): this.type = { + errorBuffer += error + this + } + def ++=(errors: Traversable[AbsTypeError]): this.type = { + errorBuffer ++= errors + this + } + def +=(warning: Warning): this.type = { + warningBuffer += warning + this + } + + def clearAll(): this.type = { + clearAllErrors(); clearAllWarnings(); + } + + def clearAllErrors(): this.type = { + errorBuffer.clear() + this + } + def clearErrors(kind: ErrorKinds.ErrorKind): this.type = { + errorBuffer.retain(_.kind != kind) + this + } + def retainErrors(kind: ErrorKinds.ErrorKind): this.type = { + errorBuffer.retain(_.kind == kind) + this + } + def clearAllWarnings(): this.type = { + warningBuffer.clear() + this + } + + def hasErrors = errorBuffer.nonEmpty + def firstError = errorBuffer.headOption + } + class ImportInfo(val tree: Import, val depth: Int) { + def pos = tree.pos + def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos + /** The prefix expression */ def qual: Tree = tree.symbol.info match { case ImportType(expr) => expr - case ErrorType => tree setType NoType // fix for #2870 - case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug + case ErrorType => tree setType NoType // fix for #2870 + case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug } /** Is name imported explicitly, not via wildcard? */ def isExplicitImport(name: Name): Boolean = tree.selectors exists (_.rename == name.toTermName) + final def isRootImport: Boolean = !tree.pos.isDefined + /** The symbol with name `name` imported from import clause `tree`. */ - def importedSymbol(name: Name): Symbol = { + def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false) + + private def recordUsage(sel: ImportSelector, result: Symbol) { + def posstr = pos.source.file.name + ":" + posOf(sel).safeLine + def resstr = if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)" else s"(expr=${tree.expr}, ${result.fullLocationString})" + debuglog(s"In $this at $posstr, selector '${selectorString(sel)}' resolved to $resstr") + allUsedSelectors(this) += sel + } + + /** If requireExplicit is true, wildcard imports are not considered. */ + def importedSymbol(name: Name, requireExplicit: Boolean): Symbol = { var result: Symbol = NoSymbol var renamed = false var selectors = tree.selectors - while (selectors != Nil && result == NoSymbol) { - if (selectors.head.rename == name.toTermName) + def current = selectors.head + while (selectors.nonEmpty && result == NoSymbol) { + if (current.rename == name.toTermName) result = qual.tpe.nonLocalMember( // new to address #2733: consider only non-local members for imports - if (name.isTypeName) selectors.head.name.toTypeName else selectors.head.name) - else if (selectors.head.name == name.toTermName) + if (name.isTypeName) current.name.toTypeName else current.name) + else if (current.name == name.toTermName) renamed = true - else if (selectors.head.name == nme.WILDCARD && !renamed) + else if (current.name == nme.WILDCARD && !renamed && !requireExplicit) result = qual.tpe.nonLocalMember(name) - selectors = selectors.tail + + if (result == NoSymbol) + selectors = selectors.tail } - result + if (settings.lint && selectors.nonEmpty && result != NoSymbol && pos != NoPosition) + recordUsage(current, result) + + // Harden against the fallout from bugs like SI-6745 + // + // [JZ] I considered issuing a devWarning and moving the + // check inside the above loop, as I believe that + // this always represents a mistake on the part of + // the caller. + if (definitions isImportable result) result + else NoSymbol + } + private def selectorString(s: ImportSelector): String = { + if (s.name == nme.WILDCARD && s.rename == null) "_" + else if (s.name == s.rename) "" + s.name + else s.name + " => " + s.rename } def allImportedSymbols: Iterable[Symbol] = - qual.tpe.members flatMap (transformImport(tree.selectors, _)) + importableMembers(qual.tpe) flatMap (transformImport(tree.selectors, _)) private def transformImport(selectors: List[ImportSelector], sym: Symbol): List[Symbol] = selectors match { case List() => List() @@ -774,10 +1288,87 @@ trait Contexts { self: Analyzer => case _ :: rest => transformImport(rest, sym) } - override def toString() = tree.toString() + override def hashCode = tree.## + override def equals(other: Any) = other match { + case that: ImportInfo => (tree == that.tree) + case _ => false + } + override def toString = tree.toString } case class ImportType(expr: Tree) extends Type { override def safeToString = "ImportType("+expr+")" } } + +object ContextMode { + private implicit def liftIntBitsToContextState(bits: Int): ContextMode = apply(bits) + def apply(bits: Int): ContextMode = new ContextMode(bits) + final val NOmode: ContextMode = 0 + + final val ReportErrors: ContextMode = 1 << 0 + final val BufferErrors: ContextMode = 1 << 1 + final val AmbiguousErrors: ContextMode = 1 << 2 + + /** Are we in a secondary constructor after the this constructor call? */ + final val ConstructorSuffix: ContextMode = 1 << 3 + + /** For method context: were returns encountered? */ + final val ReturnsSeen: ContextMode = 1 << 4 + + /** Is this context (enclosed in) a constructor call? + * (the call to the super or self constructor in the first line of a constructor.) + * In such a context, the object's fields should not be in scope + */ + final val SelfSuperCall: ContextMode = 1 << 5 + + // TODO harvest documentation for this + final val ImplicitsEnabled: ContextMode = 1 << 6 + + final val MacrosEnabled: ContextMode = 1 << 7 + + /** To selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed */ + final val EnrichmentEnabled: ContextMode = 1 << 8 + + /** Are we in a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ + final val Checking: ContextMode = 1 << 9 + + /** Are we retypechecking arguments independently from the function applied to them? See `Typer.tryTypedApply` */ + // TODO This seems to directly overlap with Mode.SNDTRYmode + final val ReTyping: ContextMode = 1 << 10 + + final val DefaultMode: ContextMode = MacrosEnabled + + private val contextModeNameMap = Map( + ReportErrors -> "ReportErrors", + BufferErrors -> "BufferErrors", + AmbiguousErrors -> "AmbiguousErrors", + ConstructorSuffix -> "ConstructorSuffix", + SelfSuperCall -> "SelfSuperCall", + ImplicitsEnabled -> "ImplicitsEnabled", + MacrosEnabled -> "MacrosEnabled", + Checking -> "Checking", + ReTyping -> "ReTyping" + ) +} + +/** + * A value class to carry the boolean flags of a context, such as whether errors should + * be buffered or reported. + */ +final class ContextMode private (val bits: Int) extends AnyVal { + import ContextMode._ + + def &(other: ContextMode): ContextMode = new ContextMode(bits & other.bits) + def |(other: ContextMode): ContextMode = new ContextMode(bits | other.bits) + def &~(other: ContextMode): ContextMode = new ContextMode(bits & ~(other.bits)) + def set(value: Boolean, mask: ContextMode) = if (value) |(mask) else &~(mask) + + def inAll(required: ContextMode) = (this & required) == required + def inAny(required: ContextMode) = (this & required) != NOmode + def inNone(prohibited: ContextMode) = (this & prohibited) == NOmode + + override def toString = + if (bits == 0) "NOmode" + else (contextModeNameMap filterKeys inAll).values.toList.sorted mkString " " +} diff --git a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala index 3e249e57bb..73572bcae9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala +++ b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala @@ -6,8 +6,6 @@ package scala.tools.nsc package typechecker -import scala.language.implicitConversions - /** A generic means of breaking down types into their subcomponents. * Types are decomposed top down, and recognizable substructure is * dispatched via self-apparently named methods. Those methods can @@ -37,8 +35,6 @@ trait DestructureTypes { def wrapSequence(nodes: List[Node]): Node def wrapAtom[U](value: U): Node - private implicit def liftToTerm(name: String): TermName = newTermName(name) - private val openSymbols = scala.collection.mutable.Set[Symbol]() private def nodeList[T](elems: List[T], mkNode: T => Node): Node = @@ -68,15 +64,6 @@ trait DestructureTypes { }, tree.productPrefix ) - def wrapSymbol(label: String, sym: Symbol): Node = { - if (sym eq NoSymbol) wrapEmpty - else atom(label, sym) - } - def wrapInfo(sym: Symbol) = sym.info match { - case TypeBounds(lo, hi) => typeBounds(lo, hi) - case PolyType(tparams, restpe) => polyFunction(tparams, restpe) - case _ => wrapEmpty - } def wrapSymbolInfo(sym: Symbol): Node = { if ((sym eq NoSymbol) || openSymbols(sym)) wrapEmpty else { @@ -99,7 +86,6 @@ trait DestructureTypes { def constant(label: String, const: Constant): Node = atom(label, const) def scope(decls: Scope): Node = node("decls", scopeMemberList(decls.toList)) - def const[T](named: (String, T)): Node = constant(named._1, Constant(named._2)) def resultType(restpe: Type): Node = this("resultType", restpe) def typeParams(tps: List[Symbol]): Node = node("typeParams", symbolList(tps)) @@ -188,7 +174,6 @@ trait DestructureTypes { case AntiPolyType(pre, targs) => product(tp, prefix(pre), typeArgs(targs)) case ClassInfoType(parents, decls, clazz) => product(tp, parentList(parents), scope(decls), wrapAtom(clazz)) case ConstantType(const) => product(tp, constant("value", const)) - case DeBruijnIndex(level, index, args) => product(tp, const("level" -> level), const("index" -> index), typeArgs(args)) case OverloadedType(pre, alts) => product(tp, prefix(pre), node("alts", typeList(alts map pre.memberType))) case RefinedType(parents, decls) => product(tp, parentList(parents), scope(decls)) case SingleType(pre, sym) => product(tp, prefix(pre), wrapAtom(sym)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index f6142a81be..b9e4b4f591 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -19,11 +19,6 @@ abstract class Duplicators extends Analyzer { import global._ import definitions.{ AnyRefClass, AnyValClass } - def retyped(context: Context, tree: Tree): Tree = { - resetClassOwners - (newBodyDuplicator(context)).typed(tree) - } - /** Retype the given tree in the given context. Use this method when retyping * a method in a different class. The typer will replace references to the this of * the old class with the new class, and map symbols through the given 'env'. The @@ -33,7 +28,7 @@ abstract class Duplicators extends Analyzer { if (oldThis ne newThis) { oldClassOwner = oldThis newClassOwner = newThis - } else resetClassOwners + } else resetClassOwners() envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList) debuglog("retyped with env: " + env) @@ -42,9 +37,6 @@ abstract class Duplicators extends Analyzer { protected def newBodyDuplicator(context: Context) = new BodyDuplicator(context) - def retypedMethod(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol): Tree = - (newBodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) - /** Return the special typer for duplicate method bodies. */ override def newTyper(context: Context): Typer = newBodyDuplicator(context) @@ -88,7 +80,7 @@ abstract class Duplicators extends Analyzer { BodyDuplicator.super.silent(_.typedType(Ident(sym.name))) match { case SilentResultValue(t) => sym1 = t.symbol - debuglog("fixed by trying harder: "+(sym, sym1, context)) + debuglog("fixed by trying harder: "+((sym, sym1, context))) case _ => } } @@ -186,31 +178,6 @@ abstract class Duplicators extends Analyzer { stats.foreach(invalidate(_, owner)) } - def retypedMethod(ddef: DefDef, oldThis: Symbol, newThis: Symbol): Tree = { - oldClassOwner = oldThis - newClassOwner = newThis - invalidateAll(ddef.tparams) - mforeach(ddef.vparamss) { vdef => - invalidate(vdef) - vdef.tpe = null - } - ddef.symbol = NoSymbol - enterSym(context, ddef) - debuglog("remapping this of " + oldClassOwner + " to " + newClassOwner) - typed(ddef) - } - - private def inspectTpe(tpe: Type) = { - tpe match { - case MethodType(_, res) => - res + ", " + res.bounds.hi + ", " + (res.bounds.hi match { - case TypeRef(_, _, args) if (args.length > 0) => args(0) + ", " + args(0).bounds.hi - case _ => "non-tref: " + res.bounds.hi.getClass - }) - case _ => - } - } - /** Optionally cast this tree into some other type, if required. * Unless overridden, just returns the tree. */ @@ -230,10 +197,10 @@ abstract class Duplicators extends Analyzer { * their symbols are recreated ad-hoc and their types are fixed inline, instead of letting the * namer/typer handle them, or Idents that refer to them. */ - override def typed(tree: Tree, mode: Int, pt: Type): Tree = { + override def typed(tree: Tree, mode: Mode, pt: Type): Tree = { debuglog("typing " + tree + ": " + tree.tpe + ", " + tree.getClass) val origtreesym = tree.symbol - if (tree.hasSymbol && tree.symbol != NoSymbol + if (tree.hasSymbolField && tree.symbol != NoSymbol && !tree.symbol.isLabel // labels cannot be retyped by the type checker as LabelDef has no ValDef/return type trees && invalidSyms.isDefinedAt(tree.symbol)) { debuglog("removed symbol " + tree.symbol) @@ -243,40 +210,35 @@ abstract class Duplicators extends Analyzer { tree match { case ttree @ TypeTree() => // log("fixing tpe: " + tree.tpe + " with sym: " + tree.tpe.typeSymbol) - ttree.tpe = fixType(ttree.tpe) - ttree + ttree modifyType fixType case Block(stats, res) => debuglog("invalidating block") invalidateAll(stats) invalidate(res) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case ClassDef(_, _, _, tmpl @ Template(parents, _, stats)) => // log("invalidating classdef " + tree) tmpl.symbol = tree.symbol.newLocalDummy(tree.pos) invalidateAll(stats, tree.symbol) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case ddef @ DefDef(_, _, _, _, tpt, rhs) => - ddef.tpt.tpe = fixType(ddef.tpt.tpe) - ddef.tpe = null - super.typed(ddef, mode, pt) + ddef.tpt modifyType fixType + super.typed(ddef.clearType(), mode, pt) case vdef @ ValDef(mods, name, tpt, rhs) => // log("vdef fixing tpe: " + tree.tpe + " with sym: " + tree.tpe.typeSymbol + " and " + invalidSyms) //if (mods.hasFlag(Flags.LAZY)) vdef.symbol.resetFlag(Flags.MUTABLE) // Martin to Iulian: lazy vars can now appear because they are no longer boxed; Please check that deleting this statement is OK. - vdef.tpt.tpe = fixType(vdef.tpt.tpe) - vdef.tpe = null - super.typed(vdef, mode, pt) + vdef.tpt modifyType fixType + super.typed(vdef.clearType(), mode, pt) case ldef @ LabelDef(name, params, rhs) => // log("label def: " + ldef) // in case the rhs contains any definitions -- TODO: is this necessary? invalidate(rhs) - ldef.tpe = null + ldef.clearType() // is this LabelDef generated by tailcalls? val isTailLabel = (ldef.params.length >= 1) && (ldef.params.head.name == nme.THIS) @@ -294,27 +256,23 @@ abstract class Duplicators extends Analyzer { val params1 = params map newParam val rhs1 = (new TreeSubstituter(params map (_.symbol), params1) transform rhs) // TODO: duplicate? - rhs1.tpe = null - super.typed(treeCopy.LabelDef(tree, name, params1, rhs1), mode, pt) + super.typed(treeCopy.LabelDef(tree, name, params1, rhs1.clearType()), mode, pt) case Bind(name, _) => // log("bind: " + tree) invalidate(tree) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case Ident(_) if tree.symbol.isLabel => debuglog("Ident to labeldef " + tree + " switched to ") tree.symbol = updateSym(tree.symbol) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case Ident(_) if (origtreesym ne null) && origtreesym.isLazy => debuglog("Ident to a lazy val " + tree + ", " + tree.symbol + " updated to " + origtreesym) tree.symbol = updateSym(origtreesym) - tree.tpe = null - super.typed(tree, mode, pt) + super.typed(tree.clearType(), mode, pt) case Select(th @ This(_), sel) if (oldClassOwner ne null) && (th.symbol == oldClassOwner) => // We use the symbol name instead of the tree name because the symbol @@ -336,9 +294,15 @@ abstract class Duplicators extends Analyzer { case ((alt, tpe)) :: Nil => log(s"Arrested overloaded type in Duplicators, narrowing to ${alt.defStringSeenAs(tpe)}\n Overload was: $memberString") Select(This(newClassOwner), alt) - case _ => - log(s"Could not disambiguate $memberString in Duplicators. Attempting name-based selection, but this may not end well...") - nameSelection + case xs => + alts filter (alt => (alt.paramss corresponds tree.symbol.paramss)(_.size == _.size)) match { + case alt :: Nil => + log(s"Resorted to parameter list arity to disambiguate to $alt\n Overload was: $memberString") + Select(This(newClassOwner), alt) + case _ => + log(s"Could not disambiguate $memberTypes. Attempting name-based selection, but we may crash later.") + nameSelection + } } } else nameSelection @@ -397,7 +361,7 @@ abstract class Duplicators extends Analyzer { case _ => debuglog("Duplicators default case: " + tree.summaryString) debuglog(" ---> " + tree) - if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { + if (tree.hasSymbolField && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } val ntree = castType(tree, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 57b9dfe3e4..7092f00bff 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -33,7 +33,7 @@ trait EtaExpansion { self: Analyzer => } /** <p> - * Expand partial function applications of type <code>type</code>. + * Expand partial function applications of type `type`. * </p><pre> * p.f(es_1)...(es_n) * ==> { @@ -56,11 +56,8 @@ trait EtaExpansion { self: Analyzer => } val defs = new ListBuffer[Tree] - /** Append to <code>defs</code> value definitions for all non-stable - * subexpressions of the function application <code>tree</code>. - * - * @param tree ... - * @return ... + /* Append to `defs` value definitions for all non-stable + * subexpressions of the function application `tree`. */ def liftoutPrefix(tree: Tree): Tree = { def liftout(tree: Tree, byName: Boolean): Tree = @@ -97,12 +94,12 @@ trait EtaExpansion { self: Analyzer => // with repeated params, there might be more or fewer args than params liftout(arg, byName(i).getOrElse(false)) } - treeCopy.Apply(tree, liftoutPrefix(fn), newArgs) setType null + treeCopy.Apply(tree, liftoutPrefix(fn), newArgs).clearType() case TypeApply(fn, args) => - treeCopy.TypeApply(tree, liftoutPrefix(fn), args) setType null + treeCopy.TypeApply(tree, liftoutPrefix(fn), args).clearType() case Select(qual, name) => val name = tree.symbol.name // account for renamed imports, SI-7233 - treeCopy.Select(tree, liftout(qual, false), name) setSymbol NoSymbol setType null + treeCopy.Select(tree, liftout(qual, byName = false), name).clearType() setSymbol NoSymbol case Ident(name) => tree } @@ -110,8 +107,7 @@ trait EtaExpansion { self: Analyzer => tree1 } - /** Eta-expand lifted tree. - */ + /* Eta-expand lifted tree. */ def expand(tree: Tree, tpe: Type): Tree = tpe match { case mt @ MethodType(paramSyms, restpe) if !mt.isImplicit => val params: List[(ValDef, Boolean)] = paramSyms.map { @@ -119,7 +115,7 @@ trait EtaExpansion { self: Analyzer => val origTpe = sym.tpe val isRepeated = definitions.isRepeatedParamType(origTpe) // SI-4176 Don't leak A* in eta-expanded function types. See t4176b.scala - val droppedStarTpe = if (settings.etaExpandKeepsStar.value) origTpe else dropRepeatedParamType(origTpe) + val droppedStarTpe = if (settings.etaExpandKeepsStar) origTpe else dropIllegalStarTypes(origTpe) val valDef = ValDef(Modifiers(SYNTHETIC | PARAM), sym.name.toTermName, TypeTree(droppedStarTpe), EmptyTree) (valDef, isRepeated) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 01ae0a7a94..b0c8baae20 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -30,11 +30,11 @@ trait Implicits { import global._ import definitions._ import ImplicitsStats._ - import typeDebug.{ ptTree, ptBlock, ptLine } + import typeDebug.{ 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, tree.pos) + inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent = true, tree.pos) def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, tree.pos) @@ -81,8 +81,8 @@ trait Implicits { val implicitSearchContext = context.makeImplicit(reportAmbiguous) val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) { - context.updateBuffer(implicitSearchContext.errBuffer.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent)) - debugwarn("update buffer: " + implicitSearchContext.errBuffer) + context.updateBuffer(implicitSearchContext.reportBuffer.errors.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent)) + debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors) } printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains @@ -112,7 +112,7 @@ trait Implicits { val tvars = tpars map (TypeVar untouchable _) val tpSubsted = tp.subst(tpars, tvars) - val search = new ImplicitSearch(EmptyTree, functionType(List(tpSubsted), AnyClass.tpe), true, context.makeImplicit(false)) + val search = new ImplicitSearch(EmptyTree, functionType(List(tpSubsted), AnyClass.tpe), true, context.makeImplicit(reportAmbiguousErrors = false)) search.allImplicitsPoly(tvars) } @@ -132,7 +132,7 @@ trait Implicits { } /* Map a polytype to one in which all type parameters and argument-dependent types are replaced by wildcards. - * Consider `implicit def b(implicit x: A): x.T = error("")`. We need to approximate DebruijnIndex types + * Consider `implicit def b(implicit x: A): x.T = error("")`. We need to approximate debruijn index types * when checking whether `b` is a valid implicit, as we haven't even searched a value for the implicit arg `x`, * so we have to approximate (otherwise it is excluded a priori). */ @@ -149,7 +149,7 @@ trait Implicits { class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter) { override def toString = "SearchResult(%s, %s)".format(tree, if (subst.isEmpty) "" else subst) - + def isFailure = false def isAmbiguousFailure = false final def isSuccess = !isFailure @@ -158,7 +158,7 @@ trait Implicits { lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { override def isFailure = true } - + lazy val AmbiguousSearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { override def isFailure = true override def isAmbiguousFailure = true @@ -198,15 +198,7 @@ trait Implicits { tp.isError } - /** Todo reconcile with definition of stability given in Types.scala */ - private def isStable(tp: Type): Boolean = tp match { - case TypeRef(pre, sym, _) => - sym.isPackageClass || - sym.isModuleClass && isStable(pre) /*|| - sym.isAliasType && isStable(tp.normalize)*/ - case _ => tp.isStable - } - def isStablePrefix = isStable(pre) + def isStablePrefix = pre.isStable override def equals(other: Any) = other match { case that: ImplicitInfo => @@ -244,11 +236,7 @@ trait Implicits { object HasMember { private val hasMemberCache = perRunCaches.newMap[Name, Type]() def apply(name: Name): Type = hasMemberCache.getOrElseUpdate(name, memberWildcardType(name, WildcardType)) - def unapply(pt: Type): Option[Name] = pt match { - case RefinedType(List(WildcardType), Scope(sym)) if sym.tpe == WildcardType => Some(sym.name) - case _ => None } - } /** An extractor for types of the form ? { name: (? >: argtpe <: Any*)restp } */ @@ -280,7 +268,7 @@ trait Implicits { */ object Function1 { val Sym = FunctionClass(1) - def unapply(tp: Type) = tp match { + def unapply(tp: Type) = tp baseType Sym match { case TypeRef(_, Sym, arg1 :: arg2 :: _) => Some((arg1, arg2)) case _ => None } @@ -310,7 +298,7 @@ trait Implicits { def pos = if (pos0 != NoPosition) pos0 else tree.pos def failure(what: Any, reason: String, pos: Position = this.pos): SearchResult = { - if (settings.XlogImplicits.value) + if (settings.XlogImplicits) reporter.echo(pos, what+" is not a valid implicit value for "+pt+" because:\n"+reason) SearchFailure } @@ -323,7 +311,7 @@ trait Implicits { (info2 == NoImplicitInfo) || (info1 != NoImplicitInfo) && { if (info1.sym.isStatic && info2.sym.isStatic) { - improvesCache get (info1, info2) match { + improvesCache get ((info1, info2)) match { case Some(b) => if (Statistics.canEnable) Statistics.incCounter(improvesCachedCount); b case None => val result = isStrictlyMoreSpecific(info1.tpe, info2.tpe, info1.sym, info2.sym) @@ -351,7 +339,7 @@ trait Implicits { * if one or both are intersection types with a pair of overlapping parent types. */ private def dominates(dtor: Type, dted: Type): Boolean = { - def core(tp: Type): Type = tp.normalize match { + def core(tp: Type): Type = tp.dealiasWiden match { case RefinedType(parents, defs) => intersectionType(parents map core, tp.typeSymbol.owner) case AnnotatedType(annots, tp, selfsym) => core(tp) case ExistentialType(tparams, result) => core(result).subst(tparams, tparams map (t => core(t.info.bounds.hi))) @@ -366,11 +354,11 @@ trait Implicits { deriveTypeWithWildcards(syms.distinct)(tp) } def sum(xs: List[Int]) = (0 /: xs)(_ + _) - def complexity(tp: Type): Int = tp.normalize match { + def complexity(tp: Type): Int = tp.dealiasWiden match { case NoPrefix => 0 case SingleType(pre, sym) => - if (sym.isPackage) 0 else complexity(tp.normalize.widen) + if (sym.isPackage) 0 else complexity(tp.dealiasWiden) case TypeRef(pre, sym, args) => complexity(pre) + sum(args map complexity) + 1 case RefinedType(parents, _) => @@ -443,10 +431,8 @@ trait Implicits { val start = if (Statistics.canEnable) Statistics.startTimer(matchesPtNanos) else null val result = normSubType(tp, pt) || isView && { pt match { - case TypeRef(_, Function1.Sym, arg1 :: arg2 :: Nil) => - matchesPtView(tp, arg1, arg2, undet) - case _ => - false + case Function1(arg1, arg2) => matchesPtView(tp, arg1, arg2, undet) + case _ => false } } if (Statistics.canEnable) Statistics.stopTimer(matchesPtNanos, start) @@ -587,21 +573,23 @@ trait Implicits { ) def fail(reason: String): SearchResult = failure(itree, reason) + def fallback = typed1(itree, EXPRmode, wildPt) try { - val itree1 = - if (isView) { - val arg1 :: arg2 :: _ = pt.typeArgs + val itree1 = if (!isView) fallback else pt match { + case Function1(arg1, arg2) => typed1( atPos(itree.pos)(Apply(itree, List(Ident("<argument>") setType approximate(arg1)))), EXPRmode, approximate(arg2) ) - } - else - typed1(itree, EXPRmode, wildPt) - - if (context.hasErrors) - return fail(context.errBuffer.head.errMsg) + case _ => fallback + } + context.firstError match { // using match rather than foreach to avoid non local return. + case Some(err) => + log("implicit adapt failed: " + err.errMsg) + return fail(err.errMsg) + case None => + } if (Statistics.canEnable) Statistics.incCounter(typedImplicits) @@ -623,7 +611,7 @@ trait Implicits { } if (context.hasErrors) - fail("hasMatchingSymbol reported error: " + context.errBuffer.head.errMsg) + fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg) else if (isLocal && !hasMatchingSymbol(itree1)) fail("candidate implicit %s is shadowed by %s".format( info.sym.fullLocationString, itree1.symbol.fullLocationString)) @@ -642,12 +630,15 @@ trait Implicits { printTyping(ptLine("" + info.sym, "tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr))) val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), - false, lubDepth(List(itree2.tpe, pt))) + upper = false, lubDepth(List(itree2.tpe, pt))) // #2421: check that we correctly instantiated type parameters outside of the implicit tree: checkBounds(itree2, NoPrefix, NoSymbol, undetParams, targs, "inferred ") - if (context.hasErrors) - return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + context.errBuffer.head.errMsg) + context.firstError match { + case Some(err) => + return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) + case None => + } // filter out failures from type inference, don't want to remove them from undetParams! // we must be conservative in leaving type params in undetparams @@ -672,23 +663,24 @@ trait Implicits { // duplicating the code here, but this is probably a // hotspot (and you can't just call typed, need to force // re-typecheck) - // TODO: the return tree is ignored. This seems to make - // no difference, but it's bad practice regardless. - - - val checked = itree2 match { + // + // This is just called for the side effect of error detection, + // see SI-6966 to see what goes wrong if we use the result of this + // as the SearchResult. + 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 } - if (context.hasErrors) - fail("typing TypeApply reported errors for the implicit tree: " + context.errBuffer.head.errMsg) - else { - val result = new SearchResult(itree2, subst) - if (Statistics.canEnable) Statistics.incCounter(foundImplicits) - printInference("[success] found %s for pt %s".format(result, ptInstantiated)) - result + context.firstError match { + case Some(err) => + fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) + case None => + val result = new SearchResult(itree2, subst) + if (Statistics.canEnable) Statistics.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)) @@ -842,7 +834,7 @@ trait Implicits { case sr if sr.isFailure => // We don't want errors that occur during checking implicit info // to influence the check of further infos. - context.condBufferFlush(_.kind != ErrorKinds.Divergent) + context.reportBuffer.retainErrors(ErrorKinds.Divergent) rankImplicits(is, acc) case newBest => best = newBest @@ -887,8 +879,8 @@ trait Implicits { } if (best.isFailure) { - /** If there is no winner, and we witnessed and caught divergence, - * now we can throw it for the error message. + /* If there is no winner, and we witnessed and caught divergence, + * now we can throw it for the error message. */ if (divergence) throw DivergentImplicit @@ -948,8 +940,8 @@ trait Implicits { */ private def companionImplicitMap(tp: Type): InfoMap = { - /** Populate implicit info map by traversing all parts of type `tp`. - * Parameters as for `getParts`. + /* Populate implicit info map by traversing all parts of type `tp`. + * Parameters as for `getParts`. */ def getClassParts(tp: Type)(implicit infoMap: InfoMap, seen: mutable.Set[Type], pending: Set[Symbol]) = tp match { case TypeRef(pre, sym, args) => @@ -981,13 +973,13 @@ trait Implicits { } } - /** Populate implicit info map by traversing all parts of type `tp`. - * This method is performance critical. - * @param tp The type for which we want to traverse parts - * @param infoMap The infoMap in which implicit infos corresponding to parts are stored - * @param seen The types that were already visited previously when collecting parts for the given infoMap - * @param pending The set of static symbols for which we are currently trying to collect their parts - * in order to cache them in infoMapCache + /* Populate implicit info map by traversing all parts of type `tp`. + * This method is performance critical. + * @param tp The type for which we want to traverse parts + * @param infoMap The infoMap in which implicit infos corresponding to parts are stored + * @param seen The types that were already visited previously when collecting parts for the given infoMap + * @param pending The set of static symbols for which we are currently trying to collect their parts + * in order to cache them in infoMapCache */ def getParts(tp: Type)(implicit infoMap: InfoMap, seen: mutable.Set[Type], pending: Set[Symbol]) { if (seen(tp)) @@ -1005,7 +997,7 @@ trait Implicits { case Some(imap) => imap case None => val result = new InfoMap - getClassParts(sym.tpe)(result, new mutable.HashSet(), pending + sym) + getClassParts(sym.tpeHK)(result, new mutable.HashSet(), pending + sym) infoMapCache(sym) = result result } @@ -1099,8 +1091,10 @@ trait Implicits { try { val tree1 = typedPos(pos.focus)(arg) - if (context.hasErrors) processMacroExpansionError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg) - else new SearchResult(tree1, EmptyTreeTypeSubstituter) + context.firstError match { + case Some(err) => processMacroExpansionError(err.errPos, err.errMsg) + case None => new SearchResult(tree1, EmptyTreeTypeSubstituter) + } } catch { case ex: TypeError => processMacroExpansionError(ex.pos, ex.msg) @@ -1117,7 +1111,7 @@ trait Implicits { case ThisType(thisSym) => gen.mkAttributedThis(thisSym) case _ => - // if ``pre'' is not a PDT, e.g. if someone wrote + // if `pre` is not a PDT, e.g. if someone wrote // implicitly[scala.reflect.macros.Context#TypeTag[Int]] // then we need to fail, because we don't know the prefix to use during type reification // upd. we also need to fail silently, because this is a very common situation @@ -1131,8 +1125,8 @@ trait Implicits { } ) // todo. migrate hardcoded materialization in Implicits to corresponding implicit macros - var materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), if (prefix != EmptyTree) List(prefix) else List())) - if (settings.XlogImplicits.value) reporter.echo(pos, "materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) + val materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), if (prefix != EmptyTree) List(prefix) else List())) + if (settings.XlogImplicits) reporter.echo(pos, "materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) if (context.macrosEnabled) success(materializer) // don't call `failure` here. if macros are disabled, we just fail silently // otherwise -Xlog-implicits will spam the long with zillions of "macros are disabled" @@ -1150,23 +1144,23 @@ trait Implicits { val full = flavor == FullManifestClass val opt = flavor == OptManifestClass - /** Creates a tree that calls the factory method called constructor in object scala.reflect.Manifest */ + /* Creates a tree that calls the factory method called constructor in object scala.reflect.Manifest */ def manifestFactoryCall(constructor: String, tparg: Type, args: Tree*): Tree = if (args contains EmptyTree) EmptyTree else typedPos(tree.pos.focus) { val mani = gen.mkManifestFactoryCall(full, constructor, tparg, args.toList) - if (settings.debug.value) println("generated manifest: "+mani) // DEBUG + if (settings.debug) println("generated manifest: "+mani) // DEBUG mani } - /** Creates a tree representing one of the singleton manifests.*/ + /* Creates a tree representing one of the singleton manifests.*/ def findSingletonManifest(name: String) = typedPos(tree.pos.focus) { Select(gen.mkAttributedRef(FullManifestModule), name) } - /** Re-wraps a type in a manifest before calling inferImplicit on the result */ + /* Re-wraps a type in a manifest before calling inferImplicit on the result */ def findManifest(tp: Type, manifestClass: Symbol = if (full) FullManifestClass else PartialManifestClass) = - inferImplicit(tree, appliedType(manifestClass, tp), true, false, context).tree + inferImplicit(tree, appliedType(manifestClass, tp), reportAmbiguous = true, isView = false, context).tree def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass) def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = { @@ -1301,7 +1295,7 @@ trait Implicits { val failstart = if (Statistics.canEnable) Statistics.startTimer(inscopeFailNanos) else null val succstart = if (Statistics.canEnable) Statistics.startTimer(inscopeSucceedNanos) else null - var result = searchImplicit(context.implicitss, true) + var result = searchImplicit(context.implicitss, isLocal = true) if (result.isFailure) { if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart) @@ -1319,23 +1313,18 @@ trait Implicits { // `materializeImplicit` does some preprocessing for `pt` // is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`? - if (result.isFailure) result = searchImplicit(implicitsOfExpectedType, false) + if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, isLocal = false) if (result.isFailure) { context.updateBuffer(previousErrs) if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { - if (wasAmbigious && settings.lint.value) - reporter.warning(tree.pos, - "Search of in-scope implicits was ambiguous, and the implicit scope was searched. In Scala 2.11.0, this code will not compile. See SI-6667. \n" + - previousErrs.map(_.errMsg).mkString("\n")) - if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits) } } - if (result.isFailure && settings.debug.value) + if (result.isFailure && settings.debug) log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) result @@ -1343,7 +1332,7 @@ trait Implicits { def allImplicits: List[SearchResult] = { def search(iss: Infoss, isLocal: Boolean) = applicableInfos(iss, isLocal).values - (search(context.implicitss, true) ++ search(implicitsOfExpectedType, false)).toList.filter(_.tree ne EmptyTree) + (search(context.implicitss, isLocal = true) ++ search(implicitsOfExpectedType, isLocal = false)).toList.filter(_.tree ne EmptyTree) } // find all implicits for some type that contains type variables @@ -1404,7 +1393,6 @@ trait Implicits { interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc? def validate: Option[String] = { - import scala.util.matching.Regex; import scala.collection.breakOut // is there a shorter way to avoid the intermediate toList? val refs = """\$\{([^}]+)\}""".r.findAllIn(msg).matchData.map(_ group 1).toSet val decls = typeParamNames.toSet @@ -1430,9 +1418,7 @@ object ImplicitsStats { val subtypeImpl = Statistics.newSubCounter(" of which in implicit", subtypeCount) val findMemberImpl = Statistics.newSubCounter(" of which in implicit", findMemberCount) val subtypeAppInfos = Statistics.newSubCounter(" of which in app impl", subtypeCount) - val subtypeImprovCount = Statistics.newSubCounter(" of which in improves", subtypeCount) val implicitSearchCount = Statistics.newCounter ("#implicit searches", "typer") - val triedImplicits = Statistics.newSubCounter(" #tried", implicitSearchCount) val plausiblyCompatibleImplicits = Statistics.newSubCounter(" #plausibly compatible", implicitSearchCount) val matchingImplicits = Statistics.newSubCounter(" #matching", implicitSearchCount) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 55e0a954f0..a17b69ca06 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -6,11 +6,10 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } +import scala.collection.immutable import scala.collection.mutable.ListBuffer import scala.util.control.ControlThrowable import symtab.Flags._ -import scala.annotation.tailrec /** This trait ... * @@ -30,8 +29,8 @@ trait Infer extends Checkable { private def assertNonCyclic(tvar: TypeVar) = assert(tvar.constr.inst != tvar, tvar.origin) - /** The formal parameter types corresponding to <code>formals</code>. - * If <code>formals</code> has a repeated last parameter, a list of + /** The formal parameter types corresponding to `formals`. + * If `formals` has a repeated last parameter, a list of * (nargs - params.length + 1) copies of its type is returned. * By-name types are replaced with their underlying type. * @@ -49,6 +48,24 @@ trait Infer extends Checkable { } else formals1 } + /** Sorts the alternatives according to the given comparison function. + * Returns a list containing the best alternative as well as any which + * the best fails to improve upon. + */ + private def bestAlternatives(alternatives: List[Symbol])(isBetter: (Symbol, Symbol) => Boolean): List[Symbol] = { + def improves(sym1: Symbol, sym2: Symbol) = ( + sym2 == NoSymbol + || sym2.isError + || sym2.hasAnnotation(BridgeClass) + || isBetter(sym1, sym2) + ) + + alternatives sortWith improves match { + case best :: rest if rest.nonEmpty => best :: rest.filterNot(alt => improves(best, alt)) + case bests => bests + } + } + /** Returns `(formals, formalsExpanded)` where `formalsExpanded` are the expected types * for the `nbSubPats` sub-patterns of an extractor pattern, of which the corresponding * unapply[Seq] call is assumed to have result type `resTp`. @@ -119,7 +136,7 @@ trait Infer extends Checkable { else if (optionArgs.nonEmpty) if (nbSubPats == 1) { val productArity = productArgs.size - if (settings.lint.value && productArity > 1 && productArity != effectiveNbSubPats) + if (productArity > 1 && productArity != effectiveNbSubPats && settings.lint) global.currentUnit.warning(pos, s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") optionArgs @@ -139,21 +156,7 @@ trait Infer extends Checkable { else (formals, formalsExpanded) } - def actualTypes(actuals: List[Type], nformals: Int): List[Type] = - if (nformals == 1 && !hasLength(actuals, 1)) - List(if (actuals.isEmpty) UnitClass.tpe else tupleType(actuals)) - else actuals - - def actualArgs(pos: Position, actuals: List[Tree], nformals: Int): List[Tree] = { - val inRange = nformals == 1 && !hasLength(actuals, 1) && actuals.lengthCompare(MaxTupleArity) <= 0 - if (inRange && !phase.erasedTypes) List(atPos(pos)(gen.mkTuple(actuals))) - else actuals - } - /** A fresh type variable with given type parameter as origin. - * - * @param tparam ... - * @return ... */ def freshVar(tparam: Symbol): TypeVar = TypeVar(tparam) @@ -176,14 +179,13 @@ trait Infer extends Checkable { case tv @ TypeVar(origin, constr) if !tv.untouchable => if (constr.inst == NoType) { throw new DeferredNoInstance(() => - "no unique instantiation of type variable " + origin + " could be found") + s"no unique instantiation of type variable $origin could be found") } else if (excludedVars(tv)) { throw new NoInstance("cyclic instantiation") } else { excludedVars += tv - val res = apply(constr.inst) - excludedVars -= tv - res + try apply(constr.inst) + finally excludedVars -= tv } case _ => mapOver(tp) @@ -191,9 +193,6 @@ trait Infer extends Checkable { } /** Is type fully defined, i.e. no embedded anytypes or wildcards in it? - * - * @param tp ... - * @return ... */ private[typechecker] def isFullyDefined(tp: Type): Boolean = tp match { case WildcardType | BoundedWildcardType(_) | NoType => @@ -226,7 +225,7 @@ trait Infer extends Checkable { * @throws NoInstance */ def solvedTypes(tvars: List[TypeVar], tparams: List[Symbol], - variances: List[Int], upper: Boolean, depth: Int): List[Type] = { + variances: List[Variance], upper: Boolean, depth: Int): List[Type] = { if (tvars.nonEmpty) printInference("[solve types] solving for " + tparams.map(_.name).mkString(", ") + " in " + tvars.mkString(", ")) @@ -265,6 +264,8 @@ trait Infer extends Checkable { * This method seems to be performance critical. */ def normalize(tp: Type): Type = tp match { + case pt @ PolyType(tparams, restpe) => + logResult(s"Normalizing $tp in infer")(normalize(restpe)) case mt @ MethodType(params, restpe) if mt.isImplicit => normalize(restpe) case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => @@ -301,7 +302,7 @@ trait Infer extends Checkable { def errorValue = if (context.reportErrors) context.owner.newErrorValue(name) else stdErrorValue def errorSym = if (tree.isType) errorClass else errorValue - if (tree.hasSymbol) + if (tree.hasSymbolField) tree setSymbol errorSym tree setType ErrorType @@ -313,22 +314,23 @@ trait Infer extends Checkable { def isPossiblyMissingArgs(found: Type, req: Type) = ( false - /** However it is that this condition is expected to imply - * "is possibly missing args", it is too weak. It is - * better to say nothing than to offer misleading guesses. + /* However it is that this condition is expected to imply + * "is possibly missing args", it is too weak. It is + * better to say nothing than to offer misleading guesses. - (found.resultApprox ne found) - && isWeaklyCompatible(found.resultApprox, req) + * (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) */ ) - def explainTypes(tp1: Type, tp2: Type) = - withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) + def explainTypes(tp1: Type, tp2: Type) = { + if (context.reportErrors) + withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) + } /* -- Tests & Checks---------------------------------------------------- */ - /** Check that <code>sym</code> is defined and accessible as a member of - * tree <code>site</code> with type <code>pre</code> in current context. + /** Check that `sym` is defined and accessible as a member of + * tree `site` with type `pre` in current context. * * Note: pre is not refchecked -- moreover, refchecking the resulting tree may not refcheck pre, * since pre may not occur in its type (callers should wrap the result in a TypeTreeWithDeferredRefCheck) @@ -337,7 +339,6 @@ trait Infer extends Checkable { if (sym.isError) { tree setSymbol sym setType ErrorType } else { - val topClass = context.owner.enclosingTopLevelClass if (context.unit.exists) context.unit.depends += sym.enclosingTopLevelClass @@ -347,7 +348,7 @@ trait Infer extends Checkable { sym1 = sym if (sym1 == NoSymbol) { - if (settings.debug.value) { + if (settings.debug) { Console.println(context) Console.println(tree) Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType)) @@ -387,7 +388,7 @@ trait Infer extends Checkable { try pre.memberType(sym1) catch { case ex: MalformedType => - if (settings.debug.value) ex.printStackTrace + if (settings.debug) ex.printStackTrace val sym2 = underlyingSymbol(sym1) val itype = pre.memberType(sym2) ErrorUtils.issueTypeError( @@ -479,16 +480,12 @@ trait Infer extends Checkable { } existentialAbstraction(tparams.toList, tp1) } + def ensureFullyDefined(tp: Type): Type = if (isFullyDefined(tp)) tp else makeFullyDefined(tp) /** Return inferred type arguments of polymorphic expression, given - * its type parameters and result type and a prototype <code>pt</code>. + * its type parameters and result type and a prototype `pt`. * If no minimal type variables exist that make the - * instantiated type a subtype of <code>pt</code>, return null. - * - * @param tparams ... - * @param restpe ... - * @param pt ... - * @return ... + * instantiated type a subtype of `pt`, return null. */ private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type, useWeaklyCompatible: Boolean = false): (List[Type], List[TypeVar]) = { val tvars = tparams map freshVar @@ -506,7 +503,7 @@ trait Infer extends Checkable { } //println("try to solve "+tvars+" "+tparams) (solvedTypes(tvars, tparams, tparams map varianceInType(varianceType), - false, lubDepth(List(restpe, pt))), tvars) + upper = false, lubDepth(List(restpe, pt))), tvars) } catch { case ex: NoInstance => (null, null) } @@ -515,24 +512,18 @@ trait Infer extends Checkable { /** Return inferred proto-type arguments of function, given * its type and value parameters and result type, and a - * prototype <code>pt</code> for the function result. + * prototype `pt` for the function result. * Type arguments need to be either determined precisely by * the prototype, or they are maximized, if they occur only covariantly * in the value parameter list. * If instantiation of a type parameter fails, * take WildcardType for the proto-type argument. - * - * @param tparams ... - * @param formals ... - * @param restype ... - * @param pt ... - * @return ... */ def protoTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type, pt: Type): List[Type] = { - /** Map type variable to its instance, or, if `variance` is covariant/contravariant, - * to its upper/lower bound */ - def instantiateToBound(tvar: TypeVar, variance: Int): Type = try { + /* Map type variable to its instance, or, if `variance` is covariant/contravariant, + * to its upper/lower bound */ + def instantiateToBound(tvar: TypeVar, variance: Variance): Type = { lazy val hiBounds = tvar.constr.hiBounds lazy val loBounds = tvar.constr.loBounds lazy val upper = glb(hiBounds) @@ -545,23 +536,21 @@ trait Infer extends Checkable { //Console.println("instantiate "+tvar+tvar.constr+" variance = "+variance);//DEBUG if (tvar.constr.inst != NoType) instantiate(tvar.constr.inst) - else if ((variance & COVARIANT) != 0 && hiBounds.nonEmpty) - setInst(upper) - else if ((variance & CONTRAVARIANT) != 0 && loBounds.nonEmpty) + else if (loBounds.nonEmpty && variance.isContravariant) setInst(lower) - else if (hiBounds.nonEmpty && loBounds.nonEmpty && upper <:< lower) + else if (hiBounds.nonEmpty && (variance.isPositive || loBounds.nonEmpty && upper <:< lower)) setInst(upper) else WildcardType - } catch { - case ex: NoInstance => WildcardType } val tvars = tparams map freshVar if (isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) map2(tparams, tvars)((tparam, tvar) => - instantiateToBound(tvar, varianceInTypes(formals)(tparam))) + try instantiateToBound(tvar, varianceInTypes(formals)(tparam)) + catch { case ex: NoInstance => WildcardType } + ) else - tvars map (tvar => WildcardType) + tvars map (_ => WildcardType) } /** [Martin] Can someone comment this please? I have no idea what it's for @@ -608,7 +597,7 @@ trait Infer extends Checkable { * * Rewrite for repeated param types: Map T* entries to Seq[T]. * @return map from tparams to inferred arg, if inference was successful, tparams that map to None are considered left undetermined - * type parameters that are inferred as `scala.Nothing` and that are not covariant in <code>restpe</code> are taken to be undetermined + * type parameters that are inferred as `scala.Nothing` and that are not covariant in `restpe` are taken to be undetermined */ def adjustTypeArgs(tparams: List[Symbol], tvars: List[TypeVar], targs: List[Type], restpe: Type = WildcardType): AdjustedTypeArgs.Result = { val buf = AdjustedTypeArgs.Result.newBuilder[Symbol, Option[Type]] @@ -616,33 +605,32 @@ trait Infer extends Checkable { foreach3(tparams, tvars, targs) { (tparam, tvar, targ) => val retract = ( targ.typeSymbol == NothingClass // only retract Nothings - && (restpe.isWildcard || (varianceInType(restpe)(tparam) & COVARIANT) == 0) // don't retract covariant occurrences + && (restpe.isWildcard || !varianceInType(restpe)(tparam).isPositive) // don't retract covariant occurrences ) - // checks opt.virtPatmat directly so one need not run under -Xexperimental to use virtpatmat buf += ((tparam, if (retract) None else Some( if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass) else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass) // this infers Foo.type instead of "object Foo" (see also widenIfNecessary) - else if (targ.typeSymbol.isModuleClass || ((opt.experimental || opt.virtPatmat) && tvar.constr.avoidWiden)) targ + else if (targ.typeSymbol.isModuleClass || tvar.constr.avoidWiden) targ else targ.widen ) )) } - buf.result + buf.result() } /** Return inferred type arguments, given type parameters, formal parameters, * argument types, result type and expected result type. - * If this is not possible, throw a <code>NoInstance</code> exception. + * If this is not possible, throw a `NoInstance` exception. * Undetermined type arguments are represented by `definitions.NothingClass.tpe`. * No check that inferred parameters conform to their bounds is made here. * * @param tparams the type parameters of the method * @param formals the value parameter types of the method - * @param restp the result type of the method + * @param restpe the result type of the method * @param argtpes the argument types of the application * @param pt the expected return type of the application * @return @see adjustTypeArgs @@ -691,11 +679,59 @@ trait Infer extends Checkable { } val targs = solvedTypes( tvars, tparams, tparams map varianceInTypes(formals), - false, lubDepth(formals) max lubDepth(argtpes) + upper = false, lubDepth(formals) max lubDepth(argtpes) ) + // Can warn about inferring Any/AnyVal as long as they don't appear + // explicitly anywhere amongst the formal, argument, result, or expected type. + def canWarnAboutAny = !(pt :: restpe :: formals ::: argtpes exists (t => (t contains AnyClass) || (t contains AnyValClass))) + def argumentPosition(idx: Int): Position = context.tree match { + case x: ValOrDefDef => x.rhs match { + case Apply(fn, args) if idx < args.size => args(idx).pos + case _ => context.tree.pos + } + case _ => context.tree.pos + } + if (settings.warnInferAny.value && context.reportErrors && canWarnAboutAny) { + foreachWithIndex(targs) ((targ, idx) => + targ.typeSymbol match { + case sym @ (AnyClass | AnyValClass) => + context.unit.warning(argumentPosition(idx), s"a type was inferred to be `${sym.name}`; this may indicate a programming error.") + case _ => + } + ) + } adjustTypeArgs(tparams, tvars, targs, restpe) } + /** One must step carefully when assessing applicability due to + * complications from varargs, tuple-conversion, named arguments. + * This method is used to filter out inapplicable methods, + * its behavior slightly configurable based on what stage of + * overloading resolution we're at. + * + * This method has boolean parameters, which is usually suboptimal + * but I didn't work out a better way. They don't have defaults, + * and the method's scope is limited. + */ + private[typechecker] def isApplicableBasedOnArity(tpe: Type, argsCount: Int, varargsStar: Boolean, tuplingAllowed: Boolean): Boolean = followApply(tpe) match { + case OverloadedType(pre, alts) => + alts exists (alt => isApplicableBasedOnArity(pre memberType alt, argsCount, varargsStar, tuplingAllowed)) + case _ => + val paramsCount = tpe.params.length + val simpleMatch = paramsCount == argsCount + val varargsTarget = isVarArgsList(tpe.params) + def varargsMatch = varargsTarget && (paramsCount - 1) <= argsCount + def tuplingMatch = tuplingAllowed && eligibleForTupleConversion(paramsCount, argsCount, varargsTarget) + + // A varargs star call, e.g. (x, y:_*) can only match a varargs method + // with the same number of parameters. See SI-5859 for an example of what + // would fail were this not enforced before we arrived at isApplicable. + if (varargsStar) + varargsTarget && simpleMatch + else + simpleMatch || varargsMatch || tuplingMatch + } + private[typechecker] def followApply(tp: Type): Type = tp match { case NullaryMethodType(restp) => val restp1 = followApply(restp) @@ -712,14 +748,6 @@ trait Infer extends Checkable { else OverloadedType(tp, appmeth.alternatives) } - def hasExactlyNumParams(tp: Type, n: Int): Boolean = tp match { - case OverloadedType(pre, alts) => - alts exists (alt => hasExactlyNumParams(pre.memberType(alt), n)) - case _ => - val len = tp.params.length - len == n || isVarArgsList(tp.params) && len <= n + 1 - } - /** * Verifies whether the named application is valid. The logic is very * similar to the one in NamesDefaults.removeNames. @@ -765,48 +793,75 @@ trait Infer extends Checkable { (argtpes1, argPos, namesOK) } - /** don't do a () to (()) conversion for methods whose second parameter - * is a varargs. This is a fairly kludgey way to address #3224. - * We'll probably find a better way to do this by identifying - * tupled and n-ary methods, but thiws is something for a future major revision. + /** True if the given parameter list can accept a tupled argument list, + * and the argument list can be tupled (based on its length.) */ - def isUnitForVarArgs(args: List[AnyRef], params: List[Symbol]): Boolean = - args.isEmpty && hasLength(params, 2) && isVarArgsList(params) + def eligibleForTupleConversion(paramsCount: Int, argsCount: Int, varargsTarget: Boolean): Boolean = { + def canSendTuple = argsCount match { + case 0 => !varargsTarget // avoid () to (()) conversion - SI-3224 + case 1 => false // can't tuple a single argument + case n => n <= MaxTupleArity // <= 22 arguments + } + def canReceiveTuple = paramsCount match { + case 1 => true + case 2 => varargsTarget + case _ => false + } + canSendTuple && canReceiveTuple + } + def eligibleForTupleConversion(formals: List[Type], argsCount: Int): Boolean = formals match { + case p :: Nil => eligibleForTupleConversion(1, argsCount, varargsTarget = isScalaRepeatedParamType(p)) + case _ :: p :: Nil if isScalaRepeatedParamType(p) => eligibleForTupleConversion(2, argsCount, varargsTarget = true) + case _ => false + } - /** Is there an instantiation of free type variables <code>undetparams</code> - * such that function type <code>ftpe</code> is applicable to - * <code>argtpes</code> and its result conform to <code>pt</code>? + /** The type of an argument list after being coerced to a tuple. + * @pre: the argument list is eligible for tuple conversion. + */ + private def typeAfterTupleConversion(argtpes: List[Type]): Type = ( + if (argtpes.isEmpty) UnitClass.tpe // aka "Tuple0" + else tupleType(argtpes map { + case NamedType(name, tp) => UnitClass.tpe // not a named arg - only assignments here + case RepeatedType(tp) => tp // but probably shouldn't be tupling a call containing :_* + case tp => tp + }) + ) + + /** If the argument list needs to be tupled for the parameter list, + * a list containing the type of the tuple. Otherwise, the original + * argument list. + */ + def tupleIfNecessary(formals: List[Type], argtpes: List[Type]): List[Type] = { + if (eligibleForTupleConversion(formals, argtpes.size)) + typeAfterTupleConversion(argtpes) :: Nil + else + argtpes + } + + /** Is there an instantiation of free type variables `undetparams` + * such that function type `ftpe` is applicable to + * `argtpes` and its result conform to `pt`? * - * @param undetparams ... * @param ftpe the type of the function (often a MethodType) - * @param argtpes the argument types; a NamedType(name, tp) for named + * @param argtpes0 the argument types; a NamedType(name, tp) for named * arguments. For each NamedType, if `name` does not exist in `ftpe`, that * type is set to `Unit`, i.e. the corresponding argument is treated as * an assignment expression (@see checkNames). - * @param pt ... - * @return ... */ private def isApplicable(undetparams: List[Symbol], ftpe: Type, argtpes0: List[Type], pt: Type): Boolean = ftpe match { case OverloadedType(pre, alts) => - alts exists (alt => isApplicable(undetparams, pre.memberType(alt), argtpes0, pt)) + alts exists (alt => isApplicable(undetparams, pre memberType alt, argtpes0, pt)) case ExistentialType(tparams, qtpe) => isApplicable(undetparams, qtpe, argtpes0, pt) case mt @ MethodType(params, _) => - val formals = formalTypes(mt.paramTypes, argtpes0.length, removeByName = false) - - def tryTupleApply: Boolean = { - // if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0 - val tupleArgTpes = actualTypes(argtpes0 map { - // no assignment is treated as named argument here - case NamedType(name, tp) => UnitClass.tpe - case tp => tp - }, formals.length) - - !sameLength(argtpes0, tupleArgTpes) && - !isUnitForVarArgs(argtpes0, params) && - isApplicable(undetparams, ftpe, tupleArgTpes, pt) + val argslen = argtpes0.length + val formals = formalTypes(mt.paramTypes, argslen, removeByName = false) + + def tryTupleApply = { + val tupled = tupleIfNecessary(mt.paramTypes, argtpes0) + (tupled ne argtpes0) && isApplicable(undetparams, ftpe, tupled, pt) } def typesCompatible(argtpes: List[Type]) = { val restpe = ftpe.resultType(argtpes) @@ -828,17 +883,16 @@ trait Infer extends Checkable { val lencmp = compareLengths(argtpes0, formals) if (lencmp > 0) tryTupleApply else if (lencmp == 0) { - if (!argtpes0.exists(_.isInstanceOf[NamedType])) { // fast track if no named arguments are used + if (!containsNamedType(argtpes0)) typesCompatible(argtpes0) - } else { // named arguments are used val (argtpes1, argPos, namesOK) = checkNames(argtpes0, params) // when using named application, the vararg param has to be specified exactly once - ( namesOK && (isIdentity(argPos) || sameLength(formals, params)) && - // nb. arguments and names are OK, check if types are compatible - typesCompatible(reorderArgs(argtpes1, argPos)) + ( namesOK + && (allArgsArePositional(argPos) || sameLength(formals, params)) + && typesCompatible(reorderArgs(argtpes1, argPos)) // nb. arguments and names are OK, check if types are compatible ) } } @@ -867,32 +921,37 @@ trait Infer extends Checkable { } /** - * Todo: Try to make isApplicable always safe (i.e. not cause TypeErrors). - * The chance of TypeErrors should be reduced through context errors + * Are arguments of the given types applicable to `ftpe`? Type argument inference + * is tried twice: firstly with the given expected type, and secondly with `WildcardType`. */ + // 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 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 + final case class Result(error: Boolean, applicable: Boolean) + def isApplicableWithExpectedType(pt0: Type): Result = { + val silentContext = context.makeSilent(reportAmbiguousErrors = false) + val applicable = newTyper(silentContext).infer.isApplicable(undetparams, ftpe, argtpes0, pt0) + Result(silentContext.hasErrors, applicable) + } + val canSecondTry = pt != WildcardType + val firstTry = isApplicableWithExpectedType(pt) + if (!firstTry.error || !canSecondTry) + firstTry.applicable + else { + val secondTry = isApplicableWithExpectedType(WildcardType) + // TODO `!secondTry.error &&` was faithfully replicated as part of the refactoring, but mayberedundant. + !secondTry.error && secondTry.applicable + } } - /** Is type <code>ftpe1</code> strictly more specific than type <code>ftpe2</code> + /** Is type `ftpe1` strictly more specific than type `ftpe2` * when both are alternatives in an overloaded function? * @see SLS (sec:overloading-resolution) - * - * @param ftpe1 ... - * @param ftpe2 ... - * @return ... */ def isAsSpecific(ftpe1: Type, ftpe2: Type): Boolean = ftpe1 match { case OverloadedType(pre, alts) => - alts exists (alt => isAsSpecific(pre.memberType(alt), ftpe2)) + alts exists (alt => isAsSpecific(pre memberType alt, ftpe2)) case et: ExistentialType => isAsSpecific(ftpe1.skolemizeExistential, ftpe2) //et.withTypeVars(isAsSpecific(_, ftpe2)) @@ -919,7 +978,7 @@ trait Infer extends Checkable { case _ => ftpe2 match { case OverloadedType(pre, alts) => - alts forall (alt => isAsSpecific(ftpe1, pre.memberType(alt))) + alts forall (alt => isAsSpecific(ftpe1, pre memberType alt)) case et: ExistentialType => et.withTypeVars(isAsSpecific(ftpe1, _)) case mt: MethodType => @@ -1063,7 +1122,7 @@ trait Infer extends Checkable { } def checkKindBounds(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol): List[String] = { - checkKindBounds0(tparams, targs, pre, owner, true) map { + checkKindBounds0(tparams, targs, pre, owner, explainErrors = true) map { case (targ, tparam, kindErrors) => kindErrors.errorMessage(targ, tparam) } @@ -1133,30 +1192,25 @@ trait Infer extends Checkable { /** Substitute free type variables `undetparams` of polymorphic argument * expression `tree` to `targs`, Error if `targs` is null. - * - * @param tree ... - * @param undetparams ... - * @param targs ... - * @param pt ... */ - private def substExpr(tree: Tree, undetparams: List[Symbol], - targs: List[Type], pt: Type) { + private def substExpr(tree: Tree, undetparams: List[Symbol], targs: List[Type], pt: Type) { if (targs eq null) { if (!tree.tpe.isErroneous && !pt.isErroneous) PolymorphicExpressionInstantiationError(tree, undetparams, pt) - } else { + } + else { new TreeTypeSubstituter(undetparams, targs).traverse(tree) notifyUndetparamsInferred(undetparams, targs) } } - /** Substitute free type variables <code>undetparams</code> of application - * <code>fn(args)</code>, given prototype <code>pt</code>. + /** Substitute free type variables `undetparams` of application + * `fn(args)`, given prototype `pt`. * * @param fn fn: the function that needs to be instantiated. * @param undetparams the parameters that need to be determined * @param args the actual arguments supplied in the call. - * @param pt the expected type of the function application + * @param pt0 the expected type of the function application * @return The type parameters that remain uninstantiated, * and that thus have not been substituted. */ @@ -1166,7 +1220,7 @@ trait Infer extends Checkable { try { val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 val formals = formalTypes(mt.paramTypes, args.length) - val argtpes = actualTypes(args map (x => elimAnonymousClass(x.tpe.deconst)), formals.length) + val argtpes = tupleIfNecessary(formals, args map (x => elimAnonymousClass(x.tpe.deconst))) val restpe = fn.tpe.resultType(argtpes) val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) = @@ -1202,25 +1256,22 @@ trait Infer extends Checkable { } } - def widen(tp: Type): Type = abstractTypesToBounds(tp) - - /** Substitute free type variables <code>undetparams</code> of type constructor - * <code>tree</code> in pattern, given prototype <code>pt</code>. + /** Substitute free type variables `undetparams` of type constructor + * `tree` in pattern, given prototype `pt`. * * @param tree the constuctor that needs to be instantiated * @param undetparams the undetermined type parameters - * @param pt the expected result type of the instance + * @param pt0 the expected result type of the instance */ def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) { - val pt = widen(pt0) + val pt = abstractTypesToBounds(pt0) val ptparams = freeTypeParamsOfTerms(pt) val ctorTp = tree.tpe val resTp = ctorTp.finalResultType debuglog("infer constr inst "+ tree +"/"+ undetparams +"/ pt= "+ pt +" pt0= "+ pt0 +" resTp: "+ resTp) - /** Compute type arguments for undetermined params - */ + /* Compute type arguments for undetermined params */ def inferFor(pt: Type): Option[List[Type]] = { val tvars = undetparams map freshVar val resTpV = resTp.instantiateTypeParams(undetparams, tvars) @@ -1232,13 +1283,13 @@ trait Infer extends Checkable { val variances = if (ctorTp.paramTypes.isEmpty) undetparams map varianceInType(ctorTp) else undetparams map varianceInTypes(ctorTp.paramTypes) - val targs = solvedTypes(tvars, undetparams, variances, true, lubDepth(List(resTp, pt))) + val targs = solvedTypes(tvars, undetparams, variances, upper = true, lubDepth(List(resTp, pt))) // checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") // no checkBounds here. If we enable it, test bug602 fails. // TODO: reinstate checkBounds, return params that fail to meet their bounds to undetparams Some(targs) } catch ifNoInstance { msg => - debuglog("NO INST "+ (tvars, tvars map (_.constr))) + debuglog("NO INST "+ ((tvars, tvars map (_.constr)))) NoConstructorInstanceError(tree, resTp, pt, msg) None } @@ -1272,109 +1323,62 @@ trait Infer extends Checkable { } } else None - (inferFor(pt) orElse inferForApproxPt) map { targs => + val inferred = inferFor(pt) orElse inferForApproxPt + + inferred match { + case Some(targs) => new TreeTypeSubstituter(undetparams, targs).traverse(tree) notifyUndetparamsInferred(undetparams, targs) - } getOrElse { - debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)")) + case _ => + def full = if (isFullyDefined(pt)) "(fully defined)" else "(not fully defined)" + devWarning(s"failed inferConstructorInstance for $tree: ${tree.tpe} undet=$undetparams, pt=$pt $full") // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) ConstrInstantiationError(tree, resTp, pt) } } - - def instBounds(tvar: TypeVar): (Type, Type) = { - val tparam = tvar.origin.typeSymbol - val instType = toOrigin(tvar.constr.inst) + def instBounds(tvar: TypeVar): TypeBounds = { + val tparam = tvar.origin.typeSymbol + val instType = toOrigin(tvar.constr.inst) + val TypeBounds(lo, hi) = tparam.info.bounds val (loBounds, hiBounds) = - if (instType != NoType && isFullyDefined(instType)) (List(instType), List(instType)) + if (isFullyDefined(instType)) (List(instType), List(instType)) else (tvar.constr.loBounds, tvar.constr.hiBounds) - val lo = lub(tparam.info.bounds.lo :: loBounds map toOrigin) - val hi = glb(tparam.info.bounds.hi :: hiBounds map toOrigin) - (lo, hi) + + TypeBounds( + lub(lo :: loBounds map toOrigin), + glb(hi :: hiBounds map toOrigin) + ) } def isInstantiatable(tvars: List[TypeVar]) = { val tvars1 = tvars map (_.cloneInternal) // Note: right now it's not clear that solving is complete, or how it can be made complete! // So we should come back to this and investigate. - solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (x => COVARIANT), false) + solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (_ => Variance.Covariant), upper = false) } - // this is quite nasty: it destructively changes the info of the syms of e.g., method type params (see #3692, where the type param T's bounds were set to >: T <: T, so that parts looped) + // this is quite nasty: it destructively changes the info of the syms of e.g., method type params + // (see #3692, where the type param T's bounds were set to > : T <: T, so that parts looped) // the changes are rolled back by restoreTypeBounds, but might be unintentially observed in the mean time def instantiateTypeVar(tvar: TypeVar) { - val tparam = tvar.origin.typeSymbol - if (false && - tvar.constr.inst != NoType && - isFullyDefined(tvar.constr.inst) && - (tparam.info.bounds containsType tvar.constr.inst)) { - context.nextEnclosing(_.tree.isInstanceOf[CaseDef]).pushTypeBounds(tparam) - tparam setInfo tvar.constr.inst - tparam resetFlag DEFERRED - debuglog("new alias of " + tparam + " = " + tparam.info) - } else { - val (lo, hi) = instBounds(tvar) - if (lo <:< hi) { - if (!((lo <:< tparam.info.bounds.lo) && (tparam.info.bounds.hi <:< hi)) // bounds were improved - && tparam != lo.typeSymbolDirect && tparam != hi.typeSymbolDirect) { // don't create illegal cycles - context.nextEnclosing(_.tree.isInstanceOf[CaseDef]).pushTypeBounds(tparam) - tparam setInfo TypeBounds(lo, hi) - debuglog("new bounds of " + tparam + " = " + tparam.info) - } else { - debuglog("redundant: "+tparam+" "+tparam.info+"/"+lo+" "+hi) - } - } else { - debuglog("inconsistent: "+tparam+" "+lo+" "+hi) - } - } - } - - /** Does `tp` contain any types that cannot be checked at run-time (i.e., after erasure, will isInstanceOf[erased(tp)] imply conceptualIsInstanceOf[tp]?) - * we should find a way to ask erasure: hey, is `tp` going to make it through you with all of its isInstanceOf resolving powers intact? - * TODO: at the very least, reduce duplication wrt checkCheckable - */ - def containsUnchecked(tp: Type): Boolean = { - def check(tp: Type, bound: List[Symbol]): Boolean = { - def isSurroundingTypeParam(sym: Symbol) = { - val e = context.scope.lookupEntry(sym.name) - ( (e ne null) - && (e.sym == sym ) - && !e.sym.isTypeParameterOrSkolem - && (e.owner == context.scope) - ) - } - def isLocalBinding(sym: Symbol) = ( - sym.isAbstractType && ( - (bound contains sym) - || (sym.name == tpnme.WILDCARD) - || isSurroundingTypeParam(sym) - ) - ) - tp.normalize match { - case SingleType(pre, _) => - check(pre, bound) - case TypeRef(_, ArrayClass, arg :: _) => - check(arg, bound) - case tp @ TypeRef(pre, sym, args) => - ( (sym.isAbstractType && !isLocalBinding(sym)) - || (args exists (x => !isLocalBinding(x.typeSymbol))) - || check(pre, bound) - ) - // case RefinedType(_, decls) if decls.nonEmpty => - // patternWarning(tp, "refinement ") - case RefinedType(parents, _) => - parents exists (p => check(p, bound)) - case ExistentialType(quantified, tp1) => - check(tp1, bound ::: quantified) - case _ => - false + val tparam = tvar.origin.typeSymbol + val TypeBounds(lo0, hi0) = tparam.info.bounds + val tb @ TypeBounds(lo1, hi1) = instBounds(tvar) + + if (lo1 <:< hi1) { + if (lo1 <:< lo0 && hi0 <:< hi1) // bounds unimproved + log(s"redundant bounds: discarding TypeBounds($lo1, $hi1) for $tparam, no improvement on TypeBounds($lo0, $hi0)") + else if (tparam == lo1.typeSymbolDirect || tparam == hi1.typeSymbolDirect) + log(s"cyclical bounds: discarding TypeBounds($lo1, $hi1) for $tparam because $tparam appears as bounds") + else { + context.enclosingCaseDef pushTypeBounds tparam + tparam setInfo logResult(s"updated bounds: $tparam from ${tparam.info} to")(tb) } } - check(tp, Nil) + else log(s"inconsistent bounds: discarding TypeBounds($lo1, $hi1)") } - /** Type intersection of simple type tp1 with general type tp2. * The result eliminates some redundancies. */ @@ -1393,16 +1397,16 @@ trait Infer extends Checkable { } def inferTypedPattern(tree0: Tree, pattp: Type, pt0: Type, canRemedy: Boolean): Type = { - val pt = widen(pt0) + val pt = abstractTypesToBounds(pt0) val ptparams = freeTypeParamsOfTerms(pt) val tpparams = freeTypeParamsOfTerms(pattp) def ptMatchesPattp = pt matchesPattern pattp.widen def pattpMatchesPt = pattp matchesPattern pt - /** If we can absolutely rule out a match we can fail early. - * 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 we can absolutely rule out a match we can fail early. + * 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) { IncompatibleScrutineeTypeError(tree0, pattp, pt) @@ -1438,9 +1442,9 @@ trait Infer extends Checkable { } tvars foreach instantiateTypeVar } - /** If the scrutinee has free type parameters but the pattern does not, - * we have to flip the arguments so the expected type is treated as more - * general when calculating the intersection. See run/bug2755.scala. + /* If the scrutinee has free type parameters but the pattern does not, + * we have to flip the arguments so the expected type is treated as more + * general when calculating the intersection. See run/bug2755.scala. */ if (tpparams.isEmpty && ptparams.nonEmpty) intersect(pattp, pt) else intersect(pt, pattp) @@ -1513,180 +1517,136 @@ trait Infer extends Checkable { } */ - /** Assign <code>tree</code> the symbol and type of the alternative which - * matches prototype <code>pt</code>, if it exists. + /** Assign `tree` the symbol and type of the alternative which + * matches prototype `pt`, if it exists. * If several alternatives match `pt`, take parameterless one. * If no alternative matches `pt`, take the parameterless one anyway. */ def inferExprAlternative(tree: Tree, pt: Type) = tree.tpe match { case OverloadedType(pre, alts) => tryTwice { isSecondTry => val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) - val noAlternatives = alts0.isEmpty - val alts1 = if (noAlternatives) alts else alts0 + val alts1 = if (alts0.isEmpty) alts else alts0 - //println("trying "+alts1+(alts1 map (_.tpe))+(alts1 map (_.locationString))+" for "+pt) - def improves(sym1: Symbol, sym2: Symbol): Boolean = - sym2 == NoSymbol || sym2.hasAnnotation(BridgeClass) || - { val tp1 = pre.memberType(sym1) + val bests = bestAlternatives(alts1) { (sym1, sym2) => + val tp1 = pre.memberType(sym1) val tp2 = pre.memberType(sym2) - (tp2 == ErrorType || - !global.typer.infer.isWeaklyCompatible(tp2, pt) && global.typer.infer.isWeaklyCompatible(tp1, pt) || - isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)) } - - val best = ((NoSymbol: Symbol) /: alts1) ((best, alt) => - if (improves(alt, best)) alt else best) - - val competing = alts1 dropWhile (alt => best == alt || improves(best, alt)) - - if (best == NoSymbol) { - if (settings.debug.value) { - tree match { - case Select(qual, _) => - Console.println("qual: " + qual + ":" + qual.tpe + - " with decls " + qual.tpe.decls + - " with members " + qual.tpe.members + - " with members " + qual.tpe.member(newTermName("$minus"))) - case _ => + + ( tp2 == ErrorType + || (!isWeaklyCompatible(tp2, pt) && isWeaklyCompatible(tp1, pt)) + || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) + ) } - } - // todo: missing test case - NoBestExprAlternativeError(tree, pt, isSecondTry) - } else if (!competing.isEmpty) { - if (noAlternatives) NoBestExprAlternativeError(tree, pt, isSecondTry) - else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt, isSecondTry) - else { + // todo: missing test case for bests.isEmpty + bests match { + case best :: Nil => tree setSymbol best setType (pre memberType best) + case best :: competing :: _ if alts0.nonEmpty => // SI-6912 Don't give up and leave an OverloadedType on the tree. // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try // unless an error is issued. We're not issuing an error, in the assumption that it would be // spurious in light of the erroneous expected type - setError(tree) + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) } - } else { -// val applicable = alts1 filter (alt => -// global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) -// checkNotShadowed(tree.pos, pre, best, applicable) - tree.setSymbol(best).setType(pre.memberType(best)) } } - } - - @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = { - val oldState = context.state - context.setBufferErrors() - val res = expr - val contextWithErrors = context.hasErrors - context.flushBuffer() - context.restoreState(oldState) - res && !contextWithErrors - } // Checks against the name of the parameter and also any @deprecatedName. private def paramMatchesName(param: Symbol, name: Name) = param.name == name || param.deprecatedParamName.exists(_ == name) - // Check the first parameter list the same way. - private def methodMatchesName(method: Symbol, name: Name) = method.paramss match { - case ps :: _ => ps exists (p => paramMatchesName(p, name)) - case _ => false + private def containsNamedType(argtpes: List[Type]): Boolean = argtpes match { + case Nil => false + case NamedType(_, _) :: _ => true + case _ :: rest => containsNamedType(rest) } - - private def resolveOverloadedMethod(argtpes: List[Type], eligible: List[Symbol]) = { + private def namesOfNamedArguments(argtpes: List[Type]) = + argtpes collect { case NamedType(name, _) => name } + + /** Given a list of argument types and eligible method overloads, whittle the + * list down to the methods which should be considered for specificity + * testing, taking into account here: + * - named arguments at the call site (keep only methods with name-matching parameters) + * - if multiple methods are eligible, drop any methods which take default arguments + * - drop any where arity cannot match under any conditions (allowing for + * overloaded applies, varargs, and tupling conversions) + * This method is conservative; it can tolerate some varieties of false positive, + * but no false negatives. + * + * @param eligible the overloaded method symbols + * @param argtpes the argument types at the call site + * @param varargsStar true if the call site has a `: _*` attached to the last argument + */ + private def overloadsToConsiderBySpecificity(eligible: List[Symbol], argtpes: List[Type], varargsStar: Boolean): List[Symbol] = { // If there are any foo=bar style arguments, and any of the overloaded // methods has a parameter named `foo`, then only those methods are considered. - val namesOfArgs = argtpes collect { case NamedType(name, _) => name } - val namesMatch = ( - if (namesOfArgs.isEmpty) Nil - else eligible filter { m => - namesOfArgs forall { name => - methodMatchesName(m, name) + val namesMatch = namesOfNamedArguments(argtpes) match { + case Nil => Nil + case names => eligible filter (m => names forall (name => m.info.params exists (p => paramMatchesName(p, name)))) } - } + if (namesMatch.nonEmpty) + namesMatch + else if (eligible.isEmpty || eligible.tail.isEmpty) + eligible + else + eligible filter (alt => + !alt.hasDefault && isApplicableBasedOnArity(alt.tpe, argtpes.length, varargsStar, tuplingAllowed = true) ) - - if (namesMatch.nonEmpty) namesMatch - else if (eligible.isEmpty || eligible.tail.isEmpty) eligible - else eligible filter { alt => - // for functional values, the `apply` method might be overloaded - val mtypes = followApply(alt.tpe) match { - case OverloadedType(_, alts) => alts map (_.tpe) - case t => t :: Nil } - // Drop those that use a default; keep those that use vararg/tupling conversion. - mtypes exists (t => - !t.typeSymbol.hasDefaultFlag && ( - compareLengths(t.params, argtpes) < 0 // tupling (*) - || hasExactlyNumParams(t, argtpes.length) // same nb or vararg - ) - ) - // (*) more arguments than parameters, but still applicable: tupling conversion works. - // todo: should not return "false" when paramTypes = (Unit) no argument is given - // (tupling would work) - } - } - /** Assign <code>tree</code> the type of an alternative which is applicable - * to <code>argtpes</code>, and whose result type is compatible with `pt`. + /** Assign `tree` the type of an alternative which is applicable + * to `argtpes`, and whose result type is compatible with `pt`. * If several applicable alternatives exist, drop the alternatives which use * default arguments, then select the most specialized one. * If no applicable alternative exists, and pt != WildcardType, try again * with pt = WildcardType. * Otherwise, if there is no best alternative, error. * - * @param argtpes contains the argument types. If an argument is named, as + * @param argtpes0 contains the argument types. If an argument is named, as * "a = 3", the corresponding type is `NamedType("a", Int)'. If the name * of some NamedType does not exist in an alternative's parameter names, * the type is replaces by `Unit`, i.e. the argument is treated as an * assignment expression. + * + * @pre tree.tpe is an OverloadedType. */ - def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], - argtpes: List[Type], pt0: Type, varArgsOnly: Boolean = false, lastInferAttempt: Boolean = true): Unit = tree.tpe match { - case OverloadedType(pre, alts) => + def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes0: List[Type], pt0: Type): Unit = { + val OverloadedType(pre, alts) = tree.tpe + var varargsStar = false + val argtpes = argtpes0 mapConserve { + case RepeatedType(tp) => varargsStar = true ; tp + case tp => tp + } + def followType(sym: Symbol) = followApply(pre memberType sym) + def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = { + val applicable0 = alts filter (alt => context inSilentMode (isApplicable(undetparams, followType(alt), argtpes, pt))) + val applicable = overloadsToConsiderBySpecificity(applicable0, argtpes, varargsStar) + val ranked = bestAlternatives(applicable)((sym1, sym2) => + isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2) + ) + ranked match { + case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous + case best :: Nil => tree setSymbol best setType (pre memberType best) // success + case Nil if pt eq WildcardType => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed + case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType + } + } + // This potentially makes up to four attempts: tryTwice may execute + // with and without views enabled, and bestForExpectedType will try again + // with pt = WildcardType if it fails with pt != WildcardType. + tryTwice { isLastTry => val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - tryTwice { isSecondTry => - debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") - - def varargsApplicableCheck(alt: Symbol) = !varArgsOnly || ( - isVarArgsList(alt.tpe.params) - && (argtpes.size >= alt.tpe.params.size) // must be checked now due to SI-5859 - ) - val applicable = resolveOverloadedMethod(argtpes, - alts filter (alt => - varargsApplicableCheck(alt) - && inSilentMode(context)(isApplicable(undetparams, followApply(pre memberType alt), argtpes, pt)) - ) - ) - - def improves(sym1: Symbol, sym2: Symbol) = { - // util.trace("improve "+sym1+sym1.locationString+" on "+sym2+sym2.locationString) - sym2 == NoSymbol || sym2.isError || sym2.hasAnnotation(BridgeClass) || - isStrictlyMoreSpecific(followApply(pre.memberType(sym1)), - followApply(pre.memberType(sym2)), sym1, sym2) - } - - val best = ((NoSymbol: Symbol) /: applicable) ((best, alt) => - if (improves(alt, best)) alt else best) - val competing = applicable.dropWhile(alt => best == alt || improves(best, alt)) - if (best == NoSymbol) { - if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt, isSecondTry && lastInferAttempt) - else inferMethodAlternative(tree, undetparams, argtpes, WildcardType, lastInferAttempt = isSecondTry) - } else if (!competing.isEmpty) { - AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt, isSecondTry && lastInferAttempt) - } else { -// checkNotShadowed(tree.pos, pre, best, applicable) - tree.setSymbol(best).setType(pre.memberType(best)) - } + debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") + bestForExpectedType(pt, isLastTry) } - case _ => } /** Try inference twice, once without views and once with views, * unless views are already disabled. - * - * @param infer ... */ def tryTwice(infer: Boolean => Unit): Unit = { if (context.implicitsEnabled) { - val saved = context.state + val savedContextMode = context.contextMode var fallback = false context.setBufferErrors() // We cache the current buffer because it is impossible to @@ -1700,29 +1660,26 @@ trait Infer extends Checkable { context.withImplicitsDisabled(infer(false)) if (context.hasErrors) { fallback = true - context.restoreState(saved) + context.contextMode = savedContextMode context.flushBuffer() infer(true) } } catch { case ex: CyclicReference => throw ex case ex: TypeError => // recoverable cyclic references - context.restoreState(saved) + context.contextMode = savedContextMode if (!fallback) infer(true) else () } finally { - context.restoreState(saved) + context.contextMode = savedContextMode context.updateBuffer(errorsToRestore) } } else infer(true) } - /** Assign <code>tree</code> the type of all polymorphic alternatives - * with <code>nparams</code> as the number of type parameters, if it exists. + /** Assign `tree` the type of all polymorphic alternatives + * with `nparams` as the number of type parameters, if it exists. * If no such polymorphic alternative exist, error. - * - * @param tree ... - * @param nparams ... */ def inferPolyAlternatives(tree: Tree, argtypes: List[Type]): Unit = { val OverloadedType(pre, alts) = tree.tpe @@ -1747,7 +1704,7 @@ trait Infer extends Checkable { } else if (sym.isOverloaded) { val xs = sym.alternatives - val tparams = new AsSeenFromMap(pre, xs.head.owner) mapOver xs.head.typeParams + val tparams = newAsSeenFromMap(pre, xs.head.owner) mapOver xs.head.typeParams val bounds = tparams map (_.tpeHK) // see e.g., #1236 val tpe = PolyType(tparams, OverloadedType(AntiPolyType(pre, bounds), xs)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 0ba30ffa73..d07297bb35 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -3,15 +3,11 @@ package typechecker import symtab.Flags._ import scala.tools.nsc.util._ -import scala.tools.nsc.util.ClassPath._ import scala.reflect.runtime.ReflectionUtils import scala.collection.mutable.ListBuffer -import scala.compat.Platform.EOL +import scala.reflect.ClassTag import scala.reflect.internal.util.Statistics import scala.reflect.macros.util._ -import java.lang.{Class => jClass} -import java.lang.reflect.{Array => jArray, Method => jMethod} -import scala.reflect.internal.util.Collections._ import scala.util.control.ControlThrowable import scala.reflect.macros.runtime.AbortMacroException @@ -47,8 +43,15 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { import definitions._ import treeInfo.{isRepeatedParamType => _, _} import MacrosStats._ + def globalSettings = global.settings + protected def findMacroClassLoader(): ClassLoader = { + val classpath = global.classPath.asURLs + macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath)) + ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + } + /** `MacroImplBinding` and its companion module are responsible for * serialization/deserialization of macro def -> impl bindings. * @@ -74,19 +77,19 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private case class MacroImplBinding( // Java class name of the class that contains the macro implementation // is used to load the corresponding object with Java reflection - val className: String, + className: String, // method name of the macro implementation // `className` and `methName` are all we need to reflectively invoke a macro implementation // because macro implementations cannot be overloaded - val methName: String, + methName: String, // flattens the macro impl's parameter lists having symbols replaced with metadata // currently metadata is an index of the type parameter corresponding to that type tag (if applicable) // f.ex. for: def impl[T: WeakTypeTag, U: WeakTypeTag, V](c: Context)(x: c.Expr[T]): (U, V) = ??? // `signature` will be equal to List(-1, -1, 0, 1) - val signature: List[Int], + signature: List[Int], // type arguments part of a macro impl ref (the right-hand side of a macro definition) // these trees don't refer to a macro impl, so we can pickle them as is - val targs: List[Tree]) + targs: List[Tree]) /** Macro def -> macro impl bindings are serialized into a `macroImpl` annotation * with synthetic content that carries the payload described in `MacroImplBinding`. @@ -128,7 +131,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // todo. refactor when fixing SI-5498 def className: String = { def loop(sym: Symbol): String = sym match { - case sym if sym.owner.isPackageClass => + case sym if sym.isTopLevel => val suffix = if (sym.isModuleClass) "$" else "" sym.fullName + suffix case sym => @@ -186,7 +189,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap val pickleVersionFormat = payload("versionFormat").asInstanceOf[Int] - if (versionFormat != pickleVersionFormat) throw new Error("macro impl binding format mismatch: expected $versionFormat, actual $pickleVersionFormat") + if (versionFormat != pickleVersionFormat) throw new Error(s"macro impl binding format mismatch: expected $versionFormat, actual $pickleVersionFormat") val className = payload("className").asInstanceOf[String] val methodName = payload("methodName").asInstanceOf[String] @@ -293,51 +296,51 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private def macroImplSig(macroDef: Symbol, tparams: List[TypeDef], vparamss: List[List[ValDef]], retTpe: Type): (List[List[Symbol]], Type) = { // had to move method's body to an object because of the recursive dependencies between sigma and param object SigGenerator { - def sigma(tpe: Type): Type = { - class SigmaTypeMap extends TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) => - val pre1 = pre match { - case ThisType(sym) if sym == macroDef.owner => - SingleType(SingleType(SingleType(NoPrefix, ctxParam), MacroContextPrefix), ExprValue) - case SingleType(NoPrefix, sym) => - mfind(vparamss)(_.symbol == sym) match { - case Some(macroDefParam) => SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue) - case _ => pre - } - case _ => - pre - } - TypeRef(pre1, sym, args map mapOver) - case _ => - mapOver(tp) - } + def WeakTagClass = getMember(MacroContextClass, tpnme.WeakTypeTag) + def ExprClass = getMember(MacroContextClass, tpnme.Expr) + val cache = scala.collection.mutable.Map[Symbol, Symbol]() + val ctxParam = makeParam(nme.macroContext, macroDef.pos, MacroContextClass.tpe, SYNTHETIC) + val paramss = List(ctxParam) :: mmap(vparamss)(param) + val implReturnType = typeRef(singleType(NoPrefix, ctxParam), ExprClass, List(sigma(retTpe))) + + object SigmaTypeMap extends TypeMap { + def mapPrefix(pre: Type) = pre match { + case ThisType(sym) if sym == macroDef.owner => + singleType(singleType(singleType(NoPrefix, ctxParam), MacroContextPrefix), ExprValue) + case SingleType(NoPrefix, sym) => + mfind(vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue)) + case _ => + mapOver(pre) + } + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = mapPrefix(pre) + val args1 = mapOverArgs(args, sym.typeParams) + if ((pre eq pre1) && (args eq args1)) tp + else typeRef(pre1, sym, args1) + case _ => + mapOver(tp) } - - new SigmaTypeMap() apply tpe } + def sigma(tpe: Type): Type = SigmaTypeMap(tpe) + + def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = + macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe + def implType(isType: Boolean, origTpe: Type): Type = { + def tsym = if (isType) WeakTagClass else ExprClass + def targ = origTpe.typeArgs.headOption getOrElse NoType - def makeParam(name: Name, pos: Position, tpe: Type, flags: Long = 0L) = - macroDef.newValueParameter(name, pos, flags) setInfo tpe - val ctxParam = makeParam(nme.macroContext, macroDef.pos, MacroContextClass.tpe, SYNTHETIC) - def implType(isType: Boolean, origTpe: Type): Type = if (isRepeatedParamType(origTpe)) - appliedType( - RepeatedParamClass.typeConstructor, - List(implType(isType, sigma(origTpe.typeArgs.head)))) - else { - val tsym = getMember(MacroContextClass, if (isType) tpnme.WeakTypeTag else tpnme.Expr) + scalaRepeatedType(implType(isType, sigma(targ))) + else typeRef(singleType(NoPrefix, ctxParam), tsym, List(sigma(origTpe))) - } - val paramCache = scala.collection.mutable.Map[Symbol, Symbol]() - def param(tree: Tree): Symbol = - paramCache.getOrElseUpdate(tree.symbol, { + } + def param(tree: Tree): Symbol = ( + cache.getOrElseUpdate(tree.symbol, { val sym = tree.symbol makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe), sym.flags) }) - - val paramss = List(ctxParam) :: mmap(vparamss)(param) - val implRetTpe = typeRef(singleType(NoPrefix, ctxParam), getMember(MacroContextClass, tpnme.Expr), List(sigma(retTpe))) + ) } import SigGenerator._ @@ -345,7 +348,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { macroTraceVerbose("tparams are: ")(tparams) macroTraceVerbose("vparamss are: ")(vparamss) macroTraceVerbose("retTpe is: ")(retTpe) - macroTraceVerbose("macroImplSig is: ")((paramss, implRetTpe)) + macroTraceVerbose("macroImplSig is: ")((paramss, implReturnType)) } /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method, @@ -362,7 +365,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // Phase I: sanity checks val macroDef = macroDdef.symbol macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos)) - assert(macroDef.isTermMacro, macroDdef) + assert(macroDef.isMacro, macroDdef) if (fastTrack contains macroDef) MacroDefIsFastTrack() if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) MacroFeatureNotEnabled() @@ -377,7 +380,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // doesn't manifest itself as an error in the resulting tree val prevNumErrors = reporter.ERROR.count var rhs1 = typer.typed1(rhs, EXPRmode, WildcardType) - def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous + def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isMacro && !rhs1.symbol.isErroneous while (rhsNeedsMacroExpansion) { rhs1 = macroExpand1(typer, rhs1) match { case Success(expanded) => @@ -390,8 +393,12 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } case Fallback(fallback) => typer.typed1(fallback, EXPRmode, WildcardType) - case Other(result) => - result + case Delayed(delayed) => + delayed + case Skipped(skipped) => + skipped + case Failure(failure) => + failure } } val typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors @@ -474,24 +481,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * Loads classes from from -cp (aka the library classpath). * Is also capable of detecting REPL and reusing its classloader. */ - lazy val macroClassloader: ClassLoader = { - if (global.forMSIL) - throw new UnsupportedOperationException("Scala reflection not available on this platform") - - val classpath = global.classPath.asURLs - macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath)) - val loader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) - - // a heuristic to detect the REPL - if (global.settings.exposeEmptyPackage.value) { - macroLogVerbose("macro classloader: initializing from a REPL classloader".format(global.classPath.asURLs)) - import scala.tools.nsc.interpreter._ - val virtualDirectory = global.settings.outputDirs.getSingleOutput.get - new AbstractFileClassLoader(virtualDirectory, loader) {} - } else { - loader - } - } + lazy val macroClassloader: ClassLoader = findMacroClassLoader() /** Produces a function that can be used to invoke macro implementation for a given macro definition: * 1) Looks up macro implementation symbol in this universe. @@ -503,7 +493,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * `null` otherwise. */ type MacroRuntime = MacroArgs => Any - private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime] + private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime]() private def macroRuntime(macroDef: Symbol): MacroRuntime = { macroTraceVerbose("looking for macro implementation: ")(macroDef) if (fastTrack contains macroDef) { @@ -541,19 +531,22 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } } - private def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = + private def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = { new { val universe: self.global.type = self.global val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] - val expandee = expandeeTree + val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse expandeeTree + val macroRole = universe.analyzer.macroExpanderAttachment(expandeeTree).role } with UnaffiliatedMacroContext { val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing) override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */) } + } /** Calculate the arguments to pass to a macro implementation when expanding the provided tree. */ case class MacroArgs(c: MacroContext, others: List[Any]) + private def macroArgs(typer: Typer, expandee: Tree): MacroArgs = { val macroDef = expandee.symbol val prefixTree = expandee.collect{ case Select(qual, name) => qual }.headOption.getOrElse(EmptyTree) @@ -582,9 +575,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val preparedArgss: List[List[Any]] = if (fastTrack contains macroDef) { - if (fastTrack(macroDef) validate context) argss + // Take a dry run of the fast track implementation + if (fastTrack(macroDef) validate expandee) argss else typer.TyperErrorGen.MacroPartialApplicationError(expandee) - } else { + } + else { // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences // consider the following example: // @@ -650,21 +645,37 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private def popMacroContext() = _openMacros = _openMacros.tail def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition - private sealed abstract class MacroExpansionResult - private case class Success(expanded: Tree) extends MacroExpansionResult - private case class Fallback(fallback: Tree) extends MacroExpansionResult { currentRun.seenMacroExpansionsFallingBack = true } - private case class Other(result: Tree) extends MacroExpansionResult - private def Delay(expanded: Tree) = Other(expanded) - private def Skip(expanded: Tree) = Other(expanded) - private def Cancel(expandee: Tree) = Other(expandee) - private def Failure(expandee: Tree) = Other(expandee) + /** Describes the role that the macro expandee is performing. + */ + type MacroRole = String + final def APPLY_ROLE: MacroRole = "APPLY_ROLE" + private val roleNames = Map(APPLY_ROLE -> "apply") /** Performs macro expansion: - * 1) Checks whether the expansion needs to be delayed (see `mustDelayMacroExpansion`) - * 2) Loads macro implementation using `macroMirror` - * 3) Synthesizes invocation arguments for the macro implementation - * 4) Checks that the result is a tree bound to this universe - * 5) Typechecks the result against the return type of the macro definition + * + * ========= Expandable trees ========= + * + * A term of one of the following shapes: + * + * Ident(<term macro>) + * Select(<any qualifier>, <term macro>) + * TypeApply(<any of the above>, <targs>) + * Apply(...Apply(<any of the above>, <args1>)...<argsN>) + * + * ========= Macro expansion ========= + * + * First of all `macroExpandXXX`: + * 1) If necessary desugars the `expandee` to fit into `macroExpand1` + * + * Then `macroExpand1`: + * 2) Checks whether the expansion needs to be delayed (see `mustDelayMacroExpansion`) + * 3) Loads macro implementation using `macroMirror` + * 4) Synthesizes invocation arguments for the macro implementation + * 5) Checks that the result is a tree or an expr bound to this universe + * + * Finally `macroExpandXXX`: + * 6) Validates the expansion against the white list of supported tree shapes + * 7) Typechecks the result as required by the circumstances of the macro application * * If -Ymacro-debug-lite is enabled, you will get basic notifications about macro expansion * along with macro expansions logged in the form that can be copy/pasted verbatim into REPL. @@ -675,60 +686,126 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * * @return * the expansion result if the expansion has been successful, - * the fallback method invocation if the expansion has been unsuccessful, but there is a fallback, + * the fallback tree if the expansion has been unsuccessful, but there is a fallback, * the expandee unchanged if the expansion has been delayed, * the expandee fully expanded if the expansion has been delayed before and has been expanded now, * the expandee with an error marker set if the expansion has been cancelled due malformed arguments or implementation * the expandee with an error marker set if there has been an error */ - def macroExpand(typer: Typer, expandee: Tree, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = { - if (settings.Ymacronoexpand.value) return expandee // SI-6812 - val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null - if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) - try { - macroExpand1(typer, expandee) match { - case Success(expanded) => - try { - def typecheck(phase: String, tree: Tree, pt: Type): Tree = { - if (tree.isErroneous) return tree - macroLogVerbose(s"typechecking against $phase $pt: $expanded") - val numErrors = reporter.ERROR.count - def hasNewErrors = reporter.ERROR.count > numErrors - val result = typer.context.withImplicitsEnabled(typer.typed(tree, EXPRmode, pt)) - macroTraceVerbose(s"""${if (hasNewErrors) "failed to typecheck" else "successfully typechecked"} against $phase $pt:\n$result\n""")(result) + private abstract class MacroExpander[Result: ClassTag](val role: MacroRole, val typer: Typer, val expandee: Tree) { + def allowExpandee(expandee: Tree): Boolean = true + def allowExpanded(expanded: Tree): Boolean = true + def allowedExpansions: String = "anything" + def allowResult(result: Result): Boolean = true + + def onSuccess(expanded: Tree): Result + def onFallback(expanded: Tree): Result + def onSuppressed(expandee: Tree): Result = expandee match { case expandee: Result => expandee } + def onDelayed(expanded: Tree): Result = expanded match { case expanded: Result => expanded } + def onSkipped(expanded: Tree): Result = expanded match { case expanded: Result => expanded } + def onFailure(expanded: Tree): Result = { typer.infer.setError(expandee); expandee match { case expandee: Result => expandee } } + + def apply(desugared: Tree): Result = { + if (isMacroExpansionSuppressed(desugared)) onSuppressed(expandee) + else expand(desugared) + } + + protected def expand(desugared: Tree): Result = { + def showDetailed(tree: Tree) = showRaw(tree, printIds = true, printTypes = true) + def summary() = s"expander = $this, expandee = ${showDetailed(expandee)}, desugared = ${if (expandee == desugared) () else showDetailed(desugared)}" + if (macroDebugVerbose) println(s"macroExpand: ${summary()}") + assert(allowExpandee(expandee), summary()) + + val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null + if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) + try { + linkExpandeeAndDesugared(expandee, desugared, role) + macroExpand1(typer, desugared) match { + case Success(expanded) => + if (allowExpanded(expanded)) { + // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc + val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext() + if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1) + if (allowResult(expanded1)) expanded1 else onFailure(expanded) + } else { + typer.TyperErrorGen.MacroInvalidExpansionError(expandee, roleNames(role), allowedExpansions) + onFailure(expanded) } + case Fallback(fallback) => onFallback(fallback) + case Delayed(delayed) => onDelayed(delayed) + case Skipped(skipped) => onSkipped(skipped) + case Failure(failure) => onFailure(failure) + } + } finally { + if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start) + } + } + } - var expectedTpe = expandee.tpe - if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType - var typechecked = typecheck("macro def return type", expanded, expectedTpe) - typechecked = typecheck("expected type", typechecked, pt) - typechecked - } finally { - popMacroContext() - } - case Fallback(fallback) => - typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) - case Other(result) => - result + /** Expands a tree that carries a term, which happens to be a term macro. + * @see MacroExpander + */ + private abstract class TermMacroExpander(role: MacroRole, typer: Typer, expandee: Tree, mode: Mode, pt: Type) + extends MacroExpander[Tree](role, typer, expandee) { + override def allowedExpansions: String = "term trees" + override def allowExpandee(expandee: Tree) = expandee.isTerm + override def onSuccess(expanded: Tree) = typer.typed(expanded, mode, pt) + override def onFallback(fallback: Tree) = typer.typed(fallback, mode, pt) + } + + /** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`. + * @see MacroExpander + */ + def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type) = { + object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, pt) { + override def onSuccess(expanded: Tree) = { + // prematurely annotate the tree with a macro expansion attachment + // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup + linkExpandeeAndExpanded(expandee, expanded) + var expectedTpe = expandee.tpe + if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType + // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled + // therefore we need to re-enable the conversions back temporarily + if (macroDebugVerbose) println(s"typecheck #1 (against expectedTpe = $expectedTpe): $expanded") + val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, expectedTpe)) + if (expanded1.isErrorTyped) { + if (macroDebugVerbose) println(s"typecheck #1 has failed: ${typer.context.reportBuffer.errors}") + expanded1 + } else { + if (macroDebugVerbose) println(s"typecheck #2 (against pt = $pt): $expanded1") + val expanded2 = typer.context.withImplicitsEnabled(super.onSuccess(expanded1)) + if (macroDebugVerbose && expanded2.isErrorTyped) println(s"typecheck #2 has failed: ${typer.context.reportBuffer.errors}") + expanded2 + } } - } finally { - if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start) } + expander(expandee) } + /** Captures statuses of macro expansions performed by `macroExpand1'. + */ + private sealed abstract class MacroStatus(val result: Tree) + private case class Success(expanded: Tree) extends MacroStatus(expanded) + private case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true } + private case class Delayed(delayed: Tree) extends MacroStatus(delayed) + private case class Skipped(skipped: Tree) extends MacroStatus(skipped) + private case class Failure(failure: Tree) extends MacroStatus(failure) + private def Delay(expanded: Tree) = Delayed(expanded) + private def Skip(expanded: Tree) = Skipped(expanded) + private def Cancel(expandee: Tree) = Failure(expandee) + /** Does the same as `macroExpand`, but without typechecking the expansion * Meant for internal use within the macro infrastructure, don't use it elsewhere. */ - private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult = + private def macroExpand1(typer: Typer, expandee: Tree): MacroStatus = { // verbose printing might cause recursive macro expansions, so I'm shutting it down here withInfoLevel(nodePrinters.InfoLevel.Quiet) { if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee) - return Cancel(typer.infer.setError(expandee)) + Cancel(typer.infer.setError(expandee)) } - - try { + else try { val runtime = macroRuntime(expandee.symbol) if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) else macroExpandWithoutRuntime(typer, expandee) @@ -736,18 +813,23 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { case typer.TyperErrorGen.MacroExpansionException => Failure(expandee) } } + } /** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded * Meant for internal use within the macro infrastructure, don't use it elsewhere. */ - private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroExpansionResult = { + private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroStatus = { val wasDelayed = isDelayed(expandee) val undetparams = calculateUndetparams(expandee) val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty (wasDelayed, nowDelayed) match { - case (true, true) => Delay(expandee) - case (true, false) => Skip(macroExpandAll(typer, expandee)) + case (true, true) => + Delay(expandee) + case (true, false) => + val expanded = macroExpandAll(typer, expandee) + if (expanded exists (_.isErroneous)) Failure(expandee) + else Skip(expanded) case (false, true) => macroLogLite("macro expansion is delayed: %s".format(expandee)) delayed += expandee -> undetparams @@ -762,15 +844,16 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { def hasNewErrors = reporter.ERROR.count > numErrors val expanded = { pushMacroContext(args.c); runtime(args) } if (hasNewErrors) MacroGeneratedTypeError(expandee) + def validateResultingTree(expanded: Tree) = { + macroLogVerbose("original:") + macroLogLite("" + expanded + "\n" + showRaw(expanded)) + val freeSyms = expanded.freeTerms ++ expanded.freeTypes + freeSyms foreach (sym => MacroFreeSymbolError(expandee, sym)) + Success(atPos(enclosingMacroPosition.focus)(expanded)) + } expanded match { - case expanded: Expr[_] => - macroLogVerbose("original:") - macroLogLite("" + expanded.tree + "\n" + showRaw(expanded.tree)) - val freeSyms = expanded.tree.freeTerms ++ expanded.tree.freeTypes - freeSyms foreach (sym => MacroFreeSymbolError(expandee, sym)) - Success(atPos(enclosingMacroPosition.focus)(expanded.tree updateAttachment MacroExpansionAttachment(expandee))) - case _ => - MacroExpansionIsNotExprError(expandee, expanded) + case expanded: Expr[_] if expandee.symbol.isTermMacro => validateResultingTree(expanded.tree) + case _ => MacroExpansionHasInvalidTypeError(expandee, expanded) } } catch { case ex: Throwable => @@ -791,7 +874,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { /** Expands a macro when a runtime (i.e. the macro implementation) cannot be loaded * Meant for internal use within the macro infrastructure, don't use it elsewhere. */ - private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroExpansionResult = { + private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroStatus = { import typer.TyperErrorGen._ val fallbackSym = expandee.symbol.nextOverriddenSymbol orElse MacroImplementationNotFoundError(expandee) macroTraceLite("falling back to: ")(fallbackSym) @@ -819,7 +902,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * 2) undetparams (sym.isTypeParameter && !sym.isSkolem) */ var hasPendingMacroExpansions = false - private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]] + private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]]() private def isDelayed(expandee: Tree) = delayed contains expandee private def calculateUndetparams(expandee: Tree): scala.collection.mutable.Set[Int] = delayed.get(expandee).getOrElse { @@ -832,7 +915,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { macroLogVerbose("calculateUndetparams: %s".format(calculated)) calculated map (_.id) } - private val undetparams = perRunCaches.newSet[Int] + private val undetparams = perRunCaches.newSet[Int]() def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = { undetparams ++= newUndets map (_.id) if (macroDebugVerbose) newUndets foreach (sym => println("undetParam added: %s".format(sym))) @@ -861,13 +944,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { new Transformer { override def transform(tree: Tree) = super.transform(tree match { // todo. expansion should work from the inside out - case tree if (delayed contains tree) && calculateUndetparams(tree).isEmpty => + case tree if (delayed contains tree) && calculateUndetparams(tree).isEmpty && !tree.isErroneous => val context = tree.attachments.get[MacroRuntimeAttachment].get.typerContext delayed -= tree context.implicitsEnabled = typer.context.implicitsEnabled context.enrichmentEnabled = typer.context.enrichmentEnabled context.macrosEnabled = typer.context.macrosEnabled - macroExpand(newTyper(context), tree, EXPRmode, WildcardType) + macroExpandApply(newTyper(context), tree, EXPRmode, WildcardType) case _ => tree }) diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 99557d1527..50383a1e78 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package typechecker import symtab.Flags._ -import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.StringOps.{ ojoin } import scala.reflect.ClassTag import scala.reflect.runtime.{ universe => ru } @@ -30,66 +29,30 @@ trait MethodSynthesis { if (sym.isLazy) ValDef(sym, body) else DefDef(sym, body) - def applyTypeInternal(tags: List[TT[_]]): Type = { - val symbols = tags map compilerSymbolFromTag - val container :: args = symbols - val tparams = container.typeConstructor.typeParams - - // Conservative at present - if manifests were more usable this could do a lot more. - // [Eugene to Paul] all right, they are now. what do you have in mind? - require(symbols forall (_ ne NoSymbol), "Must find all tags: " + symbols) - require(container.owner.isPackageClass, "Container must be a top-level class in a package: " + container) - require(tparams.size == args.size, "Arguments must match type constructor arity: " + tparams + ", " + args) - - appliedType(container, args map (_.tpe): _*) - } - - def companionType[T](implicit ct: CT[T]) = - rootMirror.getRequiredModule(ct.runtimeClass.getName).tpe - - // Use these like `applyType[List, Int]` or `applyType[Map, Int, String]` - def applyType[CC](implicit t1: TT[CC]): Type = - applyTypeInternal(List(t1)) - - def applyType[CC[X1], X1](implicit t1: TT[CC[_]], t2: TT[X1]): Type = - applyTypeInternal(List(t1, t2)) - - def applyType[CC[X1, X2], X1, X2](implicit t1: TT[CC[_,_]], t2: TT[X1], t3: TT[X2]): Type = - applyTypeInternal(List(t1, t2, t3)) - - def applyType[CC[X1, X2, X3], X1, X2, X3](implicit t1: TT[CC[_,_,_]], t2: TT[X1], t3: TT[X2], t4: TT[X3]): Type = - applyTypeInternal(List(t1, t2, t3, t4)) - - def newMethodType[F](owner: Symbol)(implicit t: TT[F]): Type = { - val fnSymbol = compilerSymbolFromTag(t) - val formals = compilerTypeFromTag(t).typeArguments - assert(fnSymbol isSubClass FunctionClass(formals.size - 1), (owner, t)) - val params = owner newSyntheticValueParams formals - MethodType(params, formals.last) - } - - /** The annotations amongst those found on the original symbol which - * should be propagated to this kind of accessor. - */ - def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = { - initial filter { ann => - // There are no meta-annotation arguments attached to `ann` - if (ann.metaAnnotations.isEmpty) { - // A meta-annotation matching `annotKind` exists on `ann`'s definition. - (ann.defaultTargets contains category) || - // `ann`'s definition has no meta-annotations, and `keepClean` is true. - (ann.defaultTargets.isEmpty && keepClean) - } - // There are meta-annotation arguments, and one of them matches `annotKind` - else ann.metaAnnotations exists (_ matches category) + /** The annotations amongst those found on the original symbol which + * should be propagated to this kind of accessor. + */ + def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = { + initial filter { ann => + // There are no meta-annotation arguments attached to `ann` + if (ann.metaAnnotations.isEmpty) { + // A meta-annotation matching `annotKind` exists on `ann`'s definition. + (ann.defaultTargets contains category) || + // `ann`'s definition has no meta-annotations, and `keepClean` is true. + (ann.defaultTargets.isEmpty && keepClean) } + // There are meta-annotation arguments, and one of them matches `annotKind` + else ann.metaAnnotations exists (_ matches category) } - } + } + } import synthesisUtil._ class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { def mkThis = This(clazz) setPos clazz.pos.focus - def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)(Select(mkThis, sym)) + def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)( + if (clazz.isClass) Select(This(clazz), sym) else Ident(sym) + ) private def isOverride(name: TermName) = clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz)) @@ -99,7 +62,7 @@ trait MethodSynthesis { overrideFlag | SYNTHETIC } def newMethodFlags(method: Symbol) = { - val overrideFlag = if (isOverride(method.name)) OVERRIDE else 0L + val overrideFlag = if (isOverride(method.name.toTermName)) OVERRIDE else 0L (method.flags | overrideFlag | SYNTHETIC) & ~DEFERRED } @@ -107,11 +70,13 @@ trait MethodSynthesis { localTyper typed ValOrDefDef(method, f(method)) private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + val name1 = name.toTermName + val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1)) finishMethod(m setInfoAndEnter info, f) } private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + val name1 = name.toTermName + val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1)) finishMethod(m setInfoAndEnter infoFn(m), f) } private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = { @@ -119,22 +84,9 @@ trait MethodSynthesis { finishMethod(clazz.info.decls enter m, f) } - private def cloneInternal(original: Symbol, f: Symbol => Tree): Tree = - cloneInternal(original, f, original.name) - def clazzMember(name: Name) = clazz.info nonPrivateMember name def typeInClazz(sym: Symbol) = clazz.thisType memberType sym - /** Function argument takes the newly created method symbol of - * the same type as `name` in clazz, and returns the tree to be - * added to the template. - */ - def overrideMethod(name: Name)(f: Symbol => Tree): Tree = - overrideMethod(clazzMember(name))(f) - - def overrideMethod(original: Symbol)(f: Symbol => Tree): Tree = - cloneInternal(original, sym => f(sym setFlag OVERRIDE)) - def deriveMethod(original: Symbol, nameFn: Name => Name)(f: Symbol => Tree): Tree = cloneInternal(original, f, nameFn(original.name)) @@ -174,7 +126,7 @@ trait MethodSynthesis { /** There are two key methods in here. * - * 1) Enter methods such as enterGetterSetterare called + * 1) Enter methods such as enterGetterSetter are called * from Namer with a tree which may generate further trees such as accessors or * implicit wrappers. Some setup is performed. In general this creates symbols * and enters them into the scope of the owner. @@ -219,14 +171,46 @@ trait MethodSynthesis { enterBeans(tree) } + /** This is called for those ValDefs which addDerivedTrees ignores, but + * which might have a warnable annotation situation. + */ + private def warnForDroppedAnnotations(tree: Tree) { + val annotations = tree.symbol.initialize.annotations + val targetClass = defaultAnnotationTarget(tree) + val retained = deriveAnnotations(annotations, targetClass, keepClean = true) + + annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass)) + } + private def issueAnnotationWarning(tree: Tree, ann: AnnotationInfo, defaultTarget: Symbol) { + global.reporter.warning(ann.pos, + s"no valid targets for annotation on ${tree.symbol} - it is discarded unused. " + + s"You may specify targets with meta-annotations, e.g. @($ann @${defaultTarget.name})") + } + def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) => // If we don't save the annotations, they seem to wander off. val annotations = stat.symbol.initialize.annotations - ( allValDefDerived(vd) + val trees = ( + allValDefDerived(vd) map (acc => atPos(vd.pos.focus)(acc derive annotations)) filterNot (_ eq EmptyTree) ) + // Verify each annotation landed safely somewhere, else warn. + // Filtering when isParamAccessor is a necessary simplification + // because there's a bunch of unwritten annotation code involving + // the propagation of annotations - constructor parameter annotations + // may need to make their way to parameters of the constructor as + // well as fields of the class, etc. + if (!mods.isParamAccessor) annotations foreach (ann => + if (!trees.exists(_.symbol hasAnnotation ann.symbol)) + issueAnnotationWarning(vd, ann, GetterTargetClass) + ) + + trees + case vd: ValDef => + warnForDroppedAnnotations(vd) + vd :: Nil case cd @ ClassDef(mods, _, _, _) if mods.isImplicit => val annotations = stat.symbol.initialize.annotations // TODO: need to shuffle annotations between wrapper and class. @@ -253,8 +237,7 @@ trait MethodSynthesis { ) def beanAccessors(vd: ValDef): List[DerivedFromValDef] = { val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil - if (forMSIL) Nil - else if (vd.symbol hasAnnotation BeanPropertyAttr) + if (vd.symbol hasAnnotation BeanPropertyAttr) BeanGetter(vd) :: setter else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr) BooleanBeanGetter(vd) :: setter @@ -312,7 +295,6 @@ trait MethodSynthesis { // Final methods to make the rest easier to reason about. final def mods = tree.mods final def basisSym = tree.symbol - final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra } trait DerivedFromClassDef extends DerivedFromMemberDef { @@ -444,7 +426,7 @@ trait MethodSynthesis { Nil, Nil, tpt, - if (mods.isDeferred) EmptyTree else gen.mkCheckInit(fieldSelection) + if (mods.isDeferred) EmptyTree else fieldSelection ) setSymbol derivedSym } } @@ -458,7 +440,7 @@ trait MethodSynthesis { case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) { class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol) extends ChangeOwnerTraverser(oldowner, newowner) { - + override def traverse(tree: Tree) { tree match { case _: DefTree => change(tree.symbol.moduleClass) @@ -489,7 +471,7 @@ trait MethodSynthesis { } } case class Setter(tree: ValDef) extends DerivedSetter { - def name = nme.getterToSetter(tree.name) + def name = tree.setterName def category = SetterTargetClass def flagsMask = SetterFlags def flagsExtra = ACCESSOR @@ -497,7 +479,7 @@ trait MethodSynthesis { override def derivedSym = basisSym.setter(enclClass) } case class Field(tree: ValDef) extends DerivedFromValDef { - def name = nme.getterToLocal(tree.name) + def name = tree.localName def category = FieldTargetClass def flagsMask = FieldFlags def flagsExtra = PrivateLocal @@ -558,7 +540,7 @@ trait MethodSynthesis { // No Symbols available. private def beanAccessorsFromNames(tree: ValDef) = { - val ValDef(mods, name, tpt, _) = tree + val ValDef(mods, _, _, _) = tree val hasBP = mods hasAnnotationNamed tpnme.BeanPropertyAnnot val hasBoolBP = mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot @@ -575,9 +557,6 @@ trait MethodSynthesis { } protected def enterBeans(tree: ValDef) { - if (forMSIL) - return - val ValDef(mods, name, _, _) = tree val beans = beanAccessorsFromNames(tree) if (beans.nonEmpty) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Modes.scala b/src/compiler/scala/tools/nsc/typechecker/Modes.scala deleted file mode 100644 index d650762ac1..0000000000 --- a/src/compiler/scala/tools/nsc/typechecker/Modes.scala +++ /dev/null @@ -1,140 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package typechecker - -/** Mode constants. - */ -trait Modes { - /** NOmode, EXPRmode and PATTERNmode are mutually exclusive. - */ - final val NOmode = 0x000 - final val EXPRmode = 0x001 - final val PATTERNmode = 0x002 - - /** TYPEmode needs a comment. <-- XXX. - */ - final val TYPEmode = 0x004 - - /** SCCmode is orthogonal to above. When set we are - * in the this or super constructor call of a constructor. - */ - final val SCCmode = 0x008 - - /** FUNmode is orthogonal to above. - * When set we are looking for a method or constructor. - */ - final val FUNmode = 0x010 - - /** POLYmode is orthogonal to above. - * When set expression types can be polymorphic. - */ - final val POLYmode = 0x020 - - /** QUALmode is orthogonal to above. When set - * expressions may be packages and Java statics modules. - */ - final val QUALmode = 0x040 - - /** TAPPmode is set for the function/type constructor - * part of a type application. When set we do not decompose PolyTypes. - */ - final val TAPPmode = 0x080 - - /** SUPERCONSTRmode is set for the super - * in a superclass constructor call super.<init>. - */ - final val SUPERCONSTRmode = 0x100 - - /** SNDTRYmode indicates that an application is typed for the 2nd time. - * In that case functions may no longer be coerced with implicit views. - */ - final val SNDTRYmode = 0x200 - - /** LHSmode is set for the left-hand side of an assignment. - */ - final val LHSmode = 0x400 - - /** STARmode is set when star patterns are allowed. - * (This was formerly called REGPATmode.) - */ - final val STARmode = 0x1000 - - /** ALTmode is set when we are under a pattern alternative. - */ - final val ALTmode = 0x2000 - - /** HKmode is set when we are typing a higher-kinded type. - * adapt should then check kind-arity based on the prototypical type's - * kind arity. Type arguments should not be inferred. - */ - final val HKmode = 0x4000 // @M: could also use POLYmode | TAPPmode - - /** BYVALmode is set when we are typing an expression - * that occurs in a by-value position. An expression e1 is in by-value - * position within expression e2 iff it will be reduced to a value at that - * position during the evaluation of e2. Examples are by-value function - * arguments or the conditional of an if-then-else clause. - * This mode has been added to support continuations. - */ - final val BYVALmode = 0x8000 - - /** TYPEPATmode is set when we are typing a type in a pattern. - */ - final val TYPEPATmode = 0x10000 - - /** RETmode is set when we are typing a return expression. - */ - final val RETmode = 0x20000 - - final private val StickyModes = EXPRmode | PATTERNmode | TYPEmode | ALTmode - - final def onlyStickyModes(mode: Int) = - mode & StickyModes - - final def forFunMode(mode: Int) = - mode & (StickyModes | SCCmode) | FUNmode | POLYmode | BYVALmode - - final def forTypeMode(mode: Int) = - if (inAnyMode(mode, PATTERNmode | TYPEPATmode)) TYPEmode | TYPEPATmode - else TYPEmode - - final def inAllModes(mode: Int, required: Int) = (mode & required) == required - final def inAnyMode(mode: Int, required: Int) = (mode & required) != 0 - final def inNoModes(mode: Int, prohibited: Int) = (mode & prohibited) == 0 - final def inHKMode(mode: Int) = (mode & HKmode) != 0 - final def inFunMode(mode: Int) = (mode & FUNmode) != 0 - final def inPolyMode(mode: Int) = (mode & POLYmode) != 0 - final def inPatternMode(mode: Int) = (mode & PATTERNmode) != 0 - final def inExprModeOr(mode: Int, others: Int) = (mode & (EXPRmode | others)) != 0 - final def inExprModeButNot(mode: Int, prohibited: Int) = - (mode & (EXPRmode | prohibited)) == EXPRmode - - /** Translates a mask of mode flags into something readable. - */ - private val modeNameMap = Map[Int, String]( - (1 << 0) -> "EXPRmode", - (1 << 1) -> "PATTERNmode", - (1 << 2) -> "TYPEmode", - (1 << 3) -> "SCCmode", - (1 << 4) -> "FUNmode", - (1 << 5) -> "POLYmode", - (1 << 6) -> "QUALmode", - (1 << 7) -> "TAPPmode", - (1 << 8) -> "SUPERCONSTRmode", - (1 << 9) -> "SNDTRYmode", - (1 << 10) -> "LHSmode", - (1 << 11) -> "<DOES NOT EXIST mode>", - (1 << 12) -> "STARmode", - (1 << 13) -> "ALTmode", - (1 << 14) -> "HKmode", - (1 << 15) -> "BYVALmode", - (1 << 16) -> "TYPEPATmode" - ) - def modeString(mode: Int): String = - if (mode == 0) "NOmode" - else (modeNameMap filterKeys (bit => inAllModes(mode, bit))).values mkString " " -} diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 379f56521b..541d60c16d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -8,9 +8,8 @@ package typechecker import scala.collection.mutable import scala.annotation.tailrec -import scala.ref.WeakReference import symtab.Flags._ -import scala.tools.nsc.io.AbstractFile +import scala.language.postfixOps /** This trait declares methods to create symbols and to enter them into scopes. * @@ -49,10 +48,10 @@ trait Namers extends MethodSynthesis { private class NormalNamer(context: Context) extends Namer(context) def newNamer(context: Context): Namer = new NormalNamer(context) - def newNamerFor(context: Context, tree: Tree): Namer = - newNamer(context.makeNewScope(tree, tree.symbol)) abstract class Namer(val context: Context) extends MethodSynth with NamerContextErrors { thisNamer => + // overridden by the presentation compiler + def saveDefaultGetter(meth: Symbol, default: Symbol) { } import NamerErrorGen._ val typer = newTyper(context) @@ -150,7 +149,7 @@ trait Namers extends MethodSynthesis { sym reset NoType setFlag newFlags setPos pos sym.moduleClass andAlso (updatePosFlags(_, pos, moduleClassFlags(flags))) - if (sym.owner.isPackageClass) { + if (sym.isTopLevel) { companionSymbolOf(sym, context) andAlso { companion => val assignNoType = companion.rawInfo match { case _: SymLoader => true @@ -173,21 +172,24 @@ trait Namers extends MethodSynthesis { else innerNamer } + // FIXME - this logic needs to be thoroughly explained + // and justified. I know it's wrong with repect to package + // objects, but I think it's also wrong in other ways. protected def conflict(newS: Symbol, oldS: Symbol) = ( ( !oldS.isSourceMethod || nme.isSetterName(newS.name) - || newS.owner.isPackageClass + || newS.isTopLevel ) && !( // @M: allow repeated use of `_` for higher-order type params (newS.owner.isTypeParameter || newS.owner.isAbstractType) // FIXME: name comparisons not successful, are these underscores // sometimes nme.WILDCARD and sometimes tpnme.WILDCARD? - && (newS.name.toString == nme.WILDCARD.toString) + && (newS.name string_== nme.WILDCARD) ) ) private def allowsOverload(sym: Symbol) = ( - sym.isSourceMethod && sym.owner.isClass && !sym.owner.isPackageClass + sym.isSourceMethod && sym.owner.isClass && !sym.isTopLevel ) private def inCurrentScope(m: Symbol): Boolean = { @@ -200,6 +202,19 @@ trait Namers extends MethodSynthesis { /** Enter symbol into given scope and return symbol itself */ def enterInScope(sym: Symbol, scope: Scope): Symbol = { + // FIXME - this is broken in a number of ways. + // + // 1) If "sym" allows overloading, that is not itself sufficient to skip + // the check, because "prev.sym" also must allow overloading. + // + // 2) There is nothing which reconciles a package's scope with + // the package object's scope. This is the source of many bugs + // with e.g. defining a case class in a package object. When + // compiling against classes, the class symbol is created in the + // package and in the package object, and the conflict is undetected. + // There is also a non-deterministic outcome for situations like + // an object with the same name as a method in the package object. + // allow for overloaded methods if (!allowsOverload(sym)) { val prev = scope.lookupEntry(sym.name) @@ -239,7 +254,7 @@ trait Namers extends MethodSynthesis { case DocDef(_, defn) => enterSym(defn) case tree @ Import(_, _) => assignSymbol(tree) - returnContext = context.makeNewImport(tree) + returnContext = context.make(tree) case _ => } returnContext @@ -300,15 +315,15 @@ trait Namers extends MethodSynthesis { case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => owner.newConstructor(pos, flags) case DefDef(_, _, _, _, _, _) => owner.newMethod(name.toTermName, pos, flags) case ClassDef(_, _, _, _) => owner.newClassSymbol(name.toTypeName, pos, flags) - case ModuleDef(_, _, _) => owner.newModule(name, pos, flags) + case ModuleDef(_, _, _) => owner.newModule(name.toTermName, pos, flags) case PackageDef(pid, _) => createPackageSymbol(pos, pid) case ValDef(_, _, _, _) => - if (isParameter) owner.newValueParameter(name, pos, flags) - else owner.newValue(name, pos, flags) + if (isParameter) owner.newValueParameter(name.toTermName, pos, flags) + else owner.newValue(name.toTermName, pos, flags) } } private def createFieldSymbol(tree: ValDef): TermSymbol = - owner.newValue(nme.getterToLocal(tree.name), tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) + owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) private def createImportSymbol(tree: Tree) = NoSymbol.newImport(tree.pos) setInfo completerOf(tree) @@ -335,11 +350,10 @@ trait Namers extends MethodSynthesis { } private def enterClassSymbol(tree: ClassDef, clazz: ClassSymbol): Symbol = { - val file = contextFile if (clazz.sourceFile != null && clazz.sourceFile != contextFile) - debugwarn("!!! Source mismatch in " + clazz + ": " + clazz.sourceFile + " vs. " + contextFile) + devWarning(s"Source file mismatch in $clazz: ${clazz.sourceFile} vs. $contextFile") - clazz.sourceFile = contextFile + clazz.associatedFile = contextFile if (clazz.sourceFile != null) { assert(currentRun.canRedefine(clazz) || clazz.sourceFile == currentRun.symSource(clazz), clazz.sourceFile) currentRun.symSource(clazz) = clazz.sourceFile @@ -353,7 +367,7 @@ trait Namers extends MethodSynthesis { val existing = context.scope.lookup(tree.name) val isRedefinition = ( existing.isType - && existing.owner.isPackageClass + && existing.isTopLevel && context.scope == existing.owner.info.decls && currentRun.canRedefine(existing) ) @@ -366,8 +380,8 @@ trait Namers extends MethodSynthesis { else assignAndEnterSymbol(tree) setFlag inConstructorFlag } clazz match { - case csym: ClassSymbol if csym.owner.isPackageClass => enterClassSymbol(tree, csym) - case _ => clazz + case csym: ClassSymbol if csym.isTopLevel => enterClassSymbol(tree, csym) + case _ => clazz } } @@ -379,8 +393,8 @@ trait Namers extends MethodSynthesis { if (sym eq NoSymbol) return val ctx = if (context.owner.isPackageObjectClass) context.outer else context - val module = if (sym.isModule) sym else ctx.scope lookup tree.name.toTermName - val clazz = if (sym.isClass) sym else ctx.scope lookup tree.name.toTypeName + val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name + val clazz = if (sym.isClass) sym else ctx.scope lookupClass tree.name val fails = ( module.isModule && clazz.isClass @@ -426,8 +440,8 @@ trait Namers extends MethodSynthesis { m.moduleClass setFlag moduleClassFlags(moduleFlags) setPrivateWithin(tree, m.moduleClass) } - if (m.owner.isPackageClass && !m.isPackage) { - m.moduleClass.sourceFile = contextFile + if (m.isTopLevel && !m.isPackage) { + m.moduleClass.associatedFile = contextFile currentRun.symSource(m) = m.moduleClass.sourceFile registerTopLevelSym(m) } @@ -489,7 +503,7 @@ trait Namers extends MethodSynthesis { typer.permanentlyHiddenWarning(pos, to0, e.sym) else if (context ne context.enclClass) { val defSym = context.prefix.member(to) filter ( - sym => sym.exists && context.isAccessible(sym, context.prefix, false)) + sym => sym.exists && context.isAccessible(sym, context.prefix, superAccess = false)) defSym andAlso (typer.permanentlyHiddenWarning(pos, to0, _)) } @@ -509,7 +523,7 @@ trait Namers extends MethodSynthesis { if (from != nme.WILDCARD && base != ErrorType) { if (isValid(from)) { // for Java code importing Scala objects - if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) { + if (!nme.isModuleName(from) || isValid(from.dropModule)) { typer.TyperErrorGen.NotAMemberError(tree, expr, from) } } @@ -546,8 +560,8 @@ trait Namers extends MethodSynthesis { val sym = copyDef.symbol val lazyType = completerOf(copyDef) - /** Assign the types of the class parameters to the parameters of the - * copy method. See comment in `Unapplies.caseClassCopyMeth` */ + /* Assign the types of the class parameters to the parameters of the + * copy method. See comment in `Unapplies.caseClassCopyMeth` */ def assignParamTypes() { val clazz = sym.owner val constructorType = clazz.primaryConstructor.tpe @@ -587,17 +601,6 @@ trait Namers extends MethodSynthesis { } } - def enterIfNotThere(sym: Symbol) { - val scope = context.scope - @tailrec def search(e: ScopeEntry) { - if ((e eq null) || (e.owner ne scope)) - scope enter sym - else if (e.sym ne sym) // otherwise, aborts since we found sym - search(e.tail) - } - search(scope lookupEntry sym.name) - } - def enterValDef(tree: ValDef) { if (noEnterGetterSetter(tree)) assignAndEnterFinishedSymbol(tree) @@ -620,7 +623,7 @@ trait Namers extends MethodSynthesis { // via "x$lzy" as can be seen in test #3927. val sym = ( if (owner.isClass) createFieldSymbol(tree) - else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, tree.mods.flags & ~IMPLICIT) + else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, (tree.mods.flags | ARTIFACT) & ~IMPLICIT) ) enterValSymbol(tree, sym setFlag MUTABLE setLazyAccessor lazyAccessor) } @@ -641,7 +644,7 @@ trait Namers extends MethodSynthesis { case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => assignAndEnterFinishedSymbol(tree) case DefDef(mods, name, tparams, _, _, _) => - val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE else 0 + val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE | ARTIFACT else 0 val sym = assignAndEnterSymbol(tree) setFlag bridgeFlag if (name == nme.copy && sym.isSynthetic) @@ -651,15 +654,12 @@ trait Namers extends MethodSynthesis { } def enterClassDef(tree: ClassDef) { - val ClassDef(mods, name, tparams, impl) = tree + val ClassDef(mods, _, _, impl) = tree val primaryConstructorArity = treeInfo.firstConstructorArgs(impl.body).size tree.symbol = enterClassSymbol(tree) tree.symbol setInfo completerOf(tree) if (mods.isCase) { - if (primaryConstructorArity > MaxFunctionArity) - MaxParametersCaseClassError(tree) - val m = ensureCompanionObject(tree, caseModuleDef) m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) } @@ -672,7 +672,7 @@ trait Namers extends MethodSynthesis { m.updateAttachment(new ConstructorDefaultsAttachment(tree, null)) } val owner = tree.symbol.owner - if (settings.lint.value && owner.isPackageObjectClass && !mods.isImplicit) { + if (settings.lint && owner.isPackageObjectClass && !mods.isImplicit) { context.unit.warning(tree.pos, "it is not recommended to define classes/objects inside of package objects.\n" + "If possible, define " + tree.symbol + " in " + owner.skipPackageObject + " instead." @@ -690,22 +690,9 @@ trait Namers extends MethodSynthesis { validateCompanionDefs(tree) } - // this logic is needed in case typer was interrupted half - // way through and then comes back to do the tree again. In - // that case the definitions that were already attributed as - // well as any default parameters of such methods need to be - // re-entered in the current scope. - protected def enterExistingSym(sym: Symbol): Context = { - if (forInteractive && sym != null && sym.owner.isTerm) { - enterIfNotThere(sym) - if (sym.isLazy) - sym.lazyAccessor andAlso enterIfNotThere - - for (defAtt <- sym.attachments.get[DefaultsOfLocalMethodAttachment]) - defAtt.defaultGetters foreach enterIfNotThere - } - this.context - } + // Hooks which are overridden in the presentation compiler + def enterExistingSym(sym: Symbol): Context = this.context + def enterIfNotThere(sym: Symbol) { } def enterSyntheticSym(tree: Tree): Symbol = { enterSym(tree) @@ -715,41 +702,55 @@ trait Namers extends MethodSynthesis { // --- Lazy Type Assignment -------------------------------------------------- - def initializeLowerBounds(tp: Type): Type = { + def findCyclicalLowerBound(tp: Type): Symbol = { tp match { case TypeBounds(lo, _) => // check that lower bound is not an F-bound - for (TypeRef(_, sym, _) <- lo) - sym.initialize + // but carefully: class Foo[T <: Bar[_ >: T]] should be allowed + for (tp1 @ TypeRef(_, sym, _) <- lo) { + if (settings.breakCycles) { + if (!sym.maybeInitialize) { + log(s"Cycle inspecting $lo for possible f-bounds: ${sym.fullLocationString}") + return sym + } + } + else sym.initialize + } case _ => } - tp + NoSymbol } def monoTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => + // this early test is there to avoid infinite baseTypes when + // adding setters and getters --> bug798 + // It is a def in an attempt to provide some insulation against + // uninitialized symbols misleading us. It is not a certainty + // this accomplishes anything, but performance is a non-consideration + // on these flag checks so it can't hurt. + def needsCycleCheck = sym.isNonClassType && !sym.isParameter && !sym.isExistential logAndValidate(sym) { - val tp = initializeLowerBounds(typeSig(tree)) + val tp = typeSig(tree) + + findCyclicalLowerBound(tp) andAlso { sym => + if (needsCycleCheck) { + // neg/t1224: trait C[T] ; trait A { type T >: C[T] <: C[C[T]] } + // To avoid an infinite loop on the above, we cannot break all cycles + log(s"Reinitializing info of $sym to catch any genuine cycles") + sym reset sym.info + sym.initialize + } + } sym setInfo { if (sym.isJavaDefined) RestrictJavaArraysMap(tp) else tp } - // this early test is there to avoid infinite baseTypes when - // adding setters and getters --> bug798 - val needsCycleCheck = (sym.isAliasType || sym.isAbstractType) && !sym.isParameter - if (needsCycleCheck && !typer.checkNonCyclic(tree.pos, tp)) - sym setInfo ErrorType + if (needsCycleCheck) { + log(s"Needs cycle check: ${sym.debugLocationString}") + if (!typer.checkNonCyclic(tree.pos, tp)) + sym setInfo ErrorType + } } - // tree match { - // case ClassDef(_, _, _, impl) => - // val parentsOK = ( - // treeInfo.isInterface(sym, impl.body) - // || (sym eq ArrayClass) - // || (sym isSubClass AnyValClass) - // ) - // if (!parentsOK) - // ensureParent(sym, AnyRefClass) - // case _ => () - // } } def moduleClassTypeCompleter(tree: ModuleDef) = { @@ -807,23 +808,19 @@ trait Namers extends MethodSynthesis { case _ => false } - - val tpe1 = dropRepeatedParamType(tpe.deconst) - val tpe2 = tpe1.widen - - // This infers Foo.type instead of "object Foo" - // See Infer#adjustTypeArgs for the polymorphic case. - if (tpe.typeSymbolDirect.isModuleClass) tpe1 - else if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag) - if (tpe2 <:< pt) tpe2 else tpe1 - else if (isHidden(tpe)) tpe2 - // In an attempt to make pattern matches involving method local vals - // compilable into switches, for a time I had a more generous condition: - // `if (sym.isFinal || sym.isLocal) tpe else tpe1` - // This led to issues with expressions like classOf[List[_]] which apparently - // depend on being deconst-ed here, so this is again the original: - else if (!sym.isFinal) tpe1 - else tpe + val shouldWiden = ( + !tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo" + && (tpe.widen <:< pt) // Don't widen our way out of conforming to pt + && ( sym.isVariable + || sym.isMethod && !sym.hasAccessorFlag + || isHidden(tpe) + ) + ) + dropIllegalStarTypes( + if (shouldWiden) tpe.widen + else if (sym.isFinal) tpe // "final val" allowed to retain constant type + else tpe.deconst + ) } /** Computes the type of the body in a ValDef or DefDef, and * assigns the type to the tpt's node. Returns the type. @@ -851,7 +848,7 @@ trait Namers extends MethodSynthesis { val sym = ( if (hasType || hasName) { - owner.typeOfThis = if (hasType) selfTypeCompleter(tpt) else owner.tpe + owner.typeOfThis = if (hasType) selfTypeCompleter(tpt) else owner.tpe_* val selfSym = owner.thisSym setPos self.pos if (hasName) selfSym setName name else selfSym } @@ -866,16 +863,11 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner def checkParent(tpt: Tree): Type = { - val tp = tpt.tpe - val inheritsSelf = tp.typeSymbol == owner - if (inheritsSelf) - InheritsItselfError(tpt) - - if (inheritsSelf || tp.isError) AnyRefClass.tpe - else tp + if (tpt.tpe.isError) AnyRefClass.tpe + else tpt.tpe } - val parents = typer.parentTypes(templ) map checkParent + val parents = typer.typedParentTypes(templ) map checkParent enterSelf(templ.self) @@ -901,11 +893,10 @@ trait Namers extends MethodSynthesis { val modClass = companionSymbolOf(clazz, context).moduleClass modClass.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => val cdef = cma.caseClass - def hasCopy(decls: Scope) = (decls lookup nme.copy) != NoSymbol + def hasCopy = (decls containsName nme.copy) || parents.exists(_ member nme.copy exists) + // SI-5956 needs (cdef.symbol == clazz): there can be multiple class symbols with the same name - if (cdef.symbol == clazz && !hasCopy(decls) && - !parents.exists(p => hasCopy(p.typeSymbol.info.decls)) && - !parents.flatMap(_.baseClasses).distinct.exists(bc => hasCopy(bc.info.decls))) + if (cdef.symbol == clazz && !hasCopy) addCopyMethod(cdef, templateNamer) } } @@ -951,9 +942,9 @@ trait Namers extends MethodSynthesis { // Assign the moduleClass info (templateSig returns a ClassInfoType) val clazz = moduleSym.moduleClass clazz setInfo pluginsTp - // clazz.tpe returns a `ModuleTypeRef(clazz)`, a typeRef that links to the module class `clazz` + // clazz.tpe_* returns a `ModuleTypeRef(clazz)`, a typeRef that links to the module class `clazz` // (clazz.info would the ClassInfoType, which is not what should be assigned to the module symbol) - clazz.tpe + clazz.tpe_* } /** @@ -997,7 +988,7 @@ trait Namers extends MethodSynthesis { var vparamSymss = enterValueParams(vparamss) - /** + /* * Creates a method type using tparamSyms and vparamsSymss as argument symbols and `respte` as result type. * All typeRefs to type skolems are replaced by references to the corresponding non-skolem type parameter, * so the resulting type is a valid external method type, it does not contain (references to) skolems. @@ -1031,7 +1022,7 @@ trait Namers extends MethodSynthesis { res.substSym(tparamSkolems, tparamSyms) } - /** + /* * Creates a schematic method type which has WildcardTypes for non specified * return or parameter types. For instance, in `def f[T](a: T, b) = ...`, the * type schema is @@ -1055,7 +1046,7 @@ trait Namers extends MethodSynthesis { // def overriddenSymbol = meth.nextOverriddenSymbol - /** + /* * If `meth` doesn't have an explicit return type, extracts the return type from the method * overridden by `meth` (if there's an unique one). This type is lateron used as the expected * type for computing the type of the rhs. The resulting type references type skolems for @@ -1111,7 +1102,7 @@ trait Namers extends MethodSynthesis { } if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { - tpt defineType context.enclClass.owner.tpe + tpt defineType context.enclClass.owner.tpe_* tpt setPos meth.pos.focus } @@ -1147,7 +1138,7 @@ trait Namers extends MethodSynthesis { // because @macroImpl annotation only gets assigned during typechecking // otherwise macro defs wouldn't be able to robustly coexist with their clients // because a client could be typechecked before a macro def that it uses - if (meth.isTermMacro) { + if (meth.isMacro) { typer.computeMacroDefType(ddef, resTpFromOverride) } @@ -1282,17 +1273,10 @@ trait Namers extends MethodSynthesis { if (!isConstr) methOwner.resetFlag(INTERFACE) // there's a concrete member now val default = parentNamer.enterSyntheticSym(defaultTree) - if (forInteractive && default.owner.isTerm) { - // save the default getters as attachments in the method symbol. if compiling the - // same local block several times (which can happen in interactive mode) we might - // otherwise not find the default symbol, because the second time it the method - // symbol will be re-entered in the scope but the default parameter will not. - val att = meth.attachments.get[DefaultsOfLocalMethodAttachment] match { - case Some(att) => att.defaultGetters += default - case None => meth.updateAttachment(new DefaultsOfLocalMethodAttachment(default)) - } - } - } else if (baseHasDefault) { + if (default.owner.isTerm) + saveDefaultGetter(meth, default) + } + else if (baseHasDefault) { // the parameter does not have a default itself, but the // corresponding parameter in the base class does. sym.setFlag(DEFAULTPARAM) @@ -1370,8 +1354,7 @@ trait Namers extends MethodSynthesis { transformed(imp) = newImport // copy symbol and type attributes back into old expression // so that the structure builder will find it. - expr.symbol = expr1.symbol - expr.tpe = expr1.tpe + expr setSymbol expr1.symbol setType expr1.tpe ImportType(expr1) } } @@ -1393,7 +1376,9 @@ trait Namers extends MethodSynthesis { if (!cdef.symbol.hasAbstractFlag) namer.enterSyntheticSym(caseModuleApplyMeth(cdef)) - namer.enterSyntheticSym(caseModuleUnapplyMeth(cdef)) + val primaryConstructorArity = treeInfo.firstConstructorArgs(cdef.impl.body).size + if (primaryConstructorArity <= MaxTupleArity) + namer.enterSyntheticSym(caseModuleUnapplyMeth(cdef)) } def addCopyMethod(cdef: ClassDef, namer: Namer) { @@ -1407,12 +1392,12 @@ trait Namers extends MethodSynthesis { */ def typeSig(tree: Tree): Type = { // log("typeSig " + tree) - /** For definitions, transform Annotation trees to AnnotationInfos, assign - * them to the sym's annotations. Type annotations: see Typer.typedAnnotated - * We have to parse definition annotations here (not in the typer when traversing - * the MemberDef tree): the typer looks at annotations of certain symbols; if - * they were added only in typer, depending on the compilation order, they may - * or may not be visible. + /* For definitions, transform Annotation trees to AnnotationInfos, assign + * them to the sym's annotations. Type annotations: see Typer.typedAnnotated + * We have to parse definition annotations here (not in the typer when traversing + * the MemberDef tree): the typer looks at annotations of certain symbols; if + * they were added only in typer, depending on the compilation order, they may + * or may not be visible. */ def annotate(annotated: Symbol) = { // typeSig might be called multiple times, e.g. on a ValDef: val, getter, setter @@ -1424,7 +1409,7 @@ trait Namers extends MethodSynthesis { AnnotationInfo lazily { val context1 = typer.context.make(ann) context1.setReportErrors() - beforeTyper(newTyper(context1) typedAnnotation ann) + enteringTyper(newTyper(context1) typedAnnotation ann) } } if (ainfos.nonEmpty) { @@ -1476,12 +1461,6 @@ trait Namers extends MethodSynthesis { tpe } - def ensureParent(clazz: Symbol, parent: Symbol) = { - val info0 = clazz.info - val info1 = includeParent(info0, parent) - if (info0 ne info1) clazz setInfo info1 - } - class LogTransitions[S](onEnter: S => String, onExit: S => String) { val enabled = settings.debug.value @inline final def apply[T](entity: S)(body: => T): T = { @@ -1542,7 +1521,7 @@ trait Namers extends MethodSynthesis { fail(ImplicitConstr) if (!(sym.isTerm || (sym.isClass && !sym.isTrait))) fail(ImplicitNotTermOrClass) - if (sym.owner.isPackageClass) + if (sym.isTopLevel) fail(ImplicitAtToplevel) } if (sym.isClass) { @@ -1650,7 +1629,7 @@ trait Namers extends MethodSynthesis { // @M an abstract type's type parameters are entered. // TODO: change to isTypeMember ? if (defnSym.isAbstractType) - newNamerFor(ctx, tree) enterSyms tparams //@M + newNamer(ctx.makeNewScope(tree, tree.symbol)) enterSyms tparams //@M restp complete sym } } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index f3736f1519..e22e2c603a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -8,7 +8,6 @@ package typechecker import symtab.Flags._ import scala.collection.mutable -import scala.ref.WeakReference import scala.reflect.ClassTag /** @@ -20,6 +19,7 @@ trait NamesDefaults { self: Analyzer => import global._ import definitions._ import NamesDefaultsErrorsGen._ + import treeInfo.WildcardStarArg // Default getters of constructors are added to the companion object in the // typeCompleter of the constructor (methodSig). To compute the signature, @@ -42,13 +42,11 @@ trait NamesDefaults { self: Analyzer => blockTyper: Typer ) { } - val noApplyInfo = NamedApplyInfo(None, Nil, Nil, null) - - def nameOf(arg: Tree) = arg match { - case AssignOrNamedArg(Ident(name), rhs) => Some(name) - case _ => None + private def nameOfNamedArg(arg: Tree) = Some(arg) collect { case AssignOrNamedArg(Ident(name), _) => name } + def isNamedArg(arg: Tree) = arg match { + case AssignOrNamedArg(Ident(_), _) => true + case _ => false } - def isNamed(arg: Tree) = nameOf(arg).isDefined /** @param pos maps indices from old to new */ def reorderArgs[T: ClassTag](args: List[T], pos: Int => Int): List[T] = { @@ -58,13 +56,13 @@ trait NamesDefaults { self: Analyzer => } /** @param pos maps indices from new to old (!) */ - def reorderArgsInv[T: ClassTag](args: List[T], pos: Int => Int): List[T] = { + private def reorderArgsInv[T: ClassTag](args: List[T], pos: Int => Int): List[T] = { val argsArray = args.toArray (argsArray.indices map (i => argsArray(pos(i)))).toList } /** returns `true` if every element is equal to its index */ - def isIdentity(a: Array[Int]) = (0 until a.length).forall(i => a(i) == i) + def allArgsArePositional(a: Array[Int]) = (0 until a.length).forall(i => a(i) == i) /** * Transform a function application into a Block, and assigns typer.context @@ -107,14 +105,14 @@ trait NamesDefaults { self: Analyzer => * @return the transformed application (a Block) together with the NamedApplyInfo. * if isNamedApplyBlock(tree), returns the existing context.namedApplyBlockInfo */ - def transformNamedApplication(typer: Typer, mode: Int, pt: Type) + def transformNamedApplication(typer: Typer, mode: Mode, pt: Type) (tree: Tree, argPos: Int => Int): Tree = { import typer._ import typer.infer._ val context = typer.context import context.unit - /** + /* * Transform a function into a block, and passing context.namedApplyBlockInfo to * the new block as side-effect. * @@ -164,14 +162,14 @@ trait NamesDefaults { self: Analyzer => // never used for constructor calls, they always have a stable qualifier def blockWithQualifier(qual: Tree, selected: Name) = { - val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos) setInfo qual.tpe + val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos, newFlags = ARTIFACT) setInfo qual.tpe blockTyper.context.scope enter sym val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType) // it stays in Vegas: SI-5720, SI-5727 qual changeOwner (blockTyper.context.owner -> sym) val newQual = atPos(qual.pos.focus)(blockTyper.typedQualifier(Ident(sym.name))) - var baseFunTransformed = atPos(baseFun.pos.makeTransparent) { + val baseFunTransformed = atPos(baseFun.pos.makeTransparent) { // setSymbol below is important because the 'selected' function might be overloaded. by // assigning the correct method symbol, typedSelect will just assign the type. the reason // to still call 'typed' is to correctly infer singleton types, SI-5259. @@ -259,7 +257,7 @@ trait NamesDefaults { self: Analyzer => } } - /** + /* * For each argument (arg: T), create a local value * x$n: T = arg * @@ -281,15 +279,15 @@ trait NamesDefaults { self: Analyzer => val repeated = isScalaRepeatedParamType(paramTpe) val argTpe = ( if (repeated) arg match { - case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe - case _ => seqType(arg.tpe) + case WildcardStarArg(expr) => expr.tpe + case _ => seqType(arg.tpe) } else // Note stabilizing can lead to a non-conformant argument when existentials are involved, e.g. neg/t3507-old.scala, hence the filter. - // We have to deconst or types inferred from literal arguments will be Constant(_), e.g. pos/z1730.scala. - gen.stableTypeFor(arg).filter(_ <:< paramTpe).getOrElse(arg.tpe).deconst - ) - val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo ( + gen.stableTypeFor(arg).filter(_ <:< paramTpe).getOrElse(arg.tpe) + // We have to deconst or types inferred from literal arguments will be Constant(_), e.g. pos/z1730.scala. + ).deconst + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos, newFlags = ARTIFACT) setInfo ( if (byName) functionType(Nil, argTpe) else argTpe ) Some((context.scope.enter(s), byName, repeated)) @@ -305,11 +303,8 @@ trait NamesDefaults { self: Analyzer => } else { new ChangeOwnerTraverser(context.owner, sym) traverse arg // fixes #4502 if (repeated) arg match { - case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => - expr - case _ => - val factory = Select(gen.mkAttributedRef(SeqModule), nme.apply) - blockTyper.typed(Apply(factory, List(resetLocalAttrs(arg)))) + case WildcardStarArg(expr) => expr + case _ => blockTyper typed gen.mkSeqApply(resetLocalAttrs(arg)) } else arg } Some(atPos(body.pos)(ValDef(sym, body).setType(NoType))) @@ -328,7 +323,7 @@ trait NamesDefaults { self: Analyzer => assert(isNamedApplyBlock(transformedFun), transformedFun) val NamedApplyInfo(qual, targs, vargss, blockTyper) = context.namedApplyBlockInfo.get._2 - val existingBlock @ Block(stats, funOnly) = transformedFun + val Block(stats, funOnly) = transformedFun // type the application without names; put the arguments in definition-site order val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt) @@ -373,7 +368,7 @@ trait NamesDefaults { self: Analyzer => } } - def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOf _): (List[Symbol], Boolean) = { + def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOfNamedArg _): (List[Symbol], Boolean) = { val namedArgs = args.dropWhile(arg => { val n = argName(arg) n.isEmpty || params.forall(p => p.name != n.get) @@ -408,7 +403,7 @@ trait NamesDefaults { self: Analyzer => // 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 { + var default1: Tree = qual match { case Some(q) => gen.mkAttributedSelect(q.duplicate, defGetter) case None => gen.mkAttributedRef(defGetter) @@ -454,20 +449,6 @@ trait NamesDefaults { self: Analyzer => } else NoSymbol } - private def savingUndeterminedTParams[T](context: Context)(fn: List[Symbol] => T): T = { - val savedParams = context.extractUndetparams() - val savedReporting = context.ambiguousErrors - - context.setAmbiguousErrors(false) - try fn(savedParams) - finally { - 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 - } - } - /** A full type check is very expensive; let's make sure there's a name * somewhere which could potentially be ambiguous before we go that route. */ @@ -482,7 +463,8 @@ trait NamesDefaults { self: Analyzer => // def f[T](x: T) = x // var x = 0 // f(x = 1) << "x = 1" typechecks with expected type WildcardType - savingUndeterminedTParams(context) { udp => + val udp = context.undetparams + context.savingUndeterminedTypeParams(reportAmbiguous = false) { val subst = new SubstTypeMap(udp, udp map (_ => WildcardType)) { override def apply(tp: Type): Type = super.apply(tp match { case TypeRef(_, ByNameParamClass, x :: Nil) => x diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 4933b8e0cb..41f54f84e4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -71,7 +71,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (sym.hasAccessBoundary) "" + sym.privateWithin.name else "" ) - def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.normalize, tp2.normalize) match { + def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match { case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => rtp1 <:< rtp2 case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => @@ -95,7 +95,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans class RefCheckTransformer(unit: CompilationUnit) extends Transformer { - var localTyper: analyzer.Typer = typer; + var localTyper: analyzer.Typer = typer var currentApplication: Tree = EmptyTree var inPattern: Boolean = false var checkedCombinations = Set[List[Type]]() @@ -133,7 +133,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } } - if (settings.lint.value) { + + // Check for doomed attempt to overload applyDynamic + if (clazz isSubClass DynamicClass) { + for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.typeParams.length)) { + unit.error(m1.pos, "implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)") + } + } + + // This has become noisy with implicit classes. + if (settings.lint && settings.developer) { clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym => // implicit classes leave both a module symbol and a method symbol as residue val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule) @@ -241,7 +250,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case class MixinOverrideError(member: Symbol, msg: String) - var mixinOverrideErrors = new ListBuffer[MixinOverrideError]() + val mixinOverrideErrors = new ListBuffer[MixinOverrideError]() def printMixinOverrideErrors() { mixinOverrideErrors.toList match { @@ -273,8 +282,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans else "") } - /** Check that all conditions for overriding `other` by `member` - * of class `clazz` are met. + /* Check that all conditions for overriding `other` by `member` + * of class `clazz` are met. */ def checkOverride(member: Symbol, other: Symbol) { debuglog("Checking validity of %s overriding %s".format(member.fullLocationString, other.fullLocationString)) @@ -299,7 +308,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans infoStringWithLocation(other), infoStringWithLocation(member) ) - else if (settings.debug.value) + else if (settings.debug) analyzer.foundReqMsg(member.tpe, other.tpe) else "" @@ -353,8 +362,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } - /** Is the intersection between given two lists of overridden symbols empty? - */ + /* Is the intersection between given two lists of overridden symbols empty? */ def intersectionIsEmpty(syms1: List[Symbol], syms2: List[Symbol]) = !(syms1 exists (syms2 contains _)) @@ -378,11 +386,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (!isOverrideAccessOK) { overrideAccessError() } else if (other.isClass) { - overrideError("cannot be used here - class definitions cannot be overridden"); + overrideError("cannot be used here - class definitions cannot be overridden") } else if (!other.isDeferred && member.isClass) { - overrideError("cannot be used here - classes can only override abstract types"); + overrideError("cannot be used here - classes can only override abstract types") } else if (other.isEffectivelyFinal) { // (1.2) - overrideError("cannot override final member"); + overrideError("cannot override final member") } else if (!other.isDeferred && !member.isAnyOverride && !member.isSynthetic) { // (*) // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. @@ -400,7 +408,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans else if (member.isAnyOverride && (other hasFlag ACCESSOR) && other.accessed.isVariable && !other.accessed.isLazy) { // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here. // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches. - if (!settings.overrideVars.value) + if (!settings.overrideVars) overrideError("cannot override a mutable variable") } else if (member.isAnyOverride && @@ -418,13 +426,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans member.isValue && !member.isLazy) { overrideError("must be declared lazy to override a concrete lazy value") } else if (other.isDeferred && member.isTermMacro) { // (1.9) - overrideError("cannot override an abstract method") + overrideError("cannot be used here - term macros cannot override abstract methods") } else if (other.isTermMacro && !member.isTermMacro) { // (1.10) - overrideError("cannot override a macro") + overrideError("cannot be used here - only term macros can override term macros") } else { checkOverrideTypes() checkOverrideDeprecated() - if (settings.warnNullaryOverride.value) { + if (settings.warnNullaryOverride) { if (other.paramss.isEmpty && !member.paramss.isEmpty) { unit.warning(member.pos, "non-nullary method overrides nullary method") } @@ -441,7 +449,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // @M: substSym if( !(sameLength(member.typeParams, other.typeParams) && (memberTp.substSym(member.typeParams, other.typeParams) =:= otherTp)) ) // (1.6) - overrideTypeError(); + overrideTypeError() } else if (other.isAbstractType) { //if (!member.typeParams.isEmpty) // (1.7) @MAT @@ -466,12 +474,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // check a type alias's RHS corresponds to its declaration // this overlaps somewhat with validateVariance if(member.isAliasType) { - // println("checkKindBounds" + ((List(member), List(memberTp.normalize), self, member.owner))) - val kindErrors = typer.infer.checkKindBounds(List(member), List(memberTp.normalize), self, member.owner) + // println("checkKindBounds" + ((List(member), List(memberTp.dealiasWiden), self, member.owner))) + val kindErrors = typer.infer.checkKindBounds(List(member), List(memberTp.dealiasWiden), self, member.owner) if(!kindErrors.isEmpty) unit.error(member.pos, - "The kind of the right-hand side "+memberTp.normalize+" of "+member.keyString+" "+ + "The kind of the right-hand side "+memberTp.dealiasWiden+" of "+member.keyString+" "+ member.varianceString + member.nameString+ " does not conform to its expected kind."+ kindErrors.toList.mkString("\n", ", ", "")) } else if (member.isAbstractType) { @@ -490,11 +498,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (member.isStable && !otherTp.isVolatile) { if (memberTp.isVolatile) overrideError("has a volatile type; cannot override a member with non-volatile type") - else memberTp.normalize.resultType match { + else memberTp.dealiasWiden.resultType match { case rt: RefinedType if !(rt =:= otherTp) && !(checkedCombinations contains rt.parents) => // might mask some inconsistencies -- check overrides checkedCombinations += rt.parents - val tsym = rt.typeSymbol; + val tsym = rt.typeSymbol if (tsym.pos == NoPosition) tsym setPos member.pos checkAllOverrides(tsym, typesOnly = true) case _ => @@ -515,9 +523,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val opc = new overridingPairs.Cursor(clazz) while (opc.hasNext) { //Console.println(opc.overriding/* + ":" + opc.overriding.tpe*/ + " in "+opc.overriding.fullName + " overrides " + opc.overridden/* + ":" + opc.overridden.tpe*/ + " in "+opc.overridden.fullName + "/"+ opc.overridden.hasFlag(DEFERRED));//debug - if (!opc.overridden.isClass) checkOverride(opc.overriding, opc.overridden); + if (!opc.overridden.isClass) checkOverride(opc.overriding, opc.overridden) - opc.next + opc.next() } printMixinOverrideErrors() @@ -549,13 +557,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def uncurryAndErase(tp: Type) = erasure.erasure(sym)(uncurry.transformInfo(sym, tp)) val tp1 = uncurryAndErase(clazz.thisType.memberType(sym)) val tp2 = uncurryAndErase(clazz.thisType.memberType(other)) - afterErasure(tp1 matches tp2) + exitingErasure(tp1 matches tp2) }) def ignoreDeferred(member: Symbol) = ( (member.isAbstractType && !member.isFBounded) || ( member.isJavaDefined && - // the test requires afterErasure so shouldn't be + // the test requires exitingErasure so shouldn't be // done if the compiler has no erasure phase available (currentRun.erasurePhase == NoPhase || javaErasedOverridingSym(member) != NoSymbol) ) @@ -718,16 +726,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans else if (clazz.isTrait && !(clazz isSubClass AnyValClass)) { // For non-AnyVal classes, prevent abstract methods in interfaces that override // final members in Object; see #4431 - for (decl <- clazz.info.decls.iterator) { - val overridden = decl.overriddenSymbol(ObjectClass) + for (decl <- clazz.info.decls) { + // Have to use matchingSymbol, not a method involving overridden symbols, + // because the scala type system understands that an abstract method here does not + // override a concrete method in Object. The jvm, however, does not. + val overridden = decl.matchingSymbol(ObjectClass, ObjectClass.tpe) if (overridden.isFinal) unit.error(decl.pos, "trait cannot redefine final method from class AnyRef") } } - /** Returns whether there is a symbol declared in class `inclazz` - * (which must be different from `clazz`) whose name and type - * seen as a member of `class.thisType` matches `member`'s. + /* Returns whether there is a symbol declared in class `inclazz` + * (which must be different from `clazz`) whose name and type + * seen as a member of `class.thisType` matches `member`'s. */ def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = { val isVarargs = hasRepeatedParam(member.tpe) @@ -739,22 +750,22 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans matches(member.tpe) || (isVarargs && matches(varargsType)) } - /** The rules for accessing members which have an access boundary are more - * restrictive in java than scala. Since java has no concept of package nesting, - * a member with "default" (package-level) access can only be accessed by members - * in the exact same package. Example: + /* The rules for accessing members which have an access boundary are more + * restrictive in java than scala. Since java has no concept of package nesting, + * a member with "default" (package-level) access can only be accessed by members + * in the exact same package. Example: * - * package a.b; - * public class JavaClass { void foo() { } } + * package a.b; + * public class JavaClass { void foo() { } } * - * The member foo() can be accessed only from members of package a.b, and not - * nested packages like a.b.c. In the analogous scala class: + * The member foo() can be accessed only from members of package a.b, and not + * nested packages like a.b.c. In the analogous scala class: * - * package a.b - * class ScalaClass { private[b] def foo() = () } + * package a.b + * class ScalaClass { private[b] def foo() = () } * - * The member IS accessible to classes in package a.b.c. The javaAccessCheck logic - * is restricting the set of matching signatures according to the above semantics. + * The member IS accessible to classes in package a.b.c. The javaAccessCheck logic + * is restricting the set of matching signatures according to the above semantics. */ def javaAccessCheck(sym: Symbol) = ( !inclazz.isJavaDefined // not a java defined member @@ -774,7 +785,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG val nonMatching: List[Symbol] = clazz.info.member(member.name).alternatives.filterNot(_.owner == clazz).filterNot(_.isFinal) - def issueError(suffix: String) = unit.error(member.pos, member.toString() + " overrides nothing" + suffix); + def issueError(suffix: String) = unit.error(member.pos, member.toString() + " overrides nothing" + suffix) nonMatching match { case Nil => issueError("") @@ -801,7 +812,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans for (i <- 0 until seenTypes.length) seenTypes(i) = Nil - /** validate all base types of a class in reverse linear order. */ + /* validate all base types of a class in reverse linear order. */ def register(tp: Type): Unit = { // if (clazz.fullName.endsWith("Collection.Projection")) // println("validate base type "+tp) @@ -829,7 +840,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case tp1 :: tp2 :: _ => unit.error(clazz.pos, "illegal inheritance;\n " + clazz + " inherits different type instances of " + baseClass + - ":\n" + tp1 + " and " + tp2); + ":\n" + tp1 + " and " + tp2) explainTypes(tp1, tp2) explainTypes(tp2, tp1) } @@ -838,161 +849,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Variance Checking -------------------------------------------------------- - private val ContraVariance = -1 - private val NoVariance = 0 - private val CoVariance = 1 - private val AnyVariance = 2 - - private val escapedPrivateLocals = new mutable.HashSet[Symbol] - - val varianceValidator = new Traverser { - - /** Validate variance of info of symbol `base` */ - private def validateVariance(base: Symbol) { - // A flag for when we're in a refinement, meaning method parameter types - // need to be checked. - var inRefinement = false - - def varianceString(variance: Int): String = - if (variance == 1) "covariant" - else if (variance == -1) "contravariant" - else "invariant"; - - /** The variance of a symbol occurrence of `tvar` - * seen at the level of the definition of `base`. - * The search proceeds from `base` to the owner of `tvar`. - * Initially the state is covariant, but it might change along the search. - */ - def relativeVariance(tvar: Symbol): Int = { - val clazz = tvar.owner - var sym = base - var state = CoVariance - while (sym != clazz && state != AnyVariance) { - //Console.println("flip: " + sym + " " + sym.isParameter());//DEBUG - // Flip occurrences of type parameters and parameters, unless - // - it's a constructor, or case class factory or extractor - // - it's a type parameter of tvar's owner. - if (sym.isParameter && !sym.owner.isConstructor && !sym.owner.isCaseApplyOrUnapply && - !(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem && - tvar.owner == sym.owner)) state = -state; - else if (!sym.owner.isClass || - sym.isTerm && ((sym.isPrivateLocal || sym.isProtectedLocal || sym.isSuperAccessor /* super accessors are implicitly local #4345*/) && !(escapedPrivateLocals contains sym))) { - // return AnyVariance if `sym` is local to a term - // or is private[this] or protected[this] - state = AnyVariance - } else if (sym.isAliasType) { - // return AnyVariance if `sym` is an alias type - // that does not override anything. This is OK, because we always - // expand aliases for variance checking. - // However, if `sym` does override a type in a base class - // we have to assume NoVariance, as there might then be - // references to the type parameter that are not variance checked. - state = if (sym.isOverridingSymbol) NoVariance else AnyVariance - } - sym = sym.owner - } - state - } - - /** Validate that the type `tp` is variance-correct, assuming - * the type occurs itself at variance position given by `variance` - */ - def validateVariance(tp: Type, variance: Int): Unit = tp match { - case ErrorType => - case WildcardType => - case BoundedWildcardType(bounds) => - validateVariance(bounds, variance) - case NoType => - case NoPrefix => - case ThisType(_) => - case ConstantType(_) => - // case DeBruijnIndex(_, _) => - case SingleType(pre, sym) => - validateVariance(pre, variance) - case TypeRef(pre, sym, args) => -// println("validate "+sym+" at "+relativeVariance(sym)) - if (sym.isAliasType/* && relativeVariance(sym) == AnyVariance*/) - validateVariance(tp.normalize, variance) - else if (sym.variance != NoVariance) { - val v = relativeVariance(sym) - if (v != AnyVariance && sym.variance != v * variance) { - //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG - def tpString(tp: Type) = tp match { - case ClassInfoType(parents, _, clazz) => "supertype "+intersectionType(parents, clazz.owner) - case _ => "type "+tp - } - unit.error(base.pos, - varianceString(sym.variance) + " " + sym + - " occurs in " + varianceString(v * variance) + - " position in " + tpString(base.info) + " of " + base); - } - } - validateVariance(pre, variance) - // @M for higher-kinded typeref, args.isEmpty - // However, these args respect variances by construction anyway - // -- the interesting case is in type application, see checkKindBounds in Infer - if (args.nonEmpty) - validateVarianceArgs(args, variance, sym.typeParams) - case ClassInfoType(parents, decls, symbol) => - validateVariances(parents, variance) - case RefinedType(parents, decls) => - validateVariances(parents, variance) - val saved = inRefinement - inRefinement = true - for (sym <- decls) - validateVariance(sym.info, if (sym.isAliasType) NoVariance else variance) - inRefinement = saved - case TypeBounds(lo, hi) => - validateVariance(lo, -variance) - validateVariance(hi, variance) - case mt @ MethodType(formals, result) => - if (inRefinement) - validateVariances(mt.paramTypes, -variance) - validateVariance(result, variance) - case NullaryMethodType(result) => - validateVariance(result, variance) - case PolyType(tparams, result) => - // type parameters will be validated separately, because they are defined explicitly. - validateVariance(result, variance) - case ExistentialType(tparams, result) => - validateVariances(tparams map (_.info), variance) - validateVariance(result, variance) - case AnnotatedType(annots, tp, selfsym) => - if (!annots.exists(_ matches uncheckedVarianceClass)) - validateVariance(tp, variance) - } - - def validateVariances(tps: List[Type], variance: Int) { - tps foreach (tp => validateVariance(tp, variance)) - } - - def validateVarianceArgs(tps: List[Type], variance: Int, tparams: List[Symbol]) { - foreach2(tps, tparams)((tp, tparam) => validateVariance(tp, variance * tparam.variance)) - } - - validateVariance(base.info, CoVariance) + object varianceValidator extends VarianceValidator { + private def tpString(tp: Type) = tp match { + case ClassInfoType(parents, _, clazz) => "supertype "+intersectionType(parents, clazz.owner) + case _ => "type "+tp } - - override def traverse(tree: Tree) { - tree match { - case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => - validateVariance(tree.symbol) - super.traverse(tree) - // ModuleDefs need not be considered because they have been eliminated already - case ValDef(_, _, _, _) => - if (!tree.symbol.hasLocalFlag) - validateVariance(tree.symbol) - case DefDef(_, _, tparams, vparamss, _, _) => - // No variance check for object-private/protected methods/values. - if (!tree.symbol.hasLocalFlag) { - validateVariance(tree.symbol) - traverseTrees(tparams) - traverseTreess(vparamss) - } - case Template(_, _, _) => - super.traverse(tree) - case _ => - } + override def issueVarianceError(base: Symbol, sym: Symbol, required: Variance) { + currentRun.currentUnit.error(base.pos, + s"${sym.variance} $sym occurs in $required position in ${tpString(base.info)} of $base") } } @@ -1041,7 +905,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val e = currentLevel.scope.lookupEntry(sym.name) if ((e ne null) && sym == e.sym) { var l = currentLevel - while (l.scope != e.owner) l = l.outer; + while (l.scope != e.owner) l = l.outer val symindex = symIndex(sym) if (l.maxindex < symindex) { l.refpos = pos @@ -1057,7 +921,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def apply(tp: Type) = mapOver(tp).normalize } - def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint.value) (fn, args) match { + def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match { case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == Option_apply => unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567 case _ => @@ -1084,7 +948,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol` def typesString = normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen) - /** Symbols which limit the warnings we can issue since they may be value types */ + /* Symbols which limit the warnings we can issue since they may be value types */ val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass) // Whether def equals(other: Any) has known behavior: it is the default @@ -1115,15 +979,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass - // test is behind a platform guard - def isJavaNumber(s: Symbol) = !forMSIL && (s isSubClass JavaNumberClass) + def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass // includes java.lang.Number if appropriate [SI-5779] def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) // used to short-circuit unrelatedTypes check if both sides are special def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) - // unused - def possibleNumericCount = onSyms(_ filter (x => isNumeric(x) || isMaybeValue(x)) size) val nullCount = onSyms(_ filter (_ == NullClass) size) def isNonsenseValueClassCompare = ( !haveSubclassRelationship @@ -1170,7 +1031,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans nonSensiblyNeq() } else if (isNumeric(receiver)) { - if (!isNumeric(actual) && !forMSIL) + if (!isNumeric(actual)) if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc" nonSensiblyNeq() } @@ -1245,8 +1106,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */ def toConstructor(pos: Position, tpe: Type): Tree = { - var rtpe = tpe.finalResultType - assert(rtpe.typeSymbol hasFlag CASE, tpe); + val rtpe = tpe.finalResultType + assert(rtpe.typeSymbol hasFlag CASE, tpe) localTyper.typedOperator { atPos(pos) { Select(New(TypeTree(rtpe)), rtpe.typeSymbol.primaryConstructor) @@ -1264,57 +1125,61 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans finally popLevel() } - /** Eliminate ModuleDefs. - * - A top level object is replaced with their module class. - * - An inner object is transformed into a module var, created on first access. + /** Eliminate ModuleDefs. In all cases the ModuleDef (carrying a module symbol) is + * replaced with a ClassDef (carrying the corresponding module class symbol) with additional + * trees created as follows: * - * In both cases, this transformation returns the list of replacement trees: - * - Top level: the module class accessor definition - * - Inner: a class definition, declaration of module var, and module var accessor + * 1) A statically reachable object (either top-level or nested only in objects) receives + * no additional trees. + * 2) An inner object which matches an existing member (e.g. implements an interface) + * receives an accessor DefDef to implement the interface. + * 3) An inner object otherwise receives a private ValDef which declares a module var + * (the field which holds the module class - it has a name like Foo$module) and an + * accessor for that field. The instance is created lazily, on first access. */ - private def eliminateModuleDefs(tree: Tree): List[Tree] = { - val ModuleDef(mods, name, impl) = tree - val sym = tree.symbol - val classSym = sym.moduleClass - val cdef = ClassDef(mods | MODULE, name.toTypeName, Nil, impl) setSymbol classSym setType NoType - - def findOrCreateModuleVar() = localTyper.typedPos(tree.pos) { - // See SI-5012, SI-6712. + private def eliminateModuleDefs(moduleDef: Tree): List[Tree] = exitingRefchecks { + val ModuleDef(_, _, impl) = moduleDef + val module = moduleDef.symbol + val site = module.owner + val moduleName = module.name.toTermName + // The typer doesn't take kindly to seeing this ClassDef; we have to + // set NoType so it will be ignored. + val cdef = ClassDef(module.moduleClass, impl) setType NoType + + // Create the module var unless the immediate owner is a class and + // the module var already exists there. See SI-5012, SI-6712. + def findOrCreateModuleVar() = { val vsym = ( - if (sym.owner.isTerm) NoSymbol - else sym.enclClass.info.decl(nme.moduleVarName(sym.name.toTermName)) + if (site.isTerm) NoSymbol + else site.info decl nme.moduleVarName(moduleName) ) - // In case we are dealing with local symbol then we already have - // to correct error with forward reference - if (vsym == NoSymbol) gen.mkModuleVarDef(sym) - else ValDef(vsym) + vsym orElse (site newModuleVarSymbol module) } - def createStaticModuleAccessor() = afterRefchecks { - val method = ( - sym.owner.newMethod(sym.name.toTermName, sym.pos, (sym.flags | STABLE) & ~MODULE) - setInfoAndEnter NullaryMethodType(sym.moduleClass.tpe) - ) - localTyper.typedPos(tree.pos)(gen.mkModuleAccessDef(method, sym)) + def newInnerObject() = { + // Create the module var unless it is already in the module owner's scope. + // The lookup is on module.enclClass and not module.owner lest there be a + // nullary method between us and the class; see SI-5012. + val moduleVar = findOrCreateModuleVar() + val rhs = gen.newModule(module, moduleVar.tpe) + val body = if (site.isTrait) rhs else gen.mkAssignAndReturn(moduleVar, rhs) + val accessor = DefDef(module, body.changeOwner(moduleVar -> module)) + + ValDef(moduleVar) :: accessor :: Nil } - def createInnerModuleAccessor(vdef: Tree) = List( - vdef, - localTyper.typedPos(tree.pos) { - val vsym = vdef.symbol - afterRefchecks { - val rhs = gen.newModule(sym, vsym.tpe) - val body = if (sym.owner.isTrait) rhs else gen.mkAssignAndReturn(vsym, rhs) - DefDef(sym, body.changeOwner(vsym -> sym)) - } - } - ) - transformTrees(cdef :: { - if (!sym.isStatic) - createInnerModuleAccessor(findOrCreateModuleVar) - else if (sym.isOverridingSymbol) - List(createStaticModuleAccessor()) + def matchingInnerObject() = { + val newFlags = (module.flags | STABLE) & ~MODULE + val newInfo = NullaryMethodType(module.moduleClass.tpe) + val accessor = site.newMethod(moduleName, module.pos, newFlags) setInfoAndEnter newInfo + + DefDef(accessor, Select(This(site), module)) :: Nil + } + val newTrees = cdef :: ( + if (module.isStatic) + if (module.isOverridingSymbol) matchingInnerObject() else Nil else - Nil - }) + newInnerObject() + ) + transformTrees(newTrees map localTyper.typedPos(moduleDef.pos)) } def transformStat(tree: Tree, index: Int): List[Tree] = tree match { @@ -1328,7 +1193,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } case ModuleDef(_, _, _) => eliminateModuleDefs(tree) case ValDef(_, _, _, _) => - val tree1 @ ValDef(_, _, _, rhs) = transform(tree) // important to do before forward reference check + val tree1 = transform(tree) // important to do before forward reference check if (tree1.symbol.isLazy) tree1 :: Nil else { val lazySym = tree.symbol.lazyAccessorOrSelf @@ -1349,7 +1214,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans catch { case ex: TypeError => unit.error(tree0.pos, ex.getMessage()) - if (settings.explaintypes.value) { + if (settings.explaintypes) { val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, argtps).bounds) (argtps, bounds).zipped map ((targ, bound) => explainTypes(bound.lo, targ)) (argtps, bounds).zipped map ((targ, bound) => explainTypes(targ, bound.hi)) @@ -1470,7 +1335,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // if the unnormalized type is accessible, that's good enough if (inaccessible.isEmpty) () // or if the normalized type is, that's good too - else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.normalize, member).isEmpty) () + else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) () // otherwise warn about the inaccessible syms in the unnormalized type else inaccessible foreach (sym => warnLessAccessible(sym, member)) } @@ -1561,9 +1426,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case TypeApply(fun, targs) => isClassTypeAccessible(fun) case Select(module, apply) => - // Fixes SI-5626. Classes in refinement types cannot be constructed with `new`. In this case, - // the companion class is actually not a ClassSymbol, but a reference to an abstract type. - module.symbol.companionClass.isClass + ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`; + // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`. + treeInfo.isQualifierSafeToElide(module) && + // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case, + // the companion class is actually not a ClassSymbol, but a reference to an abstract type. + module.symbol.companionClass.isClass + ) } val doTransform = @@ -1607,14 +1476,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans tree } private def transformSelect(tree: Select): Tree = { - val Select(qual, name) = tree + val Select(qual, _) = tree val sym = tree.symbol - /** Note: if a symbol has both @deprecated and @migration annotations and both - * warnings are enabled, only the first one checked here will be emitted. - * I assume that's a consequence of some code trying to avoid noise by suppressing - * warnings after the first, but I think it'd be better if we didn't have to - * arbitrarily choose one as more important than the other. + /* Note: if a symbol has both @deprecated and @migration annotations and both + * warnings are enabled, only the first one checked here will be emitted. + * I assume that's a consequence of some code trying to avoid noise by suppressing + * warnings after the first, but I think it'd be better if we didn't have to + * arbitrarily choose one as more important than the other. */ checkDeprecated(sym, tree.pos) if(settings.Xmigration.value != NoScalaVersion) @@ -1622,18 +1491,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkCompileTimeOnly(sym, tree.pos) checkDelayedInitSelect(qual, sym, tree.pos) - if (sym eq NoSymbol) { - unit.warning(tree.pos, "Select node has NoSymbol! " + tree + " / " + tree.tpe) - } - else if (currentClass != sym.owner && sym.hasLocalFlag) { - var o = currentClass - var hidden = false - while (!hidden && o != sym.owner && o != sym.owner.moduleClass && !o.isPackage) { - hidden = o.isTerm || o.isPrivateLocal - o = o.owner - } - if (!hidden) escapedPrivateLocals += sym - } + if (sym eq NoSymbol) + devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe) + else if (sym.hasLocalFlag) + varianceValidator.checkForEscape(sym, currentClass) def checkSuper(mix: Name) = // term should have been eliminated by super accessors @@ -1649,7 +1510,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def transformIf(tree: If): Tree = { val If(cond, thenpart, elsepart) = tree def unitIfEmpty(t: Tree): Tree = - if (t == EmptyTree) Literal(Constant()).setPos(tree.pos).setType(UnitClass.tpe) else t + if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitClass.tpe) else t cond.tpe match { case ConstantType(value) => @@ -1666,8 +1527,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // on Unit, in which case we had better let it slide. val isOk = ( sym.isGetter - || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType)) || (sym.name containsName nme.DEFAULT_GETTER_STRING) + || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType)) ) if (!isOk) unit.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead") @@ -1676,10 +1537,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Verify classes extending AnyVal meet the requirements private def checkAnyValSubclass(clazz: Symbol) = { - if ((clazz isSubClass AnyValClass) && !isPrimitiveValueClass(clazz)) { + if (clazz.isDerivedValueClass) { if (clazz.isTrait) unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") - else if ((clazz != AnyValClass) && clazz.hasFlag(ABSTRACT)) + else if (clazz.hasAbstractFlag) unit.error(clazz.pos, "`abstract' modifier cannot be used with value classes") } } @@ -1702,9 +1563,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) => checkDeprecatedOvers(tree) checkInfiniteLoop(tree.asInstanceOf[ValOrDefDef]) - if (settings.warnNullaryUnit.value) + if (settings.warnNullaryUnit) checkNullaryMethodReturnType(sym) - if (settings.warnInaccessible.value) { + if (settings.warnInaccessible) { if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic) checkAccessibilityOfReferencedTypes(tree) } @@ -1717,6 +1578,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val bridges = addVarargBridges(currentOwner) checkAllOverrides(currentOwner) checkAnyValSubclass(currentOwner) + if (currentOwner.isDerivedValueClass) + currentOwner.primaryConstructor makeNotPrivate NoSymbol // SI-6601, must be done *after* pickler! if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc") @@ -1754,7 +1617,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans enterReference(tree.pos, tpt.tpe.typeSymbol) tree - case Typed(_, Ident(tpnme.WILDCARD_STAR)) if !isRepeatedParamArg(tree) => + case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) => unit.error(tree.pos, "no `: _*' annotation allowed here\n"+ "(such annotations are only allowed in arguments to *-parameters)") tree @@ -1798,14 +1661,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans result match { case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => - if (result.symbol.isLocal || result.symbol.owner.isPackageClass) + if (result.symbol.isLocal || result.symbol.isTopLevel) varianceValidator.traverse(result) case _ => } result } catch { case ex: TypeError => - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() unit.error(tree.pos, ex.getMessage()) tree } finally { diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 64c5b41638..64fcda3b80 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -4,7 +4,127 @@ package typechecker trait StdAttachments { self: Analyzer => + import global._ + + /** Carries information necessary to expand the host tree. + * At times we need to store this info, because macro expansion can be delayed until its targs are inferred. + * After a macro application has been successfully expanded, this attachment is destroyed. + */ type UnaffiliatedMacroContext = scala.reflect.macros.runtime.Context type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type } case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) + + /** Scratchpad for the macro expander, which is used to store all intermediate data except the details about the runtime. + */ + case class MacroExpanderAttachment(original: Tree, desugared: Tree, role: MacroRole) + + /** Loads underlying MacroExpanderAttachment from a macro expandee or returns a default value for that attachment. + */ + def macroExpanderAttachment(tree: Tree): MacroExpanderAttachment = + tree.attachments.get[MacroExpanderAttachment] getOrElse { + tree match { + case Apply(fn, _) if tree.isInstanceOf[ApplyToImplicitArgs] => macroExpanderAttachment(fn) + case _ => MacroExpanderAttachment(tree, EmptyTree, APPLY_ROLE) + } + } + + /** After macro expansion is completed, links the expandee and the expansion result + * by annotating them both with a `MacroExpansionAttachment`. + */ + def linkExpandeeAndDesugared(expandee: Tree, desugared: Tree, role: MacroRole): Unit = { + val metadata = MacroExpanderAttachment(expandee, desugared, role) + expandee updateAttachment metadata + desugared updateAttachment metadata + } + + /** Is added by the macro engine to originals and results of macro expansions. + * Stores the original expandee as it entered the `macroExpand` function. + */ + case class MacroExpansionAttachment(expandee: Tree, expanded: Any) + + /** Determines whether the target is either an original or a result of a macro expansion. + * The parameter is of type `Any`, because macros can expand both into trees and into annotations. + */ + def hasMacroExpansionAttachment(any: Any): Boolean = any match { + case tree: Tree => tree.attachments.get[MacroExpansionAttachment].isDefined + case _ => false + } + + /** After macro expansion is completed, links the expandee and the expansion result by annotating them both with a `MacroExpansionAttachment`. + * The `expanded` parameter is of type `Any`, because macros can expand both into trees and into annotations. + */ + def linkExpandeeAndExpanded(expandee: Tree, expanded: Any): Unit = { + val metadata = MacroExpansionAttachment(expandee, expanded) + expandee updateAttachment metadata + expanded match { + case expanded: Tree => expanded updateAttachment metadata + case _ => // do nothing + } + } + + /** Checks whether there is any tree resulting from a macro expansion and associated with the current tree. + */ + object ExpandedIntoTree { + def unapply(tree: Tree): Option[Tree] = tree.attachments.get[MacroExpansionAttachment] match { + case Some(MacroExpansionAttachment(_, tree: Tree)) => Some(tree) + case _ => None + } + } + + /** When present, suppresses macro expansion for the host. + * This is occasionally necessary, e.g. to prohibit eta-expansion of macros. + * + * Does not affect expandability of child nodes, there's context.withMacrosDisabled for that + * (but think thrice before using that API - see the discussion at https://github.com/scala/scala/pull/1639). + */ + case object SuppressMacroExpansionAttachment + + /** Suppresses macro expansion of the tree by putting SuppressMacroExpansionAttachment on it. + */ + def suppressMacroExpansion(tree: Tree) = tree.updateAttachment(SuppressMacroExpansionAttachment) + + /** Unsuppresses macro expansion of the tree by removing SuppressMacroExpansionAttachment from it and its children. + */ + def unsuppressMacroExpansion(tree: Tree): Tree = { + tree.removeAttachment[SuppressMacroExpansionAttachment.type] + tree match { + // see the comment to `isMacroExpansionSuppressed` to learn why we need + // a special traversal strategy here + case Apply(fn, _) => unsuppressMacroExpansion(fn) + case TypeApply(fn, _) => unsuppressMacroExpansion(fn) + case _ => // do nothing + } + tree + } + + /** Determines whether a tree should not be expanded, because someone has put SuppressMacroExpansionAttachment on it or one of its children. + */ + def isMacroExpansionSuppressed(tree: Tree): Boolean = + if (tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) true + else tree match { + // we have to account for the fact that during typechecking an expandee might become wrapped, + // i.e. surrounded by an inferred implicit argument application or by an inferred type argument application. + // in that case the expandee itself will no longer be suppressed and we need to look at the core + case Apply(fn, _) => isMacroExpansionSuppressed(fn) + case TypeApply(fn, _) => isMacroExpansionSuppressed(fn) + case _ => false + } + + /** After being synthesized by the parser, primary constructors aren't fully baked yet. + * A call to super in such constructors is just a fill-me-in-later dummy resolved later + * by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and + * allows them to complete the synthesis. + */ + case class SuperArgsAttachment(argss: List[List[Tree]]) + + /** Convenience method for `SuperArgsAttachment`. + * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, + * so it really benefits from a dedicated extractor. + */ + def superArgs(tree: Tree): Option[List[List[Tree]]] = + tree.attachments.get[SuperArgsAttachment] collect { case SuperArgsAttachment(argss) => argss } + + /** Determines whether the given tree has an associated SuperArgsAttachment. + */ + def hasSuperArgs(tree: Tree): Boolean = superArgs(tree).nonEmpty }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index fa72ad64bf..e22dc73b53 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -1,3 +1,4 @@ + /* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky @@ -60,8 +61,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val clazz = qual.symbol val supername = nme.superName(name) val superAcc = clazz.info.decl(supername).suchThat(_.alias == sym) orElse { - debuglog("add super acc " + sym + sym.locationString + " to `" + clazz);//debug - val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE) setAlias sym + debuglog(s"add super acc ${sym.fullLocationString} to $clazz") + val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE | ARTIFACT) setAlias sym val tpe = clazz.thisType memberType sym match { case t if sym.isModule && !sym.isMethod => NullaryMethodType(t) case t => t @@ -90,7 +91,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT 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) + if (settings.explaintypes) explainTypes(found, req) } } @@ -129,11 +130,11 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val clazz = sup.symbol if (sym.isDeferred) { - val member = sym.overridingSymbol(clazz); + val member = sym.overridingSymbol(clazz) if (mix != tpnme.EMPTY || member == NoSymbol || !(member.isAbstractOverride && member.isIncompleteIn(clazz))) unit.error(sel.pos, ""+sym.fullLocationString+" is accessed from super. It may not be abstract "+ - "unless it is overridden by a member declared `abstract' and `override'"); + "unless it is overridden by a member declared `abstract' and `override'") } else if (mix == tpnme.EMPTY && !sym.owner.isTrait){ // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract. val intermediateClasses = clazz.info.baseClasses.tail.takeWhile(_ != sym.owner) @@ -186,18 +187,6 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT log("Expanded '%s' to '%s' in %s".format(savedName, s.name, sym)) } } - if (settings.verbose.value && forScaladoc && !sym.isAnonymousClass) { - println("========== scaladoc of "+sym+" =============================") - println(toJavaDoc(expandedDocComment(sym))) - for (member <- sym.info.members) { - println(member+":"+sym.thisType.memberInfo(member)+"\n"+ - toJavaDoc(expandedDocComment(member, sym))) - for ((useCase, comment, pos) <- useCases(member, sym)) { - println("usecase "+useCase+":"+useCase.info) - println(toJavaDoc(comment)) - } - } - } super.transform(tree) } transformClassDef @@ -224,7 +213,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT transformTemplate case TypeApply(sel @ Select(This(_), name), args) => - mayNeedProtectedAccessor(sel, args, false) + mayNeedProtectedAccessor(sel, args, goToSuper = false) // set a flag for all type parameters with `@specialized` annotation so it can be pickled case typeDef: TypeDef if typeDef.symbol.deSkolemize.hasAnnotation(definitions.SpecializedClass) => @@ -252,7 +241,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT // also exists in a superclass, because they may be surprised // to find out that a constructor parameter will shadow a // field. See SI-4762. - if (settings.lint.value) { + if (settings.lint) { if (sym.isPrivateLocal && sym.paramss.isEmpty) { qual.symbol.ancestors foreach { parent => parent.info.decls filterNot (x => x.isPrivate || x.hasLocalFlag) foreach { m2 => @@ -281,9 +270,9 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias) }).asInstanceOf[Select] debuglog("alias replacement: " + tree + " ==> " + result); //debug - localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, true)) + localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, beforeRefChecks = true)) } else { - /** + /* * A trait which extends a class and accesses a protected member * of that class cannot implement the necessary accessor method * because its implementation is in an implementation class (e.g. @@ -302,18 +291,19 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT && !sym.owner.isTrait && (sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass) && (qual.symbol.info.member(sym.name) ne NoSymbol) - && !needsProtectedAccessor(sym, tree.pos)) + && !needsProtectedAccessor(sym, tree.pos) + ) if (shouldEnsureAccessor) { log("Ensuring accessor for call to protected " + sym.fullLocationString + " from " + currentClass) ensureAccessor(sel) } else - mayNeedProtectedAccessor(sel, EmptyTree.asList, false) + mayNeedProtectedAccessor(sel, EmptyTree.asList, goToSuper = false) } case Super(_, mix) => if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { - if (!settings.overrideVars.value) + if (!settings.overrideVars) unit.error(tree.pos, "super may be not be used on " + sym.accessedOrSelf) } else if (isDisallowed(sym)) { unit.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") @@ -321,7 +311,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT transformSuperSelect(sel) case _ => - mayNeedProtectedAccessor(sel, EmptyTree.asList, true) + mayNeedProtectedAccessor(sel, EmptyTree.asList, goToSuper = true) } } transformSelect @@ -330,7 +320,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, withInvalidOwner(transform(rhs))) case TypeApply(sel @ Select(qual, name), args) => - mayNeedProtectedAccessor(sel, args, true) + mayNeedProtectedAccessor(sel, args, goToSuper = true) case Assign(lhs @ Select(qual, name), rhs) => def transformAssign = { @@ -338,8 +328,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT lhs.symbol.isJavaDefined && needsProtectedAccessor(lhs.symbol, tree.pos)) { debuglog("Adding protected setter for " + tree) - val setter = makeSetter(lhs); - debuglog("Replaced " + tree + " with " + setter); + val setter = makeSetter(lhs) + debuglog("Replaced " + tree + " with " + setter) transform(localTyper.typed(Apply(setter, List(qual, rhs)))) } else super.transform(tree) @@ -398,14 +388,14 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT * typed. */ private def makeAccessor(tree: Select, targs: List[Tree]): Tree = { - val Select(qual, name) = tree + val Select(qual, _) = tree val sym = tree.symbol val clazz = hostForAccessorOf(sym, currentClass) assert(clazz != NoSymbol, sym) debuglog("Decided for host class: " + clazz) - val accName = nme.protName(sym.originalName) + val accName = nme.protName(sym.unexpandedName) val hasArgs = sym.tpe.paramSectionCount > 0 val memberType = refChecks.toScalaRepeatedParam(sym.tpe) // fix for #2413 @@ -423,7 +413,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } val protAcc = clazz.info.decl(accName).suchThat(s => s == NoSymbol || s.tpe =:= accType(s)) orElse { - val newAcc = clazz.newMethod(nme.protName(sym.originalName), tree.pos) + val newAcc = clazz.newMethod(nme.protName(sym.unexpandedName), tree.pos, newFlags = ARTIFACT) newAcc setInfoAndEnter accType(newAcc) val code = DefDef(newAcc, { @@ -434,7 +424,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT args.foldLeft(base)(Apply(_, _)) }) - debuglog("" + code) + debuglog("created protected accessor: " + code) storeAccessorDefinition(clazz, code) newAcc } @@ -446,7 +436,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case _ => mkApply(TypeApply(selection, targs)) } } - debuglog("Replaced " + tree + " with " + res) + debuglog(s"Replaced $tree with $res") if (hasArgs) localTyper.typedOperator(res) else localTyper.typed(res) } @@ -483,9 +473,9 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT assert(clazz != NoSymbol, field) debuglog("Decided for host class: " + clazz) - val accName = nme.protSetterName(field.originalName) + val accName = nme.protSetterName(field.unexpandedName) val protectedAccessor = clazz.info decl accName orElse { - val protAcc = clazz.newMethod(accName, field.pos) + val protAcc = clazz.newMethod(accName, field.pos, newFlags = ARTIFACT) val paramTypes = List(clazz.typeOfThis, field.tpe) val params = protAcc newSyntheticValueParams paramTypes val accessorType = MethodType(params, UnitClass.tpe) @@ -517,9 +507,6 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT def accessibleThroughSubclassing = validCurrentOwner && clazz.thisSym.isSubClass(sym.owner) && !clazz.isTrait - def packageAccessBoundry(sym: Symbol) = - sym.accessBoundary(sym.enclosingPackageClass) - val isCandidate = ( sym.isProtected && sym.isJavaDefined diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 242eb9c9fe..406bc445c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -6,10 +6,10 @@ package scala.tools.nsc package typechecker -import symtab.Flags +import scala.collection.{ mutable, immutable } import symtab.Flags._ -import scala.collection.mutable import scala.collection.mutable.ListBuffer +import scala.language.postfixOps /** Synthetic method implementations for case classes and case objects. * @@ -94,8 +94,8 @@ trait SyntheticMethods extends ast.TreeDSL { // like Tags and Arrays which are not robust and infer things // which they shouldn't. val accessorLub = ( - if (opt.experimental) { - global.weakLub(accessors map (_.tpe.finalResultType))._1 match { + if (settings.Xexperimental) { + global.weakLub(accessors map (_.tpe.finalResultType)) match { case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) case tp => tp } @@ -121,23 +121,13 @@ trait SyntheticMethods extends ast.TreeDSL { (m0 ne meth) && !m0.isDeferred && !m0.isSynthetic && (m0.owner != AnyValClass) && (typeInClazz(m0) matches typeInClazz(meth)) } } - def readConstantValue[T](name: String, default: T = null.asInstanceOf[T]): T = { - clazzMember(newTermName(name)).info match { - case NullaryMethodType(ConstantType(Constant(value))) => value.asInstanceOf[T] - case _ => default - } - } def productIteratorMethod = { createMethod(nme.productIterator, iteratorOfType(accessorLub))(_ => gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(mkThis)) ) } - def projectionMethod(accessor: Symbol, num: Int) = { - createMethod(nme.productAccessorName(num), accessor.tpe.resultType)(_ => REF(accessor)) - } - /** Common code for productElement and (currently disabled) productElementName - */ + /* Common code for productElement and (currently disabled) productElementName */ def perElementMethod(name: Name, returnType: Type)(caseFn: Symbol => Tree): Tree = createSwitchMethod(name, accessors.indices, returnType)(idx => caseFn(accessors(idx))) @@ -145,8 +135,8 @@ trait SyntheticMethods extends ast.TreeDSL { var syntheticCanEqual = false - /** The canEqual method for case classes. - * def canEqual(that: Any) = that.isInstanceOf[This] + /* The canEqual method for case classes. + * def canEqual(that: Any) = that.isInstanceOf[This] */ def canEqualMethod: Tree = { syntheticCanEqual = true @@ -154,13 +144,13 @@ trait SyntheticMethods extends ast.TreeDSL { Ident(m.firstParam) IS_OBJ classExistentialType(clazz)) } - /** that match { case _: this.C => true ; case _ => false } - * where `that` is the given method's first parameter. + /* that match { case _: this.C => true ; case _ => false } + * where `that` is the given method's first parameter. * - * An isInstanceOf test is insufficient because it has weaker - * requirements than a pattern match. Given an inner class Foo and - * two different instantiations of the container, an x.Foo and and a y.Foo - * are both .isInstanceOf[Foo], but the one does not match as the other. + * An isInstanceOf test is insufficient because it has weaker + * requirements than a pattern match. Given an inner class Foo and + * two different instantiations of the container, an x.Foo and and a y.Foo + * are both .isInstanceOf[Foo], but the one does not match as the other. */ def thatTest(eqmeth: Symbol): Tree = { Match( @@ -172,19 +162,19 @@ trait SyntheticMethods extends ast.TreeDSL { ) } - /** (that.asInstanceOf[this.C]) - * where that is the given methods first parameter. + /* (that.asInstanceOf[this.C]) + * where that is the given methods first parameter. */ def thatCast(eqmeth: Symbol): Tree = gen.mkCast(Ident(eqmeth.firstParam), clazz.tpe) - /** The equality method core for case classes and inline clases. - * 1+ args: - * (that.isInstanceOf[this.C]) && { - * val x$1 = that.asInstanceOf[this.C] - * (this.arg_1 == x$1.arg_1) && (this.arg_2 == x$1.arg_2) && ... && (x$1 canEqual this) - * } - * Drop canBuildFrom part if class is final and canBuildFrom is synthesized + /* The equality method core for case classes and inline clases. + * 1+ args: + * (that.isInstanceOf[this.C]) && { + * val x$1 = that.asInstanceOf[this.C] + * (this.arg_1 == x$1.arg_1) && (this.arg_2 == x$1.arg_2) && ... && (x$1 canEqual this) + * } + * Drop canBuildFrom part if class is final and canBuildFrom is synthesized */ def equalsCore(eqmeth: Symbol, accessors: List[Symbol]) = { val otherName = context.unit.freshTermName(clazz.name + "$") @@ -199,16 +189,16 @@ trait SyntheticMethods extends ast.TreeDSL { ) } - /** The equality method for case classes. - * 0 args: - * def equals(that: Any) = that.isInstanceOf[this.C] && that.asInstanceOf[this.C].canEqual(this) - * 1+ args: - * def equals(that: Any) = (this eq that.asInstanceOf[AnyRef]) || { - * (that.isInstanceOf[this.C]) && { - * val x$1 = that.asInstanceOf[this.C] - * (this.arg_1 == x$1.arg_1) && (this.arg_2 == x$1.arg_2) && ... && (x$1 canEqual this) - * } - * } + /* The equality method for case classes. + * 0 args: + * def equals(that: Any) = that.isInstanceOf[this.C] && that.asInstanceOf[this.C].canEqual(this) + * 1+ args: + * def equals(that: Any) = (this eq that.asInstanceOf[AnyRef]) || { + * (that.isInstanceOf[this.C]) && { + * val x$1 = that.asInstanceOf[this.C] + * (this.arg_1 == x$1.arg_1) && (this.arg_2 == x$1.arg_2) && ... && (x$1 canEqual this) + * } + * } */ def equalsCaseClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => if (accessors.isEmpty) @@ -218,30 +208,35 @@ trait SyntheticMethods extends ast.TreeDSL { (mkThis ANY_EQ Ident(m.firstParam)) OR equalsCore(m, accessors) } - /** The equality method for value classes - * def equals(that: Any) = (this.asInstanceOf[AnyRef]) eq that.asInstanceOf[AnyRef]) || { - * (that.isInstanceOf[this.C]) && { - * val x$1 = that.asInstanceOf[this.C] - * (this.underlying == that.underlying + /* The equality method for value classes + * def equals(that: Any) = (this.asInstanceOf[AnyRef]) eq that.asInstanceOf[AnyRef]) || { + * (that.isInstanceOf[this.C]) && { + * val x$1 = that.asInstanceOf[this.C] + * (this.underlying == that.underlying */ def equalsDerivedValueClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => equalsCore(m, List(clazz.derivedValueClassUnbox)) } - /** The hashcode method for value classes + /* The hashcode method for value classes * def hashCode(): Int = this.underlying.hashCode */ def hashCodeDerivedValueClassMethod: Tree = createMethod(nme.hashCode_, Nil, IntClass.tpe) { m => Select(mkThisSelect(clazz.derivedValueClassUnbox), nme.hashCode_) } - /** The _1, _2, etc. methods to implement ProductN, disabled - * until we figure out how to introduce ProductN without cycles. + /* The _1, _2, etc. methods to implement ProductN, disabled + * until we figure out how to introduce ProductN without cycles. */ - def productNMethods = { + /**** + def productNMethods = { val accs = accessors.toIndexedSeq 1 to arity map (num => productProj(arity, num) -> (() => projectionMethod(accs(num - 1), num))) } + def projectionMethod(accessor: Symbol, num: Int) = { + createMethod(nme.productAccessorName(num), accessor.tpe.resultType)(_ => REF(accessor)) + } + ****/ // methods for both classes and objects def productMethods = { @@ -313,11 +308,11 @@ trait SyntheticMethods extends ast.TreeDSL { // Object_equals -> (() => createMethod(Object_equals)(m => This(clazz) ANY_EQ Ident(m.firstParam))) ) - /** If you serialize a singleton and then deserialize it twice, - * you will have two instances of your singleton unless you implement - * readResolve. Here it is implemented for all objects which have - * no implementation and which are marked serializable (which is true - * for all case objects.) + /* If you serialize a singleton and then deserialize it twice, + * you will have two instances of your singleton unless you implement + * readResolve. Here it is implemented for all objects which have + * no implementation and which are marked serializable (which is true + * for all case objects.) */ def needsReadResolve = ( clazz.isModuleClass @@ -335,18 +330,20 @@ trait SyntheticMethods extends ast.TreeDSL { else Nil ) - /** Always generate overrides for equals and hashCode in value classes, - * so they can appear in universal traits without breaking value semantics. + /* Always generate overrides for equals and hashCode in value classes, + * so they can appear in universal traits without breaking value semantics. */ def impls = { def shouldGenerate(m: Symbol) = { !hasOverridingImplementation(m) || { clazz.isDerivedValueClass && (m == Any_hashCode || m == Any_equals) && { - if (settings.lint.value) { - (clazz.info nonPrivateMember m.name) filter (m => (m.owner != AnyClass) && (m.owner != clazz) && !m.isDeferred) andAlso { m => - currentUnit.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics") - } - } + // Without a means to suppress this warning, I've thought better of it. + // + // if (settings.lint) { + // (clazz.info nonPrivateMember m.name) filter (m => (m.owner != AnyClass) && (m.owner != clazz) && !m.isDeferred) andAlso { m => + // currentUnit.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics") + // } + // } true } } @@ -368,11 +365,11 @@ trait SyntheticMethods extends ast.TreeDSL { catch { case _: TypeError if reporter.hasErrors => Nil } } - /** If this case class has any less than public accessors, - * adds new accessors at the correct locations to preserve ordering. - * Note that this must be done before the other method synthesis - * because synthesized methods need refer to the new symbols. - * Care must also be taken to preserve the case accessor order. + /* If this case class has any less than public accessors, + * adds new accessors at the correct locations to preserve ordering. + * Note that this must be done before the other method synthesis + * because synthesized methods need refer to the new symbols. + * Care must also be taken to preserve the case accessor order. */ def caseTemplateBody(): List[Tree] = { val lb = ListBuffer[Tree]() diff --git a/src/compiler/scala/tools/nsc/typechecker/Tags.scala b/src/compiler/scala/tools/nsc/typechecker/Tags.scala index d82fbd7c77..d2d7f57aef 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Tags.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Tags.scala @@ -10,16 +10,16 @@ trait Tags { trait Tag { self: Typer => - private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = beforeTyper { + private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = enteringTyper { def wrapper (tree: => Tree): Tree = if (allowMaterialization) (context.withMacrosEnabled[Tree](tree)) else (context.withMacrosDisabled[Tree](tree)) wrapper(inferImplicit( EmptyTree, taggedTp, - /*reportAmbiguous =*/ true, - /*isView =*/ false, - /*context =*/ context, - /*saveAmbiguousDivergent =*/ true, - /*pos =*/ pos + reportAmbiguous = true, + isView = false, + context, + saveAmbiguousDivergent = true, + pos ).tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 88d10f1d72..1c8d37ef39 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package typechecker -import scala.tools.nsc.symtab.Flags._ import scala.collection.mutable import mutable.ListBuffer import util.returning @@ -70,7 +69,7 @@ abstract class TreeCheckers extends Analyzer { // new symbols if (newSyms.nonEmpty) { informFn(newSyms.size + " new symbols.") - val toPrint = if (settings.debug.value) sortedNewSyms mkString " " else "" + val toPrint = if (settings.debug) sortedNewSyms mkString " " else "" newSyms.clear() if (toPrint != "") @@ -121,7 +120,7 @@ abstract class TreeCheckers extends Analyzer { def errorFn(msg: Any): Unit = {hasError = true; println("[check: %s] %s".format(phase.prev, msg))} def errorFn(pos: Position, msg: Any): Unit = errorFn(posstr(pos) + ": " + msg) def informFn(msg: Any) { - if (settings.verbose.value || settings.debug.value) + if (settings.verbose || settings.debug) println("[check: %s] %s".format(phase.prev, msg)) } @@ -138,25 +137,18 @@ abstract class TreeCheckers extends Analyzer { } def checkTrees() { - if (settings.verbose.value) + if (settings.verbose) Console.println("[consistency check at the beginning of phase " + phase + "]") currentRun.units foreach (x => wrap(x)(check(x))) } - def printingTypings[T](body: => T): T = { - val saved = global.printTypings - global.printTypings = true - val result = body - global.printTypings = saved - result - } def runWithUnit[T](unit: CompilationUnit)(body: => Unit): Unit = { hasError = false val unit0 = currentUnit currentRun.currentUnit = unit body - currentRun.advanceUnit + currentRun.advanceUnit() assertFn(currentUnit == unit, "currentUnit is " + currentUnit + ", but unit is " + unit) currentRun.currentUnit = unit0 } @@ -164,7 +156,7 @@ abstract class TreeCheckers extends Analyzer { informProgress("checking "+unit) val context = rootContext(unit) context.checking = true - tpeOfTree.clear + tpeOfTree.clear() SymbolTracker.check(phase, unit) val checker = new TreeChecker(context) runWithUnit(unit) { @@ -189,10 +181,6 @@ abstract class TreeCheckers extends Analyzer { errorFn(t1.pos, "trees differ\n old: " + treestr(t1) + "\n new: " + treestr(t2)) private def typesDiffer(tree: Tree, tp1: Type, tp2: Type) = errorFn(tree.pos, "types differ\n old: " + tp1 + "\n new: " + tp2 + "\n tree: " + tree) - private def ownersDiffer(tree: Tree, shouldBe: Symbol) = { - val sym = tree.symbol - errorFn(tree.pos, sym + " has wrong owner: " + ownerstr(sym.owner) + ", should be: " + ownerstr(shouldBe)) - } /** XXX Disabled reporting of position errors until there is less noise. */ private def noPos(t: Tree) = @@ -204,14 +192,11 @@ abstract class TreeCheckers extends Analyzer { if (t.symbol == NoSymbol) errorFn(t.pos, "no symbol: " + treestr(t)) - override def typed(tree: Tree, mode: Int, pt: Type): Tree = returning(tree) { + override def typed(tree: Tree, mode: Mode, pt: Type): Tree = returning(tree) { case EmptyTree | TypeTree() => () case _ if tree.tpe != null => - tpeOfTree.getOrElseUpdate(tree, { - val saved = tree.tpe - tree.tpe = null - saved - }) + tpeOfTree.getOrElseUpdate(tree, try tree.tpe finally tree.clearType()) + wrap(tree)(super.typed(tree, mode, pt) match { case _: Literal => () case x if x ne tree => treesDiffer(tree, x) @@ -236,7 +221,7 @@ abstract class TreeCheckers extends Analyzer { case _: ConstantType => () case _ => checkSym(tree) - /** XXX: lots of syms show up here with accessed == NoSymbol. */ + /* XXX: lots of syms show up here with accessed == NoSymbol. */ if (accessed != NoSymbol) { val agetter = accessed.getter(sym.owner) val asetter = accessed.setter(sym.owner) @@ -263,7 +248,7 @@ abstract class TreeCheckers extends Analyzer { else if (currentOwner.ownerChain takeWhile (_ != sym) exists (_ == NoSymbol)) return fail("tree symbol "+sym+" does not point to enclosing class; tree = ") - /** XXX: temporary while Import nodes are arriving untyped. */ + /* XXX: temporary while Import nodes are arriving untyped. */ case Import(_, _) => return case _ => @@ -284,7 +269,7 @@ abstract class TreeCheckers extends Analyzer { def cond(s: Symbol) = !s.isTerm || s.isMethod || s == sym.owner if (sym.owner != currentOwner) { - val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse fail("DefTree can't find owner: ") + val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse { fail("DefTree can't find owner: ") ; NoSymbol } if (sym.owner != expected) fail(sm"""| | currentOwner chain: ${currentOwner.ownerChain take 3 mkString " -> "} @@ -298,7 +283,6 @@ abstract class TreeCheckers extends Analyzer { private def checkSymbolRefsRespectScope(tree: Tree) { def symbolOf(t: Tree): Symbol = Option(tree.symbol).getOrElse(NoSymbol) - def definedSymbolOf(t: Tree): Symbol = if (t.isDef) symbolOf(t) else NoSymbol val info = Option(symbolOf(tree).info).getOrElse(NoType) val referencedSymbols: List[Symbol] = { val directRef = tree match { @@ -344,7 +328,7 @@ abstract class TreeCheckers extends Analyzer { if (oldtpe =:= tree.tpe) () else typesDiffer(tree, oldtpe, tree.tpe) - tree.tpe = oldtpe + tree setType oldtpe super.traverse(tree) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 4950a7efef..5146cf6fa7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -8,7 +8,6 @@ package typechecker import scala.collection.mutable import scala.collection.mutable.ListBuffer -import scala.util.control.ControlThrowable import scala.util.control.Exception.ultimately import symtab.Flags._ import PartialFunction._ @@ -37,15 +36,6 @@ trait TypeDiagnostics { import global._ import definitions._ - import global.typer.{ infer, context } - - /** The common situation of making sure nothing is erroneous could be - * nicer if Symbols, Types, and Trees all implemented some common interface - * in which isErroneous and similar would be placed. - */ - def noErroneousTypes(tps: Type*) = tps forall (x => !x.isErroneous) - def noErroneousSyms(syms: Symbol*) = syms forall (x => !x.isErroneous) - def noErroneousTrees(trees: Tree*) = trees forall (x => !x.isErroneous) /** For errors which are artifacts of the implementation: such messages * indicate that the restriction may be lifted in the future. @@ -58,7 +48,7 @@ trait TypeDiagnostics { /** A map of Positions to addendums - if an error involves a position in * the map, the addendum should also be printed. */ - private var addendums = perRunCaches.newMap[Position, () => String]() + private val addendums = perRunCaches.newMap[Position, () => String]() private var isTyperInPattern = false /** Devising new ways of communicating error info out of @@ -174,11 +164,6 @@ trait TypeDiagnostics { case xs => " where " + (disambiguate(xs map (_.existentialToString)) mkString ", ") } - def varianceWord(sym: Symbol): String = - if (sym.variance == 1) "covariant" - else if (sym.variance == -1) "contravariant" - else "invariant" - def explainAlias(tp: Type) = { // Don't automatically normalize standard aliases; they still will be // expanded if necessary to disambiguate simple identifiers. @@ -223,12 +208,12 @@ trait TypeDiagnostics { // force measures than comparing normalized Strings were producing error messages // like "and java.util.ArrayList[String] <: java.util.ArrayList[String]" but there // should be a cleaner way to do this. - if (found.normalize.toString == tp.normalize.toString) "" + if (found.dealiasWiden.toString == tp.dealiasWiden.toString) "" else " (and %s <: %s)".format(found, tp) ) val explainDef = { val prepend = if (isJava) "Java-defined " else "" - "%s%s is %s in %s.".format(prepend, reqsym, varianceWord(param), param) + "%s%s is %s in %s.".format(prepend, reqsym, param.variance, param) } // Don't suggest they change the class declaration if it's somewhere // under scala.* or defined in a java class, because attempting either @@ -248,11 +233,11 @@ trait TypeDiagnostics { || ((arg <:< reqArg) && param.isCovariant) || ((reqArg <:< arg) && param.isContravariant) ) - val invariant = param.variance == 0 + val invariant = param.variance.isInvariant if (conforms) Some("") - else if ((arg <:< reqArg) && invariant) mkMsg(true) // covariant relationship - else if ((reqArg <:< arg) && invariant) mkMsg(false) // contravariant relationship + else if ((arg <:< reqArg) && invariant) mkMsg(isSubtype = true) // covariant relationship + else if ((reqArg <:< arg) && invariant) mkMsg(isSubtype = false) // contravariant relationship else None // we assume in other cases our ham-fisted advice will merely serve to confuse } val messages = relationships.flatten @@ -309,7 +294,6 @@ trait TypeDiagnostics { // distinguished from the other types in the same error message private val savedName = sym.name def restoreName() = sym.name = savedName - def isAltered = sym.name != savedName def modifyName(f: String => String) = sym setName newTypeName(f(sym.name.toString)) /** Prepend java.lang, scala., or Predef. if this type originated @@ -439,6 +423,122 @@ trait TypeDiagnostics { def permanentlyHiddenWarning(pos: Position, hidden: Name, defn: Symbol) = contextWarning(pos, "imported `%s' is permanently hidden by definition of %s".format(hidden, defn.fullLocationString)) + object checkUnused { + val ignoreNames = Set[TermName]("readResolve", "readObject", "writeObject", "writeReplace") + + class UnusedPrivates extends Traverser { + val defnTrees = ListBuffer[MemberDef]() + val targets = mutable.Set[Symbol]() + val setVars = mutable.Set[Symbol]() + val treeTypes = mutable.Set[Type]() + + def defnSymbols = defnTrees.toList map (_.symbol) + def localVars = defnSymbols filter (t => t.isLocal && t.isVar) + + def qualifiesTerm(sym: Symbol) = ( + (sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocal) + && !nme.isLocalName(sym.name) + && !sym.isParameter + && !sym.isParamAccessor // could improve this, but it's a pain + && !sym.isEarlyInitialized // lots of false positives in the way these are encoded + && !(sym.isGetter && sym.accessed.isEarlyInitialized) + ) + def qualifiesType(sym: Symbol) = !sym.isDefinedInPackage + def qualifies(sym: Symbol) = ( + (sym ne null) + && (sym.isTerm && qualifiesTerm(sym) || sym.isType && qualifiesType(sym)) + ) + + override def traverse(t: Tree): Unit = { + t match { + case t: MemberDef if qualifies(t.symbol) => defnTrees += t + case t: RefTree if t.symbol ne null => targets += t.symbol + case Assign(lhs, _) if lhs.symbol != null => setVars += lhs.symbol + case _ => + } + // Only record type references which don't originate within the + // definition of the class being referenced. + if (t.tpe ne null) { + for (tp <- t.tpe ; if !treeTypes(tp) && !currentOwner.ownerChain.contains(tp.typeSymbol)) { + tp match { + case NoType | NoPrefix => + case NullaryMethodType(_) => + case MethodType(_, _) => + case _ => + log(s"$tp referenced from $currentOwner") + treeTypes += tp + } + } + // e.g. val a = new Foo ; new a.Bar ; don't let a be reported as unused. + t.tpe.prefix foreach { + case SingleType(_, sym) => targets += sym + case _ => + } + } + super.traverse(t) + } + def isUnusedType(m: Symbol): Boolean = ( + m.isType + && !m.isTypeParameterOrSkolem // would be nice to improve this + && (m.isPrivate || m.isLocal) + && !(treeTypes.exists(tp => tp exists (t => t.typeSymbolDirect == m))) + ) + def isUnusedTerm(m: Symbol): Boolean = ( + (m.isTerm) + && (m.isPrivate || m.isLocal) + && !targets(m) + && !(m.name == nme.WILDCARD) // e.g. val _ = foo + && !ignoreNames(m.name.toTermName) // serialization methods + && !isConstantType(m.info.resultType) // subject to constant inlining + && !treeTypes.exists(_ contains m) // e.g. val a = new Foo ; new a.Bar + ) + def unusedTypes = defnTrees.toList filter (t => isUnusedType(t.symbol)) + def unusedTerms = defnTrees.toList filter (v => isUnusedTerm(v.symbol)) + // local vars which are never set, except those already returned in unused + def unsetVars = localVars filter (v => !setVars(v) && !isUnusedTerm(v)) + } + + def apply(unit: CompilationUnit) = { + warnUnusedImports(unit) + + val p = new UnusedPrivates + p traverse unit.body + val unused = p.unusedTerms + unused foreach { defn: DefTree => + val sym = defn.symbol + val isDefaultGetter = sym.name containsName nme.DEFAULT_GETTER_STRING + val pos = ( + if (defn.pos.isDefined) defn.pos + else if (sym.pos.isDefined) sym.pos + else sym match { + case sym: TermSymbol => sym.referenced.pos + case _ => NoPosition + } + ) + val why = if (sym.isPrivate) "private" else "local" + val what = ( + if (isDefaultGetter) "default argument" + else if (sym.isConstructor) "constructor" + else if (sym.isVar || sym.isGetter && sym.accessed.isVar) "var" + else if (sym.isVal || sym.isGetter && sym.accessed.isVal) "val" + else if (sym.isSetter) "setter" + else if (sym.isMethod) "method" + else if (sym.isModule) "object" + else "term" + ) + unit.warning(pos, s"$why $what in ${sym.owner} is never used") + } + p.unsetVars foreach { v => + unit.warning(v.pos, s"local var ${v.name} in ${v.owner} is never set - it could be a val") + } + p.unusedTypes foreach { t => + val sym = t.symbol + val why = if (sym.isPrivate) "private" else "local" + unit.warning(t.pos, s"$why ${sym.fullLocationString} is never used") + } + } + } + object checkDead { private val exprStack: mutable.Stack[Symbol] = mutable.Stack(NoSymbol) // The method being applied to `tree` when `apply` is called. @@ -463,13 +563,13 @@ 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) - context.warning(tree.pos, "dead code following this construct", true) + if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && exprOK) + context.warning(tree.pos, "dead code following this construct", force = true) tree } // The checkDead call from typedArg is more selective. - def inMode(mode: Int, tree: Tree): Tree = { + def inMode(mode: Mode, tree: Tree): Tree = { val modeOK = (mode & (EXPRmode | BYVALmode | POLYmode)) == (EXPRmode | BYVALmode) if (modeOK) apply(tree) else tree @@ -494,7 +594,7 @@ trait TypeDiagnostics { /** Report a type error. * - * @param pos0 The position where to report the error + * @param pos The position where to report the error * @param ex The exception that caused the error */ def reportTypeError(context0: Context, pos: Position, ex: TypeError) { @@ -503,7 +603,7 @@ trait TypeDiagnostics { // 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() + if (settings.debug) ex.printStackTrace() ex match { case CyclicReference(sym, info: TypeCompleter) => diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala new file mode 100644 index 0000000000..afe6875218 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala @@ -0,0 +1,244 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package typechecker + +import java.lang.{ reflect => r } +import r.TypeVariable +import scala.reflect.NameTransformer +import NameTransformer._ +import scala.reflect.runtime.{universe => ru} +import scala.reflect.{ClassTag, classTag} + +/** A more principled system for turning types into strings. + */ +trait StructuredTypeStrings extends DestructureTypes { + val global: Global + import global._ + + case class LabelAndType(label: String, typeName: String) { } + object LabelAndType { + val empty = LabelAndType("", "") + } + case class Grouping(ldelim: String, mdelim: String, rdelim: String, labels: Boolean) { + def join(elems: String*): String = ( + if (elems.isEmpty) "" + else elems.mkString(ldelim, mdelim, rdelim) + ) + } + val NoGrouping = Grouping("", "", "", labels = false) + val ListGrouping = Grouping("(", ", ", ")", labels = false) + val ProductGrouping = Grouping("(", ", ", ")", labels = true) + val BlockGrouping = Grouping(" { ", "; ", "}", labels = false) + + private def str(level: Int)(body: => String): String = " " * level + body + private def block(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { + val l1 = str(level)(name + grouping.ldelim) + val l2 = nodes.map(_ show level + 1) + val l3 = str(level)(grouping.rdelim) + + l1 +: l2 :+ l3 mkString "\n" + } + private def maybeBlock(level: Int, grouping: Grouping)(name: String, nodes: List[TypeNode]): String = { + val threshold = 70 + + val try1 = str(level)(name + grouping.join(nodes map (_.show(0, grouping.labels)): _*)) + if (try1.length < threshold) try1 + else block(level, grouping)(name, nodes) + } + private def shortClass(x: Any) = { + if (settings.debug) { + val name = (x.getClass.getName split '.').last + val str = if (TypeStrings.isAnonClass(x.getClass)) name else (name split '$').last + + " // " + str + } + else "" + } + + sealed abstract class TypeNode { + def grouping: Grouping + def nodes: List[TypeNode] + + def show(indent: Int, showLabel: Boolean): String = maybeBlock(indent, grouping)(mkPrefix(showLabel), nodes) + def show(indent: Int): String = show(indent, showLabel = true) + def show(): String = show(0) + + def withLabel(l: String): this.type = modifyNameInfo(_.copy(label = l)) + def withType(t: String): this.type = modifyNameInfo(_.copy(typeName = t)) + + def label = nameInfo.label + def typeName = nameInfo.typeName + + protected def mkPrefix(showLabel: Boolean) = { + val pre = if (showLabel && label != "") label + " = " else "" + pre + typeName + } + override def toString = show() // + "(toString)" + private var nameInfo: LabelAndType = LabelAndType.empty + private def modifyNameInfo(f: LabelAndType => LabelAndType): this.type = { + nameInfo = f(nameInfo) + this + } + } + case class TypeAtom[T](atom: T) extends TypeNode { + def grouping = NoGrouping + def nodes = Nil + override protected def mkPrefix(showLabel: Boolean) = + super.mkPrefix(showLabel) + atom + shortClass(atom) + } + case class TypeProduct(nodes: List[TypeNode]) extends TypeNode { + def grouping: Grouping = ProductGrouping + def emptyTypeName = "" + override def typeName = if (nodes.isEmpty) emptyTypeName else super.typeName + } + + /** For a NullaryMethod, in = TypeEmpty; for MethodType(Nil, _) in = TypeNil */ + class NullaryFunction(out: TypeNode) extends TypeProduct(List(out)) { + override def typeName = "NullaryMethodType" + } + class MonoFunction(in: TypeNode, out: TypeNode) extends TypeProduct(List(in, out)) { + override def typeName = "MethodType" + } + class PolyFunction(in: TypeNode, out: TypeNode) extends TypeProduct(List(in, out)) { + override def typeName = "PolyType" + } + + class TypeList(nodes: List[TypeNode]) extends TypeProduct(nodes) { + override def grouping = ListGrouping + override def emptyTypeName = "Nil" + override def typeName = "List" + } + class TypeScope(nodes: List[TypeNode]) extends TypeProduct(nodes) { + override def grouping = BlockGrouping + override def typeName = "Scope" + override def emptyTypeName = "EmptyScope" + } + + object TypeEmpty extends TypeNode { + override def grouping = NoGrouping + override def nodes = Nil + override def label = "" + override def typeName = "" + override def show(indent: Int, showLabel: Boolean) = "" + } + + object intoNodes extends DestructureType[TypeNode] { + def withLabel(node: TypeNode, label: String): TypeNode = node withLabel label + def withType(node: TypeNode, typeName: String): TypeNode = node withType typeName + + def wrapEmpty = TypeEmpty + def wrapSequence(nodes: List[TypeNode]) = new TypeList(nodes) + def wrapProduct(nodes: List[TypeNode]) = new TypeProduct(nodes) + def wrapPoly(in: TypeNode, out: TypeNode) = new PolyFunction(in, out) + def wrapMono(in: TypeNode, out: TypeNode) = if (in == wrapEmpty) new NullaryFunction(out) else new MonoFunction(in, out) + def wrapAtom[U](value: U) = new TypeAtom(value) + } + + def show(tp: Type): String = intoNodes(tp).show() +} + + +/** Logic for turning a type into a String. The goal is to be + * able to take some arbitrary object 'x' and obtain the most precise + * String for which an injection of x.asInstanceOf[String] will + * be valid from both the JVM's and scala's perspectives. + * + * "definition" is when you want strings like + */ +trait TypeStrings { + private type JClass = java.lang.Class[_] + private val ObjectClass = classOf[java.lang.Object] + private val primitives = Set[String]("byte", "char", "short", "int", "long", "float", "double", "boolean", "void") + private val primitiveMap = (primitives.toList map { x => + val key = x match { + case "int" => "Integer" + case "char" => "Character" + case s => s.capitalize + } + val value = x match { + case "void" => "Unit" + case s => s.capitalize + } + + ("java.lang." + key) -> ("scala." + value) + }).toMap + + def isAnonClass(cl: Class[_]) = { + val xs = cl.getName.reverse takeWhile (_ != '$') + xs.nonEmpty && xs.forall(_.isDigit) + } + + def scalaName(s: String): String = { + if (s endsWith MODULE_SUFFIX_STRING) s.init + ".type" + else if (s == "void") "scala.Unit" + else if (primitives(s)) "scala." + s.capitalize + else primitiveMap.getOrElse(s, NameTransformer.decode(s)) + } + // Trying to put humpty dumpty back together again. + def scalaName(clazz: JClass): String = { + val name = clazz.getName + val enclClass = clazz.getEnclosingClass + def enclPre = enclClass.getName + MODULE_SUFFIX_STRING + def enclMatch = name startsWith enclPre + + scalaName( + if (enclClass == null || isAnonClass(clazz) || !enclMatch) name + else enclClass.getName + "." + (name stripPrefix enclPre) + ) + } + def anyClass(x: Any): JClass = if (x == null) null else x.getClass + + private def brackets(tps: String*): String = + if (tps.isEmpty) "" + else tps.mkString("[", ", ", "]") + + private def tvarString(tvar: TypeVariable[_]): String = tvarString(tvar.getBounds.toList) + private def tvarString(bounds: List[AnyRef]): String = { + val xs = bounds filterNot (_ == ObjectClass) collect { case x: JClass => x } + if (xs.isEmpty) "_" + else scalaName(xs.head) + } + private def tparamString(clazz: JClass): String = { + brackets(clazz.getTypeParameters map tvarString: _*) + } + + private def tparamString[T: ru.TypeTag] : String = { + import ru._ // get TypeRefTag in scope so that pattern match works (TypeRef is an abstract type) + def typeArguments: List[ru.Type] = ru.typeOf[T] match { case ru.TypeRef(_, _, args) => args; case _ => Nil } + brackets(typeArguments map (jc => tvarString(List(jc))): _*) + } + + /** Going for an overabundance of caution right now. Later these types + * can be a lot more precise, but right now the tags have a habit of + * introducing material which is not syntactically valid as scala source. + * When this happens it breaks the repl. It would be nice if we mandated + * that tag toString methods (or some other method, since it's bad + * practice to rely on toString for correctness) generated the VALID string + * representation of the type. + */ + def fromValue(value: Any): String = if (value == null) "Null" else fromClazz(anyClass(value)) + def fromClazz(clazz: JClass): String = scalaName(clazz) + tparamString(clazz) + def fromTag[T: ru.TypeTag : ClassTag] : String = scalaName(classTag[T].runtimeClass) + tparamString[T] + + /** Reducing fully qualified noise for some common packages. + */ + def quieter(tpe: String, alsoStrip: String*): String = { + val transforms = List( + "scala.collection.immutable." -> "immutable.", + "scala.collection.mutable." -> "mutable.", + "scala.collection.generic." -> "generic.", + "java.lang." -> "jl.", + "scala.runtime." -> "runtime." + ) ++ (alsoStrip map (_ -> "")) + + transforms.foldLeft(tpe) { + case (res, (k, v)) => res.replaceAll(k, v) + } + } +} + +object TypeStrings extends TypeStrings { } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index bb3ebb4e0f..59085cfa06 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,9 +13,10 @@ package scala.tools.nsc package typechecker import scala.collection.mutable -import scala.reflect.internal.util.{ BatchSourceFile, Statistics } +import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance } import mutable.ListBuffer import symtab.Flags._ +import Mode._ // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -24,14 +25,14 @@ import symtab.Flags._ * @author Martin Odersky * @version 1.0 */ -trait Typers extends Modes with Adaptations with Tags { +trait Typers extends Adaptations with Tags { self: Analyzer => import global._ import definitions._ import TypersStats._ - final def forArgMode(fun: Tree, mode: Int) = + final def forArgMode(fun: Tree, mode: Mode) = if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode else mode @@ -52,34 +53,41 @@ trait Typers extends Modes with Adaptations with Tags { object UnTyper extends Traverser { override def traverse(tree: Tree) = { - if (tree != EmptyTree) tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol + if (tree.canHaveAttrs) { + tree.clearType() + if (tree.hasSymbolField) tree.symbol = NoSymbol + } super.traverse(tree) } } -/* 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) => - TypeRef(ThisType(clazz), sym, List()) - case _ => - mapOver(tp) + + sealed abstract class SilentResult[+T] { + @inline final def map[U](f: T => U): SilentResult[U] = this match { + case SilentResultValue(value) => SilentResultValue(f(value)) + case x: SilentTypeError => x + } + @inline final def filter(p: T => Boolean): SilentResult[T] = this match { + case SilentResultValue(value) if !p(value) => SilentTypeError(TypeErrorWrapper(new TypeError(NoPosition, "!p"))) + case _ => this + } + @inline final def orElse[T1 >: T](f: AbsTypeError => T1): T1 = this match { + case SilentResultValue(value) => value + case SilentTypeError(err) => f(err) } } -*/ - - 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) // A transient flag to mark members of anonymous classes // that are turned private by typedBlock private final val SYNTHETIC_PRIVATE = TRANS_FLAG - private def isPastTyper = phase.id > currentRun.typerPhase.id + private final val InterpolatorCodeRegex = """\$\{.*?\}""".r + private final val InterpolatorIdentRegex = """\$\w+""".r // To enable decent error messages when the typer crashes. // TODO - this only catches trees which go through def typed, @@ -90,19 +98,27 @@ trait Typers extends Modes with Adaptations with Tags { // when true: // - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope) // - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction - // this is disabled by: -Xoldpatmat or interactive compilation (we run it for scaladoc due to SI-5933) - private def newPatternMatching = opt.virtPatmat && !forInteractive //&& !forScaladoc && (phase.id < currentRun.uncurryPhase.id) + // this is disabled by: interactive compilation (we run it for scaladoc due to SI-5933) + protected def newPatternMatching = true // presently overridden in the presentation compiler abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine } import TyperErrorGen._ + /** Overridden to false in scaladoc and/or interactive. */ + def canAdaptConstantTypeToLiteral = true + def canTranslateEmptyListToNil = true + def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree + + def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree = + typed(docDef.definition, mode, pt) + val infer = new Inferencer(context0) { override def isCoercible(tp: Type, pt: Type): Boolean = undoLog undo { // #3281 tp.isError || pt.isError || context0.implicitsEnabled && // this condition prevents chains of views - inferView(EmptyTree, tp, pt, false) != EmptyTree + inferView(EmptyTree, tp, pt, reportAmbiguous = false) != EmptyTree } } @@ -115,10 +131,7 @@ trait Typers extends Modes with Adaptations with Tags { // paramFailed cannot be initialized with params.exists(_.tpe.isError) because that would // hide some valid errors for params preceding the erroneous one. var paramFailed = false - - def mkPositionalArg(argTree: Tree, paramName: Name) = argTree - def mkNamedArg(argTree: Tree, paramName: Name) = atPos(argTree.pos)(new AssignOrNamedArg(Ident(paramName), (argTree))) - var mkArg: (Tree, Name) => Tree = mkPositionalArg + var mkArg: (Name, Tree) => Tree = (_, tree) => tree // DEPMETTODO: instantiate type vars that depend on earlier implicit args (see adapt (4.1)) // @@ -129,21 +142,21 @@ trait Typers extends Modes with Adaptations with Tags { for(ar <- argResultsBuff) paramTp = paramTp.subst(ar.subst.from, ar.subst.to) - val res = if (paramFailed || (paramTp.isError && {paramFailed = true; true})) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, false, context) + val res = if (paramFailed || (paramTp.isError && {paramFailed = true; true})) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, isView = false, context) argResultsBuff += res if (res.isSuccess) { - argBuff += mkArg(res.tree, param.name) + argBuff += mkArg(param.name, res.tree) } else { - mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args + mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args if (!param.hasDefault && !paramFailed) { - context.errBuffer.find(_.kind == ErrorKinds.Divergent) match { + context.reportBuffer.errors.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) + context.reportBuffer.clearErrors(ErrorKinds.Divergent) } case None => NoImplicitFoundError(fun, param) @@ -172,9 +185,9 @@ trait Typers extends Modes with Adaptations with Tags { } def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = - inferView(tree, from, to, reportAmbiguous, true) + inferView(tree, from, to, reportAmbiguous, saveErrors = true) - /** Infer an implicit conversion (``view'') between two types. + /** Infer an implicit conversion (`view`) between two types. * @param tree The tree which needs to be converted. * @param from The source type of the conversion * @param to The target type of the conversion @@ -194,7 +207,7 @@ trait Typers extends Modes with Adaptations with Tags { case PolyType(_, _) => EmptyTree case _ => def wrapImplicit(from: Type): Tree = { - val result = inferImplicit(tree, functionType(from :: Nil, to), reportAmbiguous, true, context, saveErrors) + val result = inferImplicit(tree, functionType(from.withoutAnnotations :: Nil, to), reportAmbiguous, isView = true, context, saveAmbiguousDivergent = saveErrors) if (result.subst != EmptyTreeTypeSubstituter) { result.subst traverse tree notifyUndetparamsInferred(result.subst.from, result.subst.to) @@ -232,10 +245,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ => tp } - /** Check that <code>tree</code> is a stable expression. - * - * @param tree ... - * @return ... + /** Check that `tree` is a stable expression. */ def checkStable(tree: Tree): Tree = ( if (treeInfo.isExprSafeToInline(tree)) tree @@ -247,7 +257,7 @@ trait Typers extends Modes with Adaptations with Tags { * of its symbol was not volatile? */ protected def isStableExceptVolatile(tree: Tree) = { - tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile && + tree.hasSymbolField && tree.symbol != NoSymbol && tree.tpe.isVolatile && { val savedTpe = tree.symbol.info val savedSTABLE = tree.symbol getFlag STABLE tree.symbol setInfo AnyRefClass.tpe @@ -289,16 +299,11 @@ trait Typers extends Modes with Adaptations with Tags { ) } - /** Check that type <code>tp</code> is not a subtype of itself. - * - * @param pos ... - * @param tp ... - * @return <code>true</code> if <code>tp</code> is not a subtype of itself. + /** Check that type `tp` is not a subtype of itself. */ def checkNonCyclic(pos: Position, tp: Type): Boolean = { def checkNotLocked(sym: Symbol) = { - sym.initialize - sym.lockOK || { CyclicAliasingOrSubtypingError(pos, sym); false } + sym.initialize.lockOK || { CyclicAliasingOrSubtypingError(pos, sym); false } } tp match { case TypeRef(pre, sym, args) => @@ -309,12 +314,6 @@ trait Typers extends Modes with Adaptations with Tags { case SingleType(pre, sym) => checkNotLocked(sym) -/* - case TypeBounds(lo, hi) => - var ok = true - for (t <- lo) ok = ok & checkNonCyclic(pos, t) - ok -*/ case st: SubType => checkNonCyclic(pos, st.supertype) case ct: CompoundType => @@ -325,19 +324,19 @@ trait Typers extends Modes with Adaptations with Tags { } def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = try { - if (!lockedSym.lock(CyclicReferenceError(pos, lockedSym))) false + if (!lockedSym.lock(CyclicReferenceError(pos, tp, lockedSym))) false else checkNonCyclic(pos, tp) } finally { lockedSym.unlock() } def checkNonCyclic(sym: Symbol) { - if (!checkNonCyclic(sym.pos, sym.tpe)) sym.setInfo(ErrorType) + if (!checkNonCyclic(sym.pos, sym.tpe_*)) sym.setInfo(ErrorType) } def checkNonCyclic(defn: Tree, tpt: Tree) { if (!checkNonCyclic(defn.pos, tpt.tpe, defn.symbol)) { - tpt.tpe = ErrorType + tpt setType ErrorType defn.symbol.setInfo(ErrorType) } } @@ -368,28 +367,13 @@ trait Typers extends Modes with Adaptations with Tags { private var scope: Scope = _ private var hiddenSymbols: List[Symbol] = _ - /** Check that type <code>tree</code> does not refer to private + /** Check that type `tree` does not refer to private * components unless itself is wrapped in something private - * (<code>owner</code> tells where the type occurs). - * - * @param owner ... - * @param tree ... - * @return ... + * (`owner` tells where the type occurs). */ def privates[T <: Tree](owner: Symbol, tree: T): T = check(owner, EmptyScope, WildcardType, tree) - /** Check that type <code>tree</code> does not refer to entities - * defined in scope <code>scope</code>. - * - * @param scope ... - * @param pt ... - * @param tree ... - * @return ... - */ - def locals[T <: Tree](scope: Scope, pt: Type, tree: T): T = - check(NoSymbol, scope, pt, tree) - private def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = { this.owner = owner this.scope = scope @@ -465,7 +449,7 @@ trait Typers extends Modes with Adaptations with Tags { } /** The qualifying class - * of a this or super with prefix <code>qual</code>. + * of a this or super with prefix `qual`. * packageOk is equal false when qualifying class symbol */ def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean) = @@ -506,11 +490,6 @@ trait Typers extends Modes with Adaptations with Tags { } @inline - final def typerReportAnyContextErrors[T](c: Context)(f: Typer => T): T = { - f(newTyper(c)) - } - - @inline final def withSavedContext[T](c: Context)(f: => T) = { val savedErrors = c.flushAndReturnBuffer() val res = f @@ -548,13 +527,13 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** Does the context of tree <code>tree</code> require a stable type? + /** Does the context of tree `tree` require a stable type? */ - private def isStableContext(tree: Tree, mode: Int, pt: Type) = - isNarrowable(tree.tpe) && ((mode & (EXPRmode | LHSmode)) == EXPRmode) && + private def isStableContext(tree: Tree, mode: Mode, pt: Type) = + isNarrowable(tree.tpe) && mode.inExprMode && mode.inNone(LHSmode) && (xtypes || (pt.isStable || - (mode & QUALmode) != 0 && !tree.symbol.isConstant || + mode.inAll(QUALmode) && !tree.symbol.isConstant || pt.typeSymbol.isAbstractType && pt.bounds.lo.isStable && !(tree.tpe <:< pt)) || pt.typeSymbol.isRefinementClass && !(tree.tpe <:< pt)) @@ -567,11 +546,13 @@ trait Typers extends Modes with Adaptations with Tags { * @return modified tree and new prefix type */ private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): (Tree, Type) = - if (isInPackageObject(sym, pre.typeSymbol)) { + if (context.isInPackageObject(sym, pre.typeSymbol)) { if (pre.typeSymbol == ScalaPackageClass && sym.isTerm) { // short cut some aliases. It seems pattern matching needs this // to notice exhaustiveness and to generate good code when // List extractors are mixed with :: patterns. See Test5 in lists.scala. + // + // TODO SI-6609 Eliminate this special case once the old pattern matcher is removed. def dealias(sym: Symbol) = (atPos(tree.pos.makeTransparent) {gen.mkAttributedRef(sym)} setPos tree.pos, sym.owner.thisType) sym.name match { @@ -600,41 +581,21 @@ trait Typers extends Modes with Adaptations with Tags { (checkAccessible(tree, sym, pre, site), pre) } - /** Is `sym` defined in package object of package `pkg`? - */ - private def isInPackageObject(sym: Symbol, pkg: Symbol) = { - def isInPkgObj(sym: Symbol) = - !sym.owner.isPackage && { - sym.owner.isPackageObjectClass && - sym.owner.owner == pkg || - pkg.isInitialized && { - // need to be careful here to not get a cyclic reference during bootstrap - val pkgobj = pkg.info.member(nme.PACKAGEkw) - pkgobj.isInitialized && - (pkgobj.info.member(sym.name).alternatives contains sym) - } - } - pkg.isPackageClass && { - if (sym.isOverloaded) sym.alternatives forall isInPkgObj - else isInPkgObj(sym) - } - } - /** Post-process an identifier or selection node, performing the following: * 1. Check that non-function pattern expressions are stable * 2. Check that packages and static modules are not used as values * 3. Turn tree type into stable type if possible and required by context. * 4. Give getClass calls a more precise type based on the type of the target of the call. */ - private def stabilize(tree: Tree, pre: Type, mode: Int, pt: Type): Tree = { - if (tree.symbol.isOverloaded && !inFunMode(mode)) + private def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = { + if (tree.symbol.isOverloaded && !mode.inFunMode) inferExprAlternative(tree, pt) val sym = tree.symbol def fail() = NotAValueError(tree, sym) if (tree.isErrorTyped) tree - else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1) + else if (mode.inPatternNotFunMode && tree.isTerm) { // (1) if (sym.isValue) { val tree1 = checkStable(tree) // A module reference in a pattern has type Foo.type, not "object Foo" @@ -668,13 +629,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ => !phase.erasedTypes } - /** - * @param tree ... - * @param mode ... - * @param pt ... - * @return ... - */ - def stabilizeFun(tree: Tree, mode: Int, pt: Type): Tree = { + def stabilizeFun(tree: Tree, mode: Mode, pt: Type): Tree = { val sym = tree.symbol val pre = tree match { case Select(qual, _) => qual.tpe @@ -724,17 +679,14 @@ trait Typers extends Modes with Adaptations with Tags { context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - if (context1.hasErrors) { - stopStats() - SilentTypeError(context1.errBuffer.head) - } else { - // If we have a successful result, emit any warnings it created. - if (context1.hasWarnings) { - context1.flushAndReturnWarningsBuffer() foreach { - case (pos, msg) => unit.warning(pos, msg) - } - } - SilentResultValue(result) + context1.firstError match { + case Some(err) => + stopStats() + SilentTypeError(err) + case None => + // If we have a successful result, emit any warnings it created. + context1.flushAndIssueWarnings() + SilentResultValue(result) } } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") @@ -771,7 +723,7 @@ trait Typers extends Modes with Adaptations with Tags { featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name def action(): Boolean = { - def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, true, false, context).isSuccess + def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, reportAmbiguous = true, isView = false, context).isSuccess def hasOption = settings.language.value exists (s => s == featureName || s == "_") val OK = hasImport || hasOption if (!OK) { @@ -845,7 +797,7 @@ trait Typers extends Modes with Adaptations with Tags { * (14) When in mode EXPRmode, apply a view * If all this fails, error */ - protected def adapt(tree: Tree, mode: Int, pt: Type, original: Tree = EmptyTree): Tree = { + protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = { def adaptToImplicitMethod(mt: MethodType): Tree = { if (context.undetparams.nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0` @@ -860,29 +812,28 @@ trait Typers extends Modes with Adaptations with Tags { // avoid throwing spurious DivergentImplicit errors if (context.hasErrors) - return setError(tree) - - withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree)){ typer1 => - if (original != EmptyTree && pt != WildcardType) - typer1.silent(tpr => { + setError(tree) + else + withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 => + if (original != EmptyTree && pt != WildcardType) ( + typer1 silent { tpr => val withImplicitArgs = tpr.applyImplicitArgs(tree) if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway else tpr.typed(withImplicitArgs, mode, pt) - }) match { - case SilentResultValue(result) => - result - case _ => + } + orElse { _ => debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) val tree1 = typed(resetAllAttrs(original), mode, WildcardType) // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin. - tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, pt) + tree1 setType pluginsTyped(tree1.tpe, this, tree1, mode, pt) if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) } + ) else typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + ) } - } def instantiateToMethodType(mt: MethodType): Tree = { val meth = tree match { @@ -890,11 +841,10 @@ trait Typers extends Modes with Adaptations with Tags { case Block(_, tree1) => tree1.symbol case _ => tree.symbol } - if (!meth.isConstructor && !meth.isTermMacro && isFunctionType(pt)) { // (4.2) + if (!meth.isConstructor && isFunctionType(pt)) { // (4.2) debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) checkParamsConvertible(tree, tree.tpe) val tree0 = etaExpand(context.unit, tree, this) - // println("eta "+tree+" ---> "+tree0+":"+tree0.tpe+" undet: "+context.undetparams+ " mode: "+Integer.toHexString(mode)) if (context.undetparams.nonEmpty) { // #2624: need to infer type arguments for eta expansion of a polymorphic method @@ -916,13 +866,13 @@ trait Typers extends Modes with Adaptations with Tags { } def adaptType(): Tree = { - if (inFunMode(mode)) { + if (mode.inFunMode) { // todo. the commented line below makes sense for typechecking, say, TypeApply(Ident(`some abstract type symbol`), List(...)) // because otherwise Ident will have its tpe set to a TypeRef, not to a PolyType, and `typedTypeApply` will fail // but this needs additional investigation, because it crashes t5228, gadts1 and maybe something else // tree setType tree.tpe.normalize tree - } else if (tree.hasSymbol && !tree.symbol.typeParams.isEmpty && !inHKMode(mode) && + } else if (tree.hasSymbolField && !tree.symbol.typeParams.isEmpty && !mode.inHKMode && !(tree.symbol.isJavaDefined && context.unit.isJava)) { // (7) // @M When not typing a higher-kinded type ((mode & HKmode) == 0) // or raw type (tree.symbol.isJavaDefined && context.unit.isJava), types must be of kind *, @@ -930,8 +880,8 @@ trait Typers extends Modes with Adaptations with Tags { // @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't? 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)) && + // @M: removed check for tree.hasSymbolField and replace tree.symbol by tree.tpe.symbol (TypeTree's must also be checked here, and they don't directly have a symbol) + mode.inHKMode && // @M: don't check tree.tpe.symbol.typeParams. check tree.tpe.typeParams!!! // (e.g., m[Int] --> tree.tpe.symbol.typeParams.length == 1, tree.tpe.typeParams.length == 0!) !sameLength(tree.tpe.typeParams, pt.typeParams) && @@ -950,7 +900,7 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** + /* * To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T, * reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant). * @@ -980,10 +930,63 @@ trait Typers extends Modes with Adaptations with Tags { def adaptConstrPattern(): Tree = { // (5) def hasUnapplyMember(tp: Type) = reallyExists(unapplyMember(tp)) val overloadedExtractorOfObject = tree.symbol filter (sym => hasUnapplyMember(sym.tpe)) - // if the tree's symbol's type does not define an extractor, maybe the tree's type does - // this is the case when we encounter an arbitrary tree as the target of an unapply call (rather than something that looks like a constructor call) - // (for now, this only happens due to wrapClassTagUnapply, but when we support parameterized extractors, it will become more common place) + // if the tree's symbol's type does not define an extractor, maybe the tree's type does. + // this is the case when we encounter an arbitrary tree as the target of an unapply call + // (rather than something that looks like a constructor call.) (for now, this only happens + // due to wrapClassTagUnapply, but when we support parameterized extractors, it will become + // more common place) val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe) + def convertToCaseConstructor(clazz: Symbol): TypeTree = { + // convert synthetic unapply of case class to case class constructor + val prefix = tree.tpe.prefix + val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) + .setOriginal(tree) + + val skolems = new mutable.ListBuffer[TypeSymbol] + object variantToSkolem extends TypeMap(trackVariance = true) { + def apply(tp: Type) = mapOver(tp) match { + // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 + case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + // must initialize or tpSym.tpe might see random type params!! + // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala + // TODO: why is that?? + tpSym.initialize + val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) + // origin must be the type param so we can deskolemize + val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) + skolems += skolem + skolem.tpe + case tp1 => tp1 + } + } + + // have to open up the existential and put the skolems in scope + // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) + val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? + val freeVars = skolems.toList + + // use "tree" for the context, not context.tree: don't make another CaseDef context, + // as instantiateTypeVar's bounds would end up there + val ctorContext = context.makeNewScope(tree, context.owner) + freeVars foreach ctorContext.scope.enter + newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) + + // simplify types without losing safety, + // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems + val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) + val extrapolated = tree1.tpe match { + case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node + ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type + copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) + case tp => tp + } + + // once the containing CaseDef has been type checked (see typedCase), + // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) + tree1 setType extrapolated + } + if (extractor != NoSymbol) { // if we did some ad-hoc overloading resolution, update the tree's symbol // do not update the symbol if the tree's symbol's type does not define an unapply member @@ -992,70 +995,28 @@ trait Typers extends Modes with Adaptations with Tags { tree setSymbol overloadedExtractorOfObject tree.tpe match { - case OverloadedType(pre, alts) => tree.tpe = overloadedType(pre, alts filter (alt => hasUnapplyMember(alt.tpe))) + case OverloadedType(pre, alts) => tree setType overloadedType(pre, alts filter (alt => hasUnapplyMember(alt.tpe))) case _ => } val unapply = unapplyMember(extractor.tpe) val clazz = unapplyParameterType(unapply) - if (unapply.isCase && clazz.isCase && !(clazz.ancestors exists (_.isCase))) { - // convert synthetic unapply of case class to case class constructor - val prefix = tree.tpe.prefix - val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) - .setOriginal(tree) - - val skolems = new mutable.ListBuffer[TypeSymbol] - object variantToSkolem extends VariantTypeMap { - def apply(tp: Type) = mapOver(tp) match { - case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => - // must initialize or tpSym.tpe might see random type params!! - // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala - // TODO: why is that?? - tpSym.initialize - val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) - // origin must be the type param so we can deskolemize - val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) - // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) - skolems += skolem - skolem.tpe - case tp1 => tp1 - } - } - - // have to open up the existential and put the skolems in scope - // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) - val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? - val freeVars = skolems.toList - - // use "tree" for the context, not context.tree: don't make another CaseDef context, - // as instantiateTypeVar's bounds would end up there - val ctorContext = context.makeNewScope(tree, context.owner) - freeVars foreach ctorContext.scope.enter - newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) - - // simplify types without losing safety, - // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems - val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) - val extrapolated = tree1.tpe match { - case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node - ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type - copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) - case tp => tp - } - - // once the containing CaseDef has been type checked (see typedCase), - // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) - tree1 setType extrapolated + if (unapply.isCase && clazz.isCase) { + convertToCaseConstructor(clazz) } else { tree } } else { - CaseClassConstructorError(tree) + val clazz = tree.tpe.typeSymbol.linkedClassOfClass + if (clazz.isCase) + convertToCaseConstructor(clazz) + else + CaseClassConstructorError(tree) } } def insertApply(): Tree = { - assert(!inHKMode(mode), modeString(mode)) //@M + assert(!mode.inHKMode, mode) //@M val adapted = adaptToName(tree, nme.apply) def stabilize0(pre: Type): Tree = stabilize(adapted, pre, EXPRmode | QUALmode, WildcardType) // TODO reconcile the overlap between Typers#stablize and TreeGen.stabilize @@ -1083,26 +1044,26 @@ trait Typers extends Modes with Adaptations with Tags { tree.tpe match { case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1) adaptAnnotations(tree, this, mode, pt) - case ct @ ConstantType(value) if inNoModes(mode, TYPEmode | FUNmode) && (ct <:< pt) && !forScaladoc && !forInteractive => // (0) + case ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0) val sym = tree.symbol if (sym != null && sym.isDeprecated) { val msg = sym.toString + sym.locationString + " is deprecated: " + sym.deprecationMessage.getOrElse("") unit.deprecationWarning(tree.pos, msg) } treeCopy.Literal(tree, value) - case OverloadedType(pre, alts) if !inFunMode(mode) => // (1) + case OverloadedType(pre, alts) if !mode.inFunMode => // (1) inferExprAlternative(tree, pt) adapt(tree, mode, pt, original) case NullaryMethodType(restpe) => // (2) adapt(tree setType restpe, mode, pt, original) - case TypeRef(_, ByNameParamClass, List(arg)) if ((mode & EXPRmode) != 0) => // (2) + case TypeRef(_, ByNameParamClass, List(arg)) if mode.inExprMode => // (2) adapt(tree setType arg, mode, pt, original) case tr @ TypeRef(_, sym, _) if sym.isAliasType && tr.dealias.isInstanceOf[ExistentialType] && ((mode & (EXPRmode | LHSmode)) == EXPRmode) => adapt(tree setType tr.dealias.skolemizeExistential(context.owner, tree), mode, pt, original) case et @ ExistentialType(_, _) if ((mode & (EXPRmode | LHSmode)) == EXPRmode) => adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt, original) - case PolyType(tparams, restpe) if inNoModes(mode, TAPPmode | PATTERNmode | HKmode) => // (3) + case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode | HKmode) => // (3) // assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function, // we're in HKmode since a higher-kinded type is expected --> hence, don't implicitly apply it to type params! // ticket #2197 triggered turning the assert into a guard @@ -1121,18 +1082,19 @@ trait Typers extends Modes with Adaptations with Tags { adaptToImplicitMethod(mt) case mt: MethodType if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) && - (context.undetparams.isEmpty || inPolyMode(mode))) && !(tree.symbol != null && tree.symbol.isTermMacro) => + (context.undetparams.isEmpty || mode.inPolyMode)) && !treeInfo.isMacroApplicationOrBlock(tree) => instantiateToMethodType(mt) case _ => - def shouldInsertApply(tree: Tree) = inAllModes(mode, EXPRmode | FUNmode) && (tree.tpe match { + def vanillaAdapt(tree: Tree) = { + def shouldInsertApply(tree: Tree) = mode.inAll(EXPRmode | FUNmode) && (tree.tpe match { case _: MethodType | _: OverloadedType | _: PolyType => false case _ => applyPossible }) def applyPossible = { def applyMeth = member(adaptToName(tree, nme.apply), nme.apply) dyna.acceptsApplyDynamic(tree.tpe) || ( - if ((mode & TAPPmode) != 0) + if (mode.inAll(TAPPmode)) tree.tpe.typeParams.isEmpty && applyMeth.filter(!_.tpe.typeParams.isEmpty) != NoSymbol else applyMeth.filter(_.tpe.paramSectionCount > 0) != NoSymbol @@ -1140,18 +1102,13 @@ trait Typers extends Modes with Adaptations with Tags { } if (tree.isType) adaptType() - else if ( - inExprModeButNot(mode, FUNmode) && !tree.isDef && // typechecking application - tree.symbol != null && tree.symbol.isTermMacro && // of a macro - !tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) - macroExpand(this, tree, mode, pt) - else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) + else if (mode.inAll(PATTERNmode | FUNmode)) adaptConstrPattern() else if (shouldInsertApply(tree)) insertApply() - else if (!context.undetparams.isEmpty && !inPolyMode(mode)) { // (9) - assert(!inHKMode(mode), modeString(mode)) //@M - if (inExprModeButNot(mode, FUNmode) && pt.typeSymbol == UnitClass) + else if (!context.undetparams.isEmpty && !mode.inPolyMode) { // (9) + assert(!mode.inHKMode, mode) //@M + if (mode.inExprModeButNot(FUNmode) && pt.typeSymbol == UnitClass) instantiateExpectingUnit(tree, mode) else instantiate(tree, mode, pt) @@ -1159,7 +1116,7 @@ trait Typers extends Modes with Adaptations with Tags { tree } else { def fallBack: Tree = { - if (inPatternMode(mode)) { + if (mode.inPatternMode) { if ((tree.symbol ne null) && tree.symbol.isModule) inferModulePattern(tree, pt) if (isPopulated(tree.tpe, approximateAbstracts(pt))) @@ -1168,19 +1125,22 @@ trait Typers extends Modes with Adaptations with Tags { val tree1 = constfold(tree, pt) // (10) (11) if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original) else { - if (inExprModeButNot(mode, FUNmode)) { + if (mode.inExprModeButNot(FUNmode)) { pt.dealias match { - case TypeRef(_, sym, _) => + // The <: Any requirement inhibits attempts to adapt continuation types + // to non-continuation types. + case TypeRef(_, sym, _) if tree.tpe <:< AnyClass.tpe => // note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially // infinite expansion if pt is constant type () - if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) { // (12) - if (settings.warnValueDiscard.value) + if (sym == UnitClass) { // (12) + if (settings.warnValueDiscard) context.unit.warning(tree.pos, "discarded non-Unit value") return typedPos(tree.pos, mode, pt) { - Block(List(tree), Literal(Constant())) + Block(List(tree), Literal(Constant(()))) } - } else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) { - if (settings.warnNumericWiden.value) + } + else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) { + if (settings.warnNumericWiden) context.unit.warning(tree.pos, "implicit numeric widening") return typedPos(tree.pos, mode, pt) { Select(tree, "to" + sym.name) @@ -1196,38 +1156,34 @@ trait Typers extends Modes with Adaptations with Tags { 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) - // convert forward views of delegate types into closures wrapped around - // the delegate's apply method (the "Invoke" method, which was translated into apply) - if (forMSIL && coercion != null && isCorrespondingDelegate(tree.tpe, pt)) { - val meth: Symbol = tree.tpe.member(nme.apply) - debuglog("replacing forward delegate view with: " + meth + ":" + meth.tpe) - return typed(Select(tree, meth), mode, pt) - } + val coercion = inferView(tree, tree.tpe, pt, reportAmbiguous = true) if (coercion != EmptyTree) { def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe - if (settings.logImplicitConv.value) + if (settings.logImplicitConv) unit.echo(tree.pos, msg) debuglog(msg) 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 + silentContext.firstError match { + case Some(err) => context.issue(err) + case None => return res + } } } } - if (settings.debug.value) { + if (settings.debug) { log("error tree = " + tree) - if (settings.explaintypes.value) explainTypes(tree.tpe, pt) + if (settings.explaintypes) explainTypes(tree.tpe, pt) } val found = tree.tpe if (!found.isErroneous && !pt.isErroneous) { if ((!context.reportErrors && isPastTyper) || tree.attachments.get[MacroExpansionAttachment].isDefined) { - val (bound, req) = pt match { - case ExistentialType(qs, tpe) => (qs, tpe) - case _ => (Nil, pt) + val bound = pt match { + case ExistentialType(qs, _) => qs + case _ => Nil } val boundOrSkolems = bound ++ pt.skolemsExceptMethodTypeParams if (boundOrSkolems.nonEmpty) { @@ -1281,9 +1237,12 @@ trait Typers extends Modes with Adaptations with Tags { fallBack } } + val tree1 = if (mode.inExprModeButNot(FUNmode) && treeInfo.isMacroApplication(tree)) macroExpandApply(this, tree, mode, pt) else tree + if (tree == tree1) vanillaAdapt(tree1) else tree1 + } } - def instantiate(tree: Tree, mode: Int, pt: Type): Tree = { + def instantiate(tree: Tree, mode: Mode, pt: Type): Tree = { inferExprInstance(tree, context.extractUndetparams(), pt) adapt(tree, mode, pt) } @@ -1291,13 +1250,11 @@ trait Typers extends Modes with Adaptations with Tags { * with expected type Unit, but if that fails, try again with pt = WildcardType * and discard the expression. */ - def instantiateExpectingUnit(tree: Tree, mode: Int): Tree = { + def instantiateExpectingUnit(tree: Tree, mode: Mode): Tree = { val savedUndetparams = context.undetparams - silent(_.instantiate(tree, mode, UnitClass.tpe)) match { - case SilentResultValue(t) => t - case _ => + silent(_.instantiate(tree, mode, UnitClass.tpe)) orElse { _ => context.undetparams = savedUndetparams - val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) + val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant(())))) typed(valueDiscard, mode, UnitClass.tpe) } } @@ -1332,7 +1289,7 @@ trait Typers extends Modes with Adaptations with Tags { inferView(qual, qual.tpe, searchTemplate, reportAmbiguous, saveErrors) match { case EmptyTree => qual case coercion => - if (settings.logImplicitConv.value) + if (settings.logImplicitConv) unit.echo(qual.pos, "applied implicit conversion from %s to %s = %s".format( qual.tpe, searchTemplate, coercion.symbol.defString)) @@ -1355,44 +1312,37 @@ trait Typers extends Modes with Adaptations with Tags { def doAdapt(restpe: Type) = //util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ") adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe), reportAmbiguous, saveErrors) - if (pt != WildcardType) { - silent(_ => doAdapt(pt)) match { - case SilentResultValue(result) if result != qual => - result - case _ => - debuglog("fallback on implicits in adaptToArguments: "+qual+" . "+name) - doAdapt(WildcardType) - } - } else + + if (pt == WildcardType) doAdapt(pt) + else silent(_ => doAdapt(pt)) filter (_ != qual) orElse (_ => + logResult(s"fallback on implicits in adaptToArguments: $qual.$name")(doAdapt(WildcardType)) + ) } /** 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, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { - def onError(reportError: => Tree): Tree = { - context.tree match { + def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def onError(reportError: => Tree): Tree = context.tree match { case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => - silent(_.typedArgs(args.map(_.duplicate), mode)) match { - case SilentResultValue(args) => - if (args exists (_.isErrorTyped)) - reportError - else - adaptToArguments(qual, name, args, WildcardType, reportAmbiguous, saveErrors) + ( silent (_.typedArgs(args.map(_.duplicate), mode)) + filter (xs => !(xs exists (_.isErrorTyped))) + map (xs => adaptToArguments(qual, name, xs, WildcardType, reportAmbiguous, saveErrors)) + orElse ( _ => reportError) + ) case _ => reportError } - case _ => - reportError + + silent(_.adaptToMember(qual, HasMember(name), reportAmbiguous = false)) orElse (err => + onError { + if (reportAmbiguous) context issue err + setError(tree) } + ) } - 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 * member `name` of arbitrary type. @@ -1402,13 +1352,6 @@ trait Typers extends Modes with Adaptations with Tags { if (member(qual, name) != NoSymbol) qual else adaptToMember(qual, HasMember(name)) - private def typePrimaryConstrBody(clazz : Symbol, cbody: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]]): Tree = { - // XXX: see about using the class's symbol.... - enclTparams foreach (sym => context.scope.enter(sym)) - namer.enterValueParams(vparamss) - typed(cbody) - } - private def validateNoCaseAncestor(clazz: Symbol) = { if (!phase.erasedTypes) { for (ancestor <- clazz.ancestors find (_.isCase)) { @@ -1510,126 +1453,246 @@ trait Typers extends Modes with Adaptations with Tags { unit.error(tparam.pos, "type parameter of value class may not be specialized") } - def parentTypes(templ: Template): List[Tree] = - if (templ.parents.isEmpty) List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) - else try { - val clazz = context.owner - // Normalize supertype and mixins so that supertype is always a class, not a trait. - var supertpt = typedTypeConstructor(templ.parents.head) - val firstParent = supertpt.tpe.typeSymbol - var mixins = templ.parents.tail map typedType - // 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.isErrorTyped) { - mixins = supertpt1 :: mixins - supertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + /** Typechecks a parent type reference. + * + * This typecheck is harder than it might look, because it should honor early + * definitions and also perform type argument inference with the help of super call + * arguments provided in `encodedtpt`. + * + * The method is called in batches (batch = 1 time per each parent type referenced), + * two batches per definition: once from namer, when entering a ClassDef or a ModuleDef + * and once from typer, when typechecking the definition. + * + * ***Arguments*** + * + * `encodedtpt` represents the parent type reference wrapped in an `Apply` node + * which indicates value arguments (i.e. type macro arguments or super constructor call arguments) + * If no value arguments are provided by the user, the `Apply` node is still + * there, but its `args` will be set to `Nil`. + * This argument is synthesized by `tools.nsc.ast.Parsers.templateParents`. + * + * `templ` is an enclosing template, which contains a primary constructor synthesized by the parser. + * Such a constructor is a DefDef which contains early initializers and maybe a super constructor call + * (I wrote "maybe" because trait constructors don't call super constructors). + * This argument is synthesized by `tools.nsc.ast.Trees.Template`. + * + * `inMixinPosition` indicates whether the reference is not the first in the + * list of parents (and therefore cannot be a class) or the opposite. + * + * ***Return value and side effects*** + * + * Returns a `TypeTree` representing a resolved parent type. + * If the typechecked parent reference implies non-nullary and non-empty argument list, + * this argument list is attached to the returned value in SuperArgsAttachment. + * The attachment is necessary for the subsequent typecheck to fixup a super constructor call + * in the body of the primary constructor (see `typedTemplate` for details). + * + * This method might invoke `typedPrimaryConstrBody`, hence it might cause the side effects + * described in the docs of that method. It might also attribute the Super(_, _) reference + * (if present) inside the primary constructor of `templ`. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * this method will be called six times: + * + * (3 times from the namer) + * typedParentType(Apply(Apply(Ident(B), List(Ident(x))), List(3)), templ, inMixinPosition = false) + * typedParentType(Apply(Ident(C), List(Ident(y))), templ, inMixinPosition = true) + * typedParentType(Apply(Ident(T), List()), templ, inMixinPosition = true) + * + * (3 times from the typer) + * <the same three calls> + */ + private def typedParentType(encodedtpt: Tree, templ: Template, inMixinPosition: Boolean): Tree = { + val app = treeInfo.dissectApplied(encodedtpt) + val (treeInfo.Applied(core, _, argss), decodedtpt) = ((app, app.callee)) + val argssAreTrivial = argss == Nil || argss == ListOfNil + + // we cannot avoid cyclic references with `initialize` here, because when type macros arrive, + // we'll have to check the probe for isTypeMacro anyways. + // therefore I think it's reasonable to trade a more specific "inherits itself" error + // for a generic, yet understandable "cyclic reference" error + var probe = typedTypeConstructor(core.duplicate).tpe.typeSymbol + if (probe == null) probe = NoSymbol + probe.initialize + + if (probe.isTrait || inMixinPosition) { + if (!argssAreTrivial) { + if (probe.isTrait) ConstrArgsInParentWhichIsTraitError(encodedtpt, probe) + else () // a class in a mixin position - this warrants an error in `validateParentClasses` + // therefore here we do nothing, e.g. don't check that the # of ctor arguments + // matches the # of ctor parameters or stuff like that + } + typedType(decodedtpt) + } else { + var supertpt = typedTypeConstructor(decodedtpt) + val supertparams = if (supertpt.hasSymbolField) supertpt.symbol.typeParams else Nil + if (supertparams.nonEmpty) { + typedPrimaryConstrBody(templ) { + val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK))) + val supercall = New(supertpe, mmap(argss)(_.duplicate)) + val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall + ctor setType supertpe // this is an essential hack, otherwise it will occasionally fail to typecheck + atPos(supertpt.pos.focus)(supercall) + } match { + case EmptyTree => MissingTypeArgumentsParentTpeError(supertpt) + case tpt => supertpt = TypeTree(tpt.tpe) setPos supertpt.pos // SI-7224: don't .focus positions of the TypeTree of a parent that exists in source } } - if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait) - supertpt.tpe = AnyRefClass.tpe - - // Determine - // - supertparams: Missing type parameters from supertype - // - supertpe: Given supertype, polymorphic in supertparams - val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else List() - var supertpe = supertpt.tpe - if (!supertparams.isEmpty) - supertpe = PolyType(supertparams, appliedType(supertpe, supertparams map (_.tpeHK))) - - // A method to replace a super reference by a New in a supercall - def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match { - case Apply(fn, args) => - treeCopy.Apply(scall, transformSuperCall(fn), args map (_.duplicate)) - case Select(Super(_, _), nme.CONSTRUCTOR) => - treeCopy.Select( - scall, - atPos(supertpt.pos.focus)(New(TypeTree(supertpe)) setType supertpe), - nme.CONSTRUCTOR) - } + // this is the place where we tell the typer what argss should be used for the super call + // if argss are nullary or empty, then (see the docs for `typedPrimaryConstrBody`) + // the super call dummy is already good enough, so we don't need to do anything + if (argssAreTrivial) supertpt else supertpt updateAttachment SuperArgsAttachment(argss) + } + } + /** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template. + * Before commencing the typecheck, replaces the `pendingSuperCall` dummy with the result of `actualSuperCall`. + * `actualSuperCall` can return `EmptyTree`, in which case the dummy is replaced with a literal unit. + * + * ***Return value and side effects*** + * + * If a super call is present in the primary constructor and is not erased by the transform, returns it typechecked. + * Otherwise (e.g. if the primary constructor is missing or the super call isn't there) returns `EmptyTree`. + * + * As a side effect, this method attributes the underlying fields of early vals. + * Early vals aren't typechecked anywhere else, so it's essential to call `typedPrimaryConstrBody` + * at least once per definition. It'd be great to disentangle this logic at some point. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * the primary constructor of `templ` will be: + * + * Block(List( + * ValDef(NoMods, x, TypeTree(), 2) + * ValDef(NoMods, y, TypeTree(), 4) + * global.pendingSuperCall, + * Literal(Constant(()))) + * + * Note the `pendingSuperCall` part. This is the representation of a fill-me-in-later supercall dummy, + * which encodes the fact that supercall argss are unknown during parsing and need to be transplanted + * from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`. + */ + private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree = treeInfo.firstConstructor(templ.body) match { - case constr @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => - // Convert constructor body to block in environment and typecheck it + case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => val (preSuperStats, superCall) = { val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) } - val cstats1 = if (superCall == EmptyTree) preSuperStats else preSuperStats :+ superCall - val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall match { - case Apply(_, _) if supertparams.nonEmpty => transformSuperCall(superCall) - case _ => cunit.duplicate - }) - val outercontext = context.outer - + val superCall1 = (superCall match { + case global.pendingSuperCall => actualSuperCall + case EmptyTree => EmptyTree + }) orElse cunit + val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1) + val clazz = context.owner assert(clazz != NoSymbol, templ) - val cscope = outercontext.makeNewScope(constr, outercontext.owner) - 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 treeInfo.Applied(_, _, argss) = superCall - val sarg = argss.flatten.headOption.getOrElse(EmptyTree) - if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) - ConstrArgsInTraitParentTpeError(sarg, firstParent) - if (!supertparams.isEmpty) - supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos - case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) + val cscope = context.outer.makeNewScope(ctor, context.outer.owner) + val cbody2 = { // called both during completion AND typing. + val typer1 = newTyper(cscope) + // XXX: see about using the class's symbol.... + clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym)) + typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate))) + typer1.typed(cbody1) } val preSuperVals = treeInfo.preSuperFields(templ.body) if (preSuperVals.isEmpty && preSuperStats.nonEmpty) - debugwarn("Wanted to zip empty presuper val list with " + preSuperStats) + devWarning("Wanted to zip empty presuper val list with " + preSuperStats) else - map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) + map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt setType ldef.symbol.tpe) + if (superCall1 == cunit) EmptyTree + else cbody2 match { + case Block(_, expr) => expr + case tree => tree + } case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) + EmptyTree } -/* experimental: early types as type arguments - val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef) - val earlyMap = new EarlyMap(clazz) - List.mapConserve(supertpt :: mixins){ tpt => - val tpt1 = checkNoEscaping.privates(clazz, tpt) - if (hasEarlyTypes) tpt1 else tpt1 setType earlyMap(tpt1.tpe) - } -*/ - //Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG + /** Makes sure that the first type tree in the list of parent types is always a class. + * If the first parent is a trait, prepend its supertype to the list until it's a class. +*/ + private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match { + case first :: rest if treeInfo.isTraitRef(first) => + def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = { + if (treeInfo.isTraitRef(supertpt)) { + val supertpt1 = typedType(supertpt) + if (!supertpt1.isErrorTyped) { + val supersupertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + return explode(supersupertpt, supertpt1 :: acc) + } + } + if (supertpt.tpe.typeSymbol == AnyClass) supertpt setType AnyRefClass.tpe + supertpt :: acc + } + explode(first, Nil) ++ rest + case _ => parents + } - // Certain parents are added in the parser before it is known whether - // that class also declared them as parents. For instance, this is an - // error unless we take corrective action here: - // - // case class Foo() extends Serializable - // - // So we strip the duplicates before typer. - def fixDuplicates(remaining: List[Tree]): List[Tree] = remaining match { + /** Certain parents are added in the parser before it is known whether + * that class also declared them as parents. For instance, this is an + * error unless we take corrective action here: + * + * case class Foo() extends Serializable + * + * So we strip the duplicates before typer. + */ + private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match { case Nil => Nil case x :: xs => val sym = x.symbol - x :: fixDuplicates( + x :: fixDuplicateSyntheticParents( if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) else xs ) } - fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) - } - catch { + def typedParentTypes(templ: Template): List[Tree] = templ.parents match { + case Nil => List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) + case first :: rest => + try { + val supertpts = fixDuplicateSyntheticParents(normalizeFirstParent( + typedParentType(first, templ, inMixinPosition = false) +: + (rest map (typedParentType(_, templ, inMixinPosition = true))))) + + // if that is required to infer the targs of a super call + // typedParentType calls typedPrimaryConstrBody to do the inferring typecheck + // as a side effect, that typecheck also assigns types to the fields underlying early vals + // however if inference is not required, the typecheck doesn't happen + // and therefore early fields have their type trees not assigned + // here we detect this situation and take preventive measures + if (treeInfo.hasUntypedPreSuperFields(templ.body)) + typedPrimaryConstrBody(templ)(EmptyTree) + + supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) + } catch { case ex: TypeError => // fallback in case of cyclic errors // @H none of the tests enter here but I couldn't rule it out + // upd. @E when a definition inherits itself, we end up here + // because `typedParentType` triggers `initialize` for parent types symbols log("Type error calculating parents in template " + templ) log("Error: " + ex) ParentTypesError(templ, ex) List(TypeTree(AnyRefClass.tpe)) } + } /** <p>Check that</p> * <ul> @@ -1669,14 +1732,16 @@ trait Typers extends Modes with Adaptations with Tags { if (psym.isFinal) pending += ParentFinalInheritanceError(parent, psym) - if (psym.hasDeprecatedInheritanceAnnotation) { + val sameSourceFile = context.unit.source.file == psym.sourceFile + + if (psym.hasDeprecatedInheritanceAnnotation && !sameSourceFile) { val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse "" val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix" unit.deprecationWarning(parent.pos, msg) } if (psym.isSealed && !phase.erasedTypes) - if (context.unit.source.file == psym.sourceFile) + if (sameSourceFile) psym addChild context.owner else pending += ParentSealedInheritanceError(parent, psym) @@ -1684,15 +1749,12 @@ trait Typers extends Modes with Adaptations with Tags { if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes && !context.owner.isSynthetic && // don't check synthetic concrete classes for virtuals (part of DEVIRTUALIZE) - !settings.noSelfCheck.value && // setting to suppress this very check + !settings.noSelfCheck && // setting to suppress this very check !selfType.isErroneous && !parent.tpe.isErroneous) { - //Console.println(context.owner);//DEBUG - //Console.println(context.owner.unsafeTypeParams);//DEBUG - //Console.println(List.fromArray(context.owner.info.closure));//DEBUG pending += ParentSelfTypeConformanceError(parent, selfType) - if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis) + if (settings.explaintypes) explainTypes(selfType, parent.tpe.typeOfThis) } if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError)) @@ -1706,13 +1768,6 @@ trait Typers extends Modes with Adaptations with Tags { for (p <- parents) validateParentClass(p, superclazz) } -/* - if (settings.Xshowcls.value != "" && - settings.Xshowcls.value == context.owner.fullName) - println("INFO "+context.owner+ - ", baseclasses = "+(context.owner.info.baseClasses map (_.fullName))+ - ", lin = "+(context.owner.info.baseClasses map (context.owner.thisType.baseType))) -*/ pending.foreach(ErrorUtils.issueTypeError) } @@ -1736,27 +1791,26 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** - * @param cdef ... - * @return ... - */ def typedClassDef(cdef: ClassDef): Tree = { -// attributes(cdef) val clazz = cdef.symbol val typedMods = typedModifiers(cdef.mods) assert(clazz != NoSymbol, cdef) reenterTypeParams(cdef.tparams) val tparams1 = cdef.tparams mapConserve (typedTypeDef) - val impl1 = newTyper(context.make(cdef.impl, clazz, newScope)).typedTemplate(cdef.impl, parentTypes(cdef.impl)) + val impl1 = newTyper(context.make(cdef.impl, clazz, newScope)).typedTemplate(cdef.impl, typedParentTypes(cdef.impl)) val impl2 = finishMethodSynthesis(impl1, clazz, context) if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.typeSymbol == AnyClass) checkEphemeral(clazz, impl2.body) - if ((clazz != ClassfileAnnotationClass) && - (clazz isNonBottomSubClass ClassfileAnnotationClass)) - restrictionWarning(cdef.pos, unit, - "subclassing Classfile does not\n"+ - "make your annotation visible at runtime. If that is what\n"+ - "you want, you must write the annotation class in Java.") + + if ((clazz isNonBottomSubClass ClassfileAnnotationClass) && (clazz != ClassfileAnnotationClass)) { + if (!clazz.owner.isPackageClass) + unit.error(clazz.pos, "inner classes cannot be classfile annotations") + else restrictionWarning(cdef.pos, unit, + """|subclassing Classfile does not + |make your annotation visible at runtime. If that is what + |you want, you must write the annotation class in Java.""".stripMargin) + } + if (!isPastTyper) { for (ann <- clazz.getAnnotation(DeprecatedAttr)) { val m = companionSymbolOf(clazz, context) @@ -1768,10 +1822,6 @@ trait Typers extends Modes with Adaptations with Tags { .setType(NoType) } - /** - * @param mdef ... - * @return ... - */ def typedModuleDef(mdef: ModuleDef): Tree = { // initialize all constructors of the linked class: the type completer (Namer.methodSig) // might add default getters to this object. example: "object T; class T(x: Int = 1)" @@ -1789,7 +1839,7 @@ trait Typers extends Modes with Adaptations with Tags { || clazz.isSerializable ) val impl1 = newTyper(context.make(mdef.impl, clazz, newScope)).typedTemplate(mdef.impl, { - parentTypes(mdef.impl) ++ ( + typedParentTypes(mdef.impl) ++ ( if (noSerializable) Nil else { clazz.makeSerializable() @@ -1800,6 +1850,9 @@ trait Typers extends Modes with Adaptations with Tags { val impl2 = finishMethodSynthesis(impl1, clazz, context) + if (mdef.symbol == PredefModule) + ensurePredefParentsAreInSameSourceFile(impl2) + // SI-5954. On second compile of a companion class contained in a package object we end up // with some confusion of names which leads to having two symbols with the same name in the // same owner. Until that can be straightened out we will warn on companion objects in package @@ -1819,9 +1872,7 @@ trait Typers extends Modes with Adaptations with Tags { def pkgObjectWarning(m : Symbol, mdef : ModuleDef, restricted : String) = { val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString - val pos = if (m.pos.isDefined) m.pos else mdef.pos - debugwarn(s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") - debugwarn(pos.lineContent + (if (pos.isDefined) " " * (pos.column - 1) + "^" else "")) + context.warning(if (m.pos.isDefined) m.pos else mdef.pos, s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") } } @@ -1830,6 +1881,12 @@ trait Typers extends Modes with Adaptations with Tags { treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType } + + private def ensurePredefParentsAreInSameSourceFile(template: Template) = { + val parentSyms = template.parents map (_.symbol) filterNot (_ == AnyRefClass) + if (parentSyms exists (_.associatedFile != PredefModule.associatedFile)) + unit.error(template.pos, s"All parents of Predef must be defined in ${PredefModule.associatedFile}.") + } /** In order to override this in the TreeCheckers Typer so synthetics aren't re-added * all the time, it is exposed here the module/class typing methods go through it. * ...but it turns out it's also the ideal spot for namer/typer coordination for @@ -1856,17 +1913,11 @@ trait Typers extends Modes with Adaptations with Tags { if (txt eq context) namer.enterSym(tree) else newNamer(txt).enterSym(tree) - /** - * @param templ ... - * @param parents1 ... - * <li> <!-- 2 --> - * Check that inner classes do not inherit from Annotation - * </li> - * @return ... + /** <!-- 2 --> Check that inner classes do not inherit from Annotation */ def typedTemplate(templ: Template, parents1: List[Tree]): Template = { val clazz = context.owner - clazz.annotations.map(_.completeInfo) + clazz.annotations.map(_.completeInfo()) if (templ.symbol == NoSymbol) templ setSymbol clazz.newLocalDummy(templ.pos) val self1 = templ.self match { @@ -1893,19 +1944,34 @@ trait Typers extends Modes with Adaptations with Tags { // the following is necessary for templates generated later assert(clazz.info.decls != EmptyScope, clazz) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body) + if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore validateParentClasses(parents1, selfType) if (clazz.isCase) validateNoCaseAncestor(clazz) + if (clazz.isTrait && hasSuperArgs(parents1.head)) + ConstrArgsInParentOfTraitError(parents1.head, clazz) - if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass) + if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.isTopLevel) unit.error(clazz.pos, "inner classes cannot be classfile annotations") if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) + val body = { val body = if (isPastTyper || reporter.hasErrors) templ.body else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) + val primaryCtor = treeInfo.firstConstructor(body) + val primaryCtor1 = primaryCtor match { + case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) => + val argss = superArgs(parents1.head) getOrElse Nil + val pos = wrappingPos(parents1.head.pos, argss.flatten) + val superCall = atPos(pos)(PrimarySuperCall(argss)) + deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos + case _ => primaryCtor + } + body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat } + } val body1 = typedStats(body, templ.symbol) @@ -1918,28 +1984,24 @@ trait Typers extends Modes with Adaptations with Tags { } } - treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe + treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe_* } /** Remove definition annotations from modifiers (they have been saved - * into the symbol's ``annotations'' in the type completer / namer) + * into the symbol's `annotations` in the type completer / namer) * * However reification does need annotation definitions to proceed. * Unfortunately, AnnotationInfo doesn't provide enough info to reify it in general case. * The biggest problem is with the "atp: Type" field, which cannot be reified in some situations * that involve locally defined annotations. See more about that in Reifiers.scala. * - * That's why the original tree gets saved into ``original'' field of AnnotationInfo (happens elsewhere). + * That's why the original tree gets saved into `original` field of AnnotationInfo (happens elsewhere). * The field doesn't get pickled/unpickled and exists only during a single compilation run. * This simultaneously allows us to reify annotations and to preserve backward compatibility. */ def typedModifiers(mods: Modifiers): Modifiers = mods.copy(annotations = Nil) setPositions mods.positions - /** - * @param vdef ... - * @return ... - */ def typedValDef(vdef: ValDef): ValDef = { val sym = vdef.symbol val valDefTyper = { @@ -1956,7 +2018,7 @@ trait Typers extends Modes with Adaptations with Tags { val sym = vdef.symbol.initialize val typedMods = typedModifiers(vdef.mods) - sym.annotations.map(_.completeInfo) + sym.annotations.map(_.completeInfo()) val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt)) checkNonCyclic(vdef, tpt1) @@ -1991,10 +2053,6 @@ trait Typers extends Modes with Adaptations with Tags { } /** Enter all aliases of local parameter accessors. - * - * @param clazz ... - * @param vparamss ... - * @param rhs ... */ def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) { debuglog(s"computing param aliases for $clazz:${clazz.primaryConstructor.tpe}:$rhs") @@ -2111,7 +2169,7 @@ trait Typers extends Modes with Adaptations with Tags { unit.error(pos, msg) false } - /** Have to examine all parameters in all lists. + /* Have to examine all parameters in all lists. */ def paramssTypes(tp: Type): List[List[Type]] = tp match { case mt @ MethodType(_, restpe) => mt.paramTypes :: paramssTypes(restpe) @@ -2130,10 +2188,10 @@ trait Typers extends Modes with Adaptations with Tags { val sym = paramType.typeSymbol def paramPos = nthParamPos(listIdx, paramIdx) - /** Not enough to look for abstract types; have to recursively check the bounds - * of each abstract type for more abstract types. Almost certainly there are other - * exploitable type soundness bugs which can be seen by bounding a type parameter - * by an abstract type which itself is bounded by an abstract type. + /* Not enough to look for abstract types; have to recursively check the bounds + * of each abstract type for more abstract types. Almost certainly there are other + * exploitable type soundness bugs which can be seen by bounding a type parameter + * by an abstract type which itself is bounded by an abstract type. */ def checkAbstract(tp0: Type, what: String): Boolean = { def check(sym: Symbol): Boolean = !sym.isAbstractType || { @@ -2157,51 +2215,6 @@ trait Typers extends Modes with Adaptations with Tags { failStruct(ddef.tpt.pos, "a user-defined value class", where = "Result type") } - def typedUseCase(useCase: UseCase) { - def stringParser(str: String): syntaxAnalyzer.Parser = { - val file = new BatchSourceFile(context.unit.source.file, str) { - override def positionInUltimateSource(pos: Position) = { - pos.withSource(context.unit.source, useCase.pos.start) - } - } - val unit = new CompilationUnit(file) - new syntaxAnalyzer.UnitParser(unit) - } - val trees = stringParser(useCase.body+";").nonLocalDefOrDcl - val enclClass = context.enclClass.owner - def defineAlias(name: Name) = - if (context.scope.lookup(name) == NoSymbol) { - lookupVariable(name.toString.substring(1), enclClass) match { - case Some(repl) => - silent(_.typedTypeConstructor(stringParser(repl).typ())) match { - case SilentResultValue(tpt) => - val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) - val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) - val newInfo = genPolyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) - alias setInfo newInfo - context.scope.enter(alias) - case _ => - } - case _ => - } - } - for (tree <- trees; t <- tree) - t match { - case Ident(name) if name startsWith '$' => defineAlias(name) - case _ => - } - useCase.aliases = context.scope.toList - namer.enterSyms(trees) - typedStats(trees, NoSymbol) - useCase.defined = context.scope.toList filterNot (useCase.aliases contains _) - if (settings.debug.value) - useCase.defined foreach (sym => println("defined use cases: %s:%s".format(sym, sym.tpe))) - } - - /** - * @param ddef ... - * @return ... - */ def typedDefDef(ddef: DefDef): DefDef = { val meth = ddef.symbol.initialize @@ -2220,13 +2233,13 @@ trait Typers extends Modes with Adaptations with Tags { val tparams1 = ddef.tparams mapConserve typedTypeDef val vparamss1 = ddef.vparamss mapConserve (_ mapConserve typedValDef) - meth.annotations.map(_.completeInfo) + meth.annotations.map(_.completeInfo()) for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1) if (isRepeatedParamType(vparam1.symbol.tpe)) StarParamNotLastError(vparam1) - var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) + val tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) checkNonCyclic(ddef, tpt1) ddef.tpt.setType(tpt1.tpe) val typedMods = typedModifiers(ddef.mods) @@ -2238,7 +2251,7 @@ trait Typers extends Modes with Adaptations with Tags { meth.owner.isAnonOrRefinementClass)) InvalidConstructorDefError(ddef) typed(ddef.rhs) - } else if (meth.isTermMacro) { + } else if (meth.isMacro) { // typechecking macro bodies is sort of unconventional // that's why we employ our custom typing scheme orchestrated outside of the typer transformedOr(ddef.rhs, typedMacroBody(this, ddef)) @@ -2294,10 +2307,10 @@ trait Typers extends Modes with Adaptations with Tags { reenterTypeParams(tdef.tparams) val tparams1 = tdef.tparams mapConserve typedTypeDef val typedMods = typedModifiers(tdef.mods) - tdef.symbol.annotations.map(_.completeInfo) + tdef.symbol.annotations.map(_.completeInfo()) // @specialized should not be pickled when compiling with -no-specialize - if (settings.nospecialization.value && currentRun.compiles(tdef.symbol)) { + if (settings.nospecialization && currentRun.compiles(tdef.symbol)) { tdef.symbol.removeAnnotation(definitions.SpecializedClass) tdef.symbol.deSkolemize.removeAnnotation(definitions.SpecializedClass) } @@ -2330,7 +2343,7 @@ trait Typers extends Modes with Adaptations with Tags { if (!nme.isLoopHeaderLabel(ldef.symbol.name) || isPastTyper) { val restpe = ldef.symbol.tpe.resultType val rhs1 = typed(ldef.rhs, restpe) - ldef.params foreach (param => param.tpe = param.symbol.tpe) + ldef.params foreach (param => param setType param.symbol.tpe) deriveLabelDef(ldef)(_ => rhs1) setType restpe } else { @@ -2338,26 +2351,20 @@ trait Typers extends Modes with Adaptations with Tags { val rhs1 = typed(ldef.rhs) val restpe = rhs1.tpe if (restpe == initpe) { // stable result, no need to check again - ldef.params foreach (param => param.tpe = param.symbol.tpe) + ldef.params foreach (param => param setType param.symbol.tpe) treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe } else { context.scope.unlink(ldef.symbol) val sym2 = namer.enterInScope( context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(List(), restpe)) val rhs2 = typed(resetAllAttrs(ldef.rhs), restpe) - ldef.params foreach (param => param.tpe = param.symbol.tpe) + ldef.params foreach (param => param setType param.symbol.tpe) deriveLabelDef(ldef)(_ => rhs2) setSymbol sym2 setType restpe } } } - /** - * @param block ... - * @param mode ... - * @param pt ... - * @return ... - */ - def typedBlock(block: Block, mode: Int, pt: Type): Block = { + def typedBlock(block: Block, mode: Mode, pt: Type): Block = { val syntheticPrivates = new ListBuffer[Symbol] try { namer.enterSyms(block.stats) @@ -2419,7 +2426,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ => stat::Nil }) val stats2 = typedStats(stats1, context.owner) - val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt) + val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode), pt) treeCopy.Block(block, stats2, expr1) .setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst) } finally { @@ -2429,12 +2436,6 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** - * @param cdef ... - * @param pattpe ... - * @param pt ... - * @return ... - */ 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) @@ -2451,83 +2452,63 @@ trait Typers extends Modes with Adaptations with Tags { if (pat1.tpe.paramSectionCount > 0) pat1 setType pat1.tpe.finalResultType - if (forInteractive) { - for (bind @ Bind(name, _) <- cdef.pat) - if (name.toTermName != nme.WILDCARD && bind.symbol != null && bind.symbol != NoSymbol) - namer.enterIfNotThere(bind.symbol) - } + for (bind @ Bind(name, _) <- cdef.pat) + if (name.toTermName != nme.WILDCARD && bind.symbol != null && bind.symbol != NoSymbol) + namer.enterIfNotThere(bind.symbol) val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree else typed(cdef.guard, BooleanClass.tpe) var body1: Tree = typed(cdef.body, pt) - val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) - if (contextWithTypeBounds.savedTypeBounds.nonEmpty) { - body1.tpe = contextWithTypeBounds restoreTypeBounds body1.tpe - + if (context.enclosingCaseDef.savedTypeBounds.nonEmpty) { + body1 modifyType context.enclosingCaseDef.restoreTypeBounds // insert a cast if something typechecked under the GADT constraints, // but not in real life (i.e., now that's we've reset the method's type skolems' // infos back to their pre-GADT-constraint state) if (isFullyDefined(pt) && !(body1.tpe <:< pt)) - body1 = typedPos(body1.pos)(gen.mkCast(body1, pt.normalize)) - + body1 = typedPos(body1.pos)(gen.mkCast(body1, pt.dealiasWiden)) } // body1 = checkNoEscaping.locals(context.scope, pt, body1) treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe } - // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher - // the flags are used to avoid accidentally deskolemizing unrelated skolems of skolems - object deskolemizeGADTSkolems extends TypeMap { - def apply(tp: Type): Type = mapOver(tp) match { - case TypeRef(pre, sym, args) if sym.isGADTSkolem => - typeRef(NoPrefix, sym.deSkolemize, args) - case tp1 => tp1 - } - } - def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) } - def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) + def adaptCase(cdef: CaseDef, mode: Mode, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) - def ptOrLub(tps: List[Type], pt: Type ) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst)) - def ptOrLubPacked(trees: List[Tree], pt: Type) = if (isFullyDefined(pt)) (pt, false) else weakLub(trees map (c => packedType(c, context.owner).deconst)) + def packedTypes(trees: List[Tree]): List[Type] = trees map (c => packedType(c, context.owner).deconst) // takes untyped sub-trees of a match and type checks them - def typedMatch(selector: Tree, cases: List[CaseDef], mode: Int, pt: Type, tree: Tree = EmptyTree): Match = { + def typedMatch(selector: Tree, cases: List[CaseDef], mode: Mode, pt: Type, tree: Tree = EmptyTree): Match = { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) val selectorTp = packCaptured(selector1.tpe.widen).skolemizeExistential(context.owner, selector) val casesTyped = typedCases(cases, selectorTp, pt) - val (resTp, needAdapt) = - if (opt.virtPatmat) ptOrLubPacked(casesTyped, pt) - else ptOrLub(casesTyped map (_.tpe), pt) - - val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp)) + def finish(cases: List[CaseDef], matchType: Type) = + treeCopy.Match(tree, selector1, cases) setType matchType - val matchTyped = treeCopy.Match(tree, selector1, casesAdapted) setType resTp - if (!newPatternMatching) // TODO: remove this in 2.11 -- only needed for old pattern matcher - new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(matchTyped) - matchTyped + if (isFullyDefined(pt)) + finish(casesTyped, pt) + else packedTypes(casesTyped) match { + case packed if sameWeakLubAsLub(packed) => finish(casesTyped, lub(packed)) + case packed => + val lub = weakLub(packed) + finish(casesTyped map (adaptCase(_, mode, lub)), lub) + } } - // match has been typed -- virtualize it if we're feeling experimental - // (virtualized matches are expanded during type checking so they have the full context available) - // otherwise, do nothing: matches are translated during phase `patmat` (unless -Xoldpatmat) - def virtualizedMatch(match_ : Match, mode: Int, pt: Type) = { - import patmat.{vpmName, PureMatchTranslator, OptimizingMatchTranslator} + // match has been typed -- virtualize it during type checking so the full context is available + def virtualizedMatch(match_ : Match, mode: Mode, pt: Type) = { + import patmat.{ vpmName, PureMatchTranslator } // TODO: add fallback __match sentinel to predef val matchStrategy: Tree = - if (!(newPatternMatching && opt.experimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen - else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { - case SilentResultValue(ms) => ms - case _ => null - } + if (!(newPatternMatching && settings.Xexperimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen + else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) orElse (_ => null) if (matchStrategy ne null) // virtualize typed((new PureMatchTranslator(this.asInstanceOf[patmat.global.analyzer.Typer] /*TODO*/, matchStrategy)).translateMatch(match_), mode, pt) @@ -2557,11 +2538,9 @@ trait Typers extends Modes with Adaptations with Tags { * an alternative TODO: add partial function AST node or equivalent and get rid of this synthesis --> do everything in uncurry (or later) * however, note that pattern matching codegen is designed to run *before* uncurry */ - def synthesizePartialFunction(paramName: TermName, paramPos: Position, tree: Tree, mode: Int, pt0: Type): Tree = { - assert(pt0.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt0.") - - val pt = deskolemizeGADTSkolems(pt0) - val targs = pt.normalize.typeArgs + def synthesizePartialFunction(paramName: TermName, paramPos: Position, tree: Tree, mode: Mode, pt: Type): Tree = { + assert(pt.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt.") + val targs = pt.dealiasWiden.typeArgs // if targs.head isn't fully defined, we can translate --> error targs match { @@ -2586,7 +2565,7 @@ trait Typers extends Modes with Adaptations with Tags { val Match(sel, cases) = tree // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up - val casesTrue = cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE_typed)).duplicate.asInstanceOf[CaseDef]) + val casesTrue = cases map (c => deriveCaseDef(c)(x => atPos(x.pos.focus)(TRUE)).duplicate.asInstanceOf[CaseDef]) // must generate a new tree every time def selector: Tree = gen.mkUnchecked( @@ -2705,7 +2684,7 @@ trait Typers extends Modes with Adaptations with Tags { methodBodyTyper.context.scope enter paramSym methodSym setInfo MethodType(List(paramSym), BooleanClass.tpe) - val defaultCase = mkDefaultCase(FALSE_typed) + val defaultCase = mkDefaultCase(FALSE) val match_ = methodBodyTyper.typedMatch(selector, casesTrue :+ defaultCase, mode, BooleanClass.tpe) DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, BooleanClass.tpe)) @@ -2753,7 +2732,7 @@ trait Typers extends Modes with Adaptations with Tags { members foreach (m => anonClass.info.decls enter m.symbol) val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)( + Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)( Apply(Select(New(Ident(anonClass.name).setSymbol(anonClass)), nme.CONSTRUCTOR), List()) )) } @@ -2765,24 +2744,17 @@ trait Typers extends Modes with Adaptations with Tags { } } - - /** - * @param fun ... - * @param mode ... - * @param pt ... - * @return ... - */ - private def typedFunction(fun: Function, mode: Int, pt: Type): Tree = { + private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = { val numVparams = fun.vparams.length if (numVparams > definitions.MaxFunctionArity) return MaxFunctionArityError(fun) def decompose(pt: Type): (Symbol, List[Type], Type) = if ((isFunctionType(pt) || (pt.typeSymbol == PartialFunctionClass && numVparams == 1 && fun.body.isInstanceOf[Match])) && // see bug901 for a reason why next conditions are needed - ( pt.normalize.typeArgs.length - 1 == numVparams + ( pt.dealiasWiden.typeArgs.length - 1 == numVparams || fun.vparams.exists(_.tpt.isEmpty) )) - (pt.typeSymbol, pt.normalize.typeArgs.init, pt.normalize.typeArgs.last) + (pt.typeSymbol, pt.dealiasWiden.typeArgs.init, pt.dealiasWiden.typeArgs.last) else (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) @@ -2797,15 +2769,13 @@ trait Typers extends Modes with Adaptations with Tags { else { fun match { case etaExpansion(vparams, fn, args) => - silent(_.typed(fn, forFunMode(mode), pt)) match { - case SilentResultValue(fn1) if context.undetparams.isEmpty => + silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 => // 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) val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) if (isFunctionType(ftpe) && isFullyDefined(ftpe)) return typedFunction(fun, mode, ftpe) - case _ => } case _ => } @@ -2834,16 +2804,13 @@ trait Typers extends Modes with Adaptations with Tags { if (context.retyping) context.scope enter vparam.symbol vparam.symbol } - val vparams = fun.vparams mapConserve (typedValDef) - // for (vparam <- vparams) { - // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () - // } + val vparams = fun.vparams mapConserve typedValDef val formals = vparamSyms map (_.tpe) val body1 = typed(fun.body, respt) val restpe = packedType(body1, fun.symbol).deconst.resultType - val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) - // body = checkNoEscaping.locals(context.scope, restpe, body) - treeCopy.Function(fun, vparams, body1).setType(funtpe) + val funtpe = appliedType(clazz, formals :+ restpe: _*) + + treeCopy.Function(fun, vparams, body1) setType funtpe } } } @@ -2861,31 +2828,15 @@ trait Typers extends Modes with Adaptations with Tags { val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil)) templ.removeAttachment[CompoundTypeTreeOriginalAttachment] templ updateAttachment att.copy(stats = stats1) - for (stat <- stats1 if stat.isDef) { - val member = stat.symbol - if (!(context.owner.ancestors forall - (bc => member.matchingSymbol(bc, context.owner.thisType) == NoSymbol))) { - member setFlag OVERRIDE + for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) + stat.symbol setFlag OVERRIDE } } - } - } def typedImport(imp : Import) : Import = (transformed remove imp) match { case Some(imp1: Import) => imp1 case _ => log("unhandled import: "+imp+" in "+unit); imp } - private def isWarnablePureExpression(tree: Tree) = tree match { - case EmptyTree | Literal(Constant(())) => false - case _ => - !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) - false - } - } - } def typedStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { val inBlock = exprOwner == context.owner @@ -2900,7 +2851,7 @@ trait Typers extends Modes with Adaptations with Tags { case imp @ Import(_, _) => imp.symbol.initialize if (!imp.symbol.isError) { - context = context.makeNewImport(imp) + context = context.make(imp) typedImport(imp) } else EmptyTree case _ => @@ -2922,7 +2873,7 @@ trait Typers extends Modes with Adaptations with Tags { ConstructorsOrderError(stat) } - if (isWarnablePureExpression(result)) context.warning(stat.pos, + if (treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) @@ -2931,8 +2882,8 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** 'accessor' and 'accessed' are so similar it becomes very difficult to - * follow the logic, so I renamed one to something distinct. + /* 'accessor' and 'accessed' are so similar it becomes very difficult to + * follow the logic, so I renamed one to something distinct. */ def accesses(looker: Symbol, accessed: Symbol) = accessed.hasLocalFlag && ( (accessed.isParamAccessor) @@ -2973,7 +2924,7 @@ trait Typers extends Modes with Adaptations with Tags { // SI-5877 The decls of a package include decls of the package object. But we don't want to add // the corresponding synthetics to the package class, only to the package object class. def shouldAdd(sym: Symbol) = - inBlock || !isInPackageObject(sym, context.owner) + inBlock || !context.isInPackageObject(sym, context.owner) for (sym <- scope if shouldAdd(sym)) for (tree <- context.unit.synthetics get sym) { newStats += typedStat(tree) // might add even more synthetics to the scope @@ -3026,14 +2977,14 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = { - val typedMode = onlyStickyModes(mode) | newmode - val t = withCondConstrTyper((mode & SCCmode) != 0)(_.typed(arg, typedMode, pt)) + def typedArg(arg: Tree, mode: Mode, newmode: Mode, pt: Type): Tree = { + val typedMode = mode.onlySticky | newmode + val t = withCondConstrTyper((mode & SCCmode) != NOmode)(_.typed(arg, typedMode, pt)) checkDead.inMode(typedMode, t) } - def typedArgs(args: List[Tree], mode: Int) = - args mapConserve (arg => typedArg(arg, mode, 0, WildcardType)) + def typedArgs(args: List[Tree], mode: Mode) = + args mapConserve (arg => typedArg(arg, mode, NOmode, WildcardType)) /** Type trees in `args0` against corresponding expected type in `adapted0`. * @@ -3043,8 +2994,8 @@ trait Typers extends Modes with Adaptations with Tags { * * (docs reverse-engineered -- AM) */ - def typedArgs(args0: List[Tree], mode: Int, formals0: List[Type], adapted0: List[Type]): List[Tree] = { - val sticky = onlyStickyModes(mode) + def typedArgs(args0: List[Tree], mode: Mode, formals0: List[Type], adapted0: List[Type]): List[Tree] = { + val sticky = mode.onlySticky def loop(args: List[Tree], formals: List[Type], adapted: List[Type]): List[Tree] = { if (args.isEmpty || adapted.isEmpty) Nil else { @@ -3052,13 +3003,11 @@ trait Typers extends Modes with Adaptations with Tags { val isVarArgs = formals.isEmpty || formals.tail.isEmpty && isRepeatedParamType(formals.head) val typedMode = sticky | ( if (isVarArgs) STARmode | BYVALmode - else if (isByNameParamType(formals.head)) 0 + else if (isByNameParamType(formals.head)) NOmode else BYVALmode ) - var tree = typedArg(args.head, mode, typedMode, adapted.head) - if (hasPendingMacroExpansions) tree = macroExpandAll(this, tree) // formals may be empty, so don't call tail - tree :: loop(args.tail, formals drop 1, adapted.tail) + typedArg(args.head, mode, typedMode, adapted.head) :: loop(args.tail, formals drop 1, adapted.tail) } } loop(args0, formals0, adapted0) @@ -3104,18 +3053,18 @@ trait Typers extends Modes with Adaptations with Tags { } } - def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { // TODO_NMT: check the assumption that args nonEmpty def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } def preSelectOverloaded(fun: Tree): Tree = { - if (fun.hasSymbol && fun.symbol.isOverloaded) { + if (fun.hasSymbolField && fun.symbol.isOverloaded) { // remove alternatives with wrong number of parameters without looking at types. - // less expensive than including them in inferMethodAlternatvie (see below). + // less expensive than including them in inferMethodAlternative (see below). def shapeType(arg: Tree): Type = arg match { case Function(vparams, body) => - functionType(vparams map (vparam => AnyClass.tpe), shapeType(body)) + functionType(vparams map (_ => AnyClass.tpe), shapeType(body)) case AssignOrNamedArg(Ident(name), rhs) => NamedType(name, shapeType(rhs)) case _ => @@ -3123,7 +3072,6 @@ trait Typers extends Modes with Adaptations with Tags { } val argtypes = args map shapeType val pre = fun.symbol.tpe.prefix - var sym = fun.symbol filter { alt => // must use pt as expected type, not WildcardType (a tempting quick fix to #2665) // now fixed by using isWeaklyCompatible in exprTypeArgs @@ -3135,20 +3083,19 @@ trait Typers extends Modes with Adaptations with Tags { // Types: "refs = Array(Map(), Map())". I determined that inference fails if there are at // least two invariant type parameters. See the test case I checked in to help backstop: // pos/isApplicableSafe.scala. - isApplicableSafe(context.undetparams, followApply(pre.memberType(alt)), argtypes, pt) + isApplicableSafe(context.undetparams, followApply(pre memberType alt), argtypes, pt) } if (sym.isOverloaded) { - val sym1 = sym filter (alt => { // eliminate functions that would result from tupling transforms // keeps alternatives with repeated params - hasExactlyNumParams(followApply(alt.tpe), argtypes.length) || - // also keep alts which define at least one default - alt.tpe.paramss.exists(_.exists(_.hasDefault)) - }) + val sym1 = sym filter (alt => + isApplicableBasedOnArity(pre memberType alt, argtypes.length, varargsStar = false, tuplingAllowed = false) + || alt.tpe.params.exists(_.hasDefault) + ) if (sym1 != NoSymbol) sym = sym1 } if (sym == NoSymbol) fun - else adapt(fun setSymbol sym setType pre.memberType(sym), forFunMode(mode), WildcardType) + else adapt(fun setSymbol sym setType pre.memberType(sym), mode.forFunMode, WildcardType) } else fun } @@ -3157,28 +3104,30 @@ trait Typers extends Modes with Adaptations with Tags { fun.tpe match { case OverloadedType(pre, alts) => def handleOverloaded = { - val undetparams = context.extractUndetparams() - - val argtpes = new ListBuffer[Type] - val amode = forArgMode(fun, mode) - val args1 = args map { - case arg @ AssignOrNamedArg(Ident(name), rhs) => - // named args: only type the righthand sides ("unknown identifier" errors otherwise) - val rhs1 = typedArg(rhs, amode, BYVALmode, WildcardType) - argtpes += NamedType(name, rhs1.tpe.deconst) - // the assign is untyped; that's ok because we call doTypedApply - atPos(arg.pos) { new AssignOrNamedArg(arg.lhs, rhs1) } - case arg => - val arg1 = typedArg(arg, amode, BYVALmode, WildcardType) - argtpes += arg1.tpe.deconst - arg1 + val undetparams = context.undetparams + val (args1, argTpes) = context.savingUndeterminedTypeParams() { + val amode = forArgMode(fun, mode) + def typedArg0(tree: Tree) = typedArg(tree, amode, BYVALmode, WildcardType) + args.map { + case arg @ AssignOrNamedArg(Ident(name), rhs) => + // named args: only type the righthand sides ("unknown identifier" errors otherwise) + val rhs1 = typedArg0(rhs) + // the assign is untyped; that's ok because we call doTypedApply + val arg1 = treeCopy.AssignOrNamedArg(arg, arg.lhs, rhs1) + (arg1, NamedType(name, rhs1.tpe.deconst)) + case arg @ treeInfo.WildcardStarArg(repeated) => + val arg1 = typedArg0(arg) + (arg1, RepeatedType(arg1.tpe.deconst)) + case arg => + val arg1 = typedArg0(arg) + (arg1, arg1.tpe.deconst) + }.unzip } - context.undetparams = undetparams 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) + inferMethodAlternative(fun, undetparams, argTpes, pt) + doTypedApply(tree, adapt(fun, mode.forFunMode, WildcardType), args1, mode, pt) } } handleOverloaded @@ -3186,51 +3135,47 @@ trait Typers extends Modes with Adaptations with Tags { case mt @ MethodType(params, _) => val paramTypes = mt.paramTypes // repeat vararg as often as needed, remove by-name - val formals = formalTypes(paramTypes, args.length) + val argslen = args.length + val formals = formalTypes(paramTypes, argslen) - /** Try packing all arguments into a Tuple and apply `fun` - * to that. This is the last thing which is tried (after - * default arguments) + /* Try packing all arguments into a Tuple and apply `fun` + * to that. This is the last thing which is tried (after + * default arguments) */ - def tryTupleApply: Option[Tree] = { - // if 1 formal, 1 arg (a tuple), otherwise unmodified args - val tupleArgs = actualArgs(tree.pos.makeTransparent, args, formals.length) - - if (!sameLength(tupleArgs, args) && !isUnitForVarArgs(args, params)) { + def tryTupleApply: Option[Tree] = ( + if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) { + val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args))) // expected one argument, but got 0 or >1 ==> try applying to tuple // the inner "doTypedApply" does "extractUndetparams" => restore when it fails val savedUndetparams = context.undetparams - silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) match { - case SilentResultValue(t) => + silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t => // Depending on user options, may warn or error here if // a Unit or tuple was inserted. Some(t) filter (tupledTree => - !inExprModeButNot(mode, FUNmode) + !mode.inExprModeButNot(FUNmode) || tupledTree.symbol == null || checkValidAdaptation(tupledTree, args) ) - case _ => - context.undetparams = savedUndetparams - None + } orElse { _ => context.undetparams = savedUndetparams ; None } } - } else None - } + else None + ) - /** Treats an application which uses named or default arguments. - * Also works if names + a vararg used: when names are used, the vararg - * parameter has to be specified exactly once. Note that combining varargs - * and defaults is ruled out by typedDefDef. + /* Treats an application which uses named or default arguments. + * Also works if names + a vararg used: when names are used, the vararg + * parameter has to be specified exactly once. Note that combining varargs + * and defaults is ruled out by typedDefDef. */ def tryNamesDefaults: Tree = { val lencmp = compareLengths(args, formals) def checkNotMacro() = { - if (fun.symbol != null && fun.symbol.filter(sym => sym != null && sym.isTermMacro && !sym.isErroneous) != NoSymbol) + if (treeInfo.isMacroApplication(fun)) tryTupleApply getOrElse duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun)) } if (mt.isErroneous) duplErrTree - else if (inPatternMode(mode)) { + else if (mode.inPatternMode) { // #2064 duplErrorTree(WrongNumberOfArgsError(tree, fun)) } else if (lencmp > 0) { @@ -3241,10 +3186,10 @@ trait Typers extends Modes with Adaptations with Tags { val (namelessArgs, argPos) = removeNames(Typer.this)(args, params) if (namelessArgs exists (_.isErroneous)) { duplErrTree - } else if (!isIdentity(argPos) && !sameLength(formals, params)) - // !isIdentity indicates that named arguments are used to re-order arguments + } else if (!allArgsArePositional(argPos) && !sameLength(formals, params)) + // !allArgsArePositional indicates that named arguments are used to re-order arguments duplErrorTree(MultipleVarargError(tree)) - else if (isIdentity(argPos) && !isNamedApplyBlock(fun)) { + else if (allArgsArePositional(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 }" checkNotMacro() @@ -3294,7 +3239,7 @@ trait Typers extends Modes with Adaptations with Tags { } if (!sameLength(formals, args) || // wrong nb of arguments - (args exists isNamed) || // uses a named argument + (args exists isNamedArg) || // uses a named argument isNamedApplyBlock(fun)) { // fun was transformed to a named apply block => // integrate this application into the block if (dyna.isApplyDynamicNamed(fun)) dyna.typedNamedApply(tree, fun, args, mode, pt) @@ -3325,31 +3270,11 @@ trait Typers extends Modes with Adaptations with Tags { // precise(foo) : foo.type => foo.type val restpe = mt.resultType(args1 map (arg => gen.stableTypeFor(arg) getOrElse arg.tpe)) def ifPatternSkipFormals(tp: Type) = tp match { - case MethodType(_, rtp) if (inPatternMode(mode)) => rtp + case MethodType(_, rtp) if (mode.inPatternMode) => rtp case _ => tp } - // Replace the Delegate-Chainer methods += and -= with corresponding - // + and - calls, which are translated in the code generator into - // Combine and Remove - if (forMSIL) { - fun match { - case Select(qual, name) => - if (isSubType(qual.tpe, DelegateClass.tpe) - && (name == encode("+=") || name == encode("-="))) { - val n = if (name == encode("+=")) nme.PLUS else nme.MINUS - val f = Select(qual, n) - // the compiler thinks, the PLUS method takes only one argument, - // but he thinks it's an instance method -> still two ref's on the stack - // -> translated by backend - val rhs = treeCopy.Apply(tree, f, args) - return typed(Assign(qual, rhs)) - } - case _ => () - } - } - - /** + /* * This is translating uses of List() into Nil. This is less * than ideal from a consistency standpoint, but it shouldn't be * altered without due caution. @@ -3357,7 +3282,7 @@ trait Typers extends Modes with Adaptations with Tags { * forced during kind-arity checking, so it is guarded by additional * tests to ensure we're sufficiently far along. */ - if (args.isEmpty && !forInteractive && fun.symbol.isInitialized && ListModule.hasCompleteInfo && (fun.symbol == List_apply)) + if (args.isEmpty && canTranslateEmptyListToNil && fun.symbol.isInitialized && ListModule.hasCompleteInfo && (fun.symbol == List_apply)) atPos(tree.pos)(gen.mkNil setType restpe) else constfold(treeCopy.Apply(tree, fun, args1) setType ifPatternSkipFormals(restpe)) @@ -3371,7 +3296,7 @@ trait Typers extends Modes with Adaptations with Tags { doTypedApply(tree, fun, args, mode, pt) } else { def handlePolymorphicCall = { - assert(!inPatternMode(mode), modeString(mode)) // this case cannot arise for patterns + assert(!mode.inPatternMode, mode) // this case cannot arise for patterns val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt) val strictTargs = map2(lenientTargs, tparams)((targ, tparam) => if (targ == WildcardType) tparam.tpeHK else targ) @@ -3397,9 +3322,8 @@ trait Typers extends Modes with Adaptations with Tags { // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun" // returns those undetparams which have not been instantiated. val undetparams = inferMethodInstance(fun, tparams, args1, pt) - val result = doTypedApply(tree, fun, args1, mode, pt) - context.undetparams = undetparams - result + try doTypedApply(tree, fun, args1, mode, pt) + finally context.undetparams = undetparams } } handlePolymorphicCall @@ -3413,15 +3337,16 @@ trait Typers extends Modes with Adaptations with Tags { if (!tree.isErrorTyped) setError(tree) else tree // @H change to setError(treeCopy.Apply(tree, fun, args)) - case otpe if inPatternMode(mode) && unapplyMember(otpe).exists => + case otpe if mode.inPatternMode && unapplyMember(otpe).exists => doTypedUnapply(tree, fun0, fun, args, mode, pt) case _ => - duplErrorTree(ApplyWithoutArgsError(tree, fun)) + if (treeInfo.isMacroApplication(tree)) duplErrorTree(MacroTooManyArgumentListsError(tree, fun.symbol)) + else duplErrorTree(ApplyWithoutArgsError(tree, fun)) } } - def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } @@ -3456,7 +3381,7 @@ trait Typers extends Modes with Adaptations with Tags { else None if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) { - //Console.println("UNAPP: need to typetest, arg.tpe = "+arg.tpe+", unappType = "+unappType) + //Console.println(s"UNAPP: need to typetest, arg: ${arg.tpe} unappType: $unappType") val (freeVars, unappFormal) = freshArgType(unappType.skolemizeExistential(context.owner, tree)) val unapplyContext = context.makeNewScope(context.tree, context.owner) freeVars foreach unapplyContext.scope.enter @@ -3466,29 +3391,25 @@ trait Typers extends Modes with Adaptations with Tags { // turn any unresolved type variables in freevars into existential skolems val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) - arg.tpe = pattp.substSym(freeVars, skolems) + arg setType pattp.substSym(freeVars, skolems) argDummy setInfo arg.tpe } - // 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))) + // clearing the type is necessary so that ref will be stabilized; see bug 881 + val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapp), List(arg))) if (fun1.tpe.isErroneous) duplErrTree else { - val resTp = fun1.tpe.finalResultType.normalize + val resTp = fun1.tpe.finalResultType.dealiasWiden val nbSubPats = args.length val (formals, formalsExpanded) = extractorFormalTypes(fun0.pos, resTp, nbSubPats, fun1.symbol, treeInfo.effectivePatternArity(args)) if (formals == null) duplErrorTree(WrongNumberOfArgsError(tree, fun)) else { val args1 = typedArgs(args, mode, formals, formalsExpanded) - // This used to be the following (failing) assert: - // assert(isFullyDefined(pt), tree+" ==> "+UnApply(fun1, args1)+", pt = "+pt) - // I modified as follows. See SI-1048. - val pt1 = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) - + val pt1 = ensureFullyDefined(pt) // SI-1048 val itype = glb(List(pt1, arg.tpe)) - arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) + arg setType pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) val unapply = UnApply(fun1, args1) setPos tree.pos setType itype // if the type that the unapply method expects for its argument is uncheckable, wrap in classtag extractor @@ -3523,16 +3444,21 @@ trait Typers extends Modes with Adaptations with Tags { // if there's a ClassTag that allows us to turn the unchecked type test for `pt` into a checked type test // return the corresponding extractor (an instance of ClassTag[`pt`]) - def extractorForUncheckedType(pos: Position, pt: Type): Option[Tree] = if (!opt.virtPatmat || isPastTyper) None else { + def extractorForUncheckedType(pos: Position, pt: Type): Option[Tree] = if (isPastTyper) None else { // only look at top-level type, can't (reliably) do anything about unchecked type args (in general) - pt.normalize.typeConstructor match { + // but at least make a proper type before passing it elsewhere + val pt1 = pt.dealiasWiden match { + case tr @ TypeRef(pre, sym, args) if args.nonEmpty => copyTypeRef(tr, pre, sym, sym.typeParams map (_.tpeHK)) // replace actual type args with dummies + case pt1 => pt1 + } + pt1 match { // if at least one of the types in an intersection is checkable, use the checkable ones // this avoids problems as in run/matchonseq.scala, where the expected type is `Coll with scala.collection.SeqLike` // Coll is an abstract type, but SeqLike of course is not - case RefinedType(parents, _) if (parents.length >= 2) && (parents.exists(tp => !infer.containsUnchecked(tp))) => + case RefinedType(ps, _) if ps.length > 1 && (ps exists infer.isCheckable) => None - case ptCheckable if infer.containsUnchecked(ptCheckable) => + case ptCheckable if infer isUncheckable ptCheckable => val classTagExtractor = resolveClassTag(pos, ptCheckable) if (classTagExtractor != EmptyTree && unapplyMember(classTagExtractor.tpe) != NoSymbol) @@ -3545,25 +3471,33 @@ trait Typers extends Modes with Adaptations with Tags { /** * Convert an annotation constructor call into an AnnotationInfo. - * - * @param annClass the expected annotation class */ - def typedAnnotation(ann: Tree, mode: Int = EXPRmode, selfsym: Symbol = NoSymbol, annClass: Symbol = AnnotationClass, requireJava: Boolean = false): AnnotationInfo = { - lazy val annotationError = AnnotationInfo(ErrorType, Nil, Nil) + def typedAnnotation(ann: Tree, mode: Mode = EXPRmode, selfsym: Symbol = NoSymbol): AnnotationInfo = { var hasError: Boolean = false val pending = ListBuffer[AbsTypeError]() + def finish(res: AnnotationInfo): AnnotationInfo = { + if (hasError) { + pending.foreach(ErrorUtils.issueTypeError) + ErroneousAnnotation + } + else res + } + def reportAnnotationError(err: AbsTypeError) = { pending += err hasError = true - annotationError + ErroneousAnnotation } - /** Calling constfold right here is necessary because some trees (negated - * floats and literals in particular) are not yet folded. + /* Calling constfold right here is necessary because some trees (negated + * floats and literals in particular) are not yet folded. */ def tryConst(tr: Tree, pt: Type): Option[LiteralAnnotArg] = { - val const: Constant = typed(constfold(tr), EXPRmode, pt) match { + // The typed tree may be relevantly different than the tree `tr`, + // e.g. it may have encountered an implicit conversion. + val ttree = typed(constfold(tr), EXPRmode, pt) + val const: Constant = ttree match { case l @ Literal(c) if !l.isErroneous => c case tree => tree.tpe match { case ConstantType(c) => c @@ -3572,29 +3506,36 @@ trait Typers extends Modes with Adaptations with Tags { } if (const == null) { - reportAnnotationError(AnnotationNotAConstantError(tr)); None + reportAnnotationError(AnnotationNotAConstantError(ttree)); None } else if (const.value == null) { reportAnnotationError(AnnotationArgNullError(tr)); None } else Some(LiteralAnnotArg(const)) } - /** Converts an untyped tree to a ClassfileAnnotArg. If the conversion fails, - * an error message is reported and None is returned. + /* Converts an untyped tree to a ClassfileAnnotArg. If the conversion fails, + * an error message is reported and None is returned. */ def tree2ConstArg(tree: Tree, pt: Type): Option[ClassfileAnnotArg] = tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) if (pt.typeSymbol == ArrayClass) => reportAnnotationError(ArrayConstantsError(tree)); None case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => - val annInfo = typedAnnotation(ann, mode, NoSymbol, pt.typeSymbol, true) + val annInfo = typedAnnotation(ann, mode, NoSymbol) + val annType = annInfo.tpe + + if (!annType.typeSymbol.isSubClass(pt.typeSymbol)) + reportAnnotationError(AnnotationTypeMismatchError(tpt, annType, annType)) + else if (!annType.typeSymbol.isSubClass(ClassfileAnnotationClass)) + reportAnnotationError(NestedAnnotationError(ann, annType)) + if (annInfo.atp.isErroneous) { hasError = true; None } else Some(NestedAnnotArg(annInfo)) // use of Array.apply[T: ClassTag](xs: T*): Array[T] // and Array.apply(x: Int, xs: Int*): Array[Int] (and similar) case Apply(fun, args) => - val typedFun = typed(fun, forFunMode(mode), WildcardType) + val typedFun = typed(fun, mode.forFunMode, WildcardType) if (typedFun.symbol.owner == ArrayModule.moduleClass && typedFun.symbol.name == nme.apply) pt match { case TypeRef(_, ArrayClass, targ :: _) => @@ -3622,44 +3563,42 @@ trait Typers extends Modes with Adaptations with Tags { } // begin typedAnnotation - val (fun, argss) = { - def extract(fun: Tree, outerArgss: List[List[Tree]]): - (Tree, List[List[Tree]]) = fun match { - case Apply(f, args) => - extract(f, args :: outerArgss) - case Select(New(tpt), nme.CONSTRUCTOR) => - (fun, outerArgss) - case _ => - reportAnnotationError(UnexpectedTreeAnnotation(fun)) - (setError(fun), outerArgss) - } - extract(ann, List()) - } - - val res = if (fun.isErroneous) annotationError - else { - val typedFun @ Select(New(tpt), _) = typed(fun, forFunMode(mode), WildcardType) - val annType = tpt.tpe + val treeInfo.Applied(fun0, targs, argss) = ann + if (fun0.isErroneous) + return finish(ErroneousAnnotation) + val typedFun0 = typed(fun0, mode.forFunMode, WildcardType) + val typedFunPart = ( + // If there are dummy type arguments in typeFun part, it suggests we + // must type the actual constructor call, not only the select. The value + // arguments are how the type arguments will be inferred. + if (targs.isEmpty && typedFun0.exists(t => t.tpe != null && isDummyAppliedType(t.tpe))) + logResult(s"Retyped $typedFun0 to find type args")(typed(argss.foldLeft(fun0)(Apply(_, _)))) + else + typedFun0 + ) + val treeInfo.Applied(typedFun @ Select(New(annTpt), _), _, _) = typedFunPart + val annType = annTpt.tpe - if (typedFun.isErroneous) annotationError + finish( + if (typedFun.isErroneous) + ErroneousAnnotation else if (annType.typeSymbol isNonBottomSubClass ClassfileAnnotationClass) { // annotation to be saved as java classfile annotation val isJava = typedFun.symbol.owner.isJavaDefined - if (!annType.typeSymbol.isNonBottomSubClass(annClass)) { - reportAnnotationError(AnnotationTypeMismatchError(tpt, annClass.tpe, annType)) - } else if (argss.length > 1) { + if (argss.length > 1) { reportAnnotationError(MultipleArgumentListForAnnotationError(ann)) - } else { + } + else { val annScope = annType.decls .filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined) val names = new scala.collection.mutable.HashSet[Symbol] - def hasValue = names exists (_.name == nme.value) names ++= (if (isJava) annScope.iterator else typedFun.tpe.params.iterator) + + def hasValue = names exists (_.name == nme.value) val args = argss match { - case List(List(arg)) if !isNamed(arg) && hasValue => - List(new AssignOrNamedArg(Ident(nme.value), arg)) - case as :: _ => as + case (arg :: Nil) :: Nil if !isNamedArg(arg) && hasValue => gen.mkNamedArg(nme.value, arg) :: Nil + case args :: Nil => args } val nvPairs = args map { @@ -3689,46 +3628,33 @@ trait Typers extends Modes with Adaptations with Tags { reportAnnotationError(AnnotationMissingArgError(ann, annType, sym)) } - if (hasError) annotationError + if (hasError) ErroneousAnnotation else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(Apply(typedFun, args).setPos(ann.pos)) } - } else if (requireJava) { - reportAnnotationError(NestedAnnotationError(ann, annType)) - } else { + } + else { val typedAnn = if (selfsym == NoSymbol) { // local dummy fixes SI-5544 val localTyper = newTyper(context.make(ann, context.owner.newLocalDummy(ann.pos))) - localTyper.typed(ann, mode, annClass.tpe) - } else { - // Since a selfsym is supplied, the annotation should have - // an extra "self" identifier in scope for type checking. - // This is implemented by wrapping the rhs - // in a function like "self => rhs" during type checking, - // and then stripping the "self =>" and substituting - // in the supplied selfsym. + localTyper.typed(ann, mode, annType) + } + else { + // Since a selfsym is supplied, the annotation should have an extra + // "self" identifier in scope for type checking. This is implemented + // by wrapping the rhs in a function like "self => rhs" during type + // checking, and then stripping the "self =>" and substituting in + // the supplied selfsym. val funcparm = ValDef(NoMods, nme.self, TypeTree(selfsym.info), EmptyTree) - val func = Function(List(funcparm), ann.duplicate) - // The .duplicate of annot.constr - // deals with problems that - // accur if this annotation is - // later typed again, which - // the compiler sometimes does. - // The problem is that "self" - // ident's within annot.constr - // will retain the old symbol - // from the previous typing. - val fun1clazz = FunctionClass(1) - val funcType = typeRef(fun1clazz.tpe.prefix, - fun1clazz, - List(selfsym.info, annClass.tpe)) - - (typed(func, mode, funcType): @unchecked) match { - case t @ Function(List(arg), rhs) => - val subs = - new TreeSymSubstituter(List(arg.symbol),List(selfsym)) - subs(rhs) + // The .duplicate of annot.constr deals with problems that accur + // if this annotation is later typed again, which the compiler + // sometimes does. The problem is that "self" ident's within + // annot.constr will retain the old symbol from the previous typing. + val func = Function(funcparm :: Nil, ann.duplicate) + val funcType = appliedType(FunctionClass(1), selfsym.info, annType) + val Function(arg :: Nil, rhs) = typed(func, mode, funcType) + + rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil) } - } def annInfo(t: Tree): AnnotationInfo = t match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => @@ -3753,16 +3679,10 @@ trait Typers extends Modes with Adaptations with Tags { if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2) unit.deprecationWarning(ann.pos, "@deprecated now takes two arguments; see the scaladoc.") - if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) annotationError + if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation else annInfo(typedAnn) + }) } - } - - if (hasError) { - pending.foreach(ErrorUtils.issueTypeError) - annotationError - } else res - } /** Compute an existential type from raw hidden symbols `syms` and type `tp` */ @@ -3813,7 +3733,8 @@ trait Typers extends Modes with Adaptations with Tags { else containsDef(owner, sym) || isRawParameter(sym) || isCapturedExistential(sym) def containsLocal(tp: Type): Boolean = tp exists (t => isLocal(t.typeSymbol) || isLocal(t.termSymbol)) - val normalizeLocals = new TypeMap { + + val dealiasLocals = new TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) => if (sym.isAliasType && containsLocal(tp)) apply(tp.dealias) @@ -3866,25 +3787,25 @@ trait Typers extends Modes with Adaptations with Tags { for (sym <- remainingSyms) addLocals(sym.existentialBound) } - val normalizedTpe = normalizeLocals(tree.tpe) - addLocals(normalizedTpe) - packSymbols(localSyms.toList, normalizedTpe) + val dealiasedType = dealiasLocals(tree.tpe) + addLocals(dealiasedType) + packSymbols(localSyms.toList, dealiasedType) } def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) = if (!checkClassType(tpt) && noGen) tpt else atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) - protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = { + protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Mode): Tree = { for (wc <- tree.whereClauses) if (wc.symbol == NoSymbol) { namer.enterSym(wc); wc.symbol setFlag EXISTENTIAL } else context.scope enter wc.symbol val whereClauses1 = typedStats(tree.whereClauses, context.owner) - for (vd @ ValDef(_, _, _, _) <- tree.whereClauses) + for (vd @ ValDef(_, _, _, _) <- whereClauses1) if (vd.symbol.tpe.isVolatile) AbstractionFromVolatileTypeError(vd) val tpt1 = typedType(tree.tpt, mode) - existentialTransform(tree.whereClauses map (_.symbol), tpt1.tpe)((tparams, tp) => { + existentialTransform(whereClauses1 map (_.symbol), tpt1.tpe)((tparams, tp) => { val original = tpt1 match { case tpt : TypeTree => atPos(tree.pos)(ExistentialTypeTree(tpt.original, tree.whereClauses)) case _ => { @@ -3898,7 +3819,7 @@ trait Typers extends Modes with Adaptations with Tags { } // lifted out of typed1 because it's needed in typedImplicit0 - protected def typedTypeApply(tree: Tree, mode: Int, fun: Tree, args: List[Tree]): Tree = fun.tpe match { + protected def typedTypeApply(tree: Tree, mode: Mode, fun: Tree, args: List[Tree]): Tree = fun.tpe match { case OverloadedType(pre, alts) => inferPolyAlternatives(fun, args map (_.tpe)) val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree) @@ -3923,7 +3844,7 @@ trait Typers extends Modes with Adaptations with Tags { val targs = args map (_.tpe) checkBounds(tree, NoPrefix, NoSymbol, tparams, targs, "") if (fun.symbol == Predef_classOf) - typedClassOf(tree, args.head, true) + typedClassOf(tree, args.head, noGen = true) else { if (!isPastTyper && fun.symbol == Any_isInstanceOf && targs.nonEmpty) { val scrutineeType = fun match { @@ -3987,7 +3908,7 @@ trait Typers extends Modes with Adaptations with Tags { // else false } - def typedNamedApply(orig: Tree, fun: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def typedNamedApply(orig: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { def argToBinding(arg: Tree): Tree = arg match { case AssignOrNamedArg(Ident(name), rhs) => gen.mkTuple(List(CODE.LIT(name.toString), rhs)) case _ => gen.mkTuple(List(CODE.LIT(""), arg)) @@ -4032,20 +3953,20 @@ trait Typers extends Modes with Adaptations with Tags { def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic def matches(t: Tree) = isDesugaredApply || treeInfo.dissectApplied(t).core == treeSelection - /** Note that the trees which arrive here are potentially some distance from - * the trees of direct interest. `cxTree` is some enclosing expression which - * may apparently be arbitrarily larger than `tree`; and `tree` itself is - * too small, having at least in some cases lost its explicit type parameters. - * This logic is designed to use `tree` to pinpoint the immediately surrounding - * Apply/TypeApply/Select node, and only then creates the dynamic call. - * See SI-6731 among others. + /* Note that the trees which arrive here are potentially some distance from + * the trees of direct interest. `cxTree` is some enclosing expression which + * may apparently be arbitrarily larger than `tree`; and `tree` itself is + * too small, having at least in some cases lost its explicit type parameters. + * This logic is designed to use `tree` to pinpoint the immediately surrounding + * Apply/TypeApply/Select node, and only then creates the dynamic call. + * See SI-6731 among others. */ def findSelection(t: Tree): Option[(TermName, Tree)] = t match { case Apply(fn, args) if hasStar(args) => DynamicVarArgUnsupported(tree, applyOp(args)) ; None case Apply(fn, args) if matches(fn) => Some((applyOp(args), fn)) case Assign(lhs, _) if matches(lhs) => Some((nme.updateDynamic, lhs)) case _ if matches(t) => Some((nme.selectDynamic, t)) - case _ => t.children flatMap findSelection headOption + case _ => (t.children flatMap findSelection).headOption } findSelection(cxTree) match { case Some((opName, treeInfo.Applied(_, targs, _))) => @@ -4057,13 +3978,9 @@ trait Typers extends Modes with Adaptations with Tags { } } - def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = { - silent(typeTree) match { - case SilentResultValue(r) => r - case SilentTypeError(err) => DynamicRewriteError(tree, err) + def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = + silent(typeTree) orElse (err => DynamicRewriteError(tree, err)) } - } - } final def deindentTyping() = context.typingIndentLevel -= 2 final def indentTyping() = context.typingIndentLevel += 2 @@ -4076,22 +3993,33 @@ trait Typers extends Modes with Adaptations with Tags { println(s) } - def typed1(tree: Tree, mode: Int, pt: Type): Tree = { - def isPatternMode = inPatternMode(mode) + def typed1(tree: Tree, mode: Mode, pt: Type): Tree = { + def isPatternMode = mode.inPatternMode + def inPatternConstructor = mode.inAll(PATTERNmode | FUNmode) + def isQualifierMode = mode.inAll(QUALmode) - //Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")") - //@M! get the type of the qualifier in a Select tree, otherwise: NoType - def prefixType(fun: Tree): Type = fun match { - case Select(qualifier, _) => qualifier.tpe -// case Ident(name) => ?? - case _ => NoType + // Lookup in the given class using the root mirror. + def lookupInOwner(owner: Symbol, name: Name): Symbol = + if (isQualifierMode) rootMirror.missingHook(owner, name) else NoSymbol + + // Lookup in the given qualifier. Used in last-ditch efforts by typedIdent and typedSelect. + def lookupInRoot(name: Name): Symbol = lookupInOwner(rootMirror.RootClass, name) + def lookupInEmpty(name: Name): Symbol = rootMirror.EmptyPackageClass.info member name + + def lookupInQualifier(qual: Tree, name: Name): Symbol = ( + if (name == nme.ERROR || qual.tpe.widen.isErroneous) + NoSymbol + else lookupInOwner(qual.tpe.typeSymbol, name) orElse { + NotAMemberError(tree, qual, name) + NoSymbol } + ) def typedAnnotated(atd: Annotated): Tree = { val ann = atd.annot val arg1 = typed(atd.arg, mode, pt) - /** mode for typing the annotation itself */ - val annotMode = mode & ~TYPEmode | EXPRmode + /* mode for typing the annotation itself */ + val annotMode = (mode &~ TYPEmode) | EXPRmode def resultingTypeTree(tpe: Type) = { // we need symbol-ful originals for reification @@ -4110,7 +4038,7 @@ trait Typers extends Modes with Adaptations with Tags { if (ann.tpe == null) { // an annotated type val selfsym = - if (!settings.selfInAnnots.value) + if (!settings.selfInAnnots) NoSymbol else arg1.tpe.selfsym orElse { @@ -4146,7 +4074,7 @@ trait Typers extends Modes with Adaptations with Tags { // Erroneous annotations were already reported in typedAnnotation arg1 // simply drop erroneous annotations else { - ann.tpe = atype + ann setType atype resultingTypeTree(atype) } } else { @@ -4157,7 +4085,7 @@ trait Typers extends Modes with Adaptations with Tags { else { if (ann.tpe == null) { val annotInfo = typedAnnotation(ann, annotMode) - ann.tpe = arg1.tpe.withAnnotation(annotInfo) + ann setType arg1.tpe.withAnnotation(annotInfo) } val atype = ann.tpe Typed(arg1, resultingTypeTree(atype)) setPos tree.pos setType atype @@ -4181,7 +4109,7 @@ trait Typers extends Modes with Adaptations with Tags { if (name != tpnme.WILDCARD) namer.enterInScope(sym) else context.scope.enter(sym) - tree setSymbol sym setType sym.tpe + tree setSymbol sym setType sym.tpeHK case name: TermName => val sym = @@ -4189,7 +4117,7 @@ trait Typers extends Modes with Adaptations with Tags { else context.owner.newValue(name, tree.pos) if (name != nme.WILDCARD) { - if ((mode & ALTmode) != 0) VariableInPatternAlternativeError(tree) + if (mode.inAll(ALTmode)) VariableInPatternAlternativeError(tree) namer.enterInScope(sym) } @@ -4214,11 +4142,11 @@ trait Typers extends Modes with Adaptations with Tags { def typedArrayValue(tree: ArrayValue) = { val elemtpt1 = typedType(tree.elemtpt, mode) - val elems1 = tree.elems mapConserve (elem => typed(elem, mode, elemtpt1.tpe)) - treeCopy.ArrayValue(tree, elemtpt1, elems1) - .setType( - (if (isFullyDefined(pt) && !phase.erasedTypes) pt - else arrayType(elemtpt1.tpe)).notNull) + val elems1 = tree.elems mapConserve (elem => typed(elem, mode, elemtpt1.tpe)) + // see run/t6126 for an example where `pt` does not suffice (tagged types) + val tpe1 = if (isFullyDefined(pt) && !phase.erasedTypes) pt else arrayType(elemtpt1.tpe) + + treeCopy.ArrayValue(tree, elemtpt1, elems1) setType tpe1 } def typedAssign(lhs: Tree, rhs: Tree): Tree = { @@ -4236,7 +4164,7 @@ trait Typers extends Modes with Adaptations with Tags { if (treeInfo.mayBeVarGetter(varsym)) { lhs1 match { case treeInfo.Applied(Select(qual, name), _, _) => - val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos + val sel = Select(qual, name.setterName) setPos lhs.pos val app = Apply(sel, List(rhs)) setPos tree.pos return typed(app, mode, pt) @@ -4258,41 +4186,43 @@ trait Typers extends Modes with Adaptations with Tags { else fail() } - def typedIf(tree: If) = { + def typedIf(tree: If): If = { val cond1 = checkDead(typed(tree.cond, EXPRmode | BYVALmode, BooleanClass.tpe)) - val thenp = tree.thenp - val elsep = tree.elsep - if (elsep.isEmpty) { // in the future, should be unnecessary - val thenp1 = typed(thenp, UnitClass.tpe) - treeCopy.If(tree, cond1, thenp1, elsep) setType thenp1.tpe - } else { - var thenp1 = typed(thenp, pt) - var elsep1 = typed(elsep, pt) - def thenTp = packedType(thenp1, context.owner) - def elseTp = packedType(elsep1, context.owner) - - // println("typedIf: "+(thenp1.tpe, elsep1.tpe, ptOrLub(List(thenp1.tpe, elsep1.tpe)),"\n", thenTp, elseTp, thenTp =:= elseTp)) - val (owntype, needAdapt) = - // in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway - // in the special (though common) case where the types are equal, it pays to pack before comparing - // especially virtpatmat needs more aggressive unification of skolemized types - // this breaks src/library/scala/collection/immutable/TrieIterator.scala - if ( opt.virtPatmat && !isPastTyper - && thenp1.tpe.annotations.isEmpty && elsep1.tpe.annotations.isEmpty // annotated types need to be lubbed regardless (at least, continations break if you by pass them like this) - && thenTp =:= elseTp - ) (thenp1.tpe.deconst, false) // use unpacked type. Important to deconst, as is done in ptOrLub, otherwise `if (???) 0 else 0` evaluates to 0 (SI-6331) - // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala - else ptOrLub(thenp1.tpe :: elsep1.tpe :: Nil, pt) - - if (needAdapt) { //isNumericValueType(owntype)) { - thenp1 = adapt(thenp1, mode, owntype) - elsep1 = adapt(elsep1, mode, owntype) - } - treeCopy.If(tree, cond1, thenp1, elsep1) setType owntype - } - } - - // under -Xexperimental (and not -Xoldpatmat), and when there's a suitable __match in scope, virtualize the pattern match + // One-legged ifs don't need a lot of analysis + if (tree.elsep.isEmpty) + return treeCopy.If(tree, cond1, typed(tree.thenp, UnitClass.tpe), tree.elsep) setType UnitClass.tpe + + val thenp1 = typed(tree.thenp, pt) + val elsep1 = typed(tree.elsep, pt) + + // in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway + // in the special (though common) case where the types are equal, it pays to pack before comparing + // especially virtpatmat needs more aggressive unification of skolemized types + // this breaks src/library/scala/collection/immutable/TrieIterator.scala + // annotated types need to be lubbed regardless (at least, continations break if you by pass them like this) + def samePackedTypes = ( + !isPastTyper + && thenp1.tpe.annotations.isEmpty + && elsep1.tpe.annotations.isEmpty + && packedType(thenp1, context.owner) =:= packedType(elsep1, context.owner) + ) + def finish(ownType: Type) = treeCopy.If(tree, cond1, thenp1, elsep1) setType ownType + // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala + // @PP: This was doing the samePackedTypes check BEFORE the isFullyDefined check, + // which based on everything I see everywhere else was a bug. I reordered it. + if (isFullyDefined(pt)) + finish(pt) + // Important to deconst, otherwise `if (???) 0 else 0` evaluates to 0 (SI-6331) + else thenp1.tpe.deconst :: elsep1.tpe.deconst :: Nil match { + case tp :: _ if samePackedTypes => finish(tp) + case tpes if sameWeakLubAsLub(tpes) => finish(lub(tpes)) + case tpes => + val lub = weakLub(tpes) + treeCopy.If(tree, cond1, adapt(thenp1, mode, lub), adapt(elsep1, mode, lub)) setType lub + } + } + + // When there's a suitable __match in scope, virtualize the pattern match // otherwise, type the Match and leave it until phase `patmat` (immediately after typer) // empty-selector matches are transformed into synthetic PartialFunction implementations when the expected type demands it def typedVirtualizedMatch(tree: Match): Tree = { @@ -4302,7 +4232,7 @@ trait Typers extends Modes with Adaptations with Tags { if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, tree, mode, pt) else { - val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 + val arity = if (isFunctionType(pt)) pt.dealiasWiden.typeArgs.length - 1 else 1 val params = for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { ValDef(Modifiers(PARAM | SYNTHETIC), @@ -4358,7 +4288,7 @@ trait Typers extends Modes with Adaptations with Tags { // given a dealiased type. val tpt0 = typedTypeConstructor(tpt) modifyType (_.dealias) if (checkStablePrefixClassType(tpt0)) - if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { + if (tpt0.hasSymbolField && !tpt0.symbol.typeParams.isEmpty) { context.undetparams = cloneSymbols(tpt0.symbol.typeParams) notifyUndetparamsAdded(context.undetparams) TypeTree().setOriginal(tpt0) @@ -4367,8 +4297,8 @@ trait Typers extends Modes with Adaptations with Tags { else tpt0 } - /** If current tree <tree> appears in <val x(: T)? = <tree>> - * return `tp with x.type' else return `tp`. + /* If current tree <tree> appears in <val x(: T)? = <tree>> + * return `tp with x.type' else return `tp`. */ def narrowRhs(tp: Type) = { val sym = context.tree.symbol context.tree match { @@ -4388,7 +4318,7 @@ trait Typers extends Modes with Adaptations with Tags { NotAMemberError(tpt, TypeTree(tp), nme.CONSTRUCTOR) setError(tpt) } - else if (!( tp == sym.thisSym.tpe // when there's no explicit self type -- with (#3612) or without self variable + 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 @@ -4418,36 +4348,15 @@ trait Typers extends Modes with Adaptations with Tags { else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) case MethodType(formals, _) => if (isFunctionType(pt)) expr1 - else expr1 match { - case Select(qual, name) if (forMSIL && - pt != WildcardType && - pt != ErrorType && - isSubType(pt, DelegateClass.tpe)) => - val scalaCaller = newScalaCaller(pt) - addScalaCallerInfo(scalaCaller, expr1.symbol) - val n: Name = scalaCaller.name - val del = Ident(DelegateClass) setType DelegateClass.tpe - val f = Select(del, n) - //val f1 = TypeApply(f, List(Ident(pt.symbol) setType pt)) - val args: List[Tree] = if(expr1.symbol.isStatic) List(Literal(Constant(null))) - else List(qual) // where the scala-method is located - val rhs = Apply(f, args) - typed(rhs) - case _ => - adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) - } + else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) case ErrorType => expr1 case _ => UnderscoreEtaError(expr1) } - /** - * @param args ... - * @return ... - */ - def tryTypedArgs(args: List[Tree], mode: Int): Option[List[Tree]] = { - val c = context.makeSilent(false) + def tryTypedArgs(args: List[Tree], mode: Mode): Option[List[Tree]] = { + val c = context.makeSilent(reportAmbiguousErrors = false) c.retyping = true try { val res = newTyper(c).typedArgs(args, mode) @@ -4456,16 +4365,14 @@ trait Typers extends Modes with Adaptations with Tags { case ex: CyclicReference => throw ex case te: TypeError => - // @H some of typer erros can still leak, + // @H some of typer errors can still leak, // for instance in continuations None - } finally { - c.flushBuffer() } } - /** Try to apply function to arguments; if it does not work, try to convert Java raw to existentials, or try to - * insert an implicit conversion. + /* Try to apply function to arguments; if it does not work, try to convert Java raw to existentials, or try to + * insert an implicit conversion. */ def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { val start = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null @@ -4506,7 +4413,7 @@ trait Typers extends Modes with Adaptations with Tags { tryTypedArgs(args, forArgMode(fun, mode)) match { case Some(args1) => val qual1 = - if (!pt.isError) adaptToArguments(qual, name, args1, pt, true, true) + if (!pt.isError) adaptToArguments(qual, name, args1, pt, reportAmbiguous = true, saveErrors = true) else qual if (qual1 ne qual) { val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos @@ -4519,121 +4426,92 @@ trait Typers extends Modes with Adaptations with Tags { setError(treeCopy.Apply(tree, fun, args)) } - silent(_.doTypedApply(tree, fun, args, mode, pt)) match { - case SilentResultValue(t) => - t - case SilentTypeError(err) => - onError(err) + silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError } - } def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = { val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable - if (args.isEmpty && stableApplication && isPatternMode) { - // treat stable function applications f() as expressions. - // - // [JZ] According to Martin, this is related to the old pattern matcher, which - // needs to typecheck after a the translation of `x.f` to `x.f()` in a prior - // compilation phase. As part of SI-7377, this has been tightened with `args.isEmpty`, - // but we should remove it altogether in Scala 2.11. - typed1(tree, mode & ~PATTERNmode | EXPRmode, pt) - } else { - val funpt = if (isPatternMode) pt else WildcardType - val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null - val opeqStart = if (Statistics.canEnable) Statistics.startTimer(failedOpEqNanos) else null - - 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)) { - if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart) - convertToAssignment(fun, qual1, name, args) - } else { - if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) - reportError - } - case _ => + val funpt = if (isPatternMode) pt else WildcardType + val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null + val opeqStart = if (Statistics.canEnable) Statistics.startTimer(failedOpEqNanos) else null + + 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)) { + if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart) + convertToAssignment(fun, qual1, name, args) + } else { if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) - reportError - } - } - silent(_.typed(fun, forFunMode(mode), funpt), - if ((mode & EXPRmode) != 0) false else context.ambiguousErrors, - if ((mode & EXPRmode) != 0) tree else context.tree) match { - case SilentResultValue(fun1) => - val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 - if (Statistics.canEnable) Statistics.incCounter(typedApplyCount) - def isImplicitMethod(tpe: Type) = tpe match { - case mt: MethodType => mt.isImplicit - case _ => false + reportError + } + case _ => + if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) + reportError + } + } + val silentResult = silent( + op = _.typed(fun, mode.forFunMode, funpt), + reportAmbiguousErrors = !mode.inExprMode && context.ambiguousErrors, + newtree = if (mode.inExprMode) tree else context.tree + ) + silentResult match { + case SilentResultValue(fun1) => + val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 + if (Statistics.canEnable) Statistics.incCounter(typedApplyCount) + val noSecondTry = ( + isPastTyper + || (fun2.symbol ne null) && fun2.symbol.isConstructor + || (fun2.tpe match { case mt: MethodType => mt.isImplicit case _ => false }) + ) + val isFirstTry = !noSecondTry && ( + fun2 match { + case Select(_, _) => mode inExprModeButNot SNDTRYmode + case _ => false } - val useTry = ( - !isPastTyper - && fun2.isInstanceOf[Select] - && !isImplicitMethod(fun2.tpe) - && ((fun2.symbol eq null) || !fun2.symbol.isConstructor) - && (mode & (EXPRmode | SNDTRYmode)) == EXPRmode - ) - val res = - if (useTry) tryTypedApply(fun2, args) - else doTypedApply(tree, fun2, args, mode, pt) + ) + if (isFirstTry) + tryTypedApply(fun2, args) + else + doTypedApply(tree, fun2, args, mode, pt) - /* - if (fun2.hasSymbol && fun2.symbol.isConstructor && (mode & EXPRmode) != 0) { - res.tpe = res.tpe.notNull - } - */ - // TODO: In theory we should be able to call: - //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 && !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) - if (checked ne res) typed { atPos(tree.pos)(checked) } - else res - } else - res - case SilentTypeError(err) => - onError({issue(err); setError(tree)}) - } + case SilentTypeError(err) => + onError({issue(err); setError(tree)}) } } - def typedApply(tree: Apply) = { - val fun = tree.fun - val args = tree.args - fun match { - case Block(stats, expr) => - typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) - case _ => - normalTypedApply(tree, fun, args) match { - case Apply(Select(New(tpt), name), args) - if (tpt.tpe != null && - tpt.tpe.typeSymbol == ArrayClass && - args.length == 1 && - erasure.GenericArray.unapply(tpt.tpe).isDefined) => // !!! todo simplify by using extractor - // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len) - // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len), where Array HK gets applied (N-1) times - // [Eugene] no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions) - val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) - val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.toTypeConstructor, List(tpe))).last - atPos(tree.pos) { - val tag = resolveClassTag(tree.pos, tagType) - if (tag.isEmpty) MissingClassTagError(tree, tagType) - else typed(new ApplyToImplicitArgs(Select(tag, nme.newArray), args)) + // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len) + // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len) + // where Array HK gets applied (N-1) times + object ArrayInstantiation { + def unapply(tree: Apply) = tree match { + case Apply(Select(New(tpt), name), arg :: Nil) if tpt.tpe != null && tpt.tpe.typeSymbol == ArrayClass => + Some(tpt.tpe) collect { + case erasure.GenericArray(level, componentType) => + val tagType = (1 until level).foldLeft(componentType)((res, _) => arrayType(res)) + + resolveClassTag(tree.pos, tagType) match { + case EmptyTree => MissingClassTagError(tree, tagType) + case tag => atPos(tree.pos)(new ApplyToImplicitArgs(Select(tag, nme.newArray), arg :: Nil)) } - case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => //SI-5696 - TooManyArgumentListsForConstructor(tree) - case tree1 => - tree1 } + case _ => None } } + def typedApply(tree: Apply) = tree match { + case Apply(Block(stats, expr), args) => + typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) + case Apply(fun, args) => + normalTypedApply(tree, fun, args) match { + case ArrayInstantiation(tree1) => typed(tree1, mode, pt) + case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => TooManyArgumentListsForConstructor(tree) //SI-5696 + case tree1 => tree1 + } + } + def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree]): Tree = { val prefix = name.toTermName stripSuffix nme.EQL def mkAssign(vble: Tree): Tree = @@ -4687,8 +4565,6 @@ trait Typers extends Modes with Adaptations with Tags { case This(_) => qual1.symbol case _ => qual1.tpe.typeSymbol } - //println(clazz+"/"+qual1.tpe.typeSymbol+"/"+qual1) - def findMixinSuper(site: Type): Type = { var ps = site.parents filter (_.typeSymbol.name == mix) if (ps.isEmpty) @@ -4696,11 +4572,6 @@ trait Typers extends Modes with Adaptations with Tags { if (ps.isEmpty) { debuglog("Fatal: couldn't find site " + site + " in " + site.parents.map(_.typeSymbol.name)) if (phase.erasedTypes && context.enclClass.owner.isImplClass) { - // println(qual1) - // println(clazz) - // println(site) - // println(site.parents) - // 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 @@ -4718,7 +4589,7 @@ trait Typers extends Modes with Adaptations with Tags { val owntype = ( if (!mix.isEmpty) findMixinSuper(clazz.tpe) - else if ((mode & SUPERCONSTRmode) != 0) clazz.info.firstParent + else if (mode.inAll(SUPERCONSTRmode)) clazz.info.firstParent else intersectionType(clazz.info.parents) ) treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) @@ -4732,14 +4603,28 @@ trait Typers extends Modes with Adaptations with Tags { if (isStableContext(tree, mode, pt)) tree setType clazz.thisType else tree } - /** Attribute a selection where <code>tree</code> is <code>qual.name</code>. - * <code>qual</code> is already attributed. - * - * @param qual ... - * @param name ... - * @return ... + /* Attribute a selection where `tree` is `qual.name`. + * `qual` is already attributed. */ def typedSelect(tree: Tree, qual: Tree, name: Name): Tree = { + val t = typedSelectInternal(tree, qual, name) + // Checking for OverloadedTypes being handed out after overloading + // resolution has already happened. + if (isPastTyper) t.tpe match { + case OverloadedType(pre, alts) => + if (alts forall (s => (s.owner == ObjectClass) || (s.owner == AnyClass) || isPrimitiveValueClass(s.owner))) () + else if (settings.debug) printCaller( + s"""|Select received overloaded type during $phase, but typer is over. + |If this type reaches the backend, we are likely doomed to crash. + |$t has these overloads: + |${alts map (s => " " + s.defStringSeenAs(pre memberType s)) mkString "\n"} + |""".stripMargin + )("") + case _ => + } + t + } + def typedSelectInternal(tree: Tree, qual: Tree, name: Name): Tree = { def asDynamicCall = dyna.mkInvoke(context.tree, tree, qual, name) map { t => dyna.wrapErrors(t, (_.typed1(t, mode, pt))) } @@ -4748,57 +4633,51 @@ trait Typers extends Modes with Adaptations with Tags { // symbol not found? --> try to convert implicitly to a type that does have the required // member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an // xml member to StringContext, which in turn has an unapply[Seq] method) - if (name != nme.CONSTRUCTOR && inExprModeOr(mode, PATTERNmode)) { - val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, true, true) + if (name != nme.CONSTRUCTOR && mode.inExprModeOr(PATTERNmode)) { + val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, reportAmbiguous = true, saveErrors = true) if ((qual1 ne qual) && !qual1.isErrorTyped) return typed(treeCopy.Select(tree, qual1, name), mode, pt) } NoSymbol } if (phase.erasedTypes && qual.isInstanceOf[Super] && tree.symbol != NoSymbol) - qual.tpe = tree.symbol.owner.tpe + qual setType tree.symbol.owner.tpe if (!reallyExists(sym)) { def handleMissing: Tree = { - if (context.unit.isJava && name.isTypeName) { - // SI-3120 Java uses the same syntax, A.B, to express selection from the - // value A and from the type A. We have to try both. - val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } - if (tree1 != EmptyTree) return typed1(tree1, mode, pt) - } - - // try to expand according to Dynamic rules. - asDynamicCall foreach (x => return x) - - debuglog( - "qual = " + qual + ":" + qual.tpe + - "\nSymbol=" + qual.tpe.termSymbol + "\nsymbol-info = " + qual.tpe.termSymbol.info + - "\nscope-id = " + qual.tpe.termSymbol.info.decls.hashCode() + "\nmembers = " + qual.tpe.members + - "\nname = " + name + "\nfound = " + sym + "\nowner = " + context.enclClass.owner) - - def makeInteractiveErrorTree = { - val tree1 = tree match { - case Select(_, _) => treeCopy.Select(tree, qual, name) - case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) - } - setError(tree1) - } - - if (name == nme.ERROR && forInteractive) - return makeInteractiveErrorTree - - if (!qual.tpe.widen.isErroneous) { - if ((mode & QUALmode) != 0) { - val lastTry = rootMirror.missingHook(qual.tpe.typeSymbol, name) - if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) + def errorTree = missingSelectErrorTree(tree, qual, name) + def asTypeSelection = ( + if (context.unit.isJava && name.isTypeName) { + // SI-3120 Java uses the same syntax, A.B, to express selection from the + // value A and from the type A. We have to try both. + atPos(tree.pos)(gen.convertToSelectFromType(qual, name)) match { + case EmptyTree => None + case tree1 => Some(typed1(tree1, mode, pt)) + } } - NotAMemberError(tree, qual, name) - } - - if (forInteractive) makeInteractiveErrorTree else setError(tree) + else None + ) + debuglog(s""" + |qual=$qual:${qual.tpe} + |symbol=${qual.tpe.termSymbol.defString} + |scope-id=${qual.tpe.termSymbol.info.decls.hashCode} + |members=${qual.tpe.members mkString ", "} + |name=$name + |found=$sym + |owner=${context.enclClass.owner} + """.stripMargin) + + // 1) Try converting a term selection on a java class into a type selection. + // 2) Try expanding according to Dynamic rules. + // 3) Try looking up the name in the qualifier. + asTypeSelection orElse asDynamicCall getOrElse (lookupInQualifier(qual, name) match { + case NoSymbol => setError(errorTree) + case found => typed1(tree setSymbol found, mode, pt) + }) } handleMissing - } else { + } + else { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) @@ -4814,16 +4693,6 @@ trait Typers extends Modes with Adaptations with Tags { (stabilize(treeAndPre._1, treeAndPre._2, mode, pt), None) } - def isPotentialNullDeference() = { - !isPastTyper && - !sym.isConstructor && - !(qual.tpe <:< NotNullClass.tpe) && !qual.tpe.isNotNull && - !(List(Any_isInstanceOf, Any_asInstanceOf) contains result.symbol) // null.is/as is not a dereference - } - // unit is null here sometimes; how are we to know when unit might be null? (See bug #2467.) - if (settings.warnSelectNullable.value && isPotentialNullDeference && unit != null) - unit.warning(tree.pos, "potential null pointer dereference: "+tree) - result match { // could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual? case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs.nonEmpty => // TODO: somehow the new qual is not checked in refchecks @@ -4838,7 +4707,7 @@ trait Typers extends Modes with Adaptations with Tags { case _ if accessibleError.isDefined => // don't adapt constructor, SI-6074 val qual1 = if (name == nme.CONSTRUCTOR) qual - else adaptToMemberWithArgs(tree, qual, name, mode, false, false) + else adaptToMemberWithArgs(tree, qual, name, mode, reportAmbiguous = false, saveErrors = false) if (!qual1.isErrorTyped && (qual1 ne qual)) typed(Select(qual1, name) setPos tree.pos, mode, pt) else @@ -4869,10 +4738,7 @@ trait Typers extends Modes with Adaptations with Tags { val tree1 = // temporarily use `filter` and an alternative for `withFilter` if (name == nme.withFilter) - silent(_ => typedSelect(tree, qual1, name)) match { - case SilentResultValue(result) => - result - case _ => + silent(_ => typedSelect(tree, qual1, name)) orElse { _ => silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { case SilentResultValue(result2) => unit.deprecationWarning( @@ -4896,277 +4762,76 @@ trait Typers extends Modes with Adaptations with Tags { } } - /** Attribute an identifier consisting of a simple name or an outer reference. + /* A symbol qualifies if: + * - it exists + * - it is not stale (stale symbols are made to disappear here) + * - if we are in a pattern constructor, method definitions do not qualify + * unless they are stable. Otherwise, 'case x :: xs' would find the :: method. + */ + def qualifies(sym: Symbol) = ( + sym.hasRawInfo + && reallyExists(sym) + && !(inPatternConstructor && sym.isMethod && !sym.isStable) + ) + + /* Attribute an identifier consisting of a simple name or an outer reference. * - * @param tree The tree representing the identifier. - * @param name The name of the identifier. - * Transformations: (1) Prefix class members with this. - * (2) Change imported symbols to selections + * @param tree The tree representing the identifier. + * @param name The name of the identifier. + * Transformations: (1) Prefix class members with this. + * (2) Change imported symbols to selections */ def typedIdent(tree: Tree, name: Name): Tree = { - var errorContainer: AbsTypeError = null - def ambiguousError(msg: String) = { - assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") - errorContainer = AmbiguousIdentError(tree, name, msg) - } - def identError(tree: AbsTypeError) = { - assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") - errorContainer = tree - } + // setting to enable unqualified idents in empty package (used by the repl) + def inEmptyPackage = if (settings.exposeEmptyPackage) lookupInEmpty(name) else NoSymbol - var defSym: Symbol = tree.symbol // the directly found symbol - var pre: Type = NoPrefix // the prefix type of defSym, if a class member - var qual: Tree = EmptyTree // the qualifier tree if transformed tree is a select - var inaccessibleSym: Symbol = NoSymbol // the first symbol that was found but that was discarded - // for being inaccessible; used for error reporting - var inaccessibleExplanation: String = "" - - // If a special setting is given, the empty package will be checked as a - // last ditch effort before failing. This method sets defSym and returns - // true if a member of the given name exists. - def checkEmptyPackage(): Boolean = { - defSym = rootMirror.EmptyPackageClass.tpe.nonPrivateMember(name) - defSym != NoSymbol + def issue(err: AbsTypeError) = { + // Avoiding some spurious error messages: see SI-2388. + val suppress = reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME) + if (!suppress) + ErrorUtils.issueTypeError(err) + + setError(tree) } - def startingIdentContext = ( // ignore current variable scope in patterns to enforce linearity - if ((mode & (PATTERNmode | TYPEPATmode)) == 0) context - else context.outer - ) - // A symbol qualifies if it exists and is not stale. Stale symbols - // are made to disappear here. In addition, - // if we are in a constructor of a pattern, we ignore all definitions - // which are methods (note: if we don't do that - // case x :: xs in class List would return the :: method) - // unless they are stable or are accessors (the latter exception is for better error messages). - def qualifies(sym: Symbol): Boolean = { - sym.hasRawInfo && // this condition avoids crashing on self-referential pattern variables - reallyExists(sym) && - ((mode & PATTERNmode | FUNmode) != (PATTERNmode | FUNmode) || !sym.isSourceMethod || sym.hasFlag(ACCESSOR)) - } - - if (defSym == NoSymbol) { - var defEntry: ScopeEntry = null // the scope entry of defSym, if defined in a local scope - - var cx = startingIdentContext - while (defSym == NoSymbol && cx != NoContext && (cx.scope ne null)) { // cx.scope eq null arises during FixInvalidSyms in Duplicators - pre = cx.enclClass.prefix - defEntry = cx.scope.lookupEntry(name) - if ((defEntry ne null) && qualifies(defEntry.sym)) { - // Right here is where SI-1987, overloading in package objects, can be - // seen to go wrong. There is an overloaded symbol, but when referring - // to the unqualified identifier from elsewhere in the package, only - // the last definition is visible. So overloading mis-resolves and is - // definition-order dependent, bad things. See run/t1987.scala. - // - // I assume the actual problem involves how/where these symbols are entered - // into the scope. But since I didn't figure out how to fix it that way, I - // catch it here by looking up package-object-defined symbols in the prefix. - if (isInPackageObject(defEntry.sym, pre.typeSymbol)) { - defSym = pre.member(defEntry.sym.name) - if (defSym ne defEntry.sym) { - qual = gen.mkAttributedQualifier(pre) - log(sm""" - | !!! Overloaded package object member resolved incorrectly. - | prefix: $pre - | Discarded: ${defEntry.sym.defString} - | Using: ${defSym.defString} - """) + val startContext = if (mode.inNone(PATTERNmode | TYPEPATmode)) context else context.outer + val nameLookup = tree.symbol match { + case NoSymbol => startContext.lookupSymbol(name, qualifies) + case sym => LookupSucceeded(EmptyTree, sym) + } + import InferErrorGen._ + nameLookup match { + case LookupAmbiguous(msg) => issue(AmbiguousIdentError(tree, name, msg)) + case LookupInaccessible(sym, msg) => issue(AccessError(tree, sym, context, msg)) + case LookupNotFound => + inEmptyPackage orElse lookupInRoot(name) match { + case NoSymbol => issue(SymbolNotFoundError(tree, name, context.owner, startContext)) + case sym => typed1(tree setSymbol sym, mode, pt) } - } - else - defSym = defEntry.sym - } - else { - cx = cx.enclClass - val foundSym = pre.member(name) filter qualifies - defSym = foundSym filter (context.isAccessible(_, pre, false)) - if (defSym == NoSymbol) { - if ((foundSym ne NoSymbol) && (inaccessibleSym eq NoSymbol)) { - inaccessibleSym = foundSym - inaccessibleExplanation = analyzer.lastAccessCheckDetails - } - cx = cx.outer - } - } - } - - val symDepth = if (defEntry eq null) cx.depth - else cx.depth - (cx.scope.nestingLevel - defEntry.owner.nestingLevel) - var impSym: Symbol = NoSymbol // the imported symbol - var imports = context.imports // impSym != NoSymbol => it is imported from imports.head - - // Java: A single-type-import declaration d in a compilation unit c of package p - // that imports a type named n shadows, throughout c, the declarations of: - // - // 1) any top level type named n declared in another compilation unit of p - // - // A type-import-on-demand declaration never causes any other declaration to be shadowed. - // - // Scala: Bindings of different kinds have a precedence defined on them: - // - // 1) Definitions and declarations that are local, inherited, or made available by a - // package clause in the same compilation unit where the definition occurs have - // highest precedence. - // 2) Explicit imports have next highest precedence. - def depthOk(imp: ImportInfo) = ( - imp.depth > symDepth - || (unit.isJava && imp.isExplicitImport(name) && imp.depth == symDepth) - ) - while (!reallyExists(impSym) && !imports.isEmpty && depthOk(imports.head)) { - impSym = imports.head.importedSymbol(name) - if (!impSym.exists) imports = imports.tail - } - - // detect ambiguous definition/import, - // update `defSym` to be the final resolved symbol, - // update `pre` to be `sym`s prefix type in case it is an imported member, - // and compute value of: - - if (defSym.exists && impSym.exists) { - // imported symbols take precedence over package-owned symbols in different - // compilation units. Defined symbols take precedence over erroneous imports. - if (defSym.isDefinedInPackage && - (!currentRun.compiles(defSym) || - context.unit.exists && defSym.sourceFile != context.unit.source.file)) - defSym = NoSymbol - else if (impSym.isError || impSym.name == nme.CONSTRUCTOR) - impSym = NoSymbol - } - if (defSym.exists) { - if (impSym.exists) - ambiguousError( - "it is both defined in "+defSym.owner + - " and imported subsequently by \n"+imports.head) - else if (!defSym.owner.isClass || defSym.owner.isPackageClass || defSym.isTypeParameterOrSkolem) - pre = NoPrefix - else - qual = atPos(tree.pos.focusStart)(gen.mkAttributedQualifier(pre)) - } else { - if (impSym.exists) { - var impSym1: Symbol = NoSymbol - var imports1 = imports.tail - - /** It's possible that seemingly conflicting identifiers are - * identifiably the same after type normalization. In such cases, - * allow compilation to proceed. A typical example is: - * package object foo { type InputStream = java.io.InputStream } - * import foo._, java.io._ - */ - def ambiguousImport() = { - // The types of the qualifiers from which the ambiguous imports come. - // If the ambiguous name is a value, these must be the same. - def t1 = imports.head.qual.tpe - def t2 = imports1.head.qual.tpe - // The types of the ambiguous symbols, seen as members of their qualifiers. - // If the ambiguous name is a monomorphic type, we can relax this far. - def mt1 = t1 memberType impSym - def mt2 = t2 memberType impSym1 - def characterize = List( - s"types: $t1 =:= $t2 ${t1 =:= t2} members: ${mt1 =:= mt2}", - s"member type 1: $mt1", - s"member type 2: $mt2", - s"$impSym == $impSym1 ${impSym == impSym1}", - s"${impSym.debugLocationString} ${impSym.getClass}", - s"${impSym1.debugLocationString} ${impSym1.getClass}" - ).mkString("\n ") - - // The symbol names are checked rather than the symbols themselves because - // each time an overloaded member is looked up it receives a new symbol. - // So foo.member("x") != foo.member("x") if x is overloaded. This seems - // likely to be the cause of other bugs too... - if (t1 =:= t2 && impSym.name == impSym1.name) - log(s"Suppressing ambiguous import: $t1 =:= $t2 && $impSym == $impSym1") - // Monomorphism restriction on types is in part because type aliases could have the - // same target type but attach different variance to the parameters. Maybe it can be - // relaxed, but doesn't seem worth it at present. - else if (mt1 =:= mt2 && name.isTypeName && impSym.isMonomorphicType && impSym1.isMonomorphicType) - log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $impSym and $impSym1 are equivalent") - else { - log(s"Import is genuinely ambiguous:\n " + characterize) - ambiguousError(s"it is imported twice in the same scope by\n${imports.head}\nand ${imports1.head}") - } - } - while (errorContainer == null && !imports1.isEmpty && - (!imports.head.isExplicitImport(name) || - imports1.head.depth == imports.head.depth)) { - impSym1 = imports1.head.importedSymbol(name) - if (reallyExists(impSym1)) { - if (imports1.head.isExplicitImport(name)) { - if (imports.head.isExplicitImport(name) || - imports1.head.depth != imports.head.depth) ambiguousImport() - impSym = impSym1 - imports = imports1 - } else if (!imports.head.isExplicitImport(name) && - imports1.head.depth == imports.head.depth) ambiguousImport() - } - imports1 = imports1.tail - } - defSym = impSym - val qual0 = imports.head.qual - if (!(shortenImports && qual0.symbol.isPackage)) // optimization: don't write out package prefixes - qual = atPos(tree.pos.focusStart)(resetPos(qual0.duplicate)) - pre = qual.tpe - } - else if (settings.exposeEmptyPackage.value && checkEmptyPackage()) - log("Allowing empty package member " + name + " due to settings.") - else { - if ((mode & QUALmode) != 0) { - val lastTry = rootMirror.missingHook(rootMirror.RootClass, name) - if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) - } - if (settings.debug.value) { - log(context.imports)//debug - } - if (inaccessibleSym eq NoSymbol) { - // Avoiding some spurious error messages: see SI-2388. - if (reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME)) () - 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 (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) - } + case LookupSucceeded(qual, sym) => + (// this -> Foo.this + if (sym.isThisSym) + typed1(This(sym.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) + else if (isPredefMemberNamed(sym, 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) setAttachments tree.attachments) - ) - val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) - // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? - val tree3 = stabilize(tree2, pre2, mode, pt) + val pre1 = if (sym.isTopLevel) sym.owner.thisType else if (qual == EmptyTree) NoPrefix else qual.tpe + val tree1 = if (qual == EmptyTree) tree else atPos(tree.pos)(Select(atPos(tree.pos.focusStart)(qual), name)) + val (tree2, pre2) = makeAccessible(tree1, sym, pre1, qual) // SI-5967 Important to replace param type A* with Seq[A] when seen from from a reference, to avoid // inference errors in pattern matching. - tree3 setType dropRepeatedParamType(tree3.tpe) + stabilize(tree2, pre2, mode, pt) modifyType dropIllegalStarTypes + }) setAttachments tree.attachments } } - } def typedIdentOrWildcard(tree: Ident) = { val name = tree.name if (Statistics.canEnable) Statistics.incCounter(typedIdentCount) - if ((name == nme.WILDCARD && (mode & (PATTERNmode | FUNmode)) == PATTERNmode) || - (name == tpnme.WILDCARD && (mode & TYPEmode) != 0)) + if ((name == nme.WILDCARD && mode.inPatternNotFunMode) || + (name == tpnme.WILDCARD && mode.inAll(TYPEmode))) tree setType makeFullyDefined(pt) else typedIdent(tree, name) @@ -5198,7 +4863,7 @@ trait Typers extends Modes with Adaptations with Tags { val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) if (tpt1.isErrorTyped) { tpt1 - } else if (!tpt1.hasSymbol) { + } else if (!tpt1.hasSymbolField) { AppliedTypeNoParametersError(tree, tpt1.tpe) } else { val tparams = tpt1.symbol.typeParams @@ -5239,7 +4904,7 @@ trait Typers extends Modes with Adaptations with Tags { 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 + if (settings.debug) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info)//debug AppliedTypeWrongNumberOfArgsError(tree, tpt1, tparams) } } @@ -5256,27 +4921,7 @@ trait Typers extends Modes with Adaptations with Tags { treeCopy.PackageDef(tree, pid1, stats1) setType NoType } - def typedDocDef(docdef: DocDef) = { - if (forScaladoc && (sym ne null) && (sym ne NoSymbol)) { - val comment = docdef.comment - fillDocComment(sym, comment) - val typer1 = newTyper(context.makeNewScope(tree, context.owner)) - for (useCase <- comment.useCases) { - typer1.silent(_.typedUseCase(useCase)) match { - case SilentTypeError(err) => - unit.warning(useCase.pos, err.errMsg) - case _ => - } - for (useCaseSym <- useCase.defined) { - if (sym.name != useCaseSym.name) - unit.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode) - } - } - } - typed(docdef.definition, mode, pt) - } - - /** + /* * The typer with the correct context for a method definition. If the method is a default getter for * a constructor default, the resulting typer has a constructor context (fixes SI-5543). */ @@ -5292,7 +4937,7 @@ trait Typers extends Modes with Adaptations with Tags { } def typedStar(tree: Star) = { - if ((mode & STARmode) == 0 && !isPastTyper) + if (mode.inNone(STARmode) && !isPastTyper) StarPatternWithVarargParametersError(tree) treeCopy.Star(tree, typed(tree.elem, mode, pt)) setType makeFullyDefined(pt) } @@ -5304,30 +4949,49 @@ trait Typers extends Modes with Adaptations with Tags { treeCopy.UnApply(tree, fun1, args1) setType pt } - def typedTry(tree: Try) = { - var block1 = typed(tree.block, pt) - var catches1 = typedCases(tree.catches, ThrowableClass.tpe, pt) - - for (cdef <- catches1 if !isPastTyper && cdef.guard.isEmpty) { - def warn(name: Name) = context.warning(cdef.pat.pos, s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning.") + def issueTryWarnings(tree: Try): Try = { + def checkForCatchAll(cdef: CaseDef) { def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol - cdef.pat match { + def warn(name: Name) = { + val msg = s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning." + context.warning(cdef.pat.pos, msg) + } + if (cdef.guard.isEmpty) cdef.pat match { case Bind(name, i @ Ident(_)) if unbound(i) => warn(name) - case i @ Ident(name) if unbound(i) => warn(name) - case _ => + case i @ Ident(name) if unbound(i) => warn(name) + case _ => } } - - val finalizer1 = - if (tree.finalizer.isEmpty) tree.finalizer - else typed(tree.finalizer, UnitClass.tpe) - val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)), pt) - if (needAdapt) { - block1 = adapt(block1, mode, owntype) - catches1 = catches1 map (adaptCase(_, mode, owntype)) + if (!isPastTyper) tree match { + case Try(_, Nil, fin) => + if (fin eq EmptyTree) + context.warning(tree.pos, "A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.") + case Try(_, catches, _) => + catches foreach checkForCatchAll } + tree + } - treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype + def typedTry(tree: Try) = { + val Try(block, catches, fin) = tree + val block1 = typed(block, pt) + val catches1 = typedCases(catches, ThrowableClass.tpe, pt) + val fin1 = if (fin.isEmpty) fin else typed(fin, UnitClass.tpe) + + def finish(ownType: Type) = treeCopy.Try(tree, block1, catches1, fin1) setType ownType + + issueTryWarnings( + if (isFullyDefined(pt)) + finish(pt) + else block1 :: catches1 map (_.tpe.deconst) match { + case tpes if sameWeakLubAsLub(tpes) => finish(lub(tpes)) + case tpes => + val lub = weakLub(tpes) + val block2 = adapt(block1, mode, lub) + val catches2 = catches1 map (adaptCase(_, mode, lub)) + treeCopy.Try(tree, block2, catches2, fin1) setType lub + } + ) } def typedThrow(tree: Throw) = { @@ -5345,16 +5009,16 @@ trait Typers extends Modes with Adaptations with Tags { // that typecheck must not trigger macro expansions, so we explicitly prohibit them // however we cannot do `context.withMacrosDisabled` // because `expr` might contain nested macro calls (see SI-6673) - val exprTyped = typed1(expr updateAttachment SuppressMacroExpansionAttachment, mode, pt) + val exprTyped = typed1(suppressMacroExpansion(expr), mode, pt) exprTyped match { - case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => + case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(exprTyped) case _ => typedEta(checkDead(exprTyped)) } - case Ident(tpnme.WILDCARD_STAR) => - val exprTyped = typed(expr, onlyStickyModes(mode), WildcardType) + case t if treeInfo isWildcardStarType t => + val exprTyped = typed(expr, mode.onlySticky, WildcardType) def subArrayType(pt: Type) = if (isPrimitiveValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt) else { @@ -5363,8 +5027,8 @@ trait Typers extends Modes with Adaptations with Tags { } val (exprAdapted, baseClass) = exprTyped.tpe.typeSymbol match { - case ArrayClass => (adapt(exprTyped, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) - case _ => (adapt(exprTyped, onlyStickyModes(mode), seqType(pt)), SeqClass) + case ArrayClass => (adapt(exprTyped, mode.onlySticky, subArrayType(pt)), ArrayClass) + case _ => (adapt(exprTyped, mode.onlySticky, seqType(pt)), SeqClass) } exprAdapted.tpe.baseType(baseClass) match { case TypeRef(_, _, List(elemtp)) => @@ -5375,14 +5039,13 @@ trait Typers extends Modes with Adaptations with Tags { case _ => val tptTyped = typedType(tpt, mode) - val exprTyped = typed(expr, onlyStickyModes(mode), tptTyped.tpe.deconst) + val exprTyped = typed(expr, mode.onlySticky, tptTyped.tpe.deconst) val treeTyped = treeCopy.Typed(tree, exprTyped, tptTyped) if (isPatternMode) { val uncheckedTypeExtractor = extractorForUncheckedType(tpt.pos, tptTyped.tpe) - // make fully defined to avoid bounded wildcard types that may be in pt from calling dropExistential (SI-2038) - val ptDefined = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) + val ptDefined = ensureFullyDefined(pt) val ownType = inferTypedPattern(tptTyped, tptTyped.tpe, ptDefined, canRemedy = uncheckedTypeExtractor.nonEmpty) treeTyped setType ownType @@ -5407,7 +5070,7 @@ trait Typers extends Modes with Adaptations with Tags { //val undets = context.undetparams // @M: fun is typed in TAPPmode because it is being applied to its actual type parameters - val fun1 = typed(fun, forFunMode(mode) | TAPPmode, WildcardType) + val fun1 = typed(fun, mode.forFunMode | TAPPmode, WildcardType) val tparams = fun1.symbol.typeParams //@M TODO: val undets_fun = context.undetparams ? @@ -5452,11 +5115,38 @@ trait Typers extends Modes with Adaptations with Tags { treeCopy.ReferenceToBoxed(tree, id1) setType tpe } + // Warn about likely interpolated strings which are missing their interpolators + def warnMissingInterpolator(tree: Literal) { + // Unfortunately implicit not found strings looks for all the world like + // missing interpolators. + def isArgToImplicitNotFound = context.enclosingApply.tree match { + case Apply(fn, _) => fn.symbol.enclClass == ImplicitNotFoundClass + case _ => false + } + tree.value match { + case Constant(s: String) => + def names = InterpolatorIdentRegex findAllIn s map (n => newTermName(n stripPrefix "$")) + def suspicious = ( + (InterpolatorCodeRegex findFirstIn s).nonEmpty + || (names exists (n => context.lookupSymbol(n, _ => true).symbol.exists)) + ) + val noWarn = ( + isArgToImplicitNotFound + || !(s contains ' ') // another heuristic - e.g. a string with only "$asInstanceOf" + ) + if (!noWarn && suspicious) + unit.warning(tree.pos, "looks like an interpolated String; did you forget the interpolator?") + case _ => + } + } + def typedLiteral(tree: Literal) = { - val value = tree.value + if (settings.lint) + warnMissingInterpolator(tree) + tree setType ( - if (value.tag == UnitTag) UnitClass.tpe - else ConstantType(value)) + if (tree.value.tag == UnitTag) UnitClass.tpe + else ConstantType(tree.value)) } def typedSingletonTypeTree(tree: SingletonTypeTree) = { @@ -5475,8 +5165,8 @@ trait Typers extends Modes with Adaptations with Tags { } def typedTypeBoundsTree(tree: TypeBoundsTree) = { - val lo1 = typedType(tree.lo, mode) - val hi1 = typedType(tree.hi, mode) + val lo1 = if (tree.lo.isEmpty) TypeTree(NothingTpe) else typedType(tree.lo, mode) + val hi1 = if (tree.hi.isEmpty) TypeTree(AnyTpe) else typedType(tree.hi, mode) treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe) } @@ -5539,7 +5229,7 @@ trait Typers extends Modes with Adaptations with Tags { case tree: TypeDef => typedTypeDef(tree) case tree: LabelDef => labelTyper(tree).typedLabelDef(tree) case tree: PackageDef => typedPackageDef(tree) - case tree: DocDef => typedDocDef(tree) + case tree: DocDef => typedDocDef(tree, mode, pt) case tree: Annotated => typedAnnotated(tree) case tree: SingletonTypeTree => typedSingletonTypeTree(tree) case tree: SelectFromTypeTree => typedSelectFromTypeTree(tree) @@ -5555,18 +5245,11 @@ trait Typers extends Modes with Adaptations with Tags { case tree: ApplyDynamic => typedApplyDynamic(tree) case tree: ReferenceToBoxed => typedReferenceToBoxed(tree) case tree: TypeTreeWithDeferredRefCheck => tree // TODO: retype the wrapped tree? TTWDRC would have to change to hold the wrapped tree (not a closure) - case tree: Import => assert(forInteractive, "!forInteractive") ; tree setType tree.symbol.tpe // should not happen in normal circumstances. case _ => abort(s"unexpected tree: ${tree.getClass}\n$tree") } } - /** - * @param tree ... - * @param mode ... - * @param pt ... - * @return ... - */ - def typed(tree: Tree, mode: Int, pt: Type): Tree = { + def typed(tree: Tree, mode: Mode, pt: Type): Tree = { lastTreeToTyper = tree indentTyping() @@ -5577,18 +5260,18 @@ trait Typers extends Modes with Adaptations with Tags { try { if (context.retyping && (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))) { - tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol + tree.clearType() + if (tree.hasSymbolField) tree.symbol = NoSymbol } val alreadyTyped = tree.tpe ne null - var tree1: Tree = if (alreadyTyped) tree else { + val tree1: Tree = if (alreadyTyped) tree else { printTyping( ptLine("typing %s: pt = %s".format(ptTree(tree), ptPlugins), "undetparams" -> context.undetparams, "implicitsEnabled" -> context.implicitsEnabled, "enrichmentEnabled" -> context.enrichmentEnabled, - "mode" -> modeString(mode), + "mode" -> mode, "silent" -> context.bufferErrors, "context.owner" -> context.owner ) @@ -5607,8 +5290,13 @@ trait Typers extends Modes with Adaptations with Tags { ) } - tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, ptPlugins) - val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, ptPlugins, tree) + tree1 modifyType (pluginsTyped(_, this, tree1, mode, ptPlugins)) + val result = + if (tree1.isEmpty) tree1 + else { + val result = adapt(tree1, mode, ptPlugins, tree) + if (hasPendingMacroExpansions) macroExpandAll(this, result) else result + } if (!alreadyTyped) { printTyping("adapted %s: %s to %s, %s".format( @@ -5619,14 +5307,14 @@ trait Typers extends Modes with Adaptations with Tags { result } catch { case ex: TypeError => - tree.tpe = null + tree.clearType() // 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(context, tree.pos, ex) setError(tree) case ex: Exception => - if (settings.debug.value) // @M causes cyclic reference error + if (settings.debug) // @M causes cyclic reference error Console.println("exception when typing "+tree+", pt = "+ptPlugins) if (context != null && context.unit.exists && tree != null) logError("AT: " + (tree.pos).dbgString, ex) @@ -5639,46 +5327,39 @@ trait Typers extends Modes with Adaptations with Tags { } def atOwner(owner: Symbol): Typer = - newTyper(context.make(context.tree, owner)) + newTyper(context.make(owner = owner)) def atOwner(tree: Tree, owner: Symbol): Typer = newTyper(context.make(tree, owner)) - /** Types expression or definition <code>tree</code>. - * - * @param tree ... - * @return ... + /** Types expression or definition `tree`. */ def typed(tree: Tree): Tree = { val ret = typed(tree, EXPRmode, WildcardType) ret } - def typedPos(pos: Position, mode: Int, pt: Type)(tree: Tree) = typed(atPos(pos)(tree), mode, pt) + def typedPos(pos: Position, mode: Mode, pt: Type)(tree: Tree) = typed(atPos(pos)(tree), mode, pt) def typedPos(pos: Position)(tree: Tree) = typed(atPos(pos)(tree)) // TODO: see if this formulation would impose any penalty, since // it makes for a lot less casting. // def typedPos[T <: Tree](pos: Position)(tree: T): T = typed(atPos(pos)(tree)).asInstanceOf[T] - /** Types expression <code>tree</code> with given prototype <code>pt</code>. - * - * @param tree ... - * @param pt ... - * @return ... + /** Types expression `tree` with given prototype `pt`. */ def typed(tree: Tree, pt: Type): Tree = typed(tree, EXPRmode, pt) - /** Types qualifier <code>tree</code> of a select node. - * E.g. is tree occurs in a context like <code>tree.m</code>. + /** Types qualifier `tree` of a select node. + * E.g. is tree occurs in a context like `tree.m`. */ - def typedQualifier(tree: Tree, mode: Int, pt: Type): Tree = + def typedQualifier(tree: Tree, mode: Mode, pt: Type): Tree = typed(tree, EXPRmode | QUALmode | POLYmode | mode & TYPEPATmode, pt) // TR: don't set BYVALmode, since qualifier might end up as by-name param to an implicit - /** Types qualifier <code>tree</code> of a select node. - * E.g. is tree occurs in a context like <code>tree.m</code>. + /** Types qualifier `tree` of a select node. + * E.g. is tree occurs in a context like `tree.m`. */ - def typedQualifier(tree: Tree, mode: Int): Tree = + def typedQualifier(tree: Tree, mode: Mode): Tree = typedQualifier(tree, mode, WildcardType) def typedQualifier(tree: Tree): Tree = typedQualifier(tree, NOmode, WildcardType) @@ -5687,7 +5368,7 @@ trait Typers extends Modes with Adaptations with Tags { def typedOperator(tree: Tree): Tree = typed(tree, EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType) - /** Types a pattern with prototype <code>pt</code> */ + /** Types a pattern with prototype `pt` */ def typedPattern(tree: Tree, pt: Type): Tree = { // We disable implicits because otherwise some constructs will // type check which should not. The pattern matcher does not @@ -5714,25 +5395,23 @@ trait Typers extends Modes with Adaptations with Tags { } /** Types a (fully parameterized) type tree */ - def typedType(tree: Tree, mode: Int): Tree = - typed(tree, forTypeMode(mode), WildcardType) + def typedType(tree: Tree, mode: Mode): Tree = + typed(tree, mode.forTypeMode, WildcardType) /** Types a (fully parameterized) type tree */ def typedType(tree: Tree): Tree = typedType(tree, NOmode) /** Types a higher-kinded type tree -- pt denotes the expected kind*/ - def typedHigherKindedType(tree: Tree, mode: Int, pt: Type): Tree = + def typedHigherKindedType(tree: Tree, mode: Mode, pt: Type): Tree = if (pt.typeParams.isEmpty) typedType(tree, mode) // kind is known and it's * else typed(tree, HKmode, pt) - def typedHigherKindedType(tree: Tree, mode: Int): Tree = + def typedHigherKindedType(tree: Tree, mode: Mode): Tree = typed(tree, HKmode, WildcardType) - def typedHigherKindedType(tree: Tree): Tree = typedHigherKindedType(tree, NOmode) - /** Types a type constructor tree used in a new or supertype */ - def typedTypeConstructor(tree: Tree, mode: Int): Tree = { - val result = typed(tree, forTypeMode(mode) | FUNmode, WildcardType) + def typedTypeConstructor(tree: Tree, mode: Mode): Tree = { + val result = typed(tree, mode.forTypeMode | FUNmode, WildcardType) // get rid of type aliases for the following check (#1241) result.tpe.dealias match { @@ -5753,7 +5432,7 @@ trait Typers extends Modes with Adaptations with Tags { def computeType(tree: Tree, pt: Type): Type = { // macros employ different logic of `computeType` - assert(!context.owner.isTermMacro, context.owner) + assert(!context.owner.isMacro, context.owner) val tree1 = typed(tree, pt) transformed(tree) = tree1 val tpe = packedType(tree1, context.owner) @@ -5762,8 +5441,8 @@ trait Typers extends Modes with Adaptations with Tags { } def computeMacroDefType(tree: Tree, pt: Type): Type = { - assert(context.owner.isTermMacro, context.owner) - assert(tree.symbol.isTermMacro, tree.symbol) + assert(context.owner.isMacro, context.owner) + assert(tree.symbol.isMacro, tree.symbol) assert(tree.isInstanceOf[DefDef], tree.getClass) val ddef = tree.asInstanceOf[DefDef] @@ -5789,32 +5468,21 @@ trait Typers extends Modes with Adaptations with Tags { case None => op } - def transformedOrTyped(tree: Tree, mode: Int, pt: Type): Tree = transformed.get(tree) match { + def transformedOrTyped(tree: Tree, mode: Mode, pt: Type): Tree = transformed.get(tree) match { case Some(tree1) => transformed -= tree; tree1 case None => typed(tree, mode, pt) } - -/* - def convertToTypeTree(tree: Tree): Tree = tree match { - case TypeTree() => tree - case _ => TypeTree(tree.tpe) } -*/ } -} object TypersStats { import scala.reflect.internal.TypesStats._ - import scala.reflect.internal.BaseTypeSeqsStats._ val typedIdentCount = Statistics.newCounter("#typechecked identifiers") val typedSelectCount = Statistics.newCounter("#typechecked selections") val typedApplyCount = Statistics.newCounter("#typechecked applications") val rawTypeFailed = Statistics.newSubCounter (" of which in failed", rawTypeCount) val subtypeFailed = Statistics.newSubCounter(" of which in failed", subtypeCount) val findMemberFailed = Statistics.newSubCounter(" of which in failed", findMemberCount) - val compoundBaseTypeSeqCount = Statistics.newSubCounter(" of which for compound types", baseTypeSeqCount) - val typerefBaseTypeSeqCount = Statistics.newSubCounter(" of which for typerefs", baseTypeSeqCount) - val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) val failedSilentNanos = Statistics.newSubTimer("time spent in failed", typerNanos) val failedApplyNanos = Statistics.newSubTimer(" failed apply", typerNanos) val failedOpEqNanos = Statistics.newSubTimer(" failed op=", typerNanos) diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 31c5a61a8c..af3f772f79 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -23,7 +23,6 @@ trait Unapplies extends ast.TreeDSL private val unapplyParamName = nme.x_0 - // In the typeCompleter (templateSig) of a case class (resp it's module), // synthetic `copy` (reps `apply`, `unapply`) methods are added. To compute // their signatures, the corresponding ClassDef is needed. During naming (in @@ -47,17 +46,6 @@ trait Unapplies extends ast.TreeDSL } } - /** returns type of the unapply method returning T_0...T_n - * for n == 0, boolean - * for n == 1, Some[T0] - * else Some[Product[Ti]] - */ - def unapplyReturnTypeExpected(argsLength: Int) = argsLength match { - case 0 => BooleanClass.tpe - case 1 => optionType(WildcardType) - case n => optionType(productType((List fill n)(WildcardType))) - } - /** returns unapply or unapplySeq if available */ def unapplyMember(tp: Type): Symbol = (tp member nme.unapply) match { case NoSymbol => tp member nme.unapplySeq @@ -128,11 +116,16 @@ trait Unapplies extends ast.TreeDSL /** The module corresponding to a case class; overrides toString to show the module's name */ def caseModuleDef(cdef: ClassDef): ModuleDef = { - // > MaxFunctionArity is caught in Namers, but for nice error reporting instead of - // an abrupt crash we trim the list here. - def primaries = constrParamss(cdef).head take MaxFunctionArity map (_.tpt) - def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && constrParamss(cdef).length == 1 - def createFun = gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) + val params = constrParamss(cdef) + def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && (params match { + case List(ps) if ps.length <= MaxFunctionArity => true + case _ => false + }) + def createFun = { + def primaries = params.head map (_.tpt) + gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) + } + def parents = if (inheritFromFun) List(createFun) else Nil def toString = DefDef( Modifiers(OVERRIDE | FINAL | SYNTHETIC), @@ -149,7 +142,7 @@ trait Unapplies extends ast.TreeDSL ModuleDef( Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin), cdef.name.toTermName, - Template(parents, emptyValDef, NoMods, Nil, ListOfNil, body, cdef.impl.pos.focus)) + Template(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus)) } private val caseMods = Modifiers(SYNTHETIC | CASE) diff --git a/src/compiler/scala/tools/nsc/typechecker/Variances.scala b/src/compiler/scala/tools/nsc/typechecker/Variances.scala deleted file mode 100644 index ea436a71fb..0000000000 --- a/src/compiler/scala/tools/nsc/typechecker/Variances.scala +++ /dev/null @@ -1,94 +0,0 @@ -/* NSC -- new scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package typechecker - -import symtab.Flags.{ VarianceFlags => VARIANCES, _ } - -/** Variances form a lattice, 0 <= COVARIANT <= Variances, 0 <= CONTRAVARIANT <= VARIANCES - */ -trait Variances { - - val global: Global - import global._ - - /** Flip between covariant and contravariant */ - private def flip(v: Int): Int = { - if (v == COVARIANT) CONTRAVARIANT - else if (v == CONTRAVARIANT) COVARIANT - else v - } - - /** Map everything below VARIANCES to 0 */ - private def cut(v: Int): Int = - if (v == VARIANCES) v else 0 - - /** Compute variance of type parameter `tparam` in types of all symbols `sym`. */ - def varianceInSyms(syms: List[Symbol])(tparam: Symbol): Int = - (VARIANCES /: syms) ((v, sym) => v & varianceInSym(sym)(tparam)) - - /** Compute variance of type parameter `tparam` in type of symbol `sym`. */ - def varianceInSym(sym: Symbol)(tparam: Symbol): Int = - if (sym.isAliasType) cut(varianceInType(sym.info)(tparam)) - else varianceInType(sym.info)(tparam) - - /** Compute variance of type parameter `tparam` in all types `tps`. */ - def varianceInTypes(tps: List[Type])(tparam: Symbol): Int = - (VARIANCES /: tps) ((v, tp) => v & varianceInType(tp)(tparam)) - - /** Compute variance of type parameter `tparam` in all type arguments - * <code>tps</code> which correspond to formal type parameters `tparams1`. - */ - def varianceInArgs(tps: List[Type], tparams1: List[Symbol])(tparam: Symbol): Int = { - var v: Int = VARIANCES; - for ((tp, tparam1) <- tps zip tparams1) { - val v1 = varianceInType(tp)(tparam) - v = v & (if (tparam1.isCovariant) v1 - else if (tparam1.isContravariant) flip(v1) - else cut(v1)) - } - v - } - - /** Compute variance of type parameter `tparam` in all type annotations `annots`. */ - def varianceInAttribs(annots: List[AnnotationInfo])(tparam: Symbol): Int = { - (VARIANCES /: annots) ((v, annot) => v & varianceInAttrib(annot)(tparam)) - } - - /** Compute variance of type parameter `tparam` in type annotation `annot`. */ - def varianceInAttrib(annot: AnnotationInfo)(tparam: Symbol): Int = { - varianceInType(annot.atp)(tparam) - } - - /** Compute variance of type parameter <code>tparam</code> in type <code>tp</code>. */ - def varianceInType(tp: Type)(tparam: Symbol): Int = tp match { - case ErrorType | WildcardType | NoType | NoPrefix | ThisType(_) | ConstantType(_) => - VARIANCES - case BoundedWildcardType(bounds) => - varianceInType(bounds)(tparam) - case SingleType(pre, sym) => - varianceInType(pre)(tparam) - case TypeRef(pre, sym, args) => - if (sym == tparam) COVARIANT - // tparam cannot occur in tp's args if tp is a type constructor (those don't have args) - else if (tp.isHigherKinded) varianceInType(pre)(tparam) - else varianceInType(pre)(tparam) & varianceInArgs(args, sym.typeParams)(tparam) - case TypeBounds(lo, hi) => - flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) - case RefinedType(parents, defs) => - varianceInTypes(parents)(tparam) & varianceInSyms(defs.toList)(tparam) - case MethodType(params, restpe) => - flip(varianceInSyms(params)(tparam)) & varianceInType(restpe)(tparam) - case NullaryMethodType(restpe) => - varianceInType(restpe)(tparam) - case PolyType(tparams, restpe) => - flip(varianceInSyms(tparams)(tparam)) & varianceInType(restpe)(tparam) - case ExistentialType(tparams, restpe) => - varianceInSyms(tparams)(tparam) & varianceInType(restpe)(tparam) - case AnnotatedType(annots, tp, _) => - varianceInAttribs(annots)(tparam) & varianceInType(tp)(tparam) - } -} |