diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
30 files changed, 6094 insertions, 5894 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..5c02516c47 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 @@ -30,8 +29,9 @@ trait Analyzer extends AnyRef val global : Global import global._ - object namerFactory extends SubComponent { + object namerFactory extends { val global: Analyzer.this.global.type = Analyzer.this.global + } with SubComponent { val phaseName = "namer" val runsAfter = List[String]("parser") val runsRightAfter = None @@ -45,8 +45,9 @@ trait Analyzer extends AnyRef } } - object packageObjects extends SubComponent { + object packageObjects extends { val global: Analyzer.this.global.type = Analyzer.this.global + } with SubComponent { val phaseName = "packageobjects" val runsAfter = List[String]() val runsRightAfter= Some("namer") @@ -72,9 +73,10 @@ trait Analyzer extends AnyRef } } - object typerFactory extends SubComponent { - import scala.reflect.internal.TypesStats.typerNanos + object typerFactory extends { val global: Analyzer.this.global.type = Analyzer.this.global + } with SubComponent { + import scala.reflect.internal.TypesStats.typerNanos val phaseName = "typer" val runsAfter = List[String]() val runsRightAfter = Some("packageobjects") @@ -88,22 +90,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..54e4fefc15 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,11 +142,11 @@ 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 - * NothingClass.tpe. + * NothingTpe. * * @param tpe The type of the return expression * @param typer The typer that was used for typing the return tree @@ -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..0eae17612d 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 RefinedType(parents, _) => parents forall isCheckable + case p => new CheckabilityChecker(AnyTpe, 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. @@ -254,9 +273,13 @@ trait Checkable { // Matching on types like case _: AnyRef { def bippy: Int } => doesn't work -- yet. case RefinedType(_, decls) if !decls.isEmpty => getContext.unit.warning(tree.pos, s"a pattern match on a refinement type is unchecked") + case RefinedType(parents, _) => + parents foreach (p => checkCheckable(tree, p, X, inPattern, canRemedy)) case _ => val checker = new CheckabilityChecker(X, P) - log(checker.summaryString) + if (checker.result == RuntimeCheckable) + log(checker.summaryString) + if (checker.neverMatches) { val addendum = if (checker.neverSubClass) "" else " (but still might match its erasure)" getContext.unit.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $P$addendum") diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala index 89e2ee44be..56ed0ee16c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala @@ -3,10 +3,10 @@ * @author Martin Odersky */ -package scala.tools.nsc +package scala +package tools.nsc package typechecker - import java.lang.ArithmeticException /** This class ... @@ -18,7 +18,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 +28,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 5d6d094b44..1f4d5cbac2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -6,55 +6,54 @@ 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 - val Normal, Access, Ambiguous, Divergent = Value - } - - import ErrorKinds.ErrorKind - - trait AbsTypeError extends Throwable { + sealed abstract class AbsTypeError extends Throwable { def errPos: Position def errMsg: String - def kind: ErrorKind + override def toString() = "[Type error at:" + errPos + "] " + errMsg } - case class NormalTypeError(underlyingTree: Tree, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) - extends AbsTypeError { - - def errPos:Position = underlyingTree.pos - override def toString() = "[Type error at:" + underlyingTree.pos + "] " + errMsg + sealed abstract class TreeTypeError extends AbsTypeError { + def underlyingTree: Tree + def errPos = underlyingTree.pos } - case class SymbolTypeError(underlyingSym: Symbol, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) + case class NormalTypeError(underlyingTree: Tree, errMsg: String) + extends TreeTypeError + + case class AccessTypeError(underlyingTree: Tree, errMsg: String) + extends TreeTypeError + + case class AmbiguousTypeError(errPos: Position, errMsg: String) + extends AbsTypeError + + case class SymbolTypeError(underlyingSym: Symbol, errMsg: String) extends AbsTypeError { def errPos = underlyingSym.pos } - case class TypeErrorWrapper(ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + case class TypeErrorWrapper(ex: TypeError) extends AbsTypeError { def errMsg = ex.msg def errPos = ex.pos } - case class TypeErrorWithUnderlyingTree(tree: Tree, ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + case class TypeErrorWithUnderlyingTree(tree: Tree, ex: TypeError) extends AbsTypeError { def errMsg = ex.msg def errPos = tree.pos @@ -68,19 +67,19 @@ trait ContextErrors { // (pt at the point of divergence gives less information to the user) // Note: it is safe to delay error message generation in this case // becasue we don't modify implicits' infos. - // only issued when -Xdivergence211 is turned on - case class DivergentImplicitTypeError(tree: Tree, pt0: Type, sym: Symbol) extends AbsTypeError { - def errPos: Position = tree.pos + case class DivergentImplicitTypeError(underlyingTree: Tree, pt0: Type, sym: Symbol) + extends TreeTypeError { def errMsg: String = errMsgForPt(pt0) - def kind = ErrorKinds.Divergent - def withPt(pt: Type): AbsTypeError = NormalTypeError(tree, errMsgForPt(pt), kind) + def withPt(pt: Type): AbsTypeError = this.copy(pt0 = pt) private def errMsgForPt(pt: Type) = s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}" } - case class AmbiguousTypeError(underlyingTree: Tree, errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Ambiguous) extends AbsTypeError + case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String) + extends TreeTypeError - case class PosAndMsgTypeError(errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) extends AbsTypeError + case class PosAndMsgTypeError(errPos: Position, errMsg: String) + extends AbsTypeError object ErrorUtils { def issueNormalTypeError(tree: Tree, msg: String)(implicit context: Context) { @@ -91,22 +90,13 @@ trait ContextErrors { issueTypeError(SymbolTypeError(sym, msg)) } - // only called when -Xdivergence211 is turned off - def issueDivergentImplicitsError(tree: Tree, msg: String)(implicit context: Context) { - issueTypeError(NormalTypeError(tree, msg, ErrorKinds.Divergent)) - } - def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) { context.issueAmbiguousError(pre, sym1, sym2, err) } def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) } - def typeErrorMsg(found: Type, req: Type, possiblyMissingArgs: Boolean) = { - def missingArgsMsg = if (possiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else "" - - "type mismatch" + foundReqMsg(found, req) + missingArgsMsg - } + def typeErrorMsg(found: Type, req: Type) = "type mismatch" + foundReqMsg(found, req) } def notAnyRefMessage(found: Type): String = { @@ -147,7 +137,7 @@ trait ContextErrors { } issueNormalTypeError(tree, "stable identifier required, but "+tree+" found." + ( - if (isStableExceptVolatile(tree)) addendum else "")) + if (treeInfo.hasVolatileType(tree)) addendum else "")) setError(tree) } @@ -172,11 +162,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 @@ -190,11 +179,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.explainTypes(foundType, req) } def WithFilterError(tree: Tree, ex: AbsTypeError) = { @@ -203,14 +191,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") @@ -318,7 +310,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) { @@ -468,7 +460,7 @@ trait ContextErrors { def AbstractionFromVolatileTypeError(vd: ValDef) = issueNormalTypeError(vd, "illegal abstraction from value with volatile type "+vd.symbol.tpe) - private[ContextErrors] def TypedApplyWrongNumberOfTpeParametersErrorMessage(fun: Tree) = + private[scala] def TypedApplyWrongNumberOfTpeParametersErrorMessage(fun: Tree) = "wrong number of type parameters for "+treeSymTypeMsg(fun) def TypedApplyWrongNumberOfTpeParametersError(tree: Tree, fun: Tree) = { @@ -484,7 +476,7 @@ trait ContextErrors { // doTypeApply //tryNamesDefaults def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) = - NormalTypeError(tree, "macros application do not support named and/or default arguments") + NormalTypeError(tree, "macro applications do not support named and/or default arguments") def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) = NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun)) @@ -525,6 +517,9 @@ trait ContextErrors { def TooManyArgsPatternError(fun: Tree) = NormalTypeError(fun, "too many arguments for unapply pattern, maximum = "+definitions.MaxTupleArity) + def WrongShapeExtractorExpansion(fun: Tree) = + NormalTypeError(fun, "extractor macros can only expand into extractor calls") + def WrongNumberOfArgsError(tree: Tree, fun: Tree) = NormalTypeError(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun)) @@ -532,7 +527,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) = { @@ -578,11 +573,13 @@ trait ContextErrors { //adapt def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { - issueNormalTypeError(tree, - "missing arguments for " + meth.fullLocationString + ( + val message = + if (meth.isMacro) MacroTooFewArgumentListsMessage + 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) } @@ -599,7 +596,12 @@ trait ContextErrors { } def CaseClassConstructorError(tree: Tree) = { - issueNormalTypeError(tree, tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method") + val baseMessage = tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method" + val addendum = directUnapplyMember(tree.symbol.info) match { + case sym if hasMultipleNonImplicitParamLists(sym) => s"\nNote: ${sym.defString} exists in ${tree.symbol}, but it cannot be used as an extractor due to its second non-implicit parameter list" + case _ => "" + } + issueNormalTypeError(tree, baseMessage + addendum) setError(tree) } @@ -663,7 +665,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 @@ -680,8 +682,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) @@ -690,26 +692,42 @@ trait ContextErrors { setError(tree) } - // same reason as for MacroBodyTypecheckException + 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") + } + 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(errorPos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions + if (msg != null) context.error(if (pos.isDefined) pos else expandee.pos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions setError(expandee) throw MacroExpansionException } - def MacroPartialApplicationError(expandee: Tree) = { + private def macroExpansionError2(expandee: Tree, msg: String) = { // 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, msg) setError(expandee) throw MacroExpansionException } + private def MacroTooFewArgumentListsMessage = "too few argument lists for macro invocation" + def MacroTooFewArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooFewArgumentListsMessage) + + private def MacroTooManyArgumentListsMessage = "too many argument lists for macro invocation" + def MacroTooManyArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooManyArgumentListsMessage) + + def MacroTooFewArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too few arguments for macro invocation") + + def MacroTooManyArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too many arguments for macro invocation") + def MacroGeneratedAbort(expandee: Tree, ex: AbortMacroException) = { // errors have been reported by the macro itself, so we do nothing here macroLogVerbose("macro expansion has been aborted") @@ -731,7 +749,7 @@ trait ContextErrors { try { // [Eugene] is there a better way? // [Paul] See Exceptional.scala and Origins.scala. - val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpand1") + val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpandWithRuntime") if (relevancyThreshold == -1) None else { var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1) @@ -771,23 +789,29 @@ 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) = { + def isUnaffiliatedExpr = expanded.isInstanceOf[scala.reflect.api.Exprs#Expr[_]] + def isUnaffiliatedTree = expanded.isInstanceOf[scala.reflect.api.Trees#TreeApi] + val expected = "expr or tree" + val actual = if (isUnaffiliatedExpr) "an expr" else if (isUnaffiliatedTree) "a tree" else "unexpected" + val isPathMismatch = expanded != null && (isUnaffiliatedExpr || isUnaffiliatedTree) 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 " of " + expanded.getClass + else if (isPathMismatch) s"$actual, but it doesn't belong to this compiler's universe" + 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 { @@ -829,14 +853,17 @@ 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 underlyingSymbol(sym).fullLocationString + " cannot be accessed in " + location + explanation } - NormalTypeError(tree, errMsg, ErrorKinds.Access) + AccessTypeError(tree, errMsg) } def NoMethodInstanceError(fn: Tree, args: List[Tree], msg: String) = @@ -881,7 +908,7 @@ trait ContextErrors { "argument types " + argtpes.mkString("(", ",", ")") + (if (pt == WildcardType) "" else " and expected result type " + pt) val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) setErrorOnLastTry(lastTry, tree) } else setError(tree) // do not even try further attempts because they should all fail // even if this is not the last attempt (because of the SO's possibility on the horizon) @@ -889,13 +916,13 @@ trait ContextErrors { } def NoBestExprAlternativeError(tree: Tree, pt: Type, lastTry: Boolean) = { - issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(tree.symbol.tpe, pt, isPossiblyMissingArgs(tree.symbol.tpe, pt)))) + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(tree.symbol.tpe, pt))) setErrorOnLastTry(lastTry, tree) } def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = { val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) setErrorOnLastTry(lastTry, tree) } @@ -909,7 +936,7 @@ trait ContextErrors { kindErrors.toList.mkString("\n", ", ", "")) } - private[ContextErrors] def NotWithinBoundsErrorMessage(prefix: String, targs: List[Type], tparams: List[Symbol], explaintypes: Boolean) = { + private[scala] def NotWithinBoundsErrorMessage(prefix: String, targs: List[Type], tparams: List[Symbol], explaintypes: Boolean) = { if (explaintypes) { val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) (targs, bounds).zipped foreach ((targ, bound) => explainTypes(bound.lo, targ)) @@ -925,7 +952,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) = @@ -1033,20 +1060,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") @@ -1097,11 +1118,11 @@ trait ContextErrors { def AbstractMemberWithModiferError(sym: Symbol, flag: Int) = - issueSymbolTypeError(sym, "abstract member may not have " + Flags.flagsToString(flag) + " modifier") + issueSymbolTypeError(sym, "abstract member may not have " + Flags.flagsToString(flag.toLong) + " modifier") def IllegalModifierCombination(sym: Symbol, flag1: Int, flag2: Int) = issueSymbolTypeError(sym, "illegal combination of modifiers: %s and %s for: %s".format( - Flags.flagsToString(flag1), Flags.flagsToString(flag2), sym)) + Flags.flagsToString(flag1.toLong), Flags.flagsToString(flag2.toLong), sym)) def IllegalDependentMethTpeError(sym: Symbol)(context: Context) = { val errorAddendum = @@ -1143,7 +1164,7 @@ trait ContextErrors { // failures which have nothing to do with implicit conversions // per se, but which manifest as implicit conversion conflicts // involving Any, are further explained from foundReqMsg. - if (AnyRefClass.tpe <:< req) ( + if (AnyRefTpe <:< req) ( if (sym == AnyClass || sym == UnitClass) ( sm"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""" @@ -1159,11 +1180,11 @@ trait ContextErrors { sm"""|Note that implicit conversions are not applicable because they are ambiguous: |${coreMsg}are possible conversion functions from $found to $req""" } - typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + ( + typeErrorMsg(found, req) + ( if (explanation == "") "" else "\n" + explanation ) } - context.issueAmbiguousError(AmbiguousTypeError(tree, tree.pos, + context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, if (isView) viewMsg else s"ambiguous implicit values:\n${coreMsg}match expected type $pt") ) @@ -1171,13 +1192,7 @@ trait ContextErrors { } def DivergingImplicitExpansionError(tree: Tree, pt: Type, sym: Symbol)(implicit context0: Context) = - if (settings.Xdivergence211.value) { - issueTypeError(DivergentImplicitTypeError(tree, pt, sym)) - } else { - issueDivergentImplicitsError(tree, - "diverging implicit expansion for type "+pt+"\nstarting with "+ - sym.fullLocationString) - } + issueTypeError(DivergentImplicitTypeError(tree, pt, sym)) } object NamesDefaultsErrorsGen { @@ -1228,141 +1243,4 @@ trait ContextErrors { setError(arg) } } - - // using an exception here is actually a good idea - // because the lifespan of this exception is extremely small and controlled - // moreover exceptions let us avoid an avalanche of "if (!hasError) do stuff" checks - case object MacroBodyTypecheckException extends Exception with scala.util.control.ControlThrowable - - trait MacroErrors { - self: MacroTyper => - - private implicit val context0 = typer.context - val context = typer.context - - // helpers - - private def lengthMsg(flavor: String, violation: String, extra: Symbol) = { - val noun = if (flavor == "value") "parameter" else "type parameter" - val message = noun + " lists have different length, " + violation + " extra " + noun - val suffix = if (extra ne NoSymbol) " " + extra.defString else "" - message + suffix - } - - private def abbreviateCoreAliases(s: String): String = List("WeakTypeTag", "Expr").foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x)) - - private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = { - var argsPart = (pss map (ps => ps map (_.defString) mkString ("(", ", ", ")"))).mkString - if (abbreviate) argsPart = abbreviateCoreAliases(argsPart) - var retPart = restpe.toString - if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart) - argsPart + ": " + retPart - } - - // 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") - withTypesExplained(rtpe <:< atpe) - } else rtpe <:< atpe - if (!ok) { - compatibilityError("type mismatch for %s: %s does not conform to %s".format(slot, abbreviateCoreAliases(rtpe.toString), abbreviateCoreAliases(atpe.toString))) - } - } - - // errors - - private def fail() = { - // need to set the IS_ERROR flag to prohibit spurious expansions - if (macroDef != null) macroDef setFlag IS_ERROR - // not setting ErrorSymbol as in `infer.setError`, because we still need to know that it's a macro - // otherwise assignTypeToTree in Namers might fail if macroDdef.tpt == EmptyTree - macroDdef setType ErrorType - throw MacroBodyTypecheckException - } - - private def genericError(tree: Tree, message: String) = { - issueNormalTypeError(tree, message) - fail() - } - - private def implRefError(message: String) = { - val treeInfo.Applied(implRef, _, _) = macroDdef.rhs - genericError(implRef, message) - } - - private def compatibilityError(message: String) = - implRefError( - "macro implementation has wrong shape:"+ - "\n required: " + showMeth(rparamss, rret, abbreviate = true) + - "\n found : " + showMeth(aparamss, aret, abbreviate = false) + - "\n" + message) - - // Phase I: sanity checks - - def MacroDefIsFastTrack() = { - macroLogVerbose("typecheck terminated unexpectedly: macro is fast track") - assert(!macroDdef.tpt.isEmpty, "fast track macros must provide result type") - throw MacroBodyTypecheckException // don't call fail, because we don't need IS_ERROR - } - - def MacroDefIsQmarkQmarkQmark() = { - macroLogVerbose("typecheck terminated unexpectedly: macro is ???") - throw MacroBodyTypecheckException - } - - def MacroFeatureNotEnabled() = { - macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled") - fail() - } - - // Phase II: typecheck the right-hand side of the macro def - - // do nothing, just fail. relevant typecheck errors have already been reported - def MacroDefUntypeableBodyError() = fail() - - def MacroDefInvalidBodyError() = genericError(macroDdef, "macro body has wrong shape:\n required: macro [<implementation object>].<method name>[[<type args>]]") - - def MacroImplNotPublicError() = implRefError("macro implementation must be public") - - def MacroImplOverloadedError() = implRefError("macro implementation cannot be overloaded") - - def MacroImplWrongNumberOfTypeArgumentsError(macroImplRef: Tree) = implRefError(typer.TyperErrorGen.TypedApplyWrongNumberOfTpeParametersErrorMessage(macroImplRef)) - - def MacroImplNotStaticError() = implRefError("macro implementation must be in statically accessible object") - - // Phase III: check compatibility between the macro def and its macro impl - // aXXX (e.g. aparams) => characteristics of the macro impl ("a" stands for "actual") - // rXXX (e.g. rparams) => characteristics of a reference macro impl signature synthesized from the macro def ("r" stands for "reference") - - def MacroImplNonTagImplicitParameters(params: List[Symbol]) = compatibilityError("macro implementations cannot have implicit parameters other than WeakTypeTag evidences") - - def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ") - - def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length))) - - def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length)))) - - def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkSubType("parameter " + rparam.name, rparam.tpe, atpe) - - def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkSubType("return type", atpe, rret) - - def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name) - - def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = { - if (isRepeated(rparam) && !isRepeated(aparam)) - compatibilityError("types incompatible for parameter " + rparam.name + ": corresponding is not a vararg parameter") - if (!isRepeated(rparam) && isRepeated(aparam)) - compatibilityError("types incompatible for parameter " + aparam.name + ": corresponding is not a vararg parameter") - } - - def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) = - compatibilityError(typer.infer.InferErrorGen.NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value)) - - def MacroImplTparamInstantiationError(atparams: List[Symbol], ex: NoInstance) = - compatibilityError( - "type parameters "+(atparams map (_.defString) mkString ", ")+" cannot be instantiated\n"+ - ex.getMessage) - } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 3fe98ed127..8d42bf94f3 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,35 @@ import scala.annotation.tailrec */ trait Contexts { self: Analyzer => import global._ + import definitions.{ JavaLangPackage, ScalaPackage, PredefModule, ScalaXmlTopScope, ScalaXmlPackage } + import ContextMode._ - object NoContext extends Context { - outer = this + object NoContext + extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit, + null) { // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer` 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 +52,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 +84,406 @@ 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))) + + // there must be a scala.xml package when xml literals were parsed in this unit + if (unit.hasXml && ScalaXmlPackage == NoSymbol) + unit.error(unit.firstXmlPos, "To compile XML syntax, the scala.xml package must be on the classpath.\nPlease see https://github.com/scala/scala/wiki/Scala-2.11#xml.") + + // scala-xml needs `scala.xml.TopScope` to be in scope globally as `$scope` + // We detect `scala-xml` by looking for `scala.xml.TopScope` and + // inject the equivalent of `import scala.xml.{TopScope => $scope}` + val contextWithXML = + if (!unit.hasXml || ScalaXmlTopScope == NoSymbol) rootImportsContext + else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope)) + + val c = contextWithXML.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, _outer: Context) { + private def outerIsNoContext = _outer eq null + final def outer: Context = if (outerIsNoContext) NoContext else _outer - 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 = { + /** The next outer context whose tree is a template or package definition */ + var enclClass: Context = _ + + @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[OpenImplicit] = List() // types for which implicit arguments - // are currently searched - // 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 + /** A bitmask containing all the boolean flags in a context, e.g. are implicit views enabled */ + var contextMode: ContextMode = ContextMode.DefaultMode - 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 + /** 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() - var savedTypeBounds: List[(Symbol, Type)] = List() // saved type bounds - // for type parameters which are narrowed in a GADT + protected def outerDepth = if (outerIsNoContext) 0 else outer.depth - var typingIndentLevel: Int = 0 - def typingIndent = " " * typingIndentLevel + val depth: Int = { + val increasesDepth = isRootImport || outerIsNoContext || (outer.scope != scope) + ( 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 - var buffer: Set[AbsTypeError] = _ - var warningsBuffer: Set[(Position, String)] = _ + /** Types for which implicit arguments are currently searched */ + var openImplicits: List[OpenImplicit] = List() + /* For a named application block (`Tree`) the corresponding `NamedApplyInfo`. */ + var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None + var prefix: Type = NoPrefix + + def inSuperInit_=(value: Boolean) = this(SuperInit) = value + def inSuperInit = this(SuperInit) + def inConstructorSuffix_=(value: Boolean) = this(ConstructorSuffix) = value + def inConstructorSuffix = this(ConstructorSuffix) + def inPatAlternative_=(value: Boolean) = this(PatternAlternative) = value + def inPatAlternative = this(PatternAlternative) + def starPatterns_=(value: Boolean) = this(StarPatterns) = value + def starPatterns = this(StarPatterns) + 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) + def inSecondTry = this(SecondTry) + def inSecondTry_=(value: Boolean) = this(SecondTry) = value + def inReturnExpr = this(ReturnExpr) + def inTypeConstructorAllowed = this(TypeConstructorAllowed) + + def defaultModeForTyped: Mode = if (inTypeConstructorAllowed) Mode.NOmode else Mode.EXPRmode + + /** 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() + + /** 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 + if (!owner.exists || 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]) + + def siteString = { + def what_s = if (owner.isConstructor) "" else owner.kindString + def where_s = if (owner.isClass) "" else "in " + enclClass.owner.decodedName + List(what_s, owner.decodedName, where_s) filterNot (_ == "") mkString " " + } + // + // 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 - } + @inline final def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op) + @inline final def withImplicitsDisabled[T](op: => T): T = withMode(disabled = ImplicitsEnabled | EnrichmentEnabled)(op) + @inline final def withImplicitsDisabledAllowEnrichment[T](op: => T): T = withMode(enabled = EnrichmentEnabled, disabled = ImplicitsEnabled)(op) + @inline final def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op) + @inline final def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op) + @inline final def withinStarPatterns[T](op: => T): T = withMode(enabled = StarPatterns)(op) + @inline final def withinSuperInit[T](op: => T): T = withMode(enabled = SuperInit)(op) + @inline final def withinSecondTry[T](op: => T): T = withMode(enabled = SecondTry)(op) + @inline final def withinPatAlternative[T](op: => T): T = withMode(enabled = PatternAlternative)(op) + + /** TypeConstructorAllowed is enabled 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. + */ + @inline final def withinTypeConstructorAllowed[T](op: => T): T = withMode(enabled = TypeConstructorAllowed)(op) + + /* TODO - consolidate returnsSeen (which seems only to be used by checkDead) + * and ReturnExpr. + */ + @inline final def withinReturnExpr[T](op: => T): T = { + enclMethod.returnsSeen = true + withMode(enabled = ReturnExpr)(op) } - def withImplicitsDisabledAllowEnrichment[T](op: => T): T = { - val saved = implicitsEnabled - implicitsEnabled = false - val savedP = enrichmentEnabled - enrichmentEnabled = true - try op - finally { - implicitsEnabled = saved - enrichmentEnabled = savedP + // See comment on FormerNonStickyModes. + @inline final def withOnlyStickyModes[T](op: => T): T = withMode(disabled = FormerNonStickyModes)(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 withMacrosEnabled[T](op: => T): T = { - val saved = macrosEnabled - macrosEnabled = true - try op - finally macrosEnabled = saved - } + // + // Child Context Creation + // - 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 + } + val isDefDef = tree match { + case _: DefDef => true + case _ => false } - tree match { - case DefDef(_, _, _, _, _, _) => - c.enclMethod = c - case _ => - c.enclMethod = this.enclMethod + val isImport = tree match { + case _: Import => 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 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.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 +498,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 +509,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 +520,10 @@ trait Contexts { self: Analyzer => argContext } + // + // Error and warning issuance + // + private def addDiagString(msg: String) = { val ds = if (diagnostic.isEmpty) "" @@ -390,19 +535,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 +559,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 +609,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,22 +648,15 @@ 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)) - def accessWithinLinked(ab: Symbol) = { - val linked = ab.linkedClassOfClass - // don't have access if there is no linked class - // (before adding the `ne NoSymbol` check, this was a no-op when linked eq NoSymbol, - // since `accessWithin(NoSymbol) == true` whatever the symbol) - (linked ne NoSymbol) && accessWithin(linked) - } + // don't have access if there is no linked class (so exclude linkedClass=NoSymbol) + def accessWithinLinked(ab: Symbol) = ab.linkedClassOfClass.fold(false)(accessWithin) - /** 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 +668,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 +713,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 +723,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 +809,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 +826,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 +844,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 +857,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.exists && ( + 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"overloaded symbol in $pre")(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,12 +1178,84 @@ trait Contexts { self: Analyzer => } } //class Context + /** A `Context` focussed on an `Import` tree */ + trait ImportContext extends Context { + private val impInfo: ImportInfo = { + val info = new ImportInfo(tree.asInstanceOf[Import], outerDepth) + if (settings.lint && !isRootImport) // excludes java.lang/scala/Predef imports + allImportInfos(unit) ::= info + info + } + override final def imports = impInfo :: super.imports + override final def firstImport = Some(impInfo) + override final def isRootImport = !tree.pos.isDefined + override final def toString = super.toString + " with " + 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(removeF: PartialFunction[AbsTypeError, Boolean]): this.type = { + errorBuffer.retain(!PartialFunction.cond(_)(removeF)) + this + } + def retainErrors(leaveF: PartialFunction[AbsTypeError, Boolean]): this.type = { + errorBuffer.retain(PartialFunction.cond(_)(leaveF)) + 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? */ @@ -745,25 +1264,53 @@ trait Contexts { self: Analyzer => /** 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 +1321,124 @@ 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 { + import scala.language.implicitConversions + 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 - iron out distinction/overlap with SecondTry. + */ + final val ReTyping: ContextMode = 1 << 10 + + /** Are we typechecking pattern alternatives. Formerly ALTmode. */ + final val PatternAlternative: ContextMode = 1 << 11 + + /** Are star patterns allowed. Formerly STARmode. */ + final val StarPatterns: ContextMode = 1 << 12 + + /** Are we typing the "super" in a superclass constructor call super.<init>. Formerly SUPERCONSTRmode. */ + final val SuperInit: ContextMode = 1 << 13 + + /* Is this the second attempt to type this tree? In that case functions + * may no longer be coerced with implicit views. Formerly SNDTRYmode. + */ + final val SecondTry: ContextMode = 1 << 14 + + /** Are we in return position? Formerly RETmode. */ + final val ReturnExpr: ContextMode = 1 << 15 + + /** Are unapplied type constructors allowed here? Formerly HKmode. */ + final val TypeConstructorAllowed: ContextMode = 1 << 16 + + /** TODO: The "sticky modes" are EXPRmode, PATTERNmode, TYPEmode. + * To mimick the sticky mode behavior, when captain stickyfingers + * comes around we need to propagate those modes but forget the other + * context modes which were once mode bits; those being so far the + * ones listed here. + */ + final val FormerNonStickyModes: ContextMode = ( + PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed + ) + + 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", + PatternAlternative -> "PatternAlternative", + StarPatterns -> "StarPatterns", + SuperInit -> "SuperInit", + SecondTry -> "SecondTry", + TypeConstructorAllowed -> "TypeConstructorAllowed" + ) +} + +/** + * 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 25a1228bf6..396f3407f3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -17,12 +17,7 @@ import scala.collection.{ mutable, immutable } */ abstract class Duplicators extends Analyzer { import global._ - import definitions.{ AnyRefClass, AnyValClass } - - def retyped(context: Context, tree: Tree): Tree = { - resetClassOwners - (newBodyDuplicator(context)).typed(tree) - } + import definitions._ /** 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 @@ -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) @@ -79,22 +74,19 @@ abstract class Duplicators extends Analyzer { override def mapOver(tpe: Type): Type = tpe match { case TypeRef(NoPrefix, sym, args) if sym.isTypeParameterOrSkolem => - var sym1 = context.scope.lookup(sym.name) - if (sym1 eq NoSymbol) { - // try harder (look in outer scopes) - // with virtpatmat, this can happen when the sym is referenced in the scope of a LabelDef but is defined in the scope of an outer DefDef (e.g., in AbstractPartialFunction's andThen) - BodyDuplicator.super.silent(_.typedType(Ident(sym.name))) match { - case SilentResultValue(t) => - sym1 = t.symbol - debuglog("fixed by trying harder: "+(sym, sym1, context)) - case _ => - } - } -// assert(sym1 ne NoSymbol, tpe) - if ((sym1 ne NoSymbol) && (sym1 ne sym)) { - debuglog("fixing " + sym + " -> " + sym1) + val sym1 = ( + context.scope lookup sym.name orElse { + // try harder (look in outer scopes) + // with virtpatmat, this can happen when the sym is referenced in the scope of a LabelDef but + // is defined in the scope of an outer DefDef (e.g., in AbstractPartialFunction's andThen) + BodyDuplicator.super.silent(_ typedType Ident(sym.name)).fold(NoSymbol: Symbol)(_.symbol) + } filter (_ ne sym) + ) + if (sym1.exists) { + debuglog(s"fixing $sym -> $sym1") typeRef(NoPrefix, sym1, mapOverArgs(args, sym1.typeParams)) - } else super.mapOver(tpe) + } + else super.mapOver(tpe) case TypeRef(pre, sym, args) => val newsym = updateSym(sym) @@ -162,7 +154,7 @@ abstract class Duplicators extends Analyzer { case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) => debuglog("ValDef " + name + " sym.info: " + vdef.symbol.info) invalidSyms(vdef.symbol) = vdef - val newowner = if (owner != NoSymbol) owner else context.owner + val newowner = owner orElse context.owner val newsym = vdef.symbol.cloneSymbol(newowner) newsym.setInfo(fixType(vdef.symbol.info)) vdef.symbol = newsym @@ -184,17 +176,6 @@ abstract class Duplicators extends Analyzer { stats.foreach(invalidate(_, owner)) } - 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. */ @@ -214,10 +195,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) @@ -227,40 +208,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) @@ -278,27 +254,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 @@ -320,9 +292,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 @@ -351,7 +329,7 @@ abstract class Duplicators extends Analyzer { super.typed(atPos(tree.pos)(tree1)) */ case Match(scrut, cases) => - val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType) + val scrut1 = typedByValueExpr(scrut) val scrutTpe = scrut1.tpe.widen val cases1 = { if (scrutTpe.isFinalType) cases filter { @@ -366,8 +344,8 @@ abstract class Duplicators extends Analyzer { // Without this, AnyRef specializations crash on patterns like // case _: Boolean => ... // Not at all sure this is safe. - else if (scrutTpe <:< AnyRefClass.tpe) - cases filterNot (_.pat.tpe <:< AnyValClass.tpe) + else if (scrutTpe <:< AnyRefTpe) + cases filterNot (_.pat.tpe <:< AnyValTpe) else cases } @@ -381,12 +359,11 @@ 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.safeOwner == 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) - val res = super.typed(ntree, mode, pt) - res + super.typed(ntree, mode, 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 35a4461ccc..3a6b25f1cd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -8,7 +8,8 @@ //todo: disallow C#D in superclass //todo: treat :::= correctly -package scala.tools.nsc +package scala +package tools.nsc package typechecker import scala.annotation.tailrec @@ -30,11 +31,11 @@ trait Implicits { import global._ import definitions._ import ImplicitsStats._ - import typeDebug.{ ptTree, ptBlock, ptLine } - import global.typer.{ printTyping, deindentTyping, indentTyping, printInference } + import typingStack.{ printTyping } + import typeDebug._ 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) @@ -59,40 +60,31 @@ trait Implicits { * @return A search result */ def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean, pos: Position): SearchResult = { - printInference("[infer %s] %s with pt=%s in %s".format( - if (isView) "view" else "implicit", - tree, pt, context.owner.enclClass) - ) - printTyping( - ptBlock("infer implicit" + (if (isView) " view" else ""), - "tree" -> tree, - "pt" -> pt, - "undetparams" -> context.outer.undetparams - ) - ) - indentTyping() - + // Note that the isInvalidConversionTarget seems to make a lot more sense right here, before all the + // work is performed, than at the point where it presently exists. + val shouldPrint = printTypings && !context.undetparams.isEmpty val rawTypeStart = if (Statistics.canEnable) Statistics.startCounter(rawTypeImpl) else null val findMemberStart = if (Statistics.canEnable) Statistics.startCounter(findMemberImpl) else null val subtypeStart = if (Statistics.canEnable) Statistics.startCounter(subtypeImpl) else null val start = if (Statistics.canEnable) Statistics.startTimer(implicitNanos) else null - if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty) - printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) + if (shouldPrint) + typingStack.printTyping(tree, "typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit - if ((result.isFailure || !settings.Xdivergence211.value) && saveAmbiguousDivergent && implicitSearchContext.hasErrors) { - context.updateBuffer(implicitSearchContext.errBuffer.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent)) - debugwarn("update buffer: " + implicitSearchContext.errBuffer) + if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.hasErrors) { + context.updateBuffer(implicitSearchContext.reportBuffer.errors.collect { + case dte: DivergentImplicitTypeError => dte + case ate: AmbiguousImplicitTypeError => ate + }) + debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors) } - printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains if (Statistics.canEnable) Statistics.stopTimer(implicitNanos, start) if (Statistics.canEnable) Statistics.stopCounter(rawTypeImpl, rawTypeStart) if (Statistics.canEnable) Statistics.stopCounter(findMemberImpl, findMemberStart) if (Statistics.canEnable) Statistics.stopCounter(subtypeImpl, subtypeStart) - deindentTyping() - printTyping("Implicit search yielded: "+ result) + result } @@ -101,24 +93,14 @@ trait Implicits { def inferImplicit(tree: Tree, pt: Type, isView: Boolean, context: Context, silent: Boolean, withMacrosDisabled: Boolean, pos: Position, onError: (Position, String) => Unit): Tree = { val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) def wrapper(inference: => SearchResult) = wrapper1(inference) - def fail(reason: Option[String]) = { - if (!silent) { - if (context.hasErrors) onError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg) - else onError(pos, reason getOrElse "implicit search has failed. to find out the reason, turn on -Xlog-implicits") - } - EmptyTree - } - try { - wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) match { - case failure if failure.tree.isEmpty => fail(None) - case success => success.tree - } - } catch { - case ex: DivergentImplicit => - if (settings.Xdivergence211.value) - debugwarn("this shouldn't happen. DivergentImplicit exception has been thrown with -Xdivergence211 turned on: "+ex) - fail(Some("divergent implicit expansion")) + val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) + if (result.isFailure && !silent) { + val err = context.firstError + val errPos = err.map(_.errPos).getOrElse(pos) + val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + onError(errPos, errMsg) } + result.tree } /** Find all views from type `tp` (in which `tpars` are free) @@ -137,7 +119,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), AnyTpe), true, context.makeImplicit(reportAmbiguousErrors = false)) search.allImplicitsPoly(tvars) } @@ -149,6 +131,16 @@ trait Implicits { private val implicitsCache = new LinkedHashMap[Type, Infoss] private val infoMapCache = new LinkedHashMap[Symbol, InfoMap] private val improvesCache = perRunCaches.newMap[(ImplicitInfo, ImplicitInfo), Boolean]() + private val implicitSearchId = { var id = 1 ; () => try id finally id += 1 } + + private def isInvalidConversionTarget(tpe: Type): Boolean = tpe match { + case Function1(_, out) => AnyRefClass.tpe <:< out + case _ => false + } + private def isInvalidConversionSource(tpe: Type): Boolean = tpe match { + case Function1(in, _) => in <:< NullClass.tpe + case _ => false + } def resetImplicits() { implicitsCache.clear() @@ -157,7 +149,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). */ @@ -177,7 +169,6 @@ trait Implicits { def isFailure = false def isAmbiguousFailure = false - // only used when -Xdivergence211 is turned on def isDivergent = false final def isSuccess = !isFailure } @@ -186,7 +177,6 @@ trait Implicits { override def isFailure = true } - // only used when -Xdivergence211 is turned on lazy val DivergentSearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { override def isFailure = true override def isDivergent = true @@ -231,15 +221,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 => @@ -249,7 +231,10 @@ trait Implicits { case _ => false } override def hashCode = name.## + pre.## + sym.## - override def toString = name + ": " + tpe + override def toString = ( + if (tpeCache eq null) name + ": ?" + else name + ": " + tpe + ) } /** A class which is used to track pending implicits to prevent infinite implicit searches. @@ -281,16 +266,13 @@ 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 } */ object HasMethodMatching { - val dummyMethod = NoSymbol.newTermSymbol(newTermName("typer$dummy")) + val dummyMethod = NoSymbol.newTermSymbol("typer$dummy") setInfo NullaryMethodType(AnyTpe) + def templateArgType(argtpe: Type) = new BoundedWildcardType(TypeBounds.lower(argtpe)) def apply(name: Name, argtpes: List[Type], restpe: Type): Type = { @@ -317,7 +299,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 } @@ -332,27 +314,30 @@ trait Implicits { * (useful when we infer synthetic stuff and pass EmptyTree in the `tree` argument) * If it's set to NoPosition, then position-based services will use `tree.pos` */ - class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context, pos0: Position = NoPosition) - extends Typer(context0) with ImplicitsContextErrors { - printTyping( - ptBlock("new ImplicitSearch", - "tree" -> tree, - "pt" -> pt, - "isView" -> isView, - "context0" -> context0, - "undetparams" -> context.outer.undetparams - ) - ) -// assert(tree.isEmpty || tree.pos.isDefined, tree) + class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context, pos0: Position = NoPosition) extends Typer(context0) with ImplicitsContextErrors { + val searchId = implicitSearchId() + private def typingLog(what: String, msg: String) = + typingStack.printTyping(tree, f"[search #$searchId] $what $msg") + + import infer._ + if (Statistics.canEnable) Statistics.incCounter(implicitSearchCount) + + /** The type parameters to instantiate */ + val undetParams = if (isView) Nil else context.outer.undetparams + val wildPt = approximate(pt) + + def undet_s = if (undetParams.isEmpty) "" else undetParams.mkString(" inferring ", ", ", "") + def tree_s = typeDebug ptTree tree + def ctx_s = fullSiteString(context) + typingLog("start", s"`$tree_s`$undet_s, searching for adaptation to pt=$pt $ctx_s") + 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 } - - import infer._ /** Is implicit info `info1` better than implicit info `info2`? */ def improves(info1: ImplicitInfo, info2: ImplicitInfo) = { @@ -360,7 +345,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) @@ -388,7 +373,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))) @@ -403,11 +388,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, _) => @@ -425,14 +410,8 @@ trait Implicits { overlaps(dtor1, dted1) && (dtor1 =:= dted1 || complexity(dtor1) > complexity(dted1)) } - if (Statistics.canEnable) Statistics.incCounter(implicitSearchCount) - - /** The type parameters to instantiate */ - val undetParams = if (isView) List() else context.outer.undetparams - /** The expected type with all undetermined type parameters replaced with wildcards. */ def approximate(tp: Type) = deriveTypeWithWildcards(undetParams)(tp) - val wildPt = approximate(pt) /** Try to construct a typed tree from given implicit info with given * expected type. @@ -458,45 +437,21 @@ trait Implicits { (context.openImplicits find { case OpenImplicit(info, tp, tree1) => !info.sym.isMacro && tree1.symbol == tree.symbol && dominates(pt, tp)}) match { case Some(pending) => //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG - if (settings.Xdivergence211.value) DivergentSearchFailure - else throw DivergentImplicit + DivergentSearchFailure case None => - def pre211DivergenceLogic() = { try { context.openImplicits = OpenImplicit(info, pt, tree) :: context.openImplicits // println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG - typedImplicit0(info, ptChecked, isLocal) - } catch { - case ex: DivergentImplicit => + val result = typedImplicit0(info, ptChecked, isLocal) + if (result.isDivergent) { //println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG - if (context.openImplicits.tail.isEmpty) { - if (!pt.isErroneous && !info.sym.isMacro) - DivergingImplicitExpansionError(tree, pt, info.sym)(context) - SearchFailure - } else { - throw DivergentImplicit - } + if (context.openImplicits.tail.isEmpty && !pt.isErroneous) + DivergingImplicitExpansionError(tree, pt, info.sym)(context) + } + result } finally { context.openImplicits = context.openImplicits.tail } - } - def post211DivergenceLogic() = { - try { - context.openImplicits = OpenImplicit(info, pt, tree) :: context.openImplicits - // println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG - val result = typedImplicit0(info, ptChecked, isLocal) - if (result.isDivergent) { - //println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG - if (context.openImplicits.tail.isEmpty && !pt.isErroneous) - DivergingImplicitExpansionError(tree, pt, info.sym)(context) - } - result - } finally { - context.openImplicits = context.openImplicits.tail - } - } - if (settings.Xdivergence211.value) post211DivergenceLogic() - else pre211DivergenceLogic() } } @@ -512,10 +467,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) @@ -615,22 +568,12 @@ trait Implicits { private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = { if (Statistics.canEnable) Statistics.incCounter(plausiblyCompatibleImplicits) - printTyping ( - ptBlock("typedImplicit0", - "info.name" -> info.name, - "ptChecked" -> ptChecked, - "pt" -> wildPt, - "orig" -> ptBlock("info", - "undetParams" -> undetParams, - "info.pre" -> info.pre - ).replaceAll("\\n", "\n ") - ) - ) - - if (ptChecked || matchesPt(info)) - typedImplicit1(info, isLocal) - else - SearchFailure + val ok = ptChecked || matchesPt(info) && { + def word = if (isLocal) "local " else "" + typingLog("match", s"$word$info") + true + } + if (ok) typedImplicit1(info, isLocal) else SearchFailure } private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = { @@ -651,36 +594,33 @@ trait Implicits { Select(gen.mkAttributedQualifier(info.pre), implicitMemberName) } } - printTyping("typedImplicit1 %s, pt=%s, from implicit %s:%s".format( - typeDebug.ptTree(itree), wildPt, info.name, info.tpe) - ) + typingLog("considering", typeDebug.ptTree(itree)) 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) - printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt)) val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun } else adapt(itree1, EXPRmode, wildPt) - printTyping("adapted implicit %s:%s to %s".format( - itree1.symbol, itree2.tpe, wildPt) - ) + typingStack.showAdapt(itree, itree2, pt, context) def hasMatchingSymbol(tree: Tree): Boolean = (tree.symbol == info.sym) || { tree match { @@ -692,7 +632,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)) @@ -700,23 +640,19 @@ trait Implicits { val tvars = undetParams map freshVar def ptInstantiated = pt.instantiateTypeParams(undetParams, tvars) - printInference("[search] considering %s (pt contains %s) trying %s against pt=%s".format( - if (undetParams.isEmpty) "no tparams" else undetParams.map(_.name).mkString(", "), - typeVarsInType(ptInstantiated) filterNot (_.isGround) match { case Nil => "no tvars" ; case tvs => tvs.mkString(", ") }, - itree2.tpe, pt - )) - if (matchesPt(itree2.tpe, ptInstantiated, undetParams)) { if (tvars.nonEmpty) - printTyping(ptLine("" + info.sym, "tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr))) + typingLog("solve", ptLine("tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr))) - val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), - false, lubDepth(List(itree2.tpe, pt))) + val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), upper = false, lubDepth(itree2.tpe :: pt :: Nil)) // #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 @@ -741,23 +677,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) + typingLog("success", s"inferred value of type $ptInstantiated is $result") + result } } else fail("incompatible: %s does not match expected type %s".format(itree2.tpe, ptInstantiated)) @@ -865,26 +802,6 @@ trait Implicits { /** Preventing a divergent implicit from terminating implicit search, * so that if there is a best candidate it can still be selected. - * - * The old way of handling divergence. - * Only enabled when -Xdivergence211 is turned off. - */ - private var divergence = false - private val divergenceHandler: PartialFunction[Throwable, SearchResult] = { - var remaining = 1; - { case x: DivergentImplicit if remaining > 0 => - remaining -= 1 - divergence = true - log("discarding divergent implicit during implicit search") - SearchFailure - } - } - - /** Preventing a divergent implicit from terminating implicit search, - * so that if there is a best candidate it can still be selected. - * - * The new way of handling divergence. - * Only enabled when -Xdivergence211 is turned on. */ object DivergentImplicitRecovery { // symbol of the implicit that caused the divergence. @@ -897,7 +814,7 @@ trait Implicits { if (search.isDivergent && countdown > 0) { countdown -= 1 implicitSym = i.sym - log("discarding divergent implicit ${implicitSym} during implicit search") + log(s"discarding divergent implicit $implicitSym during implicit search") SearchFailure } else search } @@ -915,10 +832,7 @@ trait Implicits { matches sortBy (x => if (isView) -x.useCountView else -x.useCountArg) } if (eligible.nonEmpty) - printInference("[search%s] %s with pt=%s in %s, eligible:\n %s".format( - if (isView) " view" else "", - tree, pt, context.owner.enclClass, eligible.mkString("\n ")) - ) + printTyping(tree, eligible.size + s" eligible for pt=$pt at ${fullSiteString(context)}") /** Faster implicit search. Overall idea: * - prune aggressively @@ -928,24 +842,15 @@ trait Implicits { @tailrec private def rankImplicits(pending: Infos, acc: Infos): Infos = pending match { case Nil => acc case i :: is => - def pre211tryImplicitInfo(i: ImplicitInfo) = - try typedImplicit(i, ptChecked = true, isLocal) - catch divergenceHandler - - def post211tryImplicitInfo(i: ImplicitInfo) = - DivergentImplicitRecovery(typedImplicit(i, ptChecked = true, isLocal), i) - - { - if (settings.Xdivergence211.value) post211tryImplicitInfo(i) - else pre211tryImplicitInfo(i) - } match { - // only used if -Xdivergence211 is turned on + DivergentImplicitRecovery(typedImplicit(i, ptChecked = true, isLocal), i) match { case sr if sr.isDivergent => Nil 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 { + case err: DivergentImplicitTypeError => true + } rankImplicits(is, acc) case newBest => best = newBest @@ -954,10 +859,7 @@ trait Implicits { try improves(i, alt) catch { case e: CyclicReference => - if (printInfers) { - println(i+" discarded because cyclic reference occurred") - e.printStackTrace() - } + debugwarn(s"Discarding $i during implicit search due to cyclic reference") true } }) @@ -990,12 +892,11 @@ 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 || DivergentImplicitRecovery.sym != null) { - if (settings.Xdivergence211.value) DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym)(context) - else throw DivergentImplicit + if (DivergentImplicitRecovery.sym != null) { + DivergingImplicitExpansionError(tree, pt, DivergentImplicitRecovery.sym)(context) } if (invalidImplicits.nonEmpty) @@ -1053,8 +954,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) => @@ -1086,13 +987,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)) @@ -1101,23 +1002,21 @@ trait Implicits { tp match { case TypeRef(pre, sym, args) => if (sym.isClass) { - if (!((sym.name == tpnme.REFINE_CLASS_NAME) || - (sym.name startsWith tpnme.ANON_CLASS_NAME) || - (sym.name == tpnme.ROOT))) { + if (!sym.isAnonOrRefinementClass && !sym.isRoot) { if (sym.isStatic && !(pending contains sym)) infoMap ++= { infoMapCache get sym match { 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 } } else getClassParts(tp) - args foreach (getParts(_)) + args foreach getParts } } else if (sym.isAliasType) { getParts(tp.normalize) // SI-7180 Normalize needed to expand HK type refs @@ -1145,9 +1044,9 @@ trait Implicits { val infoMap = new InfoMap getParts(tp)(infoMap, new mutable.HashSet(), Set()) - printInference( - ptBlock("companionImplicitMap " + tp, infoMap.toSeq.map({ case (k, v) => ("" + k, v.mkString(", ")) }): _*) - ) + if (infoMap.nonEmpty) + printTyping(tree, infoMap.size + " implicits in companion scope") + infoMap } @@ -1204,8 +1103,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) @@ -1222,7 +1123,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 @@ -1236,8 +1137,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" @@ -1255,23 +1156,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 = { @@ -1313,8 +1214,8 @@ trait Implicits { // looking for a manifest of a type parameter that hasn't been inferred by now, // can't do much, but let's not fail else if (undetParams contains sym) { - // #3859: need to include the mapping from sym -> NothingClass.tpe in the SearchResult - mot(NothingClass.tpe, sym :: from, NothingClass.tpe :: to) + // #3859: need to include the mapping from sym -> NothingTpe in the SearchResult + mot(NothingTpe, sym :: from, NothingTpe :: to) } else { // a manifest should have been found by normal searchImplicit EmptyTree @@ -1406,7 +1307,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) @@ -1421,34 +1322,44 @@ trait Implicits { val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits result = materializeImplicit(pt) - // `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) - log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) + if (result.isSuccess && isView) { + def maybeInvalidConversionError(msg: String) { + // We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError" + // which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690. + if (context.ambiguousErrors) + context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg)) + } + if (isInvalidConversionTarget(pt)) { + maybeInvalidConversionError("the result type of an implicit conversion must be more specific than AnyRef") + result = SearchFailure + } + else if (isInvalidConversionSource(pt)) { + maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion") + result = SearchFailure + } + } + if (result.isFailure) + debuglog("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) result } 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 @@ -1509,7 +1420,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 @@ -1535,9 +1445,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) @@ -1557,7 +1465,3 @@ object ImplicitsStats { val implicitCacheAccs = Statistics.newCounter ("implicit cache accesses", "typer") val implicitCacheHits = Statistics.newSubCounter("implicit cache hits", implicitCacheAccs) } - -// only used when -Xdivergence211 is turned off -class DivergentImplicit extends Exception -object DivergentImplicit extends DivergentImplicit diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 55e0a954f0..03f680525c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -7,12 +7,11 @@ 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 scala.reflect.internal.Depth -/** This trait ... +/** This trait contains methods related to type parameter inference. * * @author Martin Odersky * @version 1.0 @@ -22,138 +21,64 @@ trait Infer extends Checkable { import global._ import definitions._ - import typer.printInference import typeDebug.ptBlock - -/* -- Type parameter inference utility functions --------------------------- */ - - 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 - * (nargs - params.length + 1) copies of its type is returned. - * By-name types are replaced with their underlying type. + import typeDebug.str.parentheses + import typingStack.{ printTyping } + + /** The formal parameter types corresponding to `formals`. + * If `formals` has a repeated last parameter, a list of + * (numArgs - numFormals + 1) copies of its type is appended + * to the other formals. By-name types are replaced with their + * underlying type. * * @param removeByName allows keeping ByName parameters. Used in NamesDefaults. * @param removeRepeated allows keeping repeated parameter (if there's one argument). Used in NamesDefaults. */ - def formalTypes(formals: List[Type], nargs: Int, removeByName: Boolean = true, removeRepeated: Boolean = true): List[Type] = { - val formals1 = if (removeByName) formals mapConserve { - case TypeRef(_, ByNameParamClass, List(arg)) => arg - case formal => formal - } else formals - if (isVarArgTypes(formals1) && (removeRepeated || formals.length != nargs)) { - val ft = formals1.last.dealiasWiden.typeArgs.head - formals1.init ::: (for (i <- List.range(formals1.length - 1, nargs)) yield ft) - } else formals1 + def formalTypes(formals: List[Type], numArgs: Int, removeByName: Boolean = true, removeRepeated: Boolean = true): List[Type] = { + val numFormals = formals.length + val formals1 = if (removeByName) formals mapConserve dropByName else formals + val expandLast = ( + (removeRepeated || numFormals != numArgs) + && isVarArgTypes(formals1) + ) + def lastType = formals1.last.dealiasWiden.typeArgs.head + def expanded(n: Int) = (1 to n).toList map (_ => lastType) + + if (expandLast) + formals1.init ::: expanded(numArgs - numFormals + 1) + else + formals1 } - /** 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`. - * - * `formals` are the formal types before expanding a potential repeated parameter (must come last in `formals`, if at all) - * - * @param nbSubPats The number of arguments to the extractor pattern - * @param effectiveNbSubPats `nbSubPats`, unless there is one sub-pattern which, after unwrapping - * bind patterns, is a Tuple pattern, in which case it is the number of - * elements. Used to issue warnings about binding a `TupleN` to a single value. - * @throws TypeError when the unapply[Seq] definition is ill-typed - * @returns (null, null) when the expected number of sub-patterns cannot be satisfied by the given extractor - * - * This is the spec currently implemented -- TODO: update it. - * - * 8.1.8 ExtractorPatterns - * - * An extractor pattern x(p1, ..., pn) where n ≥ 0 is of the same syntactic form as a constructor pattern. - * However, instead of a case class, the stable identifier x denotes an object which has a member method named unapply or unapplySeq that matches the pattern. - * - * An `unapply` method with result type `R` in an object `x` matches the - * pattern `x(p_1, ..., p_n)` if it takes exactly one argument and, either: - * - `n = 0` and `R =:= Boolean`, or - * - `n = 1` and `R <:< Option[T]`, for some type `T`. - * The argument pattern `p1` is typed in turn with expected type `T`. - * - Or, `n > 1` and `R <:< Option[Product_n[T_1, ..., T_n]]`, for some - * types `T_1, ..., T_n`. The argument patterns `p_1, ..., p_n` are - * typed with expected types `T_1, ..., T_n`. - * - * An `unapplySeq` method in an object `x` matches the pattern `x(p_1, ..., p_n)` - * if it takes exactly one argument and its result type is of the form `Option[S]`, - * where either: - * - `S` is a subtype of `Seq[U]` for some element type `U`, (set `m = 0`) - * - or `S` is a `ProductX[T_1, ..., T_m]` and `T_m <: Seq[U]` (`m <= n`). - * - * The argument patterns `p_1, ..., p_n` are typed with expected types - * `T_1, ..., T_m, U, ..., U`. Here, `U` is repeated `n-m` times. - * + /** 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. */ - def extractorFormalTypes(pos: Position, resTp: Type, nbSubPats: Int, - unappSym: Symbol, effectiveNbSubPats: Int): (List[Type], List[Type]) = { - val isUnapplySeq = unappSym.name == nme.unapplySeq - val booleanExtractor = resTp.typeSymbolDirect == BooleanClass - - def seqToRepeatedChecked(tp: Type) = { - val toRepeated = seqToRepeated(tp) - if (tp eq toRepeated) throw new TypeError("(the last tuple-component of) the result type of an unapplySeq must be a Seq[_]") - else toRepeated - } - - // empty list --> error, otherwise length == 1 - lazy val optionArgs = resTp.baseType(OptionClass).typeArgs - // empty list --> not a ProductN, otherwise product element types - def productArgs = getProductArgs(optionArgs.head) - - val formals = - // convert Seq[T] to the special repeated argument type - // so below we can use formalTypes to expand formals to correspond to the number of actuals - if (isUnapplySeq) { - if (optionArgs.nonEmpty) - productArgs match { - case Nil => List(seqToRepeatedChecked(optionArgs.head)) - case normalTps :+ seqTp => normalTps :+ seqToRepeatedChecked(seqTp) - } - else throw new TypeError(s"result type $resTp of unapplySeq defined in ${unappSym.fullLocationString} does not conform to Option[_]") - } else { - if (booleanExtractor && nbSubPats == 0) Nil - else if (optionArgs.nonEmpty) - if (nbSubPats == 1) { - val productArity = productArgs.size - if (settings.lint.value && productArity > 1 && productArity != effectiveNbSubPats) - global.currentUnit.warning(pos, - s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") - optionArgs - } - // TODO: update spec to reflect we allow any ProductN, not just TupleN - else productArgs - else - throw new TypeError(s"result type $resTp of unapply defined in ${unappSym.fullLocationString} does not conform to Option[_] or Boolean") - } - - // for unapplySeq, replace last vararg by as many instances as required by nbSubPats - val formalsExpanded = - if (isUnapplySeq && formals.nonEmpty) formalTypes(formals, nbSubPats) - else formals + private def bestAlternatives(alternatives: List[Symbol])(isBetter: (Symbol, Symbol) => Boolean): List[Symbol] = { + def improves(sym1: Symbol, sym2: Symbol) = ( + (sym2 eq NoSymbol) + || sym2.isError + || (sym2 hasAnnotation BridgeClass) + || isBetter(sym1, sym2) + ) - if (formalsExpanded.lengthCompare(nbSubPats) != 0) (null, null) - else (formals, formalsExpanded) + alternatives sortWith improves match { + case best :: rest if rest.nonEmpty => best :: rest.filterNot(alt => improves(best, alt)) + case bests => bests + } } - 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 + // we must not allow CyclicReference to be thrown when sym.info is called + // in checkAccessible, because that would mark the symbol erroneous, which it + // is not. But if it's a true CyclicReference then macro def will report it. + // See comments to TypeSigError for an explanation of this special case. + // [Eugene] is there a better way? + private object CheckAccessibleMacroCycle extends TypeCompleter { + val tree = EmptyTree + override def complete(sym: Symbol) = () } /** A fresh type variable with given type parameter as origin. - * - * @param tparam ... - * @return ... */ def freshVar(tparam: Symbol): TypeVar = TypeVar(tparam) @@ -170,50 +95,34 @@ trait Infer extends Checkable { */ object instantiate extends TypeMap { private var excludedVars = immutable.Set[TypeVar]() + private def applyTypeVar(tv: TypeVar): Type = tv match { + case TypeVar(origin, constr) if !constr.instValid => throw new DeferredNoInstance(() => s"no unique instantiation of type variable $origin could be found") + case _ if excludedVars(tv) => throw new NoInstance("cyclic instantiation") + case TypeVar(_, constr) => + excludedVars += tv + try apply(constr.inst) + finally excludedVars -= tv + } def apply(tp: Type): Type = tp match { - case WildcardType | BoundedWildcardType(_) | NoType => - throw new NoInstance("undetermined type") - 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") - } else if (excludedVars(tv)) { - throw new NoInstance("cyclic instantiation") - } else { - excludedVars += tv - val res = apply(constr.inst) - excludedVars -= tv - res - } - case _ => - mapOver(tp) + case WildcardType | BoundedWildcardType(_) | NoType => throw new NoInstance("undetermined type") + case tv: TypeVar if !tv.untouchable => applyTypeVar(tv) + case _ => mapOver(tp) } } + @inline final def falseIfNoInstance(body: => Boolean): Boolean = + try body catch { case _: NoInstance => false } + /** 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 => - false - case NoPrefix | ThisType(_) | ConstantType(_) => - true - case TypeRef(pre, sym, args) => - isFullyDefined(pre) && (args forall isFullyDefined) - case SingleType(pre, sym) => - isFullyDefined(pre) - case RefinedType(ts, decls) => - ts forall isFullyDefined - case TypeVar(origin, constr) if (constr.inst == NoType) => - false - case _ => - try { - instantiate(tp); true - } catch { - case ex: NoInstance => false - } + case WildcardType | BoundedWildcardType(_) | NoType => false + case NoPrefix | ThisType(_) | ConstantType(_) => true + case TypeRef(pre, _, args) => isFullyDefined(pre) && (args forall isFullyDefined) + case SingleType(pre, _) => isFullyDefined(pre) + case RefinedType(ts, _) => ts forall isFullyDefined + case TypeVar(_, constr) if constr.inst == NoType => false + case _ => falseIfNoInstance({ instantiate(tp) ; true }) } /** Solve constraint collected in types `tvars`. @@ -225,32 +134,17 @@ trait Infer extends Checkable { * @param upper When `true` search for max solution else min. * @throws NoInstance */ - def solvedTypes(tvars: List[TypeVar], tparams: List[Symbol], - variances: List[Int], upper: Boolean, depth: Int): List[Type] = { - - if (tvars.nonEmpty) - printInference("[solve types] solving for " + tparams.map(_.name).mkString(", ") + " in " + tvars.mkString(", ")) - - if (!solve(tvars, tparams, variances, upper, depth)) { - // no panic, it's good enough to just guess a solution, we'll find out - // later whether it works. *ZAP* @M danger, Will Robinson! this means - // that you should never trust inferred type arguments! - // - // Need to call checkBounds on the args/typars or type1 on the tree - // for the expression that results from type inference see e.g., #2421: - // implicit search had been ignoring this caveat - // throw new DeferredNoInstance(() => - // "no solution exists for constraints"+(tvars map boundsString)) + def solvedTypes(tvars: List[TypeVar], tparams: List[Symbol], variances: List[Variance], upper: Boolean, depth: Depth): List[Type] = { + if (tvars.isEmpty) Nil else { + printTyping("solving for " + parentheses((tparams, tvars).zipped map ((p, tv) => s"${p.name}: $tv"))) + // !!! What should be done with the return value of "solve", which is at present ignored? + // The historical commentary says "no panic, it's good enough to just guess a solution, + // we'll find out later whether it works", meaning don't issue an error here when types + // don't conform to bounds. That means you can never trust the results of implicit search. + // For an example where this was not being heeded, SI-2421. + solve(tvars, tparams, variances, upper, depth) + tvars map instantiate } - for (tvar <- tvars ; if tvar.constr.inst == tvar) { - if (tvar.origin.typeSymbol.info eq ErrorType) - // this can happen if during solving a cyclic type parameter - // such as T <: T gets completed. See #360 - tvar.constr.inst = ErrorType - else - abort(tvar.origin+" at "+tvar.origin.typeSymbol.owner) - } - tvars map instantiate } def skipImplicit(tp: Type) = tp match { @@ -265,16 +159,15 @@ trait Infer extends Checkable { * This method seems to be performance critical. */ def normalize(tp: Type): Type = tp match { - case mt @ MethodType(params, restpe) if mt.isImplicit => - normalize(restpe) - case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => - functionType(mt.paramTypes, normalize(restpe)) - case NullaryMethodType(restpe) => - normalize(restpe) - case ExistentialType(tparams, qtpe) => - newExistentialType(tparams, normalize(qtpe)) - case tp1 => - tp1 // @MAT aliases already handled by subtyping + case PolyType(_, restpe) => + logResult(sm"""|Normalizing PolyType in infer: + | was: $restpe + | now""")(normalize(restpe)) + case mt @ MethodType(_, restpe) if mt.isImplicit => normalize(restpe) + case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => functionType(mt.paramTypes, normalize(restpe)) + case NullaryMethodType(restpe) => normalize(restpe) + case ExistentialType(tparams, qtpe) => newExistentialType(tparams, normalize(qtpe)) + case _ => tp // @MAT aliases already handled by subtyping } private lazy val stdErrorClass = rootMirror.RootClass.newErrorClass(tpnme.ERROR) @@ -286,12 +179,8 @@ trait Infer extends Checkable { /* -- Error Messages --------------------------------------------------- */ def setError[T <: Tree](tree: T): T = { - debuglog("set error: "+ tree) - // this breaks -Ydebug pretty radically - // if (settings.debug.value) { // DEBUG - // println("set error: "+tree); - // throw new Error() - // } + // SI-7388, one can incur a cycle calling sym.toString + // (but it'd be nicer if that weren't so) def name = { val sym = tree.symbol val nameStr = try sym.toString catch { case _: CyclicReference => sym.nameString } @@ -301,7 +190,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 @@ -311,102 +200,87 @@ trait Infer extends Checkable { def issue(err: AbsTypeError): Unit = context.issue(err) - 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. + def explainTypes(tp1: Type, tp2: Type) = { + if (context.reportErrors) + withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) + } - (found.resultApprox ne found) - && isWeaklyCompatible(found.resultApprox, req) - */ - ) + // When filtering sym down to the accessible alternatives leaves us empty handed. + private def checkAccessibleError(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree = { + 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)) + } + ErrorUtils.issueTypeError(AccessError(tree, sym, pre, context.enclClass.owner, + if (settings.check.isDefault) + analyzer.lastAccessCheckDetails + else + ptBlock("because of an internal error (no accessible symbol)", + "sym.ownerChain" -> sym.ownerChain, + "underlyingSymbol(sym)" -> underlyingSymbol(sym), + "pre" -> pre, + "site" -> site, + "tree" -> tree, + "sym.accessBoundary(sym.owner)" -> sym.accessBoundary(sym.owner), + "context.owner" -> context.owner, + "context.outer.enclClass.owner" -> context.outer.enclClass.owner + ) + ))(context) - def explainTypes(tp1: Type, tp2: Type) = - withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) + setError(tree) + } /* -- 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. + * @PP: In case it's not abundantly obvious to anyone who might read + * this, the method does a lot more than "check" these things, as does + * nearly every method in the compiler, so don't act all shocked. + * This particular example "checks" its way to assigning both the + * symbol and type of the incoming tree, in addition to forcing lots + * of symbol infos on its way to transforming java raw types (but + * only of terms - why?) * * 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) */ - def checkAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree = - if (sym.isError) { - tree setSymbol sym setType ErrorType - } else { - val topClass = context.owner.enclosingTopLevelClass - if (context.unit.exists) - context.unit.depends += sym.enclosingTopLevelClass - - var sym1 = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) - // Console.println("check acc " + (sym, sym1) + ":" + (sym.tpe, sym1.tpe) + " from " + pre);//DEBUG - if (sym1 == NoSymbol && sym.isJavaDefined && context.unit.isJava) // don't try to second guess Java; see #4402 - sym1 = sym - - if (sym1 == NoSymbol) { - if (settings.debug.value) { - Console.println(context) - Console.println(tree) - Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType)) - } - ErrorUtils.issueTypeError(AccessError(tree, sym, pre, context.enclClass.owner, - if (settings.check.isDefault) - analyzer.lastAccessCheckDetails - else - ptBlock("because of an internal error (no accessible symbol)", - "sym.ownerChain" -> sym.ownerChain, - "underlyingSymbol(sym)" -> underlyingSymbol(sym), - "pre" -> pre, - "site" -> site, - "tree" -> tree, - "sym.accessBoundary(sym.owner)" -> sym.accessBoundary(sym.owner), - "context.owner" -> context.owner, - "context.outer.enclClass.owner" -> context.outer.enclClass.owner - ) - ))(context) - setError(tree) - } - else { - if (context.owner.isTermMacro && (sym1 hasFlag LOCKED)) { - // we must not let CyclicReference to be thrown from sym1.info - // because that would mark sym1 erroneous, which it is not - // but if it's a true CyclicReference then macro def will report it - // see comments to TypeSigError for an explanation of this special case - // [Eugene] is there a better way? - val dummy = new TypeCompleter { val tree = EmptyTree; override def complete(sym: Symbol) {} } - throw CyclicReference(sym1, dummy) - } + def checkAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree = { + def malformed(ex: MalformedType, instance: Type): Type = { + val what = if (ex.msg contains "malformed type") "is malformed" else s"contains a ${ex.msg}" + val message = s"\n because its instance type $instance $what" + val error = AccessError(tree, sym, pre, context.enclClass.owner, message) + ErrorUtils.issueTypeError(error)(context) + ErrorType + } + def accessible = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) match { + case NoSymbol if sym.isJavaDefined && context.unit.isJava => sym // don't try to second guess Java; see #4402 + case sym1 => sym1 + } + // XXX So... what's this for exactly? + if (context.unit.exists) + context.unit.depends += sym.enclosingTopLevelClass - if (sym1.isTerm) - sym1.cookJavaRawInfo() // xform java rawtypes into existentials - - val owntype = { - try pre.memberType(sym1) - catch { - case ex: MalformedType => - if (settings.debug.value) ex.printStackTrace - val sym2 = underlyingSymbol(sym1) - val itype = pre.memberType(sym2) - ErrorUtils.issueTypeError( - AccessError(tree, sym, pre, context.enclClass.owner, - "\n because its instance type "+itype+ - (if ("malformed type: "+itype.toString==ex.msg) " is malformed" - else " contains a "+ex.msg)))(context) - ErrorType - } - } - tree setSymbol sym1 setType { + if (sym.isError) + tree setSymbol sym setType ErrorType + else accessible match { + case NoSymbol => checkAccessibleError(tree, sym, pre, site) + case sym if context.owner.isTermMacro && (sym hasFlag LOCKED) => throw CyclicReference(sym, CheckAccessibleMacroCycle) + case sym => + val sym1 = if (sym.isTerm) sym.cookJavaRawInfo() else sym // xform java rawtypes into existentials + val owntype = ( + try pre memberType sym1 + catch { case ex: MalformedType => malformed(ex, pre memberType underlyingSymbol(sym)) } + ) + tree setSymbol sym1 setType ( pre match { case _: SuperType => owntype map (tp => if (tp eq pre) site.symbol.thisType else tp) case _ => owntype } - } - } + ) } - + } /** "Compatible" means conforming after conversions. * "Raising to a thunk" is not implicit; therefore, for purposes of applicability and @@ -417,45 +291,38 @@ trait Infer extends Checkable { * since that induces a tie between m(=>A) and m(=>A,B*) [SI-3761] */ private def isCompatible(tp: Type, pt: Type): Boolean = { - def isCompatibleByName(tp: Type, pt: Type): Boolean = pt match { - case TypeRef(_, ByNameParamClass, List(res)) if !isByNameParamType(tp) => isCompatible(tp, res) - case _ => false - } + def isCompatibleByName(tp: Type, pt: Type): Boolean = ( + isByNameParamType(pt) + && !isByNameParamType(tp) + && isCompatible(tp, dropByName(pt)) + ) val tp1 = normalize(tp) - (tp1 weak_<:< pt) || isCoercible(tp1, pt) || isCompatibleByName(tp, pt) + + ( (tp1 weak_<:< pt) + || isCoercible(tp1, pt) + || isCompatibleByName(tp, pt) + ) } - def isCompatibleArgs(tps: List[Type], pts: List[Type]) = - (tps corresponds pts)(isCompatible) + def isCompatibleArgs(tps: List[Type], pts: List[Type]) = (tps corresponds pts)(isCompatible) - def isWeaklyCompatible(tp: Type, pt: Type): Boolean = - pt.typeSymbol == UnitClass || // can perform unit coercion - isCompatible(tp, pt) || - tp.isInstanceOf[MethodType] && // can perform implicit () instantiation - tp.params.isEmpty && isCompatible(tp.resultType, pt) + def isWeaklyCompatible(tp: Type, pt: Type): Boolean = { + def isCompatibleNoParamsMethod = tp match { + case MethodType(Nil, restpe) => isCompatible(restpe, pt) + case _ => false + } + ( pt.typeSymbol == UnitClass // can perform unit coercion + || isCompatible(tp, pt) + || isCompatibleNoParamsMethod // can perform implicit () instantiation + ) + } - /** Like weakly compatible but don't apply any implicit conversions yet. + /* Like weakly compatible but don't apply any implicit conversions yet. * Used when comparing the result type of a method with its prototype. - * - * [Martin] I think Infer is also created by Erasure, with the default - * implementation of isCoercible - * [Paulp] (Assuming the above must refer to my comment on isCoercible) - * Nope, I examined every occurrence of Inferencer in trunk. It - * appears twice as a self-type, once at its definition, and once - * where it is instantiated in Typers. There are no others. - * - % ack -A0 -B0 --no-filename '\bInferencer\b' src - self: Inferencer => - self: Inferencer => - class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { - val infer = new Inferencer(context0) { */ def isConservativelyCompatible(tp: Type, pt: Type): Boolean = context.withImplicitsDisabled(isWeaklyCompatible(tp, pt)) - /** This is overridden in the Typer.infer with some logic, but since - * that's the only place in the compiler an Inferencer is ever created, - * I suggest this should either be abstract or have the implementation. - */ + // Overridden at the point of instantiation, where inferView is visible. def isCoercible(tp: Type, pt: Type): Boolean = false /* -- Type instantiation------------------------------------------------ */ @@ -464,112 +331,99 @@ trait Infer extends Checkable { * by existentially bound variables. */ def makeFullyDefined(tp: Type): Type = { - val tparams = new ListBuffer[Symbol] + var tparams: List[Symbol] = Nil def addTypeParam(bounds: TypeBounds): Type = { val tparam = context.owner.newExistential(newTypeName("_"+tparams.size), context.tree.pos.focus) setInfo bounds - tparams += tparam + tparams ::= tparam tparam.tpe } val tp1 = tp map { - case WildcardType => - addTypeParam(TypeBounds.empty) - case BoundedWildcardType(bounds) => - addTypeParam(bounds) - case t => t + case WildcardType => addTypeParam(TypeBounds.empty) + case BoundedWildcardType(bounds) => addTypeParam(bounds) + case t => t } - existentialAbstraction(tparams.toList, tp1) + if (tp eq tp1) tp + else existentialAbstraction(tparams.reverse, 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>. - * 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 ... + * type vars, its type parameters and result type and a prototype `pt`. + * If the type variables cannot be instantiated such that the type + * conforms to `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 - val instResTp = restpe.instantiateTypeParams(tparams, tvars) - if ( if (useWeaklyCompatible) isWeaklyCompatible(instResTp, pt) else isCompatible(instResTp, pt) ) { - try { - // If the restpe is an implicit method, and the expected type is fully defined - // optimize type variables wrt to the implicit formals only; ignore the result type. - // See test pos/jesper.scala - val varianceType = restpe match { - case mt: MethodType if mt.isImplicit && isFullyDefined(pt) => - MethodType(mt.params, AnyClass.tpe) - case _ => - restpe - } - //println("try to solve "+tvars+" "+tparams) - (solvedTypes(tvars, tparams, tparams map varianceInType(varianceType), - false, lubDepth(List(restpe, pt))), tvars) - } catch { - case ex: NoInstance => (null, null) - } - } else (null, null) + private def exprTypeArgs(tvars: List[TypeVar], tparams: List[Symbol], restpe: Type, pt: Type, useWeaklyCompatible: Boolean): List[Type] = { + def restpeInst = restpe.instantiateTypeParams(tparams, tvars) + def conforms = if (useWeaklyCompatible) isWeaklyCompatible(restpeInst, pt) else isCompatible(restpeInst, pt) + // If the restpe is an implicit method, and the expected type is fully defined + // optimize type variables wrt to the implicit formals only; ignore the result type. + // See test pos/jesper.scala + def variance = restpe match { + case mt: MethodType if mt.isImplicit && isFullyDefined(pt) => MethodType(mt.params, AnyTpe) + case _ => restpe + } + def solve() = solvedTypes(tvars, tparams, tparams map varianceInType(variance), upper = false, lubDepth(restpe :: pt :: Nil)) + + if (conforms) + try solve() catch { case _: NoInstance => null } + else + null } + /** Overload which allocates fresh type vars. + * The other one exists because apparently inferExprInstance needs access to the typevars + * after the call, and its wasteful to return a tuple and throw it away almost every time. + */ + private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type, useWeaklyCompatible: Boolean): List[Type] = + exprTypeArgs(tparams map freshVar, tparams, restpe, pt, useWeaklyCompatible) /** 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 { + def protoTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type, pt: Type): List[Type] = { + // Map type variable to its instance, or, if `variance` is variant, + // to its upper or 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) - lazy val lower = lub(loBounds) + lazy val upper = glb(hiBounds) + lazy val lower = lub(loBounds) def setInst(tp: Type): Type = { tvar setInst tp - assertNonCyclic(tvar)//debug + assert(tvar.constr.inst != tvar, tvar.origin) instantiate(tvar.constr.inst) } - //Console.println("instantiate "+tvar+tvar.constr+" variance = "+variance);//DEBUG - if (tvar.constr.inst != NoType) + if (tvar.constr.instValid) 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 * and the code is not exactly readable. */ object AdjustedTypeArgs { - val Result = scala.collection.mutable.LinkedHashMap - type Result = scala.collection.mutable.LinkedHashMap[Symbol, Option[Type]] + val Result = mutable.LinkedHashMap + type Result = mutable.LinkedHashMap[Symbol, Option[Type]] def unapply(m: Result): Some[(List[Symbol], List[Type])] = Some(toLists( (m collect {case (p, Some(a)) => (p, a)}).unzip )) @@ -586,7 +440,7 @@ trait Infer extends Checkable { def unapply(m: Result): Some[(List[Symbol], List[Type], List[Type], List[Symbol])] = Some(toLists{ val (ok, nok) = m.map{case (p, a) => (p, a.getOrElse(null))}.partition(_._2 ne null) val (okArgs, okTparams) = ok.unzip - (okArgs, okTparams, m.values.map(_.getOrElse(NothingClass.tpe)), nok.keys) + (okArgs, okTparams, m.values.map(_.getOrElse(NothingTpe)), nok.keys) }) } @@ -608,7 +462,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 +470,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. - * Undetermined type arguments are represented by `definitions.NothingClass.tpe`. + * If this is not possible, throw a `NoInstance` exception. + * Undetermined type arguments are represented by `definitions.NothingTpe`. * 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 @@ -689,35 +542,70 @@ trait Infer extends Checkable { "argument expression's type is not compatible with formal parameter type" + foundReqMsg(tp1, pt1)) } } - val targs = solvedTypes( - tvars, tparams, tparams map varianceInTypes(formals), - false, lubDepth(formals) max lubDepth(argtpes) - ) + val targs = solvedTypes(tvars, tparams, tparams map varianceInTypes(formals), 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) if (restp1 eq restp) tp else restp1 case _ => - val appmeth = { - //OPT cut down on #closures by special casing non-overloaded case - // was: tp.nonPrivateMember(nme.apply) filter (_.isPublic) - val result = tp.nonPrivateMember(nme.apply) - if ((result eq NoSymbol) || !result.isOverloaded && result.isPublic) result - else result filter (_.isPublic) + //OPT cut down on #closures by special casing non-overloaded case + // was: tp.nonPrivateMember(nme.apply) filter (_.isPublic) + tp nonPrivateMember nme.apply match { + case NoSymbol => tp + case sym if !sym.isOverloaded && sym.isPublic => OverloadedType(tp, sym.alternatives) + case sym => OverloadedType(tp, sym.filter(_.isPublic).alternatives) } - if (appmeth == NoSymbol) tp - 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 } /** @@ -731,7 +619,7 @@ trait Infer extends Checkable { * to the corresponding position in params * - namesOK is false when there's an invalid use of named arguments */ - private def checkNames(argtpes: List[Type], params: List[Symbol]) = { + private def checkNames(argtpes: List[Type], params: List[Symbol]): (List[Type], Array[Int], Boolean) = { val argPos = Array.fill(argtpes.length)(-1) var positionalAllowed, namesOK = true var index = 0 @@ -743,7 +631,7 @@ trait Infer extends Checkable { if (pos == -1) { if (positionalAllowed) { // treat assignment as positional argument argPos(index) = index - res = UnitClass.tpe + res = UnitTpe } else // unknown parameter name namesOK = false } else if (argPos.contains(pos)) { // parameter specified twice @@ -765,207 +653,190 @@ 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 + } + + /** 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) UnitTpe // aka "Tuple0" + else tupleType(argtpes map { + case NamedType(name, tp) => UnitTpe // not a named arg - only assignments here + case RepeatedType(tp) => tp // but probably shouldn't be tupling a call containing :_* + case tp => tp + }) + ) - /** 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>? + /** 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 + } + + private def isApplicableToMethod(undetparams: List[Symbol], mt: MethodType, argtpes0: List[Type], pt: Type): Boolean = { + val formals = formalTypes(mt.paramTypes, argtpes0.length, removeByName = false) + def missingArgs = missingParams[Type](argtpes0, mt.params, x => Some(x) collect { case NamedType(n, _) => n }) + def argsTupled = tupleIfNecessary(mt.paramTypes, argtpes0) + def argsPlusDefaults = missingArgs match { + case (args, _) if args forall (_.hasDefault) => argtpes0 ::: makeNamedTypes(args) + case _ => argsTupled + } + // If args eq the incoming arg types, fail; otherwise recurse with these args. + def tryWithArgs(args: List[Type]) = ( + (args ne argtpes0) + && isApplicable(undetparams, mt, args, pt) + ) + def tryInstantiating(args: List[Type]) = falseIfNoInstance { + val restpe = mt resultType args + val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, args, pt) + val restpeInst = restpe.instantiateTypeParams(okparams, okargs) + // #2665: must use weak conformance, not regular one (follow the monomorphic case above) + exprTypeArgs(leftUndet, restpeInst, pt, useWeaklyCompatible = true) match { + case null => false + case _ => isWithinBounds(NoPrefix, NoSymbol, okparams, okargs) + } + } + def typesCompatible(args: List[Type]) = undetparams match { + case Nil => isCompatibleArgs(args, formals) && isWeaklyCompatible(mt resultType args, pt) + case _ => tryInstantiating(args) + } + + // when using named application, the vararg param has to be specified exactly once + def reorderedTypesCompatible = checkNames(argtpes0, mt.params) match { + case (_, _, false) => false // names are not ok + case (_, pos, _) if !allArgsArePositional(pos) && !sameLength(formals, mt.params) => false // different length lists and all args not positional + case (args, pos, _) => typesCompatible(reorderArgs(args, pos)) + } + compareLengths(argtpes0, formals) match { + case 0 if containsNamedType(argtpes0) => reorderedTypesCompatible // right number of args, wrong order + case 0 => typesCompatible(argtpes0) // fast track if no named arguments are used + case x if x > 0 => tryWithArgs(argsTupled) // too many args, try tupling + case _ => tryWithArgs(argsPlusDefaults) // too few args, try adding defaults or tupling + } + } + + /** Is there an instantiation of free type variables `undetparams` such that + * function type `ftpe` is applicable to `argtpes0` 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 = + 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)) - 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) - } - def typesCompatible(argtpes: List[Type]) = { - val restpe = ftpe.resultType(argtpes) - if (undetparams.isEmpty) { - isCompatibleArgs(argtpes, formals) && isWeaklyCompatible(restpe, pt) - } else { - try { - val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt) - // #2665: must use weak conformance, not regular one (follow the monomorphic case above) - (exprTypeArgs(leftUndet, restpe.instantiateTypeParams(okparams, okargs), pt, useWeaklyCompatible = true)._1 ne null) && - isWithinBounds(NoPrefix, NoSymbol, okparams, okargs) - } catch { - case ex: NoInstance => false - } - } - } - - // very similar logic to doTypedApply in typechecker - 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 - 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)) - ) - } - } - else { - // not enough arguments, check if applicable using defaults - val missing = missingParams[Type](argtpes0, params, { - case NamedType(name, _) => Some(name) - case _ => None - })._1 - if (missing forall (_.hasDefault)) { - // add defaults as named arguments - val argtpes1 = argtpes0 ::: (missing map (p => NamedType(p.name, p.tpe))) - isApplicable(undetparams, ftpe, argtpes1, pt) - } - else tryTupleApply - } - - case NullaryMethodType(restpe) => // strip nullary method type, which used to be done by the polytype case below - isApplicable(undetparams, restpe, argtpes0, pt) - case PolyType(tparams, restpe) => - createFromClonedSymbols(tparams, restpe)((tps1, restpe1) => isApplicable(tps1 ::: undetparams, restpe1, argtpes0, pt)) - case ErrorType => - true - case _ => - false + case OverloadedType(pre, alts) => alts exists (alt => isApplicable(undetparams, pre memberType alt, argtpes0, pt)) + case ExistentialType(_, qtpe) => isApplicable(undetparams, qtpe, argtpes0, pt) + case mt @ MethodType(_, _) => isApplicableToMethod(undetparams, mt, argtpes0, pt) + case NullaryMethodType(restpe) => isApplicable(undetparams, restpe, argtpes0, pt) + case PolyType(tparams, restpe) => createFromClonedSymbols(tparams, restpe)((tps1, res1) => isApplicable(tps1 ::: undetparams, res1, argtpes0, pt)) + case ErrorType => true + case _ => false } + ) /** - * 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`. */ - 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 + // 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 = { + def applicableExpectingPt(pt: Type): Boolean = { + val silent = context.makeSilent(reportAmbiguousErrors = false) + val result = newTyper(silent).infer.isApplicable(undetparams, ftpe, argtpes0, pt) + if (silent.hasErrors && !pt.isWildcard) + applicableExpectingPt(WildcardType) // second try + else + result + } + applicableExpectingPt(pt) } - /** 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)) - case et: ExistentialType => - isAsSpecific(ftpe1.skolemizeExistential, ftpe2) - //et.withTypeVars(isAsSpecific(_, ftpe2)) - case NullaryMethodType(res) => - isAsSpecific(res, ftpe2) - case mt: MethodType if mt.isImplicit => - isAsSpecific(ftpe1.resultType, ftpe2) - case mt @ MethodType(params, _) if params.nonEmpty => - var argtpes = mt.paramTypes - if (isVarArgsList(params) && isVarArgsList(ftpe2.params)) - argtpes = argtpes map (argtpe => - if (isRepeatedParamType(argtpe)) argtpe.typeArgs.head else argtpe) - isApplicable(List(), ftpe2, argtpes, WildcardType) - case PolyType(tparams, NullaryMethodType(res)) => - isAsSpecific(PolyType(tparams, res), ftpe2) - case PolyType(tparams, mt: MethodType) if mt.isImplicit => - isAsSpecific(PolyType(tparams, mt.resultType), ftpe2) - case PolyType(_, (mt @ MethodType(params, _))) if params.nonEmpty => - isApplicable(List(), ftpe2, mt.paramTypes, WildcardType) - // case NullaryMethodType(res) => - // isAsSpecific(res, ftpe2) - case ErrorType => - true - case _ => - ftpe2 match { - case OverloadedType(pre, alts) => - alts forall (alt => isAsSpecific(ftpe1, pre.memberType(alt))) - case et: ExistentialType => - et.withTypeVars(isAsSpecific(ftpe1, _)) - case mt: MethodType => - !mt.isImplicit || isAsSpecific(ftpe1, mt.resultType) - case NullaryMethodType(res) => - isAsSpecific(ftpe1, res) - case PolyType(tparams, NullaryMethodType(res)) => - isAsSpecific(ftpe1, PolyType(tparams, res)) - case PolyType(tparams, mt: MethodType) => - !mt.isImplicit || isAsSpecific(ftpe1, PolyType(tparams, mt.resultType)) - case _ => - isAsSpecificValueType(ftpe1, ftpe2, List(), List()) - } + def isAsSpecific(ftpe1: Type, ftpe2: Type): Boolean = { + def checkIsApplicable(argtpes: List[Type]) = isApplicable(Nil, ftpe2, argtpes, WildcardType) + def bothAreVarargs = isVarArgsList(ftpe1.params) && isVarArgsList(ftpe2.params) + def onRight = ftpe2 match { + case OverloadedType(pre, alts) => alts forall (alt => isAsSpecific(ftpe1, pre memberType alt)) + case et: ExistentialType => et.withTypeVars(isAsSpecific(ftpe1, _)) + case mt @ MethodType(_, restpe) => !mt.isImplicit || isAsSpecific(ftpe1, restpe) + case NullaryMethodType(res) => isAsSpecific(ftpe1, res) + case PolyType(tparams, NullaryMethodType(restpe)) => isAsSpecific(ftpe1, PolyType(tparams, restpe)) + case PolyType(tparams, mt @ MethodType(_, restpe)) => !mt.isImplicit || isAsSpecific(ftpe1, PolyType(tparams, restpe)) + case _ => isAsSpecificValueType(ftpe1, ftpe2, Nil, Nil) + } + ftpe1 match { + case OverloadedType(pre, alts) => alts exists (alt => isAsSpecific(pre memberType alt, ftpe2)) + case et: ExistentialType => isAsSpecific(et.skolemizeExistential, ftpe2) + case NullaryMethodType(restpe) => isAsSpecific(restpe, ftpe2) + case mt @ MethodType(_, restpe) if mt.isImplicit => isAsSpecific(restpe, ftpe2) + case mt @ MethodType(_, _) if bothAreVarargs => checkIsApplicable(mt.paramTypes mapConserve repeatedToSingle) + case mt @ MethodType(params, _) if params.nonEmpty => checkIsApplicable(mt.paramTypes) + case PolyType(tparams, NullaryMethodType(restpe)) => isAsSpecific(PolyType(tparams, restpe), ftpe2) + case PolyType(tparams, mt @ MethodType(_, restpe)) if mt.isImplicit => isAsSpecific(PolyType(tparams, restpe), ftpe2) + case PolyType(_, mt @ MethodType(params, _)) if params.nonEmpty => checkIsApplicable(mt.paramTypes) + case ErrorType => true + case _ => onRight + } } - private def isAsSpecificValueType(tpe1: Type, tpe2: Type, undef1: List[Symbol], undef2: List[Symbol]): Boolean = (tpe1, tpe2) match { - case (PolyType(tparams1, rtpe1), _) => + private def isAsSpecificValueType(tpe1: Type, tpe2: Type, undef1: List[Symbol], undef2: List[Symbol]): Boolean = tpe1 match { + case PolyType(tparams1, rtpe1) => isAsSpecificValueType(rtpe1, tpe2, undef1 ::: tparams1, undef2) - case (_, PolyType(tparams2, rtpe2)) => - isAsSpecificValueType(tpe1, rtpe2, undef1, undef2 ::: tparams2) - case _ => - existentialAbstraction(undef1, tpe1) <:< existentialAbstraction(undef2, tpe2) + case _ => + tpe2 match { + case PolyType(tparams2, rtpe2) => isAsSpecificValueType(tpe1, rtpe2, undef1, undef2 ::: tparams2) + case _ => existentialAbstraction(undef1, tpe1) <:< existentialAbstraction(undef2, tpe2) + } } - -/* - def isStrictlyMoreSpecific(ftpe1: Type, ftpe2: Type): Boolean = - ftpe1.isError || isAsSpecific(ftpe1, ftpe2) && - (!isAsSpecific(ftpe2, ftpe1) || - !ftpe1.isInstanceOf[OverloadedType] && ftpe2.isInstanceOf[OverloadedType] || - phase.erasedTypes && covariantReturnOverride(ftpe1, ftpe2)) -*/ /** Is sym1 (or its companion class in case it is a module) a subclass of * sym2 (or its companion class in case it is a module)? */ def isProperSubClassOrObject(sym1: Symbol, sym2: Symbol): Boolean = ( - (sym1 != sym2) && (sym1 != NoSymbol) && ( - (sym1 isSubClass sym2) - || (sym1.isModuleClass && isProperSubClassOrObject(sym1.linkedClassOfClass, sym2)) - || (sym2.isModuleClass && isProperSubClassOrObject(sym1, sym2.linkedClassOfClass)) - ) + (sym1 ne sym2) + && (sym1 ne NoSymbol) + && ( (sym1 isSubClass sym2) + || (sym1.isModuleClass && isProperSubClassOrObject(sym1.linkedClassOfClass, sym2)) + || (sym2.isModuleClass && isProperSubClassOrObject(sym1, sym2.linkedClassOfClass)) + ) ) /** is symbol `sym1` defined in a proper subclass of symbol `sym2`? */ - def isInProperSubClassOrObject(sym1: Symbol, sym2: Symbol) = - sym2 == NoSymbol || isProperSubClassOrObject(sym1.owner, sym2.owner) + def isInProperSubClassOrObject(sym1: Symbol, sym2: Symbol) = ( + (sym2 eq NoSymbol) + || isProperSubClassOrObject(sym1.safeOwner, sym2.owner) + ) def isStrictlyMoreSpecific(ftpe1: Type, ftpe2: Type, sym1: Symbol, sym2: Symbol): Boolean = { // ftpe1 / ftpe2 are OverloadedTypes (possibly with one single alternative) if they @@ -978,92 +849,36 @@ trait Infer extends Checkable { (!phase.erasedTypes || covariantReturnOverride(ftpe1, ftpe2))) 1 else 0) val subClassCount = (if (isInProperSubClassOrObject(sym1, sym2)) 1 else 0) - (if (isInProperSubClassOrObject(sym2, sym1)) 1 else 0) -// println("is more specific? "+sym1+":"+ftpe1+sym1.locationString+"/"+sym2+":"+ftpe2+sym2.locationString+":"+ -// specificCount+"/"+subClassCount) specificCount + subClassCount > 0 } } -/* - ftpe1.isError || { - if (isAsSpecific(ftpe1, ftpe2)) - (!isAsSpecific(ftpe2, ftpe1) || - isProperSubClassOrObject(sym1.owner, sym2.owner) || - !ftpe1.isInstanceOf[OverloadedType] && ftpe2.isInstanceOf[OverloadedType] || - phase.erasedTypes && covariantReturnOverride(ftpe1, ftpe2)) - else - !isAsSpecific(ftpe2, ftpe1) && - isProperSubClassOrObject(sym1.owner, sym2.owner) - } -*/ - private def covariantReturnOverride(ftpe1: Type, ftpe2: Type): Boolean = (ftpe1, ftpe2) match { - case (MethodType(_, rtpe1), MethodType(_, rtpe2)) => - rtpe1 <:< rtpe2 || rtpe2.typeSymbol == ObjectClass - case _ => - false - } -/* - /** Is type `tpe1` a strictly better expression alternative than type `tpe2`? - */ - def isStrictlyBetterExpr(tpe1: Type, tpe2: Type) = { - isMethod(tpe2) && !isMethod(tpe1) || - isNullary(tpe1) && !isNullary(tpe2) || - isStrictlyBetter(tpe1, tpe2) - } - /** Is type `tpe1` a strictly better alternative than type `tpe2`? - * non-methods are always strictly better than methods - * nullary methods are always strictly better than non-nullary - * if both are non-nullary methods, then tpe1 is strictly better than tpe2 if - * - tpe1 specializes tpe2 and tpe2 does not specialize tpe1 - * - tpe1 and tpe2 specialize each other and tpe1 has a strictly better resulttype than - * tpe2 - */ - def isStrictlyBetter(tpe1: Type, tpe2: Type) = { - def isNullary(tpe: Type): Boolean = tpe match { - case tp: RewrappingTypeProxy => isNullary(tp.underlying) - case _ => tpe.paramSectionCount == 0 || tpe.params.isEmpty - } - def isMethod(tpe: Type): Boolean = tpe match { - case tp: RewrappingTypeProxy => isMethod(tp.underlying) - case MethodType(_, _) | PolyType(_, _) => true - case _ => false - } - def hasStrictlyBetterResult = - resultIsBetter(tpe1, tpe2, List(), List()) && !resultIsBetter(tpe2, tpe1, List(), List()) - if (!isMethod(tpe1)) - isMethod(tpe2) || hasStrictlyBetterResult - - isNullary(tpe1) && !isNullary(tpe2) || - is - - else if (isNullary(tpe1)) - isMethod(tpe2) && (!isNullary(tpe2) || hasStrictlyBetterResult) - else - specializes(tpe1, tpe2) && (!specializes(tpe2, tpe1) || hasStrictlyBetterResult) + private def covariantReturnOverride(ftpe1: Type, ftpe2: Type): Boolean = ftpe1 match { + case MethodType(_, rtpe1) => + ftpe2 match { + case MethodType(_, rtpe2) => rtpe1 <:< rtpe2 || rtpe2.typeSymbol == ObjectClass + case _ => false + } + case _ => false } -*/ /** error if arguments not within bounds. */ - def checkBounds(tree: Tree, pre: Type, owner: Symbol, - tparams: List[Symbol], targs: List[Type], prefix: String): Boolean = - if ((targs exists (_.isErroneous)) || (tparams exists (_.isErroneous))) true - else { - //@M validate variances & bounds of targs wrt variances & bounds of tparams - //@M TODO: better place to check this? - //@M TODO: errors for getters & setters are reported separately - val kindErrors = checkKindBounds(tparams, targs, pre, owner) - kindErrors match { - case Nil => - def notWithinBounds() = NotWithinBounds(tree, prefix, targs, tparams, Nil) - isWithinBounds(pre, owner, tparams, targs) || {notWithinBounds(); false} - case errors => - def kindBoundErrors() = KindBoundErrors(tree, prefix, targs, tparams, errors) - (targs contains WildcardType) || {kindBoundErrors(); false} - } + def checkBounds(tree: Tree, pre: Type, owner: Symbol, tparams: List[Symbol], targs: List[Type], prefix: String): Boolean = { + def issueBoundsError() = { NotWithinBounds(tree, prefix, targs, tparams, Nil) ; false } + def issueKindBoundErrors(errs: List[String]) = { KindBoundErrors(tree, prefix, targs, tparams, errs) ; false } + //@M validate variances & bounds of targs wrt variances & bounds of tparams + //@M TODO: better place to check this? + //@M TODO: errors for getters & setters are reported separately + def check() = checkKindBounds(tparams, targs, pre, owner) match { + case Nil => isWithinBounds(pre, owner, tparams, targs) || issueBoundsError() + case errs => (targs contains WildcardType) || issueKindBoundErrors(errs) } + targs.exists(_.isErroneous) || tparams.exists(_.isErroneous) || check() + } + 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) } @@ -1078,21 +893,13 @@ trait Infer extends Checkable { * attempts fail, an error is produced. */ def inferArgumentInstance(tree: Tree, undetparams: List[Symbol], strictPt: Type, lenientPt: Type) { - printInference( - ptBlock("inferArgumentInstance", - "tree" -> tree, - "tree.tpe" -> tree.tpe, - "undetparams" -> undetparams, - "strictPt" -> strictPt, - "lenientPt" -> lenientPt - ) - ) - var targs = exprTypeArgs(undetparams, tree.tpe, strictPt)._1 + printTyping(tree, s"inferring arg instance based on pt0=$strictPt, pt1=$lenientPt") + var targs = exprTypeArgs(undetparams, tree.tpe, strictPt, useWeaklyCompatible = false) if ((targs eq null) || !(tree.tpe.subst(undetparams, targs) <:< strictPt)) - targs = exprTypeArgs(undetparams, tree.tpe, lenientPt)._1 + targs = exprTypeArgs(undetparams, tree.tpe, lenientPt, useWeaklyCompatible = false) substExpr(tree, undetparams, targs, lenientPt) - printInference("[inferArgumentInstance] finished, targs = " + targs) + printTyping(tree, s"infer arg instance from pt0=$strictPt, pt1=$lenientPt; targs=$targs") } /** Infer type arguments `targs` for `tparams` of polymorphic expression in `tree`, given prototype `pt`. @@ -1101,31 +908,23 @@ trait Infer extends Checkable { * If passed, infers against specified type `treeTp` instead of `tree.tp`. */ def inferExprInstance(tree: Tree, tparams: List[Symbol], pt: Type = WildcardType, treeTp0: Type = null, keepNothings: Boolean = true, useWeaklyCompatible: Boolean = false): List[Symbol] = { - val treeTp = if(treeTp0 eq null) tree.tpe else treeTp0 // can't refer to tree in default for treeTp0 - val (targs, tvars) = exprTypeArgs(tparams, treeTp, pt, useWeaklyCompatible) - printInference( - ptBlock("inferExprInstance", - "tree" -> tree, - "tree.tpe"-> tree.tpe, - "tparams" -> tparams, - "pt" -> pt, - "targs" -> targs, - "tvars" -> tvars - ) - ) + val treeTp = if (treeTp0 eq null) tree.tpe else treeTp0 // can't refer to tree in default for treeTp0 + val tvars = tparams map freshVar + val targs = exprTypeArgs(tvars, tparams, treeTp, pt, useWeaklyCompatible) + def infer_s = map3(tparams, tvars, targs)((tparam, tvar, targ) => s"$tparam=$tvar/$targ") mkString "," + printTyping(tree, s"infer expr instance from pt=$pt, $infer_s") if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226 substExpr(tree, tparams, targs, pt) List() } else { val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, tvars, targs) - printInference( - ptBlock("inferExprInstance/AdjustedTypeArgs", - "okParams" -> okParams, - "okArgs" -> okArgs, - "leftUndet" -> leftUndet - ) - ) + def solved_s = map2(okParams, okArgs)((p, a) => s"$p=$a") mkString "," + def undet_s = leftUndet match { + case Nil => "" + case ps => ps.mkString(", undet=", ",", "") + } + printTyping(tree, s"infer solved $solved_s$undet_s") substExpr(tree, okParams, okArgs, pt) leftUndet } @@ -1133,30 +932,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,20 +960,12 @@ 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) = methTypeArgs(undetparams, formals, restpe, argtpes, pt) - printInference("[infer method] solving for %s in %s based on (%s)%s (%s)".format( - undetparams.map(_.name).mkString(", "), - fn.tpe, - argtpes.mkString(", "), - restpe, - (okparams map (_.name), okargs).zipped.map(_ + "=" + _).mkString("solved: ", ", ", "") - )) - if (checkBounds(fn, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")) { val treeSubst = new TreeTypeSubstituter(okparams, okargs) treeSubst traverseTrees fn :: args @@ -1202,25 +988,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 +1015,16 @@ 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))) + + // Note: this is the only place where solvedTypes (or, indirectly, solve) is called + // with upper = true. + val targs = solvedTypes(tvars, undetparams, variances, upper = true, lubDepth(resTp :: pt :: Nil)) // 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 +1058,68 @@ trait Infer extends Checkable { } } else None - (inferFor(pt) orElse inferForApproxPt) map { 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)")) - // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) - ConstrInstantiationError(tree, resTp, pt) + inferFor(pt) orElse inferForApproxPt match { + case Some(targs) => + new TreeTypeSubstituter(undetparams, targs).traverse(tree) + notifyUndetparamsInferred(undetparams, targs) + case _ => + def not = if (isFullyDefined(pt)) "" else "not " + devWarning(s"failed inferConstructorInstance for $tree: ${tree.tpe} undet=$undetparams, pt=$pt (${not}fully defined)") + 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, Depth.AnyDepth) } - // 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) + val enclCase = context.enclosingCaseDef + def enclCase_s = enclCase.toString.replaceAll("\\n", " ").take(60) + + if (enclCase.savedTypeBounds.nonEmpty) log( + sm"""|instantiateTypeVar with nonEmpty saved type bounds { + | enclosing $enclCase_s + | saved ${enclCase.savedTypeBounds} + | tparam ${tparam.shortSymbolClass} ${tparam.defString} + |}""") + + 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 { + enclCase 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 +1138,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 +1183,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) @@ -1500,193 +1245,139 @@ trait Infer extends Checkable { /* -- Overload Resolution ---------------------------------------------- */ -/* - def checkNotShadowed(pos: Position, pre: Type, best: Symbol, eligible: List[Symbol]) = - if (!phase.erasedTypes) - for (alt <- eligible) { - if (isProperSubClassOrObject(alt.owner, best.owner)) - error(pos, - "erroneous reference to overloaded definition,\n"+ - "most specific definition is: "+best+best.locationString+" of type "+pre.memberType(best)+ - ",\nyet alternative definition "+alt+alt.locationString+" of type "+pre.memberType(alt)+ - "\nis defined in a subclass") - } -*/ - - /** 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 - - //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 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 _ => - } - } - // 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 { + def inferExprAlternative(tree: Tree, pt: Type): Tree = { + def tryOurBests(pre: Type, alts: List[Symbol], isSecondTry: Boolean): Unit = { + val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt)) + val alts1 = if (alts0.isEmpty) alts else alts0 + val bests = bestAlternatives(alts1) { (sym1, sym2) => + val tp1 = pre memberType sym1 + val tp2 = pre memberType sym2 + + ( (tp2 eq ErrorType) + || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt) + || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) + ) + } + // 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) - } - } 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)) + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) } } - } - - @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 + tree.tpe match { + case OverloadedType(pre, alts) => tryTwice(tryOurBests(pre, alts, _)) ; tree + case _ => tree + } } // 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) - } - } - ) - - 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) + 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) + ) } - /** 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) => - 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)) - } + 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.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed + case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType } - case _ => + } + // 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 + debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") + bestForExpectedType(pt, isLastTry) + } } /** 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,65 +1391,59 @@ 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 + * which have the same number of type parameters as does `argtypes` + * with all argtypes are within the corresponding type parameter bounds. * 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 - val sym0 = tree.symbol filter (alt => sameLength(alt.typeParams, argtypes)) - def fail(kind: PolyAlternativeErrorKind.ErrorType) = - PolyAlternativeError(tree, argtypes, sym0, kind) - - if (sym0 == NoSymbol) return ( - if (alts exists (_.typeParams.nonEmpty)) - fail(PolyAlternativeErrorKind.WrongNumber) - else fail(PolyAlternativeErrorKind.NoParams)) - - val (resSym, resTpe) = { - if (!sym0.isOverloaded) - (sym0, pre.memberType(sym0)) - else { - val sym = sym0 filter (alt => isWithinBounds(pre, alt.owner, alt.typeParams, argtypes)) - if (sym == NoSymbol) { - if (argtypes forall (x => !x.isErroneous)) - fail(PolyAlternativeErrorKind.ArgsDoNotConform) - return - } - else if (sym.isOverloaded) { - val xs = sym.alternatives - val tparams = new AsSeenFromMap(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)) - - (sym setInfo tpe, tpe) - } - else (sym, pre.memberType(sym)) - } + // Alternatives with a matching length type parameter list + val matchingLength = tree.symbol filter (alt => sameLength(alt.typeParams, argtypes)) + def allMonoAlts = alts forall (_.typeParams.isEmpty) + def errorKind = matchingLength match { + case NoSymbol if allMonoAlts => PolyAlternativeErrorKind.NoParams // no polymorphic method alternative + case NoSymbol => PolyAlternativeErrorKind.WrongNumber // wrong number of tparams + case _ => PolyAlternativeErrorKind.ArgsDoNotConform // didn't conform to bounds + } + def fail() = PolyAlternativeError(tree, argtypes, matchingLength, errorKind) + def finish(sym: Symbol, tpe: Type) = tree setSymbol sym setType tpe + // Alternatives which conform to bounds + def checkWithinBounds(sym: Symbol) = sym.alternatives match { + case Nil if argtypes.exists(_.isErroneous) => + case Nil => fail() + case alt :: Nil => finish(alt, pre memberType alt) + case alts @ (hd :: _) => + log(s"Attaching AntiPolyType-carrying overloaded type to $sym") + // Multiple alternatives which are within bounds; spin up an + // overloaded type which carries an "AntiPolyType" as a prefix. + val tparams = newAsSeenFromMap(pre, hd.owner) mapOver hd.typeParams + val bounds = tparams map (_.tpeHK) // see e.g., #1236 + val tpe = PolyType(tparams, OverloadedType(AntiPolyType(pre, bounds), alts)) + finish(sym setInfo tpe, tpe) + } + matchingLength.alternatives match { + case Nil => fail() + case alt :: Nil => finish(alt, pre memberType alt) + case _ => checkWithinBounds(matchingLength filter (alt => isWithinBounds(pre, alt.owner, alt.typeParams, argtypes))) } - // Side effects tree with symbol and type - tree setSymbol resSym setType resTpe } } } - diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index d6ec5f2cb0..b3675d6a82 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -1,19 +1,21 @@ package scala.tools.nsc package typechecker +import java.lang.Math.min 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 +import scala.reflect.macros.runtime.{AbortMacroException, MacroRuntimes} +import scala.reflect.runtime.{universe => ru} +import scala.reflect.macros.compiler.DefaultMacroCompiler +import scala.tools.reflect.FastTrack +import scala.runtime.ScalaRunTime +import Fingerprint._ /** * Code to deal with macros, namely with: @@ -40,15 +42,22 @@ import scala.reflect.macros.runtime.AbortMacroException * (Expr(elems)) * (TypeTag(Int)) */ -trait Macros extends scala.tools.reflect.FastTrack with Traces { +trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { self: Analyzer => import global._ 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. * @@ -71,22 +80,32 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * Includes a path to load the implementation via Java reflection, * and various accounting information necessary when composing an argument list for the reflective invocation. */ - private case class MacroImplBinding( + case class MacroImplBinding( + // Is this macro impl a bundle (a trait extending Macro) or a vanilla def? + val isBundle: Boolean, // 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, - // 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], + methName: String, + // flattens the macro impl's parameter lists having symbols replaced with their fingerprints + // currently fingerprints are calculated solely from types of the symbols: + // * c.Expr[T] => LiftedTyped + // * c.Tree => LiftedUntyped + // * c.WeakTypeTag[T] => Tagged(index of the type parameter corresponding to that type tag) + // * everything else (e.g. scala.reflect.macros.Context) => Other + // f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: Context)(x: c.Expr[T], y: c.Tree): (U, V) = ??? + // `signature` will be equal to List(List(Other), List(LiftedTyped, LiftedUntyped), List(Tagged(0), Tagged(2))) + signature: List[List[Fingerprint]], // 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]) { + + // Was this binding derived from a `def ... = macro ???` definition? + def is_??? = className == Predef_???.owner.javaClassName && methName == Predef_???.name.encoded + } /** Macro def -> macro impl bindings are serialized into a `macroImpl` annotation * with synthetic content that carries the payload described in `MacroImplBinding`. @@ -99,36 +118,40 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * * @scala.reflect.macros.internal.macroImpl( * `macro`( - * "signature" = List(-1), + * "isBundle" = false, + * "signature" = List(Other), * "methodName" = "impl", - * "versionFormat" = 1, + * "versionFormat" = <current version format>, * "className" = "Macros$")) */ - private object MacroImplBinding { - val versionFormat = 1 + object MacroImplBinding { + val versionFormat = 5.0 def pickleAtom(obj: Any): Tree = obj match { case list: List[_] => Apply(Ident(ListModule), list map pickleAtom) case s: String => Literal(Constant(s)) - case i: Int => Literal(Constant(i)) + case d: Double => Literal(Constant(d)) + case b: Boolean => Literal(Constant(b)) + case f: Fingerprint => Literal(Constant(f.value)) } def unpickleAtom(tree: Tree): Any = tree match { case Apply(list @ Ident(_), args) if list.symbol == ListModule => args map unpickleAtom case Literal(Constant(s: String)) => s - case Literal(Constant(i: Int)) => i + case Literal(Constant(d: Double)) => d + case Literal(Constant(b: Boolean)) => b + case Literal(Constant(i: Int)) => Fingerprint(i) } def pickle(macroImplRef: Tree): Tree = { - val MacroImplReference(owner, macroImpl, targs) = macroImplRef - val paramss = macroImpl.paramss + val MacroImplReference(isBundle, owner, macroImpl, targs) = macroImplRef // 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 => @@ -139,13 +162,21 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { loop(owner) } - def signature: List[Int] = { - val transformed = transformTypeTagEvidenceParams(paramss, (param, tparam) => tparam) - transformed.flatten map (p => if (p.isTerm) -1 else p.paramPos) + def signature: List[List[Fingerprint]] = { + def fingerprint(tpe: Type): Fingerprint = tpe.dealiasWiden match { + case TypeRef(_, RepeatedParamClass, underlying :: Nil) => fingerprint(underlying) + case ExprClassOf(_) => LiftedTyped + case TreeType() => LiftedUntyped + case _ => Other + } + + val transformed = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => tparam) + mmap(transformed)(p => if (p.isTerm) fingerprint(p.info) else Tagged(p.paramPos)) } val payload = List[(String, Any)]( "versionFormat" -> versionFormat, + "isBundle" -> isBundle, "className" -> className, "methodName" -> macroImpl.name.toString, "signature" -> signature @@ -185,472 +216,241 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val Apply(_, pickledPayload) = wrapped 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") + def fail(msg: String) = abort(s"bad macro impl binding: $msg") + def unpickle[T](field: String, clazz: Class[T]): T = { + def failField(msg: String) = fail(s"$field $msg") + if (!payload.contains(field)) failField("is supposed to be there") + val raw: Any = payload(field) + if (raw == null) failField(s"is not supposed to be null") + val expected = ScalaRunTime.box(clazz) + val actual = raw.getClass + if (!expected.isAssignableFrom(actual)) failField(s"has wrong type: expected $expected, actual $actual") + raw.asInstanceOf[T] + } + + val pickleVersionFormat = unpickle("versionFormat", classOf[Double]) + if (versionFormat != pickleVersionFormat) fail(s"expected version format $versionFormat, actual $pickleVersionFormat") - val className = payload("className").asInstanceOf[String] - val methodName = payload("methodName").asInstanceOf[String] - val signature = payload("signature").asInstanceOf[List[Int]] - MacroImplBinding(className, methodName, signature, targs) + val isBundle = unpickle("isBundle", classOf[Boolean]) + val className = unpickle("className", classOf[String]) + val methodName = unpickle("methodName", classOf[String]) + val signature = unpickle("signature", classOf[List[List[Fingerprint]]]) + MacroImplBinding(isBundle, className, methodName, signature, targs) } } - private def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { + def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { val pickle = MacroImplBinding.pickle(macroImplRef) macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(pickle), Nil) } - private def loadMacroImplBinding(macroDef: Symbol): MacroImplBinding = { + def loadMacroImplBinding(macroDef: Symbol): MacroImplBinding = { val Some(AnnotationInfo(_, List(pickle), _)) = macroDef.getAnnotation(MacroImplAnnotation) MacroImplBinding.unpickle(pickle) } - /** Transforms parameters lists of a macro impl. - * The `transform` function is invoked only for WeakTypeTag evidence parameters. - * - * The transformer takes two arguments: a value parameter from the parameter list - * and a type parameter that is witnesses by the value parameter. - * - * If the transformer returns a NoSymbol, the value parameter is not included from the result. - * If the transformer returns something else, this something else is included in the result instead of the value parameter. - * - * Despite of being highly esoteric, this function significantly simplifies signature analysis. - * For example, it can be used to strip macroImpl.paramss from the evidences (necessary when checking def <-> impl correspondence) - * or to streamline creation of the list of macro arguments. - */ - private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = { - if (paramss.isEmpty || paramss.last.isEmpty) return paramss // no implicit parameters in the signature => nothing to do - if (paramss.head.isEmpty || !(paramss.head.head.tpe <:< MacroContextClass.tpe)) return paramss // no context parameter in the signature => nothing to do - def transformTag(param: Symbol): Symbol = param.tpe.dealias match { - case TypeRef(SingleType(SingleType(NoPrefix, c), universe), WeakTypeTagClass, targ :: Nil) - if c == paramss.head.head && universe == MacroContextUniverse => - transform(param, targ.typeSymbol) - case _ => - param - } - val transformed = paramss.last map transformTag filter (_ ne NoSymbol) - if (transformed.isEmpty) paramss.init else paramss.init :+ transformed - } - - def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroImpl: Symbol): Type = { - // Step I. Transform c.Expr[T] to T - var runtimeType = macroImpl.tpe.finalResultType.dealias match { - case TypeRef(_, ExprClass, runtimeType :: Nil) => runtimeType - case _ => AnyTpe // so that macro impls with rhs = ??? don't screw up our inference - } - - // Step II. Transform type parameters of a macro implementation into type arguments in a macro definition's body - runtimeType = runtimeType.substituteTypes(macroImpl.typeParams, loadMacroImplBinding(macroDdef.symbol).targs.map(_.tpe)) - - // Step III. Transform c.prefix.value.XXX to this.XXX and implParam.value.YYY to defParam.YYY - def unsigma(tpe: Type): Type = - transformTypeTagEvidenceParams(macroImpl.paramss, (param, tparam) => NoSymbol) match { - case (implCtxParam :: Nil) :: implParamss => - val implToDef = flatMap2(implParamss, macroDdef.vparamss)(map2(_, _)((_, _))).toMap - object UnsigmaTypeMap extends TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) => - val pre1 = pre match { - case SingleType(SingleType(SingleType(NoPrefix, c), prefix), value) if c == implCtxParam && prefix == MacroContextPrefix && value == ExprValue => - ThisType(macroDdef.symbol.owner) - case SingleType(SingleType(NoPrefix, implParam), value) if value == ExprValue => - implToDef get implParam map (defParam => SingleType(NoPrefix, defParam.symbol)) getOrElse pre + def computeMacroDefTypeFromMacroImplRef(macroDdef: DefDef, macroImplRef: Tree): Type = { + macroImplRef match { + case MacroImplReference(_, _, macroImpl, targs) => + // Step I. Transform c.Expr[T] to T and everything else to Any + var runtimeType = decreaseMetalevel(macroImpl.info.finalResultType) + + // Step II. Transform type parameters of a macro implementation into type arguments in a macro definition's body + runtimeType = runtimeType.substituteTypes(macroImpl.typeParams, targs map (_.tpe)) + + // Step III. Transform c.prefix.value.XXX to this.XXX and implParam.value.YYY to defParam.YYY + def unsigma(tpe: Type): Type = + transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol) match { + case (implCtxParam :: Nil) :: implParamss => + val implToDef = flatMap2(implParamss, macroDdef.vparamss)(map2(_, _)((_, _))).toMap + object UnsigmaTypeMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = pre match { + case SingleType(SingleType(SingleType(NoPrefix, c), prefix), value) if c == implCtxParam && prefix == MacroContextPrefix && value == ExprValue => + ThisType(macroDdef.symbol.owner) + case SingleType(SingleType(NoPrefix, implParam), value) if value == ExprValue => + implToDef get implParam map (defParam => SingleType(NoPrefix, defParam.symbol)) getOrElse pre + case _ => + pre + } + val args1 = args map mapOver + TypeRef(pre1, sym, args1) case _ => - pre + mapOver(tp) } - val args1 = args map mapOver - TypeRef(pre1, sym, args1) - case _ => - mapOver(tp) - } - } - - UnsigmaTypeMap(tpe) - case _ => - tpe - } - - unsigma(runtimeType) - } - - /** A reference macro implementation signature compatible with a given macro definition. - * - * In the example above for the following macro def: - * def foo[T](xs: List[T]): T = macro fooBar - * - * This function will return: - * (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]]): c.Expr[T] - * - * Note that type tag evidence parameters are not included into the result. - * Type tag context bounds for macro impl tparams are optional. - * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. - * - * @param macroDef The macro definition symbol - * @param tparams The type parameters of the macro definition - * @param vparamss The value parameters of the macro definition - * @param retTpe The return type of the macro definition - */ - 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) + + UnsigmaTypeMap(tpe) case _ => - mapOver(tp) + tpe } - } - new SigmaTypeMap() apply tpe - } - - 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) - 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, { - 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))) + unsigma(runtimeType) + case _ => + ErrorType } - - import SigGenerator._ - macroLogVerbose(sm""" - |generating macroImplSigs for: $macroDef - |tparams are: $tparams - |vparamss are: $vparamss - |retTpe is: $retTpe - |macroImplSig is: $paramss, $implRetTpe - """.trim) - (paramss, implRetTpe) } - /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method, + /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method or a top-level macro bundle, * and that that method is signature-wise compatible with the given macro definition. * - * @return Typechecked rhs of the given macro definition if everything is okay. + * @return Macro impl reference for the given macro definition if everything is okay. * EmptyTree if an error occurs. */ - def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = - try new MacroTyper(typer, macroDdef).typed - catch { case MacroBodyTypecheckException => EmptyTree } - - class MacroTyper(val typer: Typer, val macroDdef: DefDef) extends MacroErrors { - // Phase I: sanity checks + def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = { val macroDef = macroDdef.symbol - macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos)) - assert(macroDef.isTermMacro, macroDdef) - if (fastTrack contains macroDef) MacroDefIsFastTrack() - if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) MacroFeatureNotEnabled() - - // we use typed1 instead of typed, because otherwise adapt is going to mess us up - // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail - // unfortunately, this means that we have to manually trigger macro expansion - // because it's adapt which is responsible for automatic expansion during typechecking - def typecheckRhs(rhs: Tree): Tree = { - try { - // interestingly enough, just checking isErroneous doesn't cut it - // e.g. a "type arguments [U] do not conform to method foo's type parameter bounds" error - // 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 - while (rhsNeedsMacroExpansion) { - rhs1 = macroExpand1(typer, rhs1) match { - case Success(expanded) => - try { - val typechecked = typer.typed1(expanded, EXPRmode, WildcardType) - macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) - typechecked - } finally { - popMacroContext() - } - case Delay(delayed) => - typer.instantiate(delayed, EXPRmode, WildcardType) - case Fallback(fallback) => - typer.typed1(fallback, EXPRmode, WildcardType) - case Other(result) => - result - } - } - val typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors - if (typecheckedWithErrors) MacroDefUntypeableBodyError() - rhs1 - } catch { - case ex: TypeError => - typer.reportTypeError(context, rhs.pos, ex) - MacroDefUntypeableBodyError() - } - } - - // Phase II: typecheck the right-hand side of the macro def - val typed = typecheckRhs(macroDdef.rhs) - typed match { - case MacroImplReference(_, meth, _) if meth == Predef_??? => - bindMacroImpl(macroDef, typed) - MacroDefIsQmarkQmarkQmark() - case MacroImplReference(owner, meth, targs) => - if (!meth.isMethod) MacroDefInvalidBodyError() - if (!meth.isPublic) MacroImplNotPublicError() - if (meth.isOverloaded) MacroImplOverloadedError() - if (!owner.isStaticOwner && !owner.moduleClass.isStaticOwner) MacroImplNotStaticError() - if (meth.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError(typed) - bindMacroImpl(macroDef, typed) - case _ => - MacroDefInvalidBodyError() - } - - // Phase III: check compatibility between the macro def and its macro impl - // this check ignores type tag evidence parameters, because type tag context bounds are optional - // aXXX (e.g. aparamss) => characteristics of the macro impl ("a" stands for "actual") - // rXXX (e.g. rparamss) => characteristics of a reference macro impl signature synthesized from the macro def ("r" stands for "reference") - val macroImpl = typed.symbol - val aparamss = transformTypeTagEvidenceParams(macroImpl.paramss, (param, tparam) => NoSymbol) - val aret = macroImpl.tpe.finalResultType - val macroDefRet = - if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe - else computeMacroDefTypeFromMacroImpl(macroDdef, macroImpl) - val (rparamss, rret) = macroImplSig(macroDef, macroDdef.tparams, macroDdef.vparamss, macroDefRet) - - val implicitParams = aparamss.flatten filter (_.isImplicit) - if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams) - if (aparamss.length != rparamss.length) MacroImplParamssMismatchError() - - val atparams = macroImpl.typeParams - val atvars = atparams map freshVar - def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars) - - try { - map2(aparamss, rparamss)((aparams, rparams) => { - if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams) - if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams) - }) - - // cannot fuse these loops because if aparamss.flatten != rparamss.flatten - // then `atpeToRtpe` is going to fail with an unsound substitution - map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => { - if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam) - if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam) - val aparamtpe = aparam.tpe.dealias match { - case RefinedType(List(tpe), Scope(sym)) if tpe =:= MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe - case tpe => tpe - } - checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) - }) - - checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret) - - val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe)) - val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, depth = maxLubDepth) - val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, "")) - boundsOk match { - case SilentResultValue(true) => // do nothing, success - case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams) - } - } catch { - case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex) - } - } - - /** Macro classloader that is used to resolve and run macro implementations. - * 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") + assert(macroDef.isMacro, macroDdef) - 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 - } - } - - /** 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. - * 2) Loads its enclosing class from the macro classloader. - * 3) Loads the companion of that enclosing class from the macro classloader. - * 4) Resolves macro implementation within the loaded companion. - * - * @return Requested runtime if macro implementation can be loaded successfully from either of the mirrors, - * `null` otherwise. - */ - type MacroRuntime = MacroArgs => Any - private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime] - private def macroRuntime(macroDef: Symbol): MacroRuntime = { - macroLogVerbose(s"looking for macro implementation: $macroDef") + macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos)) if (fastTrack contains macroDef) { - macroLogVerbose("macro expansion is serviced by a fast track") - fastTrack(macroDef) + macroLogVerbose("typecheck terminated unexpectedly: macro is fast track") + assert(!macroDdef.tpt.isEmpty, "fast track macros must provide result type") + EmptyTree } else { - macroRuntimesCache.getOrElseUpdate(macroDef, { - val binding = loadMacroImplBinding(macroDef) - val className = binding.className - val methName = binding.methName - macroLogVerbose(s"resolved implementation as $className.$methName") + def fail() = { if (macroDef != null) macroDef setFlag IS_ERROR; macroDdef setType ErrorType; EmptyTree } + def success(macroImplRef: Tree) = { bindMacroImpl(macroDef, macroImplRef); macroImplRef } - if (binding.className == Predef_???.owner.fullName.toString && binding.methName == Predef_???.name.encoded) { - args => throw new AbortMacroException(args.c.enclosingPosition, "macro implementation is missing") - } else { - // I don't use Scala reflection here, because it seems to interfere with JIT magic - // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20% - // I'm not sure what's the reason - for me it's pure voodoo - // upd. my latest experiments show that everything's okay - // it seems that in 2.10.1 we can easily switch to Scala reflection - try { - macroLogVerbose(s"loading implementation class: $className") - macroLogVerbose(s"classloader is: ${ReflectionUtils.show(macroClassloader)}") - val implObj = ReflectionUtils.staticSingletonInstance(macroClassloader, className) - // relies on the fact that macro impls cannot be overloaded - // so every methName can resolve to at maximum one method - val implMeths = implObj.getClass.getDeclaredMethods.find(_.getName == methName) - val implMeth = implMeths getOrElse { throw new NoSuchMethodException(s"$className.$methName") } - macroLogVerbose(s"successfully loaded macro impl as ($implObj, $implMeth)") - args => implMeth.invoke(implObj, ((args.c +: args.others) map (_.asInstanceOf[AnyRef])): _*) - } catch { - case ex: Exception => - macroLogVerbose(s"macro runtime failed to load: ${ex.toString}") - macroDef setFlag IS_ERROR - null - } - } - }) + if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) { + macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled") + fail() + } else { + val macroDdef1: macroDdef.type = macroDdef + val typer1: typer.type = typer + val macroCompiler = new { + val global: self.global.type = self.global + val typer: self.global.analyzer.Typer = typer1.asInstanceOf[self.global.analyzer.Typer] + val macroDdef: self.global.DefDef = macroDdef1 + } with DefaultMacroCompiler + val macroImplRef = macroCompiler.resolveMacroImpl + if (macroImplRef.isEmpty) fail() else success(macroImplRef) + } } } - private def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = + 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) - val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) - var typeArgs = List[Tree]() - val exprArgs = ListBuffer[List[Expr[_]]]() - def collectMacroArgs(tree: Tree): Unit = tree match { - case Apply(fn, args) => - // todo. infer precise typetag for this Expr, namely the declared type of the corresponding macro impl argument - exprArgs.prepend(args map (arg => context.Expr[Nothing](arg)(TypeTag.Nothing))) - collectMacroArgs(fn) - case TypeApply(fn, args) => - typeArgs = args - collectMacroArgs(fn) - case _ => - } - collectMacroArgs(expandee) + val macroDef = expandee.symbol + val paramss = macroDef.paramss + val treeInfo.Applied(core, targs, argss) = expandee + val prefix = core match { case Select(qual, _) => qual; case _ => EmptyTree } + val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefix, expandee)) - val argcDoesntMatch = macroDef.paramss.length != exprArgs.length - val nullaryArgsEmptyParams = exprArgs.isEmpty && macroDef.paramss == ListOfNil - if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(expandee) } + macroLogVerbose(sm""" + |context: $context + |prefix: $prefix + |targs: $targs + |argss: $argss + |paramss: $paramss + """.trim) - val argss: List[List[Any]] = exprArgs.toList - macroLogVerbose(s"context: $context") - macroLogVerbose(s"argss: $argss") + import typer.TyperErrorGen._ + val isNullaryArgsEmptyParams = argss.isEmpty && paramss == ListOfNil + if (paramss.length < argss.length) MacroTooManyArgumentListsError(expandee) + if (paramss.length > argss.length && !isNullaryArgsEmptyParams) MacroTooFewArgumentListsError(expandee) - val preparedArgss: List[List[Any]] = + val macroImplArgs: List[Any] = if (fastTrack contains macroDef) { - if (fastTrack(macroDef) validate context) argss - else typer.TyperErrorGen.MacroPartialApplicationError(expandee) - } else { - // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences - // consider the following example: - // - // class D[T] { - // class C[U] { - // def foo[V] = macro Impls.foo[T, U, V] - // } - // } - // - // val outer1 = new D[Int] - // val outer2 = new outer1.C[String] - // outer2.foo[Boolean] - // - // then T and U need to be inferred from the lexical scope of the call using `asSeenFrom` - // whereas V won't be resolved by asSeenFrom and need to be loaded directly from `expandee` which needs to contain a TypeApply node - // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim - val binding = loadMacroImplBinding(macroDef) - macroLogVerbose(s"binding: $binding") - val tags = binding.signature filter (_ != -1) map (paramPos => { - val targ = binding.targs(paramPos).tpe.typeSymbol - val tpe = if (targ.isTypeParameterOrSkolem) { - if (targ.owner == macroDef) { - // doesn't work when macro def is compiled separately from its usages - // then targ is not a skolem and isn't equal to any of macroDef.typeParams - // val argPos = targ.deSkolemize.paramPos - val argPos = macroDef.typeParams.indexWhere(_.name == targ.name) - typeArgs(argPos).tpe + // Take a dry run of the fast track implementation + if (fastTrack(macroDef) validate expandee) argss.flatten + else MacroTooFewArgumentListsError(expandee) + } + else { + def calculateMacroArgs(binding: MacroImplBinding) = { + val signature = if (binding.isBundle) binding.signature else binding.signature.tail + macroLogVerbose(s"binding: $binding") + + // STEP I: prepare value arguments of the macro expansion + // wrap argss in c.Expr if necessary (i.e. if corresponding macro impl param is of type c.Expr[T]) + // expand varargs (nb! varargs can apply to any parameter section, not necessarily to the last one) + val trees = map3(argss, paramss, signature)((args, defParams, implParams) => { + val isVarargs = isVarArgsList(defParams) + if (isVarargs) { + if (defParams.length > args.length + 1) MacroTooFewArgumentsError(expandee) + } else { + if (defParams.length < args.length) MacroTooManyArgumentsError(expandee) + if (defParams.length > args.length) MacroTooFewArgumentsError(expandee) + } + + val wrappedArgs = mapWithIndex(args)((arg, j) => { + val fingerprint = implParams(min(j, implParams.length - 1)) + fingerprint match { + case LiftedTyped => context.Expr[Nothing](arg)(TypeTag.Nothing) // TODO: SI-5752 + case LiftedUntyped => arg + case _ => abort(s"unexpected fingerprint $fingerprint in $binding with paramss being $paramss " + + s"corresponding to arg $arg in $argss") + } + }) + + if (isVarargs) { + val (normal, varargs) = wrappedArgs splitAt (defParams.length - 1) + normal :+ varargs // pack all varargs into a single Seq argument (varargs Scala style) + } else wrappedArgs + }) + macroLogVerbose(s"trees: $trees") + + // STEP II: prepare type arguments of the macro expansion + // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences + // consider the following example: + // + // class D[T] { + // class C[U] { + // def foo[V] = macro Impls.foo[T, U, V] + // } + // } + // + // val outer1 = new D[Int] + // val outer2 = new outer1.C[String] + // outer2.foo[Boolean] + // + // then T and U need to be inferred from the lexical scope of the call using `asSeenFrom` + // whereas V won't be resolved by asSeenFrom and need to be loaded directly from `expandee` which needs to contain a TypeApply node + // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim + val tags = signature.flatten collect { case f if f.isTag => f.paramPos } map (paramPos => { + val targ = binding.targs(paramPos).tpe.typeSymbol + val tpe = if (targ.isTypeParameterOrSkolem) { + if (targ.owner == macroDef) { + // doesn't work when macro def is compiled separately from its usages + // then targ is not a skolem and isn't equal to any of macroDef.typeParams + // val argPos = targ.deSkolemize.paramPos + val argPos = macroDef.typeParams.indexWhere(_.name == targ.name) + targs(argPos).tpe + } else + targ.tpe.asSeenFrom( + if (prefix == EmptyTree) macroDef.owner.tpe else prefix.tpe, + macroDef.owner) } else - targ.tpe.asSeenFrom( - if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe, - macroDef.owner) - } else - targ.tpe - context.WeakTypeTag(tpe) - }) - macroLogVerbose(s"tags: $tags") - - // transforms argss taking into account varargness of paramss - // note that typetag context bounds are only declared on macroImpls - // so this optional arglist might not match macroDef's paramlist - // nb! varargs can apply to any parameter section, not necessarily to the last one - mapWithIndex(argss :+ tags)((as, i) => { - val mapsToParamss = macroDef.paramss.indices contains i - if (mapsToParamss) { - val ps = macroDef.paramss(i) - if (isVarArgsList(ps)) { - val (normal, varargs) = as splitAt (ps.length - 1) - normal :+ varargs // pack all varargs into a single List argument - } else as - } else as - }) + targ.tpe + context.WeakTypeTag(tpe) + }) + macroLogVerbose(s"tags: $tags") + + // if present, tags always come in a separate parameter/argument list + // that's because macro impls can't have implicit parameters other than c.WeakTypeTag[T] + (trees :+ tags).flatten + } + + val binding = loadMacroImplBinding(macroDef) + if (binding.is_???) Nil + else calculateMacroArgs(binding) } - macroLogVerbose(s"preparedArgss: $preparedArgss") - MacroArgs(context, preparedArgss.flatten) + macroLogVerbose(s"macroImplArgs: $macroImplArgs") + MacroArgs(context, macroImplArgs) } /** Keeps track of macros in-flight. @@ -662,21 +462,38 @@ 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 Delay(delayed: Tree) extends MacroExpansionResult - private case class Fallback(fallback: Tree) extends MacroExpansionResult { currentRun.seenMacroExpansionsFallingBack = true } - private case class Other(result: Tree) extends MacroExpansionResult - 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 = scala.tools.nsc.typechecker.MacroRole + final def APPLY_ROLE = MacroRole.Apply + final def UNAPPLY_ROLE = MacroRole.Unapply /** 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 the default expansion scheme + * that is understood by `macroExpandWithRuntime` / `macroExpandWithoutRuntime` + * + * Then `macroExpandWithRuntime`: + * 2) Checks whether the expansion needs to be delayed + * 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. @@ -687,123 +504,206 @@ 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)) - macroLogVerbose(s"""${if (hasNewErrors) "failed to typecheck" else "successfully typechecked"} against $phase $pt:\n$result""") - 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) + } - var expectedTpe = expandee.tpe - if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType - // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc - val expanded0 = duplicateAndKeepPositions(expanded) - val expanded1 = typecheck("macro def return type", expanded0, expectedTpe) - val expanded2 = typecheck("expected type", expanded1, pt) - expanded2 - } finally { - popMacroContext() + 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()) + linkExpandeeAndDesugared(expandee, desugared, role) + + val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null + if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) + try { + withInfoLevel(nodePrinters.InfoLevel.Quiet) { // verbose printing might cause recursive macro expansions + if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { + val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" + macroLogVerbose(s"cancelled macro expansion because of $reason: $expandee") + onFailure(typer.infer.setError(expandee)) + } else try { + val expanded = { + val runtime = macroRuntime(expandee.symbol) + if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) + else macroExpandWithoutRuntime(typer, expandee) + } + expanded 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, role.name, allowedExpansions) + onFailure(expanded) + } + case Fallback(fallback) => onFallback(fallback) + case Delayed(delayed) => onDelayed(delayed) + case Skipped(skipped) => onSkipped(skipped) + case Failure(failure) => onFailure(failure) + } + } catch { + case typer.TyperErrorGen.MacroExpansionException => onFailure(expandee) } - case Delay(delayed) => - // =========== THE SITUATION =========== - // - // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee), - // then there are two possible situations we're in: - // - // 1) We're in POLYmode, when the typer tests the waters wrt type inference - // (e.g. as in typedArgToPoly in doTypedApply). - // - // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type - // (e.g. if we're an argument to a function call, then this means that no previous argument lists - // can determine our type variables for us). - // - // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that - // there's nothing outrageously wrong with our undetermined type params (from what I understand!). - // - // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer - // the undetermined type params. Therefore we need to do something ourselves or otherwise this - // expandee will forever remaing not expanded (see SI-5692). - // - // A traditional way out of this conundrum is to call `instantiate` and let the inferencer - // try to find the way out. It works for simple cases, but sometimes, if the inferencer lacks - // information, it will be forced to approximate. - // - // =========== THE PROBLEM =========== - // - // Consider the following example (thanks, Miles!): - // - // // Iso represents an isomorphism between two datatypes: - // // 1) An arbitrary one (e.g. a random case class) - // // 2) A uniform representation for all datatypes (e.g. an HList) - // trait Iso[T, U] { - // def to(t : T) : U - // def from(u : U) : T - // } - // implicit def materializeIso[T, U]: Iso[T, U] = macro ??? - // - // case class Foo(i: Int, s: String, b: Boolean) - // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c) - // foo(Foo(23, "foo", true)) - // - // In the snippet above, even though we know that there's a fundep going from T to U - // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype, - // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information - // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want. - val shouldInstantiate = typer.context.undetparams.nonEmpty && !inPolyMode(mode) - if (shouldInstantiate) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) - else delayed - case Fallback(fallback) => - typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) - case Other(result) => - result + } + } finally { + if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start) } - } finally { - if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start) } } - /** Does the same as `macroExpand`, but without typechecking the expansion - * Meant for internal use within the macro infrastructure, don't use it elsewhere. + /** 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 */ - private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult = - // 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" - macroLogVerbose(s"cancelled macro expansion because of $reason: $expandee") - return Cancel(typer.infer.setError(expandee)) + def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = { + 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) + // approximation is necessary for whitebox macros to guide type inference + // read more in the comments for onDelayed below + def approximate(tp: Type) = { + val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol } + deriveTypeWithWildcards(undetparams)(tp) + } + val macroPtApprox = approximate(if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe) + // `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 macroPtApprox = $macroPtApprox): $expanded") + val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, macroPtApprox)) + 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 + } } - - try { - val runtime = macroRuntime(expandee.symbol) - if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) - else macroExpandWithoutRuntime(typer, expandee) - } catch { - case typer.TyperErrorGen.MacroExpansionException => Failure(expandee) + override def onDelayed(delayed: Tree) = { + // =========== THE SITUATION =========== + // + // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee), + // then there are two possible situations we're in: + // 1) We're in POLYmode, when the typer tests the waters wrt type inference + // (e.g. as in typedArgToPoly in doTypedApply). + // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type + // (e.g. if we're an argument to a function call, then this means that no previous argument lists + // can determine our type variables for us). + // + // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that + // there's nothing outrageously wrong with our undetermined type params (from what I understand!). + // + // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer + // the undetermined type params. Therefore we need to do something ourselves or otherwise this + // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum + // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases, + // but sometimes, if the inferencer lacks information, it will be forced to approximate. + // + // =========== THE PROBLEM =========== + // + // Consider the following example (thanks, Miles!): + // + // Iso represents an isomorphism between two datatypes: + // 1) An arbitrary one (e.g. a random case class) + // 2) A uniform representation for all datatypes (e.g. an HList) + // + // trait Iso[T, U] { + // def to(t : T) : U + // def from(u : U) : T + // } + // implicit def materializeIso[T, U]: Iso[T, U] = macro ??? + // + // case class Foo(i: Int, s: String, b: Boolean) + // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c) + // foo(Foo(23, "foo", true)) + // + // In the snippet above, even though we know that there's a fundep going from T to U + // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype, + // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information + // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want. + // + // =========== THE SOLUTION =========== + // + // To give materializers a chance to say their word before vanilla inference kicks in, + // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo) + // and then trigger macro expansion with the undetermined type parameters still there. + // Thanks to that the materializer can take a look at what's going on and react accordingly. + val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode + if (shouldInstantiate) { + forced += delayed + typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false) + macroExpandApply(typer, delayed, mode, pt) + } else delayed } } + expander(expandee) + } + + /** Expands a term macro used in unapply role as `u.Quasiquote(StringContext("", "")).q.unapply(x)` in `case q"$x" => ...`. + * @see MacroExpander + */ + def macroExpandUnapply(typer: Typer, original: Tree, fun: Tree, unapply: Symbol, args: List[Tree], mode: Mode, pt: Type) = { + val expandee = treeCopy.Apply(original, gen.mkAttributedSelect(fun, unapply), args) + object expander extends TermMacroExpander(UNAPPLY_ROLE, typer, expandee, mode, pt) { + override def allowedExpansions: String = "unapply trees" + override def allowExpandee(expandee: Tree) = expandee.isInstanceOf[Apply] + private def unsupported(what: String) = abort("unapply macros currently don't support " + what) + override def onFallback(fallback: Tree) = unsupported("fallback") + override def onDelayed(delayed: Tree) = unsupported("advanced interaction with type inference") + } + expander(original) + } + + 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) /** 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 @@ -829,15 +729,17 @@ 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 expanded: Tree if expandee.symbol.isTermMacro => validateResultingTree(expanded) + case _ => MacroExpansionHasInvalidTypeError(expandee, expanded) } } catch { case ex: Throwable => @@ -858,7 +760,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) macroLogLite(s"falling back to: $fallbackSym") @@ -886,10 +788,12 @@ 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 forced = perRunCaches.newWeakSet[Tree] + 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 { + if (forced(expandee)) scala.collection.mutable.Set[Int]() + else delayed.getOrElse(expandee, { val calculated = scala.collection.mutable.Set[Symbol]() expandee foreach (sub => { def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym @@ -898,8 +802,8 @@ 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))) @@ -928,13 +832,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 }) @@ -946,3 +850,34 @@ object MacrosStats { val macroExpandCount = Statistics.newCounter ("#macro expansions", "typer") val macroExpandNanos = Statistics.newSubTimer("time spent in macroExpand", typerNanos) } + +class Fingerprint private[Fingerprint](val value: Int) extends AnyVal { + def paramPos = { assert(isTag, this); value } + def isTag = value >= 0 + def isOther = this == Other + def isExpr = this == LiftedTyped + def isTree = this == LiftedUntyped + override def toString = this match { + case Other => "Other" + case LiftedTyped => "Expr" + case LiftedUntyped => "Tree" + case _ => s"Tag($value)" + } +} + +object Fingerprint { + def apply(value: Int) = new Fingerprint(value) + def Tagged(tparamPos: Int) = new Fingerprint(tparamPos) + val Other = new Fingerprint(-1) + val LiftedTyped = new Fingerprint(-2) + val LiftedUntyped = new Fingerprint(-3) +} + +class MacroRole private[MacroRole](val name: String) extends AnyVal { + override def toString = name +} + +object MacroRole { + val Apply = new MacroRole("apply") + val Unapply = new MacroRole("unapply") +} diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 99557d1527..3a5845c8ca 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 } @@ -26,70 +25,34 @@ trait MethodSynthesis { type TT[T] = ru.TypeTag[T] type CT[T] = ClassTag[T] - def ValOrDefDef(sym: Symbol, body: Tree) = + def newValOrDefDef(sym: Symbol, body: Tree) = 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,19 +62,21 @@ 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 } private def finishMethod(method: Symbol, f: Symbol => Tree): Tree = - localTyper typed ValOrDefDef(method, f(method)) + localTyper typed newValOrDefDef(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)) @@ -151,7 +103,7 @@ trait MethodSynthesis { createMethod(original)(m => gen.mkMethodCall(newMethod, transformArgs(m.paramss.head map Ident))) def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = { - createMethod(name, List(IntClass.tpe), returnType) { m => + createMethod(name, List(IntTpe), returnType) { m => val arg0 = Ident(m.firstParam) val default = DEFAULT ==> THROW(IndexOutOfBoundsExceptionClass, arg0) val cases = range.map(num => CASE(LIT(num)) ==> f(num)).toList :+ default @@ -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 @@ -276,7 +259,7 @@ trait MethodSynthesis { * So it's important that creating an instance of Derived does not have a side effect, * or if it has a side effect, control that it is done only once. */ - trait Derived { + sealed trait Derived { /** The tree from which we are deriving a synthetic member. Typically, that's * given as an argument of the instance. */ @@ -305,22 +288,21 @@ trait MethodSynthesis { def derivedTree: Tree } - trait DerivedFromMemberDef extends Derived { + sealed trait DerivedFromMemberDef extends Derived { def tree: MemberDef def enclClass: Symbol // 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 { + sealed trait DerivedFromClassDef extends DerivedFromMemberDef { def tree: ClassDef final def enclClass = basisSym.owner.enclClass } - trait DerivedFromValDef extends DerivedFromMemberDef { + sealed trait DerivedFromValDef extends DerivedFromMemberDef { def tree: ValDef final def enclClass = basisSym.enclClass @@ -359,10 +341,10 @@ trait MethodSynthesis { logDerived(derivedTree) } } - trait DerivedGetter extends DerivedFromValDef { + sealed trait DerivedGetter extends DerivedFromValDef { // TODO } - trait DerivedSetter extends DerivedFromValDef { + sealed trait DerivedSetter extends DerivedFromValDef { override def isSetter = true private def setterParam = derivedSym.paramss match { case (p :: Nil) :: _ => p @@ -396,11 +378,11 @@ trait MethodSynthesis { def name: TermName = tree.name.toTermName } - abstract class BaseGetter(tree: ValDef) extends DerivedGetter { + sealed abstract class BaseGetter(tree: ValDef) extends DerivedGetter { def name = tree.name def category = GetterTargetClass def flagsMask = GetterFlags - def flagsExtra = ACCESSOR | ( if (tree.mods.isMutable) 0 else STABLE ) + def flagsExtra = ACCESSOR.toLong | ( if (tree.mods.isMutable) 0 else STABLE ) override def validate() { assert(derivedSym != NoSymbol, tree) @@ -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 @@ -528,7 +510,7 @@ trait MethodSynthesis { def flagsExtra = 0 override def derivedSym = enclClass.info decl name } - trait AnyBeanGetter extends BeanAccessor with DerivedGetter { + sealed trait AnyBeanGetter extends BeanAccessor with DerivedGetter { def category = BeanGetterTargetClass override def validate() { if (derivedSym == NoSymbol) { @@ -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 bb938074cb..454f913412 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. * @@ -36,7 +35,8 @@ trait Namers extends MethodSynthesis { } def apply(tree: Tree) = { val r = transform(tree) - if (r.exists(_.isEmpty)) TypeTree() + if (r exists { case tt: TypeTree => tt.isEmpty case _ => false }) + TypeTree() else r } } @@ -49,10 +49,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 +150,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 +173,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 +203,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 +255,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 @@ -275,10 +291,13 @@ trait Namers extends MethodSynthesis { } private def logAssignSymbol(tree: Tree, sym: Symbol): Symbol = { - sym.name.toTermName match { + if (isPastTyper) sym.name.toTermName match { case nme.IMPORT | nme.OUTER | nme.ANON_CLASS_NAME | nme.ANON_FUN_NAME | nme.CONSTRUCTOR => () case _ => - log("[+symbol] " + sym.debugLocationString) + tree match { + case md: DefDef => log("[+symbol] " + sym.debugLocationString) + case _ => + } } tree.symbol = sym sym @@ -300,15 +319,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 +354,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 +371,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 +384,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 } } @@ -375,12 +393,10 @@ trait Namers extends MethodSynthesis { * has been defined in a separate file. */ private def validateCompanionDefs(tree: ImplDef) { - val sym = tree.symbol - if (sym eq NoSymbol) return - + val sym = tree.symbol orElse { 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 +442,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 +505,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 +525,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 +562,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 +603,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 +625,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 +646,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 +656,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 +674,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 +692,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 +704,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) = { @@ -764,7 +767,7 @@ trait Namers extends MethodSynthesis { def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym => logAndValidate(sym) { sym setInfo { - val tp = if (isSetter) MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitClass.tpe) + val tp = if (isSetter) MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitTpe) else NullaryMethodType(typeSig(tree)) pluginsTypeSigAccessor(tp, typer, tree, sym) } @@ -807,23 +810,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 +850,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 +865,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) AnyRefTpe + else tpt.tpe } - val parents = typer.parentTypes(templ) map checkParent + val parents = typer.typedParentTypes(templ) map checkParent enterSelf(templ.self) @@ -901,11 +895,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 +944,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 +990,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 +1024,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 +1048,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 @@ -1093,6 +1086,9 @@ trait Namers extends MethodSynthesis { overriddenTp = overriddenTp.resultType } + // SI-7668 Substitute parameters from the parent method with those of the overriding method. + overriddenTp = overriddenTp.substSym(overridden.paramss.flatten, vparamss.flatten.map(_.symbol)) + overriddenTp match { case NullaryMethodType(rtpe) => overriddenTp = rtpe case MethodType(List(), rtpe) => overriddenTp = rtpe @@ -1111,7 +1107,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 +1143,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) } @@ -1276,23 +1272,16 @@ trait Namers extends MethodSynthesis { val defaultTree = atPos(vparam.pos.focus) { DefDef( - Modifiers(meth.flags & DefaultGetterFlags) | SYNTHETIC | DEFAULTPARAM | oflag, + Modifiers(meth.flags & DefaultGetterFlags) | (SYNTHETIC | DEFAULTPARAM | oflag).toLong, name, deftParams, defvParamss, defTpt, defRhs) } 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) @@ -1358,20 +1347,22 @@ trait Namers extends MethodSynthesis { private def importSig(imp: Import) = { val Import(expr, selectors) = imp val expr1 = typer.typedQualifier(expr) - typer checkStable expr1 + if (expr1.symbol != null && expr1.symbol.isRootPackage) RootImportError(imp) if (expr1.isErrorTyped) ErrorType else { + if (!treeInfo.isStableIdentifierPattern(expr1)) + typer.TyperErrorGen.UnstableTreeError(expr1) + val newImport = treeCopy.Import(imp, expr1, selectors).asInstanceOf[Import] checkSelectors(newImport) 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 +1384,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 +1400,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 @@ -1425,7 +1418,7 @@ trait Namers extends MethodSynthesis { annCtx.setReportErrors() // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. AnnotationInfo lazily { - beforeTyper(newTyper(annCtx) typedAnnotation ann) + enteringTyper(newTyper(annCtx) typedAnnotation ann) } } if (ainfos.nonEmpty) { @@ -1477,12 +1470,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 = { @@ -1512,8 +1499,8 @@ trait Namers extends MethodSynthesis { private object RestrictJavaArraysMap extends TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(pre, ArrayClass, List(elemtp)) - if elemtp.typeSymbol.isAbstractType && !(elemtp <:< ObjectClass.tpe) => - TypeRef(pre, ArrayClass, List(intersectionType(List(elemtp, ObjectClass.tpe)))) + if elemtp.typeSymbol.isAbstractType && !(elemtp <:< ObjectTpe) => + TypeRef(pre, ArrayClass, List(intersectionType(List(elemtp, ObjectTpe)))) case _ => mapOver(tp) } @@ -1535,7 +1522,7 @@ trait Namers extends MethodSynthesis { AbstractMemberWithModiferError(sym, flag) } def checkNoConflict(flag1: Int, flag2: Int) { - if (sym hasAllFlags flag1 | flag2) + if (sym hasAllFlags flag1.toLong | flag2) IllegalModifierCombination(sym, flag1, flag2) } if (sym.isImplicit) { @@ -1543,7 +1530,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) { @@ -1651,7 +1638,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 } } @@ -1705,8 +1692,13 @@ trait Namers extends MethodSynthesis { * call this method? */ def companionSymbolOf(original: Symbol, ctx: Context): Symbol = { + val owner = original.owner + // SI-7264 Force the info of owners from previous compilation runs. + // Doing this generally would trigger cycles; that's what we also + // use the lower-level scan through the current Context as a fall back. + if (!currentRun.compiles(owner)) owner.initialize original.companionSymbol orElse { - ctx.lookup(original.name.companionName, original.owner).suchThat(sym => + ctx.lookup(original.name.companionName, owner).suchThat(sym => (original.isTerm || sym.hasModuleFlag) && (sym isCoDefinedWith original) ) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index aafff8a48e..dea4c46e79 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 uncheckedBounds(qual.tpe) + val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos, newFlags = ARTIFACT) setInfo uncheckedBounds(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. @@ -204,7 +202,7 @@ trait NamesDefaults { self: Analyzer => if (module == NoSymbol) None else { val ref = atPos(pos.focus)(gen.mkAttributedRef(pre, module)) - if (module.isStable && pre.isStable) // fixes #4524. the type checker does the same for + if (treeInfo.admitsTypeSelection(ref)) // fixes #4524. the type checker does the same for ref.setType(singleType(pre, module)) // typedSelect, it calls "stabilize" on the result. Some(ref) } @@ -259,7 +257,7 @@ trait NamesDefaults { self: Analyzer => } } - /** + /* * For each argument (arg: T), create a local value * x$n: T = arg * @@ -281,8 +279,8 @@ 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 { // TODO In 83c9c764b, we tried to a stable type here to fix SI-7234. But the resulting TypeTree over a @@ -291,7 +289,7 @@ trait NamesDefaults { self: Analyzer => arg.tpe } ).widen // have to widen or types inferred from literal defaults will be singletons - val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo { + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos, newFlags = ARTIFACT) setInfo { val tp = if (byName) functionType(Nil, argTpe) else argTpe uncheckedBounds(tp) } @@ -308,11 +306,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))) @@ -331,7 +326,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) @@ -379,7 +374,9 @@ trait NamesDefaults { self: Analyzer => } } - def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOf _): (List[Symbol], Boolean) = { + def makeNamedTypes(syms: List[Symbol]) = syms map (sym => NamedType(sym.name, sym.tpe)) + + 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) @@ -414,7 +411,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) @@ -460,20 +457,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. */ @@ -488,12 +471,10 @@ 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 - case _ => tp - }) + override def apply(tp: Type): Type = super.apply(dropByName(tp)) } // This throws an exception which is caught in `tryTypedApply` (as it // uses `silent`) - unfortunately, tryTypedApply recovers from the diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala new file mode 100644 index 0000000000..8bf9ce49be --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -0,0 +1,471 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package tools +package nsc +package typechecker + +import scala.collection.mutable +import symtab.Flags +import Mode._ + + /** + * + * A pattern match such as + * + * x match { case Foo(a, b) => ...} + * + * Might match an instance of any of the following definitions of Foo. + * Note the analogous treatment between case classes and unapplies. + * + * case class Foo(xs: Int*) + * case class Foo(a: Int, xs: Int*) + * case class Foo(a: Int, b: Int) + * case class Foo(a: Int, b: Int, xs: Int*) + * + * object Foo { def unapplySeq(x: Any): Option[Seq[Int]] } + * object Foo { def unapplySeq(x: Any): Option[(Int, Seq[Int])] } + * object Foo { def unapply(x: Any): Option[(Int, Int)] } + * object Foo { def unapplySeq(x: Any): Option[(Int, Int, Seq[Int])] } + */ + +trait PatternTypers { + self: Analyzer => + + import global._ + import definitions._ + + private object FixedAndRepeatedTypes { + def unapply(types: List[Type]) = types match { + case init :+ last if isRepeatedParamType(last) => Some((init, dropRepeated(last))) + case _ => Some((types, NoType)) + } + } + + // 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: interactive compilation (we run it for scaladoc due to SI-5933) + protected def newPatternMatching = true // presently overridden in the presentation compiler + + trait PatternTyper { + self: Typer => + + import TyperErrorGen._ + import infer._ + + private def unit = context.unit + + // 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) + private def hasUnapplyMember(tpe: Type): Boolean = reallyExists(unapplyMember(tpe)) + private def hasUnapplyMember(sym: Symbol): Boolean = hasUnapplyMember(sym.tpe_*) + private def hasUnapplyMember(fun: Tree): Boolean = hasUnapplyMember(fun.symbol) || hasUnapplyMember(fun.tpe) + + // ad-hoc overloading resolution to deal with unapplies and case class constructors + // If some but not all alternatives survive filtering the tree's symbol with `p`, + // then update the tree's symbol and type to exclude the filtered out alternatives. + private def inPlaceAdHocOverloadingResolution(fun: Tree)(p: Symbol => Boolean): Tree = fun.symbol filter p match { + case sym if sym.exists && (sym ne fun.symbol) => fun setSymbol sym modifyType (tp => filterOverloadedAlts(tp)(p)) + case _ => fun + } + private def filterOverloadedAlts(tpe: Type)(p: Symbol => Boolean): Type = tpe match { + case OverloadedType(pre, alts) => overloadedType(pre, alts filter p) + case tp => tp + } + + def typedConstructorPattern(fun0: Tree, pt: Type) = { + // Do some ad-hoc overloading resolution and update the tree's symbol and type + // do not update the symbol if the tree's symbol's type does not define an unapply member + // (e.g. since it's some method that returns an object with an unapply member) + val fun = inPlaceAdHocOverloadingResolution(fun0)(hasUnapplyMember) + def caseClass = fun.tpe.typeSymbol.linkedClassOfClass + + // Dueling test cases: pos/overloaded-unapply.scala, run/case-class-23.scala, pos/t5022.scala + // A case class with 23+ params has no unapply method. + // A case class constructor be overloaded with unapply methods in the companion. + if (caseClass.isCase && !unapplyMember(fun.tpe).isOverloaded) + convertToCaseConstructor(fun, caseClass, pt) + else if (hasUnapplyMember(fun)) + fun + else + CaseClassConstructorError(fun) + } + + def expectedPatternTypes(fun: Tree, args: List[Tree]): List[Type] = + newExtractorShape(fun, args).expectedPatternTypes + + def typedPatternArgs(fun: Tree, args: List[Tree], mode: Mode): List[Tree] = + typedArgsForFormals(args, newExtractorShape(fun, args).formals, mode) + + def typedArgsForFormals(args: List[Tree], formals: List[Type], mode: Mode): List[Tree] = { + def typedArgWithFormal(arg: Tree, pt: Type) = { + val newMode = if (isByNameParamType(pt)) mode.onlySticky else mode.onlySticky | BYVALmode + typedArg(arg, mode, newMode, dropByName(pt)) + } + val FixedAndRepeatedTypes(fixed, elem) = formals + val front = (args, fixed).zipped map typedArgWithFormal + def rest = context withinStarPatterns (args drop front.length map (typedArgWithFormal(_, elem))) + + elem match { + case NoType => front + case _ => front ::: rest + } + } + + private def boundedArrayType(bound: Type): Type = { + val tparam = context.owner freshExistential "" setInfo (TypeBounds upper bound) + newExistentialType(tparam :: Nil, arrayType(tparam.tpe_*)) + } + + protected def typedStarInPattern(tree: Tree, mode: Mode, pt: Type) = { + val Typed(expr, tpt) = tree + val exprTyped = typed(expr, mode) + val baseClass = exprTyped.tpe.typeSymbol match { + case ArrayClass => ArrayClass + case _ => SeqClass + } + val starType = baseClass match { + case ArrayClass if isPrimitiveValueType(pt) || !isFullyDefined(pt) => arrayType(pt) + case ArrayClass => boundedArrayType(pt) + case _ => seqType(pt) + } + val exprAdapted = adapt(exprTyped, mode, starType) + exprAdapted.tpe baseType baseClass match { + case TypeRef(_, _, elemtp :: Nil) => treeCopy.Typed(tree, exprAdapted, tpt setType elemtp) setType elemtp + case _ => setError(tree) + } + } + + protected def typedInPattern(tree: Typed, mode: Mode, pt: Type) = { + val Typed(expr, tpt) = tree + val tptTyped = typedType(tpt, mode) + val tpe = tptTyped.tpe + val exprTyped = typed(expr, mode, tpe.deconst) + val extractor = extractorForUncheckedType(tpt.pos, tpe) + + val canRemedy = tpe match { + case RefinedType(_, decls) if !decls.isEmpty => false + case RefinedType(parents, _) if parents exists isUncheckable => false + case _ => extractor.nonEmpty + } + + val ownType = inferTypedPattern(tptTyped, tpe, pt, canRemedy) + val treeTyped = treeCopy.Typed(tree, exprTyped, tptTyped) setType ownType + + extractor match { + case EmptyTree => treeTyped + case _ => wrapClassTagUnapply(treeTyped, extractor, tpe) + } + } + + def newExtractorShape(tree: Tree): ExtractorShape = tree match { + case Apply(fun, args) => ExtractorShape(fun, args) + case UnApply(fun, args) => ExtractorShape(fun, args) + } + def newExtractorShape(fun: Tree, args: List[Tree]): ExtractorShape = ExtractorShape(fun, args) + + case class CaseClassInfo(clazz: Symbol, classType: Type) { + def constructor = clazz.primaryConstructor + def constructorType = classType.prefix memberType clazz memberType constructor + def paramTypes = constructorType.paramTypes + def accessors = clazz.caseFieldAccessors + def accessorTypes = accessors map (m => (classType memberType m).finalResultType) + // def inverted = MethodType(clazz :: Nil, tupleType(accessorTypes)) + } + object NoCaseClassInfo extends CaseClassInfo(NoSymbol, NoType) { + override def toString = "NoCaseClassInfo" + } + + case class UnapplyMethodInfo(unapply: Symbol, tpe: Type) { + def name = unapply.name + def isUnapplySeq = name == nme.unapplySeq + def unapplyType = tpe memberType method + def resultType = tpe.finalResultType + def method = unapplyMember(tpe) + def paramType = firstParamType(unapplyType) + def rawGet = if (isBool) UnitTpe else typeOfMemberNamedGetOrSelf(resultType) + def rawTypes = if (isBool) Nil else typesOfSelectorsOrSelf(rawGet) + def rawArity = rawTypes.size + def isBool = resultType =:= BooleanTpe // aka "Tuple0" or "Option[Unit]" + def isNothing = rawGet =:= NothingTpe + def isCase = method.isCase + } + + object NoUnapplyMethodInfo extends UnapplyMethodInfo(NoSymbol, NoType) { + override def toString = "NoUnapplyMethodInfo" + } + + case class ExtractorShape(fun: Tree, args: List[Tree]) { + def pos = fun.pos + private def symbol = fun.symbol + private def tpe = fun.tpe + + val ccInfo = tpe.typeSymbol.linkedClassOfClass match { + case clazz if clazz.isCase => CaseClassInfo(clazz, tpe) + case _ => NoCaseClassInfo + } + val exInfo = UnapplyMethodInfo(symbol, tpe) + import exInfo.{ rawGet, rawTypes, isUnapplySeq } + + override def toString = s"ExtractorShape($fun, $args)" + + def unapplyMethod = exInfo.method + def unapplyType = exInfo.unapplyType + def unapplyParamType = exInfo.paramType + def caseClass = ccInfo.clazz + def enclClass = symbol.enclClass + + // TODO - merge these. The difference between these two methods is that expectedPatternTypes + // expands the list of types so it is the same length as the number of patterns, whereas formals + // leaves the varargs type unexpanded. + def formals = ( + if (isUnapplySeq) productTypes :+ varargsType + else if (elementArity == 0) productTypes + else if (isSingle) squishIntoOne() + else wrongArity(patternFixedArity) + ) + def expectedPatternTypes = elementArity match { + case 0 => productTypes + case _ if elementArity > 0 && isUnapplySeq => productTypes ::: elementTypes + case _ if productArity > 1 && patternFixedArity == 1 => squishIntoOne() + case _ => wrongArity(patternFixedArity) + } + + def elementType = elementTypeOfLastSelectorOrSelf(rawGet) + + private def hasBogusExtractor = directUnapplyMember(tpe).exists && !unapplyMethod.exists + private def expectedArity = "" + productArity + ( if (isUnapplySeq) "+" else "") + private def wrongArityMsg(n: Int) = ( + if (hasBogusExtractor) s"$enclClass does not define a valid extractor method" + else s"wrong number of patterns for $enclClass offering $rawTypes_s: expected $expectedArity, found $n" + ) + private def rawTypes_s = rawTypes match { + case Nil => "()" + case tp :: Nil => "" + tp + case tps => tps.mkString("(", ", ", ")") + } + + private def err(msg: String) = { unit.error(pos, msg) ; throw new TypeError(msg) } + private def wrongArity(n: Int) = err(wrongArityMsg(n)) + + def squishIntoOne() = { + if (settings.lint) + unit.warning(pos, s"$enclClass expects $expectedArity patterns to hold $rawGet but crushing into $productArity-tuple to fit single pattern (SI-6675)") + + rawGet :: Nil + } + // elementArity is the number of non-sequence patterns minus the + // the number of non-sequence product elements returned by the extractor. + // If it is zero, there is a perfect match between those parts, and + // if there is a wildcard star it will match any sequence. + // If it is positive, there are more patterns than products, + // so a sequence will have to fill in the elements. If it is negative, + // there are more products than patterns, which is a compile time error. + def elementArity = patternFixedArity - productArity + def patternFixedArity = treeInfo effectivePatternArity args + def productArity = productTypes.size + def isSingle = !isUnapplySeq && (patternFixedArity == 1) + + def productTypes = if (isUnapplySeq) rawTypes dropRight 1 else rawTypes + def elementTypes = List.fill(elementArity)(elementType) + def varargsType = scalaRepeatedType(elementType) + } + + private class VariantToSkolemMap extends TypeMap(trackVariance = true) { + private val skolemBuffer = mutable.ListBuffer[TypeSymbol]() + + def skolems = try skolemBuffer.toList finally skolemBuffer.clear() + def apply(tp: Type): Type = mapOver(tp) match { + // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 + case tp @ TypeRef(NoPrefix, tpSym, Nil) if tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + if (variance.isInvariant) { + // if (variance.isInvariant) tpSym.tpeHK.bounds + devWarning(s"variantToSkolem skipping rewrite of $tpSym due to invariance") + return tp + } + val bounds = ( + if (variance.isPositive) TypeBounds.upper(tpSym.tpeHK) + else TypeBounds.lower(tpSym.tpeHK) + ) + // origin must be the type param so we can deskolemize + val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) + skolemBuffer += skolem + skolem.tpe_* + case tp1 => tp1 + } + } + /* + * 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). + * + * Consider the following example: + * + * class AbsWrapperCov[+A] + * case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] + * + * def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { + * case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it + * wrapped // : Wrapped[_ <: T] + * } + * + * this method should type check if and only if Wrapped is covariant in its type parameter + * + * when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], + * we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T} + * as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound + * + * since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?), + * we can simply replace skolems that represent method type parameters as seen from the method's body + * by other skolems that are (upper/lower)-bounded by that type-parameter skolem + * (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt) + * + * see test/files/../t5189*.scala + */ + private def convertToCaseConstructor(tree: Tree, caseClass: Symbol, pt: Type): Tree = { + val variantToSkolem = new VariantToSkolemMap + val caseConstructorType = tree.tpe.prefix memberType caseClass memberType caseClass.primaryConstructor + val tree1 = TypeTree(caseConstructorType) setOriginal tree + + // 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 = variantToSkolem.skolems + + // use "tree" for the context, not context.tree: don't make another CaseDef context, + // as instantiateTypeVar's bounds would end up there + log(s"convert ${tree.summaryString}: ${tree.tpe} to case constructor, pt=$ptSafe") + + val ctorContext = context.makeNewScope(tree, context.owner) + freeVars foreach ctorContext.scope.enter + newTyper(ctorContext).infer.inferConstructorInstance(tree1, caseClass.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 extrapolator = new ExistentialExtrapolation(freeVars) + def extrapolate(tp: Type) = extrapolator extrapolate 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 modifyType { + case MethodType(ctorArgs, restpe) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node + copyMethodType(tree1.tpe, ctorArgs map (_ modifyInfo extrapolate), extrapolate(restpe)) // no need to clone ctorArgs, this is OUR method type + case tp => tp + } + } + + 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 } + + if (args.length > MaxTupleArity) + return duplErrorTree(TooManyArgsPatternError(fun)) + + def freshArgType(tp: Type): Type = tp match { + case MethodType(param :: _, _) => param.tpe + case PolyType(tparams, restpe) => createFromClonedSymbols(tparams, freshArgType(restpe))(polyType) + case OverloadedType(_, _) => OverloadedUnapplyError(fun) ; ErrorType + case _ => UnapplyWithSingleArgError(fun) ; ErrorType + } + val shape = newExtractorShape(fun, args) + import shape.{ unapplyParamType, unapplyType, unapplyMethod } + + def extractor = extractorForUncheckedType(shape.pos, unapplyParamType) + def canRemedy = unapplyParamType match { + case RefinedType(_, decls) if !decls.isEmpty => false + case RefinedType(parents, _) if parents exists isUncheckable => false + case _ => extractor.nonEmpty + } + + def freshUnapplyArgType(): Type = { + val GenPolyType(freeVars, unappFormal) = freshArgType(unapplyType.skolemizeExistential(context.owner, tree)) + val unapplyContext = context.makeNewScope(context.tree, context.owner) + freeVars foreach unapplyContext.scope.enter + val pattp = newTyper(unapplyContext).infer.inferTypedPattern(tree, unappFormal, pt, canRemedy) + // turn any unresolved type variables in freevars into existential skolems + val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) + pattp.substSym(freeVars, skolems) + } + + val unapplyArg = ( + context.owner.newValue(nme.SELECTOR_DUMMY, fun.pos, Flags.SYNTHETIC) setInfo ( + if (isApplicableSafe(Nil, unapplyType, pt :: Nil, WildcardType)) pt + else freshUnapplyArgType() + ) + ) + // clearing the type is necessary so that ref will be stabilized; see bug 881 + val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapplyMethod), Ident(unapplyArg) :: Nil)) + + def makeTypedUnApply() = { + // the union of the expected type and the inferred type of the argument to unapply + val glbType = glb(ensureFullyDefined(pt) :: unapplyArg.tpe_* :: Nil) + val wrapInTypeTest = canRemedy && !(fun1.symbol.owner isNonBottomSubClass ClassTagClass) + val args1 = typedPatternArgs(fun1, args, mode) + val result = UnApply(fun1, args1) setPos tree.pos setType glbType + + if (wrapInTypeTest) + wrapClassTagUnapply(result, extractor, glbType) + else + result + } + + if (fun1.tpe.isErroneous) + duplErrTree + else if (unapplyMethod.isMacro && !fun1.isInstanceOf[Apply]) + duplErrorTree(WrongShapeExtractorExpansion(tree)) + else + makeTypedUnApply() + } + + def wrapClassTagUnapply(uncheckedPattern: Tree, classTagExtractor: Tree, pt: Type): Tree = { + // TODO: disable when in unchecked match + // we don't create a new Context for a Match, so find the CaseDef, + // then go out one level and navigate back to the match that has this case + val args = List(uncheckedPattern) + val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) + // must call doTypedUnapply directly, as otherwise we get undesirable rewrites + // and re-typechecks of the target of the unapply call in PATTERNmode, + // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, + // but an arbitrary tree as is the case here + val res = doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) + + log(sm""" + |wrapClassTagUnapply { + | pattern: $uncheckedPattern + | extract: $classTagExtractor + | pt: $pt + | res: $res + |}""".trim) + + res + } + + // 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): Tree = { + if (isPastTyper || (pt eq NoType)) EmptyTree else { + pt match { + case RefinedType(parents, decls) if !decls.isEmpty || (parents exists isUncheckable) => return EmptyTree + case _ => + } + // only look at top-level type, can't (reliably) do anything about unchecked type args (in general) + // 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 + } + if (isCheckable(pt1)) EmptyTree + else resolveClassTag(pos, pt1) match { + case tree if unapplyMember(tree.tpe).exists => tree + case _ => devWarning(s"Cannot create runtime type test for $pt1") ; EmptyTree + } + } + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 09c4878b2f..32e908e03b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -59,7 +59,22 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans override def changesBaseClasses = false override def transformInfo(sym: Symbol, tp: Type): Type = { - if (sym.isModule && !sym.isStatic) sym setFlag (lateMETHOD | STABLE) + // !!! This is a sketchy way to do things. + // It would be better to replace the module symbol with a method symbol + // rather than creating this module/method hybrid which must be special + // cased all over the place. Look for the call sites which use(d) some + // variation of "isMethod && !isModule", which to an observer looks like + // a nonsensical condition. (It is now "isModuleNotMethod".) + if (sym.isModule && !sym.isStatic) { + sym setFlag lateMETHOD | STABLE + // Note that this as far as we can see it works equally well + // to set the METHOD flag here and dump lateMETHOD, but it does + // mean that under separate compilation the typer will see + // modules as methods (albeit stable ones with singleton types.) + // So for now lateMETHOD lives while we try to convince ourselves + // we can live without it or deliver that info some other way. + log(s"Stabilizing module method for ${sym.fullLocationString}") + } super.transformInfo(sym, tp) } @@ -71,7 +86,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,9 +110,14 @@ 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 + @inline final def savingInPattern[A](body: => A): A = { + val saved = inPattern + try body finally inPattern = saved + } + var checkedCombinations = Set[List[Type]]() // only one overloaded alternative is allowed to define default arguments @@ -111,7 +131,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val defaultMethodNames = defaultGetters map (sym => nme.defaultGetterToMethod(sym.name)) defaultMethodNames.toList.distinct foreach { name => - val methods = clazz.info.findMember(name, 0L, METHOD, false).alternatives + val methods = clazz.info.findMember(name, 0L, METHOD, stableOnly = false).alternatives def hasDefaultParam(tpe: Type): Boolean = tpe match { case MethodType(params, restpe) => (params exists (_.hasDefault)) || hasDefaultParam(restpe) case _ => false @@ -133,7 +153,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) @@ -187,7 +216,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val inherited = clazz.info.nonPrivateMemberAdmitting(member.name, VBRIDGE) // Delaying calling memberType as long as possible - if (inherited ne NoSymbol) { + if (inherited.exists) { val jtpe = toJavaRepeatedParam(self memberType member) // this is a bit tortuous: we look for non-private members or bridges // if we find a bridge everything is OK. If we find another member, @@ -241,7 +270,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 +302,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 +328,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 +382,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 +406,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 && !other.hasFlag(DEFAULTMETHOD) && !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 +428,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 +446,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 && member.extendedOverriddenSymbols.forall(_.isDeferred)) { // (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 +469,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 +494,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) { @@ -488,13 +516,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } if (member.isStable && !otherTp.isVolatile) { - if (memberTp.isVolatile) + // (1.4), pt 2 -- member.isStable && memberTp.isVolatile started being possible after SI-6815 + // (before SI-6815, !symbol.tpe.isVolatile was implied by symbol.isStable) + // TODO: allow overriding when @uncheckedStable? + 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 +546,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 +580,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) ) @@ -578,8 +609,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def stubImplementations: List[String] = { // Grouping missing methods by the declaring class val regrouped = missingMethods.groupBy(_.owner).toList - def membersStrings(members: List[Symbol]) = - members.sortBy("" + _.name) map (m => m.defStringSeenAs(clazz.tpe memberType m) + " = ???") + def membersStrings(members: List[Symbol]) = { + members foreach fullyInitializeSymbol + members.sortBy(_.name) map (m => m.defStringSeenAs(clazz.tpe_* memberType m) + " = ???") + } if (regrouped.tail.isEmpty) membersStrings(regrouped.head._2) @@ -718,16 +751,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, ObjectTpe) 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 +775,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 +810,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 +837,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 +865,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 +874,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 +930,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 +946,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 _ => @@ -1069,6 +958,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match { case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) => + // Make sure the 'eq' or 'ne' method is the one in AnyRef. def isReferenceOp = fn.symbol == Object_eq || fn.symbol == Object_ne def isNew(tree: Tree) = tree match { case Function(_, _) @@ -1088,7 +978,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 @@ -1113,21 +1003,18 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // equals method inherited from Object or a case class synthetic equals (for // which we know the logic.) def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) - def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info) + def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info) def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass 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 @@ -1174,7 +1061,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() } @@ -1201,7 +1088,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // better to have lubbed and lost def warnIfLubless(): Unit = { val common = global.lub(List(actual.tpe, receiver.tpe)) - if (ObjectClass.tpe <:< common) + if (ObjectTpe <:< common) unrelatedTypes() } // warn if actual has a case parent that is not same as receiver's; @@ -1249,8 +1136,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) @@ -1268,57 +1155,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 { @@ -1332,7 +1223,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 @@ -1353,7 +1244,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)) @@ -1474,7 +1365,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)) } @@ -1485,6 +1376,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans member.typeParams.map(_.info.bounds.hi.widen) foreach checkAccessibilityOfType } + private def checkByNameRightAssociativeDef(tree: DefDef) { + tree match { + case DefDef(_, name, _, params :: _, _, _) => + if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol))) + unit.warning(tree.pos, + "by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.") + case _ => + } + } + /** Check that a deprecated val or def does not override a * concrete, non-deprecated method. If it does, then * deprecation is meaningless. @@ -1546,6 +1447,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def applyRefchecksToAnnotations(tree: Tree): Unit = { def applyChecks(annots: List[AnnotationInfo]) = { + annots foreach (annot => checkCompileTimeOnly(annot.atp.typeSymbol, annot.pos)) checkAnnotations(annots map (_.atp), tree) transformTrees(annots flatMap (_.args)) } @@ -1584,9 +1486,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 = @@ -1630,14 +1536,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) @@ -1645,18 +1551,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.exists) + 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 @@ -1672,7 +1570,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(UnitTpe) else t cond.tpe match { case ConstantType(value) => @@ -1689,8 +1587,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") @@ -1699,10 +1597,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") } } @@ -1725,12 +1623,16 @@ 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) } + tree match { + case dd: DefDef => checkByNameRightAssociativeDef(dd) + case _ => + } tree case Template(parents, self, body) => @@ -1740,6 +1642,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") @@ -1791,12 +1695,13 @@ 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 case Ident(name) => + checkCompileTimeOnly(tree.symbol, tree.pos) transformCaseApply(tree, if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) { assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug @@ -1816,19 +1721,33 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case _ => tree } + // skip refchecks in patterns.... result = result match { case CaseDef(pat, guard, body) => - inPattern = true - val pat1 = transform(pat) - inPattern = false + val pat1 = savingInPattern { + inPattern = true + transform(pat) + } treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) case LabelDef(_, _, _) if treeInfo.hasSynthCaseSymbol(result) => - val old = inPattern - inPattern = true - val res = deriveLabelDef(result)(transform) // TODO SI-7756 Too broad! The code from the original case body should be fully refchecked! - inPattern = old - res + savingInPattern { + inPattern = true + deriveLabelDef(result)(transform) + } + case Apply(fun, args) if fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol) => + savingInPattern { + // SI-7756 If we were in a translated pattern, we can now switch out of pattern mode, as the label apply signals + // that we are in the user-supplied code in the case body. + // + // Relies on the translation of: + // (null: Any) match { case x: List[_] => x; x.reverse; case _ => }' + // to: + // <synthetic> val x2: List[_] = (x1.asInstanceOf[List[_]]: List[_]); + // matchEnd4({ x2; x2.reverse}) // case body is an argument to a label apply. + inPattern = false + super.transform(result) + } case ValDef(_, _, _, _) if treeInfo.hasSynthCaseSymbol(result) => deriveValDef(result)(transform) // SI-7716 Don't refcheck the tpt of the synthetic val that holds the selector. case _ => @@ -1837,14 +1756,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..bbd51b5564 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -4,7 +4,156 @@ package typechecker trait StdAttachments { self: Analyzer => - type UnaffiliatedMacroContext = scala.reflect.macros.runtime.Context + 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.contexts.Context type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type } case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) -}
\ No newline at end of file + + /** 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 = + ( settings.Ymacronoexpand.value // SI-6812 + || tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined + || (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 + + /** @see markMacroImplRef + */ + case object MacroImplRefAttachment + + /** Marks the tree as a macro impl reference, which is a naked reference to a method. + * + * This is necessary for typechecking macro impl references (see `DefaultMacroCompiler.defaultResolveMacroImpl`), + * because otherwise typing a naked reference will result in the "follow this method with `_' if you want to + * treat it as a partially applied function" errors. + * + * This mark suppresses adapt except for when the annottee is a macro application. + */ + def markMacroImplRef(tree: Tree): Tree = tree.updateAttachment(MacroImplRefAttachment) + + /** Unmarks the tree as a macro impl reference (see `markMacroImplRef` for more information). + * + * This is necessary when a tree that was previously deemed to be a macro impl reference, + * typechecks to be a macro application. Then we need to unmark it, expand it and try to treat + * its expansion as a macro impl reference. + */ + def unmarkMacroImplRef(tree: Tree): Tree = tree.removeAttachment[MacroImplRefAttachment.type] + + /** Determines whether a tree should or should not be adapted, + * because someone has put MacroImplRefAttachment on it. + */ + def isMacroImplRef(tree: Tree): Boolean = tree.attachments.get[MacroImplRefAttachment.type].isDefined +} diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index bad49385aa..12d6bb2e6a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -1,9 +1,11 @@ + /* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.tools.nsc +package scala +package tools.nsc package typechecker import scala.collection.{ mutable, immutable } @@ -28,7 +30,7 @@ import symtab.Flags._ */ abstract class SuperAccessors extends transform.Transform with transform.TypingTransformers { import global._ - import definitions.{ UnitClass, ObjectClass, isRepeatedParamType, isByNameParamType, Any_asInstanceOf } + import definitions._ import analyzer.{ restrictionError } /** the following two members override abstract members in Transform */ @@ -60,11 +62,11 @@ 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 + case t if sym.isModuleNotMethod => NullaryMethodType(t) + case t => t } acc setInfoAndEnter (tpe cloneInfo acc) // Diagnostic for SI-7091 @@ -108,11 +110,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) @@ -165,18 +167,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 @@ -203,7 +193,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) => @@ -231,7 +221,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 => @@ -260,9 +250,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. @@ -279,20 +269,21 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT && sym.enclClass != currentClass && !sym.owner.isPackageClass // SI-7091 no accessor needed package owned (ie, top level) symbols && !sym.owner.isTrait - && (sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass) - && (qual.symbol.info.member(sym.name) ne NoSymbol) - && !needsProtectedAccessor(sym, tree.pos)) + && sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass + && qual.symbol.info.member(sym.name).exists + && !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") @@ -300,7 +291,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 @@ -309,7 +300,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 = { @@ -317,8 +308,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) @@ -377,14 +368,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 @@ -402,7 +393,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, { @@ -413,7 +404,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 } @@ -425,7 +416,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) } @@ -462,12 +453,12 @@ 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) + val accessorType = MethodType(params, UnitTpe) protAcc setInfoAndEnter accessorType val obj :: value :: Nil = params @@ -496,9 +487,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..61295b5abd 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,13 +94,13 @@ 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 } } - else AnyClass.tpe + else AnyTpe ) def forwardToRuntime(method: Symbol): Tree = @@ -121,46 +121,36 @@ 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))) - // def productElementNameMethod = perElementMethod(nme.productElementName, StringClass.tpe)(x => LIT(x.name.toString)) + // def productElementNameMethod = perElementMethod(nme.productElementName, StringTpe)(x => LIT(x.name.toString)) 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 - createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => + createMethod(nme.canEqual_, List(AnyTpe), BooleanTpe)(m => 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,18 +189,18 @@ 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 => + def equalsCaseClassMethod: Tree = createMethod(nme.equals_, List(AnyTpe), BooleanTpe) { m => if (accessors.isEmpty) if (clazz.isFinal) thatTest(m) else thatTest(m) AND ((thatCast(m) DOT nme.canEqual_)(mkThis)) @@ -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 => + def equalsDerivedValueClassMethod: Tree = createMethod(nme.equals_, List(AnyTpe), BooleanTpe) { 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 => + def hashCodeDerivedValueClassMethod: Tree = createMethod(nme.hashCode_, Nil, IntTpe) { 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 = { @@ -259,19 +254,20 @@ trait SyntheticMethods extends ast.TreeDSL { def hashcodeImplementation(sym: Symbol): Tree = { sym.tpe.finalResultType.typeSymbol match { - case UnitClass | NullClass => Literal(Constant(0)) - case BooleanClass => If(Ident(sym), Literal(Constant(1231)), Literal(Constant(1237))) - case IntClass | ShortClass | ByteClass | CharClass => Ident(sym) - case LongClass => callStaticsMethod("longHash")(Ident(sym)) - case DoubleClass => callStaticsMethod("doubleHash")(Ident(sym)) - case FloatClass => callStaticsMethod("floatHash")(Ident(sym)) - case _ => callStaticsMethod("anyHash")(Ident(sym)) + case UnitClass | NullClass => Literal(Constant(0)) + case BooleanClass => If(Ident(sym), Literal(Constant(1231)), Literal(Constant(1237))) + case IntClass => Ident(sym) + case ShortClass | ByteClass | CharClass => Select(Ident(sym), nme.toInt) + case LongClass => callStaticsMethod("longHash")(Ident(sym)) + case DoubleClass => callStaticsMethod("doubleHash")(Ident(sym)) + case FloatClass => callStaticsMethod("floatHash")(Ident(sym)) + case _ => callStaticsMethod("anyHash")(Ident(sym)) } } def specializedHashcode = { - createMethod(nme.hashCode_, Nil, IntClass.tpe) { m => - val accumulator = m.newVariable(newTermName("acc"), m.pos, SYNTHETIC) setInfo IntClass.tpe + createMethod(nme.hashCode_, Nil, IntTpe) { m => + val accumulator = m.newVariable(newTermName("acc"), m.pos, SYNTHETIC) setInfo IntTpe val valdef = ValDef(accumulator, Literal(Constant(0xcafebabe))) val mixes = accessors map (acc => Assign( @@ -313,11 +309,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 +331,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 } } @@ -359,7 +357,7 @@ trait SyntheticMethods extends ast.TreeDSL { // This method should be generated as private, but apparently if it is, then // it is name mangled afterward. (Wonder why that is.) So it's only protected. // For sure special methods like "readResolve" should not be mangled. - List(createMethod(nme.readResolve, Nil, ObjectClass.tpe)(m => { m setFlag PRIVATE ; REF(clazz.sourceModule) })) + List(createMethod(nme.readResolve, Nil, ObjectTpe)(m => { m setFlag PRIVATE ; REF(clazz.sourceModule) })) } else Nil ) @@ -368,11 +366,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..32a66aa4dd 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) } @@ -30,7 +30,7 @@ trait Tags { * However we found out that we don't really need this concept, so it got removed. * * @param pos Position for error reporting. Please, provide meaningful value. - * @param tp Type we're looking a ClassTag for, e.g. resolveClassTag(pos, IntClass.tpe) will look for ClassTag[Int]. + * @param tp Type we're looking a ClassTag for, e.g. resolveClassTag(pos, IntTpe) will look for ClassTag[Int]. * @param allowMaterialization If true (default) then the resolver is allowed to launch materialization macros when there's no class tag in scope. * If false then materialization macros are prohibited from running. * @@ -49,7 +49,7 @@ trait Tags { * @param pre Prefix that represents a universe this type tag will be bound to. * If `pre` is set to `NoType`, then any type tag in scope will do, regardless of its affiliation. * If `pre` is set to `NoType`, and tag resolution involves materialization, then `mkRuntimeUniverseRef` will be used. - * @param tp Type we're looking a TypeTag for, e.g. resolveTypeTag(pos, mkRuntimeUniverseRef, IntClass.tpe, false) will look for scala.reflect.runtime.universe.TypeTag[Int]. + * @param tp Type we're looking a TypeTag for, e.g. resolveTypeTag(pos, mkRuntimeUniverseRef, IntTpe, false) will look for scala.reflect.runtime.universe.TypeTag[Int]. * @param concrete If true then the result must not contain unresolved (i.e. not spliced) type parameters and abstract type members. * If false then the function will always succeed (abstract types will be reified as free types). * @param allowMaterialization If true (default) then the resolver is allowed to launch materialization macros when there's no type tag in scope. @@ -69,4 +69,4 @@ trait Tags { resolveTag(pos, taggedTp, allowMaterialization) } } -}
\ No newline at end of file +} 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 2270e812eb..1af176736b 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 @@ -136,7 +126,7 @@ trait TypeDiagnostics { else if (!member.isDeferred) member.accessed else { val getter = if (member.isSetter) member.getter(member.owner) else member - val flags = if (getter.setter(member.owner) != NoSymbol) DEFERRED | MUTABLE else DEFERRED + val flags = if (getter.setter(member.owner) != NoSymbol) DEFERRED.toLong | MUTABLE else DEFERRED getter.owner.newValue(getter.name.toTermName, getter.pos, flags) setInfo getter.tpe.resultType } @@ -153,7 +143,7 @@ trait TypeDiagnostics { def defaultMessage = moduleMessage + preResultString + tree.tpe def applyMessage = defaultMessage + tree.symbol.locationString - if ((sym eq null) || (sym eq NoSymbol)) { + if (!tree.hasExistingSymbol) { if (isTyperInPattern) patternMessage else exprMessage } @@ -174,18 +164,13 @@ 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. - if ((tp eq tp.normalize) || tp.typeSymbolDirect.isInDefaultNamespace) "" - else { + val deepDealias = DealiasedType(tp) + if (tp eq deepDealias) "" else { // A sanity check against expansion being identical to original. - val s = "" + DealiasedType(tp) + val s = "" + deepDealias if (s == "" + tp) "" else "\n (which expands to) " + s } @@ -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 @@ -268,7 +253,7 @@ trait TypeDiagnostics { // For found/required errors where AnyRef would have sufficed: // explain in greater detail. def explainAnyVsAnyRef(found: Type, req: Type): String = { - if (AnyRefClass.tpe <:< req) notAnyRefMessage(found) else "" + if (AnyRefTpe <:< req) notAnyRefMessage(found) else "" } // TODO - figure out how to avoid doing any work at all @@ -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 @@ -442,6 +426,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. @@ -466,17 +566,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 = { - val modeOK = (mode & (EXPRmode | BYVALmode | POLYmode)) == (EXPRmode | BYVALmode) - if (modeOK) apply(tree) - else tree - } + def inMode(mode: Mode, tree: Tree): Tree = if (mode.typingMonoExprByValue) apply(tree) else tree } private def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded @@ -497,7 +593,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) { @@ -506,7 +602,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 bf2170310f..dd16b5be85 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -9,13 +9,15 @@ // Added: Thu Apr 12 18:23:58 2007 //todo: disallow C#D in superclass //todo: treat :::= correctly -package scala.tools.nsc +package scala +package 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,16 +26,15 @@ import symtab.Flags._ * @author Martin Odersky * @version 1.0 */ -trait Typers extends Modes with Adaptations with Tags { +trait Typers extends Adaptations with Tags with TypersTracking with PatternTypers { self: Analyzer => import global._ import definitions._ import TypersStats._ - final def forArgMode(fun: Tree, mode: Int) = - if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode - else mode + final def forArgMode(fun: Tree, mode: Mode) = + if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode else mode // namer calls typer.computeType(rhs) on DefDef / ValDef when tpt is empty. the result // is cached here and re-used in typedDefDef / typedValDef @@ -52,60 +53,64 @@ 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 fold[U](none: => U)(f: T => U): U = this match { + case SilentResultValue(value) => f(value) + case _ => none + } + @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 - - // To enable decent error messages when the typer crashes. - // TODO - this only catches trees which go through def typed, - // but there are all kinds of back ways - typedClassDef, etc. etc. - // Funnel everything through one doorway. - var lastTreeToTyper: Tree = EmptyTree + private final val InterpolatorCodeRegex = """\$\{.*?\}""".r + private final val InterpolatorIdentRegex = """\$\w+""".r - // 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) - - abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors { + abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with PatternTyper with TyperContextErrors { import context0.unit - import typeDebug.{ ptTree, ptBlock, ptLine } + import typeDebug.{ ptTree, ptBlock, ptLine, inGreen, inRed } import TyperErrorGen._ 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 - } + // See SI-3281 re undoLog + override def isCoercible(tp: Type, pt: Type) = undoLog undo viewExists(tp, pt) } + /** 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) + /** Find implicit arguments and pass them to given tree. */ def applyImplicitArgs(fun: Tree): Tree = fun.tpe match { @@ -115,10 +120,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,28 +131,27 @@ 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 { - case Some(divergentImplicit) if !settings.Xdivergence211.value => + context.reportBuffer.errors.collectFirst { + case dte: DivergentImplicitTypeError => dte + } match { + case Some(divergent) => // DivergentImplicit error has higher priority than "no implicit found" // no need to issue the problem again if we are still in silent mode if (context.reportErrors) { - context.issue(divergentImplicit) - context.condBufferFlush(_.kind == ErrorKinds.Divergent) - } - case Some(divergentImplicit: DivergentImplicitTypeError) if settings.Xdivergence211.value => - if (context.reportErrors) { - context.issue(divergentImplicit.withPt(paramTp)) - context.condBufferFlush(_.kind == ErrorKinds.Divergent) + context.issue(divergent.withPt(paramTp)) + context.reportBuffer.clearErrors { + case dte: DivergentImplicitTypeError => true + } } - case None => + case _ => NoImplicitFoundError(fun, param) } paramFailed = true @@ -176,10 +177,17 @@ trait Typers extends Modes with Adaptations with Tags { fun } + def viewExists(from: Type, to: Type): Boolean = ( + !from.isError + && !to.isError + && context.implicitsEnabled + && (inferView(EmptyTree, from, to, reportAmbiguous = false) != EmptyTree) + ) + 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,12 +202,12 @@ trait Typers extends Modes with Adaptations with Tags { debuglog("infer view from "+from+" to "+to)//debug if (isPastTyper) EmptyTree else from match { - case MethodType(_, _) => EmptyTree + case MethodType(_, _) => EmptyTree case OverloadedType(_, _) => EmptyTree - case PolyType(_, _) => EmptyTree - case _ => + 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) @@ -237,32 +245,6 @@ trait Typers extends Modes with Adaptations with Tags { case _ => tp } - /** Check that <code>tree</code> is a stable expression. - * - * @param tree ... - * @return ... - */ - def checkStable(tree: Tree): Tree = ( - if (treeInfo.isExprSafeToInline(tree)) tree - else if (tree.isErrorTyped) tree - else UnstableTreeError(tree) - ) - - /** Would tree be a stable (i.e. a pure expression) if the type - * of its symbol was not volatile? - */ - protected def isStableExceptVolatile(tree: Tree) = { - tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile && - { val savedTpe = tree.symbol.info - val savedSTABLE = tree.symbol getFlag STABLE - tree.symbol setInfo AnyRefClass.tpe - tree.symbol setFlag STABLE - val result = treeInfo.isExprSafeToInline(tree) - tree.symbol setInfo savedTpe - tree.symbol setFlag savedSTABLE - result - } - } private def errorNotClass(tpt: Tree, found: Type) = { ClassTypeRequiredError(tpt, found); false } private def errorNotStable(tpt: Tree, found: Type) = { TypeNotAStablePrefixError(tpt, found); false } @@ -294,16 +276,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) => @@ -314,12 +291,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 => @@ -330,19 +301,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) } } @@ -373,28 +344,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 @@ -470,7 +426,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) = @@ -511,11 +467,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 @@ -533,8 +484,6 @@ trait Typers extends Modes with Adaptations with Tags { typer1 } else this - final val xtypes = false - /** Is symbol defined and not stale? */ def reallyExists(sym: Symbol) = { @@ -553,15 +502,21 @@ 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) && - (xtypes || - (pt.isStable || - (mode & QUALmode) != 0 && !tree.symbol.isConstant || - pt.typeSymbol.isAbstractType && pt.bounds.lo.isStable && !(tree.tpe <:< pt)) || - pt.typeSymbol.isRefinementClass && !(tree.tpe <:< pt)) + private def isStableContext(tree: Tree, mode: Mode, pt: Type) = { + def ptSym = pt.typeSymbol + def expectsStable = ( + pt.isStable + || mode.inQualMode && !tree.symbol.isConstant + || !(tree.tpe <:< pt) && (ptSym.isAbstractType && pt.bounds.lo.isStable || ptSym.isRefinementClass) + ) + + ( isNarrowable(tree.tpe) + && mode.typingExprNotLhs + && expectsStable + ) + } /** Make symbol accessible. This means: * If symbol refers to package object, insert `.package` as second to last selector. @@ -572,11 +527,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 { @@ -605,66 +562,59 @@ 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 + * 1. Check that non-function pattern expressions are stable (ignoring volatility concerns -- SI-6815) + * (and narrow the type of modules: a module reference in a pattern has type Foo.type, not "object Foo") * 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 = { + // Side effect time! Don't be an idiot like me and think you + // can move "val sym = tree.symbol" before this line, because + // inferExprAlternative side-effects the tree's symbol. + 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) - if (sym.isValue) { - val tree1 = checkStable(tree) - // A module reference in a pattern has type Foo.type, not "object Foo" - if (sym.isModule && !sym.isMethod) tree1 setType singleType(pre, sym) - else tree1 - } - else fail() - } else if ((mode & (EXPRmode | QUALmode)) == EXPRmode && !sym.isValue && !phase.erasedTypes) { // (2) - fail() - } else { - if (sym.isStable && pre.isStable && !isByNameParamType(tree.tpe) && - (isStableContext(tree, mode, pt) || sym.isModule && !sym.isMethod)) - tree.setType(singleType(pre, sym)) - // To fully benefit from special casing the return type of - // getClass, we have to catch it immediately so expressions - // like x.getClass().newInstance() are typed with the type of x. - else if ( isGetClass(tree.symbol) - // TODO: If the type of the qualifier is inaccessible, we can cause private types - // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this - // so for now it requires the type symbol be public. - && pre.typeSymbol.isPublic) - tree setType MethodType(Nil, getClassReturnType(pre)) - else - tree - } + val isStableIdPattern = mode.typingPatternNotConstructor && tree.isTerm + + def isModuleTypedExpr = ( + treeInfo.admitsTypeSelection(tree) + && (isStableContext(tree, mode, pt) || sym.isModuleNotMethod) + ) + def isStableValueRequired = ( + isStableIdPattern + || mode.in(all = EXPRmode, none = QUALmode) && !phase.erasedTypes + ) + // To fully benefit from special casing the return type of + // getClass, we have to catch it immediately so expressions like + // x.getClass().newInstance() are typed with the type of x. TODO: If the + // type of the qualifier is inaccessible, we can cause private types to + // escape scope here, e.g. pos/t1107. I'm not sure how to properly handle + // this so for now it requires the type symbol be public. + def isGetClassCall = isGetClass(sym) && pre.typeSymbol.isPublic + + def narrowIf(tree: Tree, condition: Boolean) = + if (condition) tree setType singleType(pre, sym) else tree + + def checkStable(tree: Tree): Tree = + if (treeInfo.isStableIdentifierPattern(tree)) tree + else UnstableTreeError(tree) + + if (tree.isErrorTyped) + tree + else if (!sym.isValue && isStableValueRequired) // (2) + NotAValueError(tree, sym) + else if (isStableIdPattern) // (1) + // A module reference in a pattern has type Foo.type, not "object Foo" + narrowIf(checkStable(tree), sym.isModuleNotMethod) + else if (isModuleTypedExpr) // (3) + narrowIf(tree, true) + else if (isGetClassCall) // (4) + tree setType MethodType(Nil, getClassReturnType(pre)) + else + tree } private def isNarrowable(tpe: Type): Boolean = unwrapWrapperTypes(tpe) match { @@ -672,22 +622,21 @@ 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 - case _ => NoPrefix + case _ => NoPrefix + } + def stabilizable = ( + pre.isStable + && sym.tpe.params.isEmpty + && (isStableContext(tree, mode, pt) || sym.isModule) + ) + tree.tpe match { + case MethodType(_, _) if stabilizable => tree setType MethodType(Nil, singleType(pre, sym)) // TODO: should this be a NullaryMethodType? + case _ => tree } - if (tree.tpe.isInstanceOf[MethodType] && pre.isStable && sym.tpe.params.isEmpty && - (isStableContext(tree, mode, pt) || sym.isModule)) - tree.setType(MethodType(List(), singleType(pre, sym))) // TODO: should this be a NullaryMethodType? - else tree } /** The member with given name of given qualifier tree */ @@ -728,17 +677,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") @@ -775,7 +721,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) { @@ -849,10 +795,12 @@ 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 hasUndets = context.undetparams.nonEmpty + def hasUndetsInMonoMode = hasUndets && !mode.inPolyMode def adaptToImplicitMethod(mt: MethodType): Tree = { - if (context.undetparams.nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0` + if (hasUndets) { // (9) -- should revisit dropped condition `hasUndetsInMonoMode` // dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed // needed for implicits in 2.8 collection library -- maybe once #3346 is fixed, we can reinstate the condition? context.undetparams = inferExprInstance(tree, context.extractUndetparams(), pt, @@ -864,28 +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 => { - 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 _ => - debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) - val tree1 = typed(resetAllAttrs(original), mode, WildcardType) + 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) + } + orElse { _ => + val resetTree = resetLocalAttrs(original) + debuglog(s"fallback on implicits: ${tree}/$resetTree") + val tree1 = typed(resetTree, mode) // 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) - } + } + ) + else + typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + ) } def instantiateToMethodType(mt: MethodType): Tree = { @@ -894,174 +842,78 @@ 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) - debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) + if (!meth.isConstructor && isFunctionType(pt)) { // (4.2) + debuglog(s"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 - // context.undetparams contains clones of meth.typeParams (fresh ones were generated in etaExpand) - // need to run typer on tree0, since etaExpansion sets the tpe's of its subtrees to null - // can't type with the expected type, as we can't recreate the setup in (3) without calling typed - // (note that (3) does not call typed to do the polymorphic type instantiation -- - // it is called after the tree has been typed with a polymorphic expected result type) - instantiate(typed(tree0, mode, WildcardType), mode, pt) - } else + + // #2624: need to infer type arguments for eta expansion of a polymorphic method + // context.undetparams contains clones of meth.typeParams (fresh ones were generated in etaExpand) + // need to run typer on tree0, since etaExpansion sets the tpe's of its subtrees to null + // can't type with the expected type, as we can't recreate the setup in (3) without calling typed + // (note that (3) does not call typed to do the polymorphic type instantiation -- + // it is called after the tree has been typed with a polymorphic expected result type) + if (hasUndets) + instantiate(typed(tree0, mode), mode, pt) + else typed(tree0, mode, pt) - } else if (!meth.isConstructor && mt.params.isEmpty) { // (4.3) - adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt, original) - } else if (context.implicitsEnabled) { + } + else if (!meth.isConstructor && mt.params.isEmpty) // (4.3) + adapt(typed(Apply(tree, Nil) setPos tree.pos), mode, pt, original) + else if (context.implicitsEnabled) MissingArgsForMethodTpeError(tree, meth) - } else { + else setError(tree) - } } def adaptType(): Tree = { - if (inFunMode(mode)) { - // 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 + // @M When not typing a type constructor (!context.inTypeConstructorAllowed) + // or raw type (tree.symbol.isJavaDefined && context.unit.isJava), types must be of kind *, + // and thus parameterized types must be applied to their type arguments + // @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't? + def properTypeRequired = ( + tree.hasSymbolField + && !context.inTypeConstructorAllowed + && !(tree.symbol.isJavaDefined && context.unit.isJava) + ) + // @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!) + // @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) + def kindArityMismatch = ( + context.inTypeConstructorAllowed + && !sameLength(tree.tpe.typeParams, pt.typeParams) + ) + // Note that we treat Any and Nothing as kind-polymorphic. + // We can't perform this check when typing type arguments to an overloaded method before the overload is resolved + // (or in the case of an error type) -- this is indicated by pt == WildcardType (see case TypeApply in typed1). + def kindArityMismatchOk = tree.tpe.typeSymbol match { + case NothingClass | AnyClass => true + case _ => pt == WildcardType + } + + // todo. It would make sense when mode.inFunMode to instead use + // tree setType tree.tpe.normalize + // when 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 + if (mode.inFunMode) tree - } else if (tree.hasSymbol && !tree.symbol.typeParams.isEmpty && !inHKMode(mode) && - !(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 *, - // and thus parameterized types must be applied to their type arguments - // @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't? + else if (properTypeRequired && tree.symbol.typeParams.nonEmpty) // (7) 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: 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) && - !(tree.tpe.typeSymbol == AnyClass || - tree.tpe.typeSymbol == NothingClass || - pt == WildcardType)) { - // Check that the actual kind arity (tree.symbol.typeParams.length) conforms to the expected - // kind-arity (pt.typeParams.length). Full checks are done in checkKindBounds in Infer. - // Note that we treat Any and Nothing as kind-polymorphic. - // We can't perform this check when typing type arguments to an overloaded method before the overload is resolved - // (or in the case of an error type) -- this is indicated by pt == WildcardType (see case TypeApply in typed1). + else if (kindArityMismatch && !kindArityMismatchOk) // (7.1) @M: check kind-arity KindArityMismatchError(tree, pt) - } else tree match { // (6) + else tree match { // (6) case TypeTree() => tree case _ => TypeTree(tree.tpe) setOriginal tree } } - /** - * 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). - * - * Consider the following example: - * - * class AbsWrapperCov[+A] - * case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] - * - * def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { - * case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it - * wrapped // : Wrapped[_ <: T] - * } - * - * this method should type check if and only if Wrapped is covariant in its type parameter - * - * when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], - * we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T} - * as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound - * - * since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?), - * we can simply replace skolems that represent method type parameters as seen from the method's body - * by other skolems that are (upper/lower)-bounded by that type-parameter skolem - * (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt) - * - * see test/files/../t5189*.scala - */ - 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) - val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe) - 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 - // (e.g. since it's some method that returns an object with an unapply member) - if (overloadedExtractorOfObject != NoSymbol) - tree setSymbol overloadedExtractorOfObject - - tree.tpe match { - case OverloadedType(pre, alts) => tree.tpe = 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 - } else { - tree - } - } else { - CaseClassConstructorError(tree) - } - } - def insertApply(): Tree = { - assert(!inHKMode(mode), modeString(mode)) //@M + assert(!context.inTypeConstructorAllowed, mode) //@M val adapted = adaptToName(tree, nme.apply) - def stabilize0(pre: Type): Tree = stabilize(adapted, pre, EXPRmode | QUALmode, WildcardType) + def stabilize0(pre: Type): Tree = stabilize(adapted, pre, MonoQualifierModes, WildcardType) + // TODO reconcile the overlap between Typers#stablize and TreeGen.stabilize val qual = adapted match { case This(_) => @@ -1082,31 +934,199 @@ trait Typers extends Modes with Adaptations with Tags { Select(qual setPos tree.pos.makeTransparent, nme.apply) } } + def adaptConstant(value: Constant): Tree = { + 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) + } + + // Ignore type errors raised in later phases that are due to mismatching types with existential skolems + // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. + // Here's my hypothsis why this happens. The pattern matcher defines a variable of type + // + // val x: T = expr + // + // where T is the type of expr, but T contains existential skolems ts. + // In that case, this value definition does not typecheck. + // The value definition + // + // val x: T forSome { ts } = expr + // + // would typecheck. Or one can simply leave out the type of the `val`: + // + // val x = expr + // + // SI-6029 shows another case where we also fail (in uncurry), but this time the expected + // type is an existential type. + // + // The reason for both failures have to do with the way we (don't) transform + // skolem types along with the trees that contain them. We'd need a + // radically different approach to do it. But before investing a lot of time to + // to do this (I have already sunk 3 full days with in the end futile attempts + // to consistently transform skolems and fix 6029), I'd like to + // investigate ways to avoid skolems completely. + // + // upd. The same problem happens when we try to typecheck the result of macro expansion against its expected type + // (which is the return type of the macro definition instantiated in the context of expandee): + // + // Test.scala:2: error: type mismatch; + // found : $u.Expr[Class[_ <: Object]] + // required: reflect.runtime.universe.Expr[Class[?0(in value <local Test>)]] where type ?0(in value <local Test>) <: Object + // scala.reflect.runtime.universe.reify(new Object().getClass) + // ^ + // Therefore following Martin's advice I use this logic to recover from skolem errors after macro expansions + // (by adding the ` || tree.attachments.get[MacroExpansionAttachment].isDefined` clause to the conditional above). + // + def adaptMismatchedSkolems() = { + def canIgnoreMismatch = ( + !context.reportErrors && isPastTyper + || tree.attachments.get[MacroExpansionAttachment].isDefined + ) + def bound = pt match { + case ExistentialType(qs, _) => qs + case _ => Nil + } + def msg = sm""" + |Recovering from existential or skolem type error in + | $tree + |with type: ${tree.tpe} + | pt: $pt + | context: ${context.tree} + | adapted + """.trim + + val boundOrSkolems = if (canIgnoreMismatch) bound ++ pt.skolemsExceptMethodTypeParams else Nil + boundOrSkolems match { + case Nil => AdaptTypeError(tree, tree.tpe, pt) ; setError(tree) + case _ => logResult(msg)(adapt(tree, mode, deriveTypeWithWildcards(boundOrSkolems)(pt))) + } + } + + def fallbackAfterVanillaAdapt(): Tree = { + def isPopulatedPattern = { + if ((tree.symbol ne null) && tree.symbol.isModule) + inferModulePattern(tree, pt) + + isPopulated(tree.tpe, approximateAbstracts(pt)) + } + if (mode.inPatternMode && isPopulatedPattern) + return tree + + val tree1 = constfold(tree, pt) // (10) (11) + if (tree1.tpe <:< pt) + return adapt(tree1, mode, pt, original) + + if (mode.typingExprNotFun) { + // The <: Any requirement inhibits attempts to adapt continuation types + // to non-continuation types. + if (tree.tpe <:< AnyTpe) pt.dealias match { + case TypeRef(_, 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(())))) + case TypeRef(_, sym, _) 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)) + case _ => + } + if (pt.dealias.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt)) // (13) + return typed(adaptAnnotations(tree, this, mode, pt), mode, pt) + + if (hasUndets) + return instantiate(tree, mode, pt) + + if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { + // (14); the condition prevents chains of views + debuglog("inferring view from " + tree.tpe + " to " + pt) + inferView(tree, tree.tpe, pt, reportAmbiguous = true) match { + case EmptyTree => + case coercion => + def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe + 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) + silentContext.firstError match { + case Some(err) => context.issue(err) + case None => return res + } + } + } + } + + debuglog("error tree = " + tree) + if (settings.debug && settings.explaintypes) + explainTypes(tree.tpe, pt) + + if (tree.tpe.isErroneous || pt.isErroneous) + setError(tree) + else + adaptMismatchedSkolems() + } + + def vanillaAdapt(tree: Tree) = { + def applyPossible = { + def applyMeth = member(adaptToName(tree, nme.apply), nme.apply) + def hasPolymorphicApply = applyMeth.alternatives exists (_.tpe.typeParams.nonEmpty) + def hasMonomorphicApply = applyMeth.alternatives exists (_.tpe.paramSectionCount > 0) + + dyna.acceptsApplyDynamic(tree.tpe) || ( + if (mode.inTappMode) + tree.tpe.typeParams.isEmpty && hasPolymorphicApply + else + hasMonomorphicApply + ) + } + def shouldInsertApply(tree: Tree) = mode.typingExprFun && { + tree.tpe match { + case _: MethodType | _: OverloadedType | _: PolyType => false + case _ => applyPossible + } + } + if (tree.isType) + adaptType() + else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree)) + macroExpandApply(this, tree, mode, pt) + else if (mode.typingConstructorPattern) + typedConstructorPattern(tree, pt) + else if (shouldInsertApply(tree)) + insertApply() + else if (hasUndetsInMonoMode) { // (9) + assert(!context.inTypeConstructorAllowed, context) //@M + instantiatePossiblyExpectingUnit(tree, mode, pt) + } + else if (tree.tpe <:< pt) + tree + else + fallbackAfterVanillaAdapt() + } // begin adapt - tree.tpe match { + if (isMacroImplRef(tree)) { + if (treeInfo.isMacroApplication(tree)) adapt(unmarkMacroImplRef(tree), mode, pt, original) + else tree + } else 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) - 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 ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0) + adaptConstant(value) + 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, arg :: Nil) 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 tp if mode.typingExprNotLhs && isExistentialType(tp) => + adapt(tree setType tp.dealias.skolemizeExistential(context.owner, tree), mode, pt, original) + case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode) && !context.inTypeConstructorAllowed => // (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 @@ -1115,176 +1135,24 @@ trait Typers extends Modes with Adaptations with Tags { // -- are we sure we want to expand aliases this early? // -- what caused this change in behaviour?? val tparams1 = cloneSymbols(tparams) - val tree1 = if (tree.isType) tree - else TypeApply(tree, tparams1 map (tparam => - TypeTree(tparam.tpeHK) setPos tree.pos.focus)) setPos tree.pos + val tree1 = ( + if (tree.isType) tree + else TypeApply(tree, tparams1 map (tparam => TypeTree(tparam.tpeHK) setPos tree.pos.focus)) setPos tree.pos + ) context.undetparams ++= tparams1 notifyUndetparamsAdded(tparams1) adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original) - case mt: MethodType if mt.isImplicit && ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1) - adaptToImplicitMethod(mt) - case mt: MethodType if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) && - (context.undetparams.isEmpty || inPolyMode(mode))) && !(tree.symbol != null && tree.symbol.isTermMacro) => + case mt: MethodType if mode.typingExprNotFunNotLhs && mt.isImplicit => // (4.1) + adaptToImplicitMethod(mt) + case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode && !treeInfo.isMacroApplicationOrBlock(tree) => instantiateToMethodType(mt) - case _ => - def shouldInsertApply(tree: Tree) = inAllModes(mode, 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) - tree.tpe.typeParams.isEmpty && applyMeth.filter(!_.tpe.typeParams.isEmpty) != NoSymbol - else - applyMeth.filter(_.tpe.paramSectionCount > 0) != NoSymbol - ) - } - if (tree.isType) - adaptType() - else if ( - inExprModeButNot(mode, FUNmode) && !tree.isDef && // typechecking application - tree.symbol != null && tree.symbol.isTermMacro && // of a macro - !isMacroExpansionSuppressed(tree)) - macroExpand(this, tree, mode, pt) - else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) - adaptConstrPattern() - else if (shouldInsertApply(tree)) - insertApply() - else if (context.undetparams.nonEmpty && !inPolyMode(mode)) { // (9) - assert(!inHKMode(mode), modeString(mode)) //@M - instantiatePossiblyExpectingUnit(tree, mode, pt) - } else if (tree.tpe <:< pt) { - tree - } else { - def fallBack: Tree = { - if (inPatternMode(mode)) { - if ((tree.symbol ne null) && tree.symbol.isModule) - inferModulePattern(tree, pt) - if (isPopulated(tree.tpe, approximateAbstracts(pt))) - return tree - } - val tree1 = constfold(tree, pt) // (10) (11) - if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original) - else { - if (inExprModeButNot(mode, FUNmode)) { - pt.dealias match { - case TypeRef(_, sym, _) => - // 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) - context.unit.warning(tree.pos, "discarded non-Unit value") - return typedPos(tree.pos, mode, pt) { - Block(List(tree), Literal(Constant())) - } - } else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) { - if (settings.warnNumericWiden.value) - context.unit.warning(tree.pos, "implicit numeric widening") - return typedPos(tree.pos, mode, pt) { - Select(tree, "to" + sym.name) - } - } - case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (13) - return typed(adaptAnnotations(tree, this, mode, pt), mode, pt) - case _ => - } - if (!context.undetparams.isEmpty) { - return instantiate(tree, mode, pt) - } - 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) - } - if (coercion != EmptyTree) { - def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe - if (settings.logImplicitConv.value) - 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 - } - } - } - if (settings.debug.value) { - log("error tree = " + tree) - if (settings.explaintypes.value) 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 boundOrSkolems = bound ++ pt.skolemsExceptMethodTypeParams - if (boundOrSkolems.nonEmpty) { - // Ignore type errors raised in later phases that are due to mismatching types with existential skolems - // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. - // Here's my hypothsis why this happens. The pattern matcher defines a variable of type - // - // val x: T = expr - // - // where T is the type of expr, but T contains existential skolems ts. - // In that case, this value definition does not typecheck. - // The value definition - // - // val x: T forSome { ts } = expr - // - // would typecheck. Or one can simply leave out the type of the `val`: - // - // val x = expr - // - // SI-6029 shows another case where we also fail (in uncurry), but this time the expected - // type is an existential type. - // - // The reason for both failures have to do with the way we (don't) transform - // skolem types along with the trees that contain them. We'd need a - // radically different approach to do it. But before investing a lot of time to - // to do this (I have already sunk 3 full days with in the end futile attempts - // to consistently transform skolems and fix 6029), I'd like to - // investigate ways to avoid skolems completely. - // - // upd. The same problem happens when we try to typecheck the result of macro expansion against its expected type - // (which is the return type of the macro definition instantiated in the context of expandee): - // - // Test.scala:2: error: type mismatch; - // found : $u.Expr[Class[_ <: Object]] - // required: reflect.runtime.universe.Expr[Class[?0(in value <local Test>)]] where type ?0(in value <local Test>) <: Object - // scala.reflect.runtime.universe.reify(new Object().getClass) - // ^ - // Therefore following Martin's advice I use this logic to recover from skolem errors after macro expansions - // (by adding the ` || tree.attachments.get[MacroExpansionAttachment].isDefined` clause to the conditional above). - // - log("recovering from existential or skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) - return adapt(tree, mode, deriveTypeWithWildcards(boundOrSkolems)(pt)) - } - } - // create an actual error - AdaptTypeError(tree, found, pt) - } - setError(tree) - } - } - fallBack - } + vanillaAdapt(tree) } } - 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) } @@ -1292,19 +1160,17 @@ 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 _ => - context.undetparams = savedUndetparams - val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) - typed(valueDiscard, mode, UnitClass.tpe) + silent(_.instantiate(tree, mode, UnitTpe)) orElse { _ => + context.undetparams = savedUndetparams + val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant(())))) + typed(valueDiscard, mode, UnitTpe) } } - def instantiatePossiblyExpectingUnit(tree: Tree, mode: Int, pt: Type): Tree = { - if (inExprModeButNot(mode, FUNmode) && pt.typeSymbol == UnitClass) + def instantiatePossiblyExpectingUnit(tree: Tree, mode: Mode, pt: Type): Tree = { + if (mode.typingExprNotFun && pt.typeSymbol == UnitClass) instantiateExpectingUnit(tree, mode) else instantiate(tree, mode, pt) @@ -1340,7 +1206,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)) @@ -1363,43 +1229,36 @@ 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 { - 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) - case _ => - reportError - } - case _ => - reportError - } - } - silent(_.adaptToMember(qual, HasMember(name), false)) match { - case SilentResultValue(res) => res - case SilentTypeError(err) => onError({if (reportAmbiguous) { context.issue(err) }; setError(tree)}) + 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)) + filter (xs => !(xs exists (_.isErrorTyped))) + map (xs => adaptToArguments(qual, name, xs, WildcardType, reportAmbiguous, saveErrors)) + orElse ( _ => reportError) + ) + case _ => + reportError } + + silent(_.adaptToMember(qual, HasMember(name), reportAmbiguous = false)) orElse (err => + onError { + if (reportAmbiguous) context issue err + setError(tree) + } + ) } /** Try to apply an implicit conversion to `qual` to that it contains a @@ -1410,13 +1269,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)) { @@ -1518,126 +1370,255 @@ 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 { + val supertpt = typedTypeConstructor(decodedtpt) + val supertparams = if (supertpt.hasSymbolField) supertpt.symbol.typeParams else Nil + def inferParentTypeArgs: Tree = { + 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); supertpt + case tpt => 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) - } + val supertptWithTargs = if (supertparams.isEmpty || context.unit.isJava) supertpt else inferParentTypeArgs + + // 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) supertptWithTargs else supertptWithTargs 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) - } -/* 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) + EmptyTree } -*/ - //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] = { + @annotation.tailrec + def explode0(parents: List[Tree]): List[Tree] = { + val supertpt :: rest = parents // parents is always non-empty here - it only grows + if (supertpt.tpe.typeSymbol == AnyClass) { + supertpt setType AnyRefTpe + parents + } else if (treeInfo isTraitRef supertpt) { + val supertpt1 = typedType(supertpt) + def supersuper = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + if (supertpt1.isErrorTyped) rest + else explode0(supersuper :: supertpt1 :: rest) + } else parents + } + + def explode(parents: List[Tree]) = + if (treeInfo isTraitRef parents.head) explode0(parents) + else parents + + if (parents.isEmpty) Nil else explode(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 { - case Nil => Nil - case x :: xs => - val sym = x.symbol - x :: fixDuplicates( - if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) - else xs - ) - } + /** 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 :: fixDuplicateSyntheticParents( + if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) + else xs + ) + } - fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, 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 - log("Type error calculating parents in template " + templ) - log("Error: " + ex) - ParentTypesError(templ, ex) - List(TypeTree(AnyRefClass.tpe)) - } + def typedParentTypes(templ: Template): List[Tree] = templ.parents match { + case Nil => List(atPos(templ.pos)(TypeTree(AnyRefTpe))) + 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(AnyRefTpe)) + } + } /** <p>Check that</p> * <ul> @@ -1677,14 +1658,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) @@ -1692,15 +1675,11 @@ 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 !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)) @@ -1714,13 +1693,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) } @@ -1730,7 +1702,7 @@ trait Typers extends Modes with Adaptations with Tags { for (tparam <- clazz.typeParams) { if (classinfo.expansiveRefs(tparam) contains tparam) { val newinfo = ClassInfoType( - classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefClass.tpe))), + classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefTpe))), classinfo.decls, clazz) clazz.setInfo { @@ -1744,27 +1716,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) @@ -1776,10 +1747,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)" @@ -1797,17 +1764,20 @@ 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() - List(TypeTree(SerializableClass.tpe) setPos clazz.pos.focus) + List(TypeTree(SerializableTpe) setPos clazz.pos.focus) } ) }) 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 @@ -1827,9 +1797,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.") } } @@ -1838,6 +1806,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 @@ -1864,17 +1838,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 { @@ -1901,19 +1869,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) @@ -1926,28 +1909,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 = { @@ -1964,7 +1943,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) @@ -1999,10 +1978,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") @@ -2052,7 +2027,7 @@ trait Typers extends Modes with Adaptations with Tags { orElse (superAcc getter superAcc.owner) filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias) ) - if (alias.exists && !alias.accessed.isVariable) { + if (alias.exists && !alias.accessed.isVariable && !isRepeatedParamType(alias.accessed.info)) { val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match { case acc if !acc.isDeferred && acc.hasAccessorFlag => acc.accessed case acc => acc @@ -2121,7 +2096,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) @@ -2140,10 +2115,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 || { @@ -2167,51 +2142,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 @@ -2230,13 +2160,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) @@ -2248,7 +2178,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)) @@ -2304,10 +2234,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) } @@ -2331,7 +2261,7 @@ trait Typers extends Modes with Adaptations with Tags { case ldef @ LabelDef(_, _, _) => if (ldef.symbol == NoSymbol) ldef.symbol = namer.enterInScope( - context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(List(), UnitClass.tpe)) + context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(List(), UnitTpe)) case _ => } } @@ -2340,7 +2270,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 { @@ -2348,26 +2278,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) @@ -2429,7 +2353,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 { @@ -2439,12 +2363,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) @@ -2459,85 +2377,67 @@ trait Typers extends Modes with Adaptations with Tags { // list, so substitute the final result type of the method, i.e. the type // of the case class. if (pat1.tpe.paramSectionCount > 0) - pat1 setType pat1.tpe.finalResultType + pat1 modifyType (_.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) + else typed(cdef.guard, BooleanTpe) 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)) - + if (isFullyDefined(pt) && !(body1.tpe <:< pt)) { + log(s"Adding cast to pattern because ${body1.tpe} does not conform to expected type $pt") + 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 = { - val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) + def typedMatch(selector: Tree, cases: List[CaseDef], mode: Mode, pt: Type, tree: Tree = EmptyTree): Match = { + val selector1 = checkDead(typedByValueExpr(selector)) 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) + def finish(cases: List[CaseDef], matchType: Type) = + treeCopy.Match(tree, selector1, cases) setType matchType - val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, resTp)) - - 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)), reportAmbiguousErrors = false) orElse (_ => null) if (matchStrategy ne null) // virtualize typed((new PureMatchTranslator(this.asInstanceOf[patmat.global.analyzer.Typer] /*TODO*/, matchStrategy)).translateMatch(match_), mode, pt) @@ -2567,11 +2467,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 { @@ -2585,7 +2483,7 @@ trait Typers extends Modes with Adaptations with Tags { val argTp :: resTp :: Nil = targs // targs must conform to Any for us to synthesize an applyOrElse (fallback to apply otherwise -- typically for @cps annotated targs) - val targsValidParams = targs forall (_ <:< AnyClass.tpe) + val targsValidParams = targs forall (_ <:< AnyTpe) val anonClass = (context.owner newAnonymousFunctionClass tree.pos @@ -2596,7 +2494,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( @@ -2713,12 +2611,12 @@ trait Typers extends Modes with Adaptations with Tags { val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) methodBodyTyper.context.scope enter paramSym - methodSym setInfo MethodType(List(paramSym), BooleanClass.tpe) + methodSym setInfo MethodType(List(paramSym), BooleanTpe) - val defaultCase = mkDefaultCase(FALSE_typed) - val match_ = methodBodyTyper.typedMatch(selector, casesTrue :+ defaultCase, mode, BooleanClass.tpe) + val defaultCase = mkDefaultCase(FALSE) + val match_ = methodBodyTyper.typedMatch(selector, casesTrue :+ defaultCase, mode, BooleanTpe) - DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, BooleanClass.tpe)) + DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, BooleanTpe)) } // only used for @cps annotated partial functions @@ -2727,7 +2625,7 @@ trait Typers extends Modes with Adaptations with Tags { val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL | OVERRIDE) val paramSym = mkParam(methodSym) - methodSym setInfo MethodType(List(paramSym), AnyClass.tpe) + methodSym setInfo MethodType(List(paramSym), AnyTpe) val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) @@ -2763,7 +2661,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()) )) } @@ -2775,28 +2673,16 @@ 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 - || fun.vparams.exists(_.tpt.isEmpty) - )) - (pt.typeSymbol, pt.normalize.typeArgs.init, pt.normalize.typeArgs.last) - else - (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) - - val (clazz, argpts, respt) = decompose(pt) + val FunctionSymbol = FunctionClass(numVparams) + val (argpts, respt) = pt baseType FunctionSymbol match { + case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) + case _ => (fun.vparams map (_ => NoType), WildcardType) + } if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) else { @@ -2807,15 +2693,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 _ => } @@ -2844,16 +2728,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(FunctionSymbol, formals :+ restpe: _*) + + treeCopy.Function(fun, vparams, body1) setType funtpe } } } @@ -2871,13 +2752,8 @@ 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 } } @@ -2885,17 +2761,6 @@ trait Typers extends Modes with Adaptations with Tags { 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 @@ -2910,7 +2775,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 _ => @@ -2924,7 +2789,7 @@ trait Typers extends Modes with Adaptations with Tags { } else newTyper(context.make(stat, exprOwner)) // XXX this creates a spurious dead code warning if an exception is thrown // in a constructor, even if it is the only thing in the constructor. - val result = checkDead(localTyper.typed(stat, EXPRmode | BYVALmode, WildcardType)) + val result = checkDead(localTyper.typedByValueExpr(stat)) if (treeInfo.isSelfOrSuperConstrCall(result)) { context.inConstructorSuffix = true @@ -2932,7 +2797,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" ) @@ -2941,8 +2806,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) @@ -2983,7 +2848,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 @@ -3036,42 +2901,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.inSccMode)(_.typed(arg, typedMode, pt)) checkDead.inMode(typedMode, t) } - def typedArgs(args: List[Tree], mode: Int) = - args mapConserve (arg => typedArg(arg, mode, 0, WildcardType)) - - /** Type trees in `args0` against corresponding expected type in `adapted0`. - * - * The mode in which each argument is typed is derived from `mode` and - * whether the arg was originally by-name or var-arg (need `formals0` for that) - * the default is by-val, of course. - * - * (docs reverse-engineered -- AM) - */ - def typedArgs(args0: List[Tree], mode: Int, formals0: List[Type], adapted0: List[Type]): List[Tree] = { - val sticky = onlyStickyModes(mode) - def loop(args: List[Tree], formals: List[Type], adapted: List[Type]): List[Tree] = { - if (args.isEmpty || adapted.isEmpty) Nil - else { - // No formals left or * indicates varargs. - val isVarArgs = formals.isEmpty || formals.tail.isEmpty && isRepeatedParamType(formals.head) - val typedMode = sticky | ( - if (isVarArgs) STARmode | BYVALmode - else if (isByNameParamType(formals.head)) 0 - else BYVALmode - ) - val tree = typedArg(args.head, mode, typedMode, adapted.head) - // formals may be empty, so don't call tail - tree :: loop(args.tail, formals drop 1, adapted.tail) - } - } - loop(args0, formals0, adapted0) - } + def typedArgs(args: List[Tree], mode: Mode) = + args mapConserve (arg => typedArg(arg, mode, NOmode, WildcardType)) /** Does function need to be instantiated, because a missing parameter * in an argument closure overlaps with an uninstantiated formal? @@ -3113,26 +2950,25 @@ 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 (_ => AnyTpe), shapeType(body)) case AssignOrNamedArg(Ident(name), rhs) => NamedType(name, shapeType(rhs)) case _ => - NothingClass.tpe + NothingTpe } 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 @@ -3144,20 +2980,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 } @@ -3166,28 +3001,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 @@ -3195,65 +3032,62 @@ 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: 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) - || tupledTree.symbol == null - || checkValidAdaptation(tupledTree, args) + val keepTree = ( + !mode.typingExprNotFun + || t.symbol == null + || checkValidAdaptation(t, args) ) - case _ => - context.undetparams = savedUndetparams - None - } - } else None - } + if (keepTree) t else EmptyTree + } orElse { _ => context.undetparams = savedUndetparams ; EmptyTree } + } + else EmptyTree + ) - /** 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) - tryTupleApply getOrElse duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun)) + if (treeInfo.isMacroApplication(fun)) + tryTupleApply orElse 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) { - tryTupleApply getOrElse duplErrorTree(TooManyArgsNamesDefaultsError(tree, fun)) + tryTupleApply orElse duplErrorTree(TooManyArgsNamesDefaultsError(tree, fun)) } else if (lencmp == 0) { // we don't need defaults. names were used, so this application is transformed // into a block (@see transformNamedApplication in NamesDefaults) val (namelessArgs, argPos) = removeNames(Typer.this)(args, params) if (namelessArgs exists (_.isErroneous)) { 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() @@ -3296,14 +3130,14 @@ trait Typers extends Modes with Adaptations with Tags { if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { - tryTupleApply getOrElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) + tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) } } } } 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) @@ -3312,53 +3146,31 @@ trait Typers extends Modes with Adaptations with Tags { val tparams = context.extractUndetparams() if (tparams.isEmpty) { // all type params are defined def handleMonomorphicCall: Tree = { - // In order for checkDead not to be misled by the unfortunate special - // case of AnyRef#synchronized (which is implemented with signature T => T - // but behaves as if it were (=> T) => T) we need to know what is the actual - // target of a call. Since this information is no longer available from - // typedArg, it is recorded here. - val args1 = - // no expected type when jumping to a match label -- anything goes (this is ok since we're typing the translation of well-typed code) - // ... except during erasure: we must take the expected type into account as it drives the insertion of casts! - // I've exhausted all other semi-clean approaches I could think of in balancing GADT magic, SI-6145, CPS type-driven transforms and other existential trickiness - // (the right thing to do -- packing existential types -- runs into limitations in subtyping existential types, - // casting breaks SI-6145, - // not casting breaks GADT typing as it requires sneaking ill-typed trees past typer) - if (!phase.erasedTypes && fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol)) + // no expected type when jumping to a match label -- anything goes (this is ok since we're typing the translation of well-typed code) + // ... except during erasure: we must take the expected type into account as it drives the insertion of casts! + // I've exhausted all other semi-clean approaches I could think of in balancing GADT magic, SI-6145, CPS type-driven transforms and other existential trickiness + // (the right thing to do -- packing existential types -- runs into limitations in subtyping existential types, + // casting breaks SI-6145, + // not casting breaks GADT typing as it requires sneaking ill-typed trees past typer) + def noExpectedType = !phase.erasedTypes && fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol) + + val args1 = ( + if (noExpectedType) typedArgs(args, forArgMode(fun, mode)) else - typedArgs(args, forArgMode(fun, mode), paramTypes, formals) + typedArgsForFormals(args, paramTypes, forArgMode(fun, mode)) + ) // instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case: // val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo) // precise(foo) : foo.type => foo.type - val restpe = mt.resultType(args1 map (arg => gen.stableTypeFor(arg) getOrElse arg.tpe)) + val restpe = mt.resultType(args1 map (arg => gen stableTypeFor arg orElse 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. @@ -3366,7 +3178,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)) @@ -3380,7 +3192,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) @@ -3406,9 +3218,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 @@ -3422,157 +3233,45 @@ 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 => - doTypedUnapply(tree, fun0, fun, args, mode, pt) + case ExtractorType(unapply) if mode.inPatternMode => + if (unapply == QuasiquoteClass_api_unapply) macroExpandUnapply(this, tree, fun, unapply, args, mode, pt) + else 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 duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) - def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } - - val otpe = fun.tpe - - if (args.length > MaxTupleArity) - return duplErrorTree(TooManyArgsPatternError(fun)) - - // - def freshArgType(tp: Type): (List[Symbol], Type) = tp match { - case MethodType(param :: _, _) => - (Nil, param.tpe) - case PolyType(tparams, restpe) => - createFromClonedSymbols(tparams, freshArgType(restpe)._2)((ps, t) => ((ps, t))) - // No longer used, see test case neg/t960.scala (#960 has nothing to do with it) - case OverloadedType(_, _) => - OverloadedUnapplyError(fun) - (Nil, ErrorType) - case _ => - UnapplyWithSingleArgError(fun) - (Nil, ErrorType) - } - - val unapp = unapplyMember(otpe) - val unappType = otpe.memberType(unapp) - val argDummy = context.owner.newValue(nme.SELECTOR_DUMMY, fun.pos, SYNTHETIC) setInfo pt - val arg = Ident(argDummy) setType pt - - val uncheckedTypeExtractor = - if (unappType.paramTypes.nonEmpty) - extractorForUncheckedType(tree.pos, unappType.paramTypes.head) - else None - - if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) { - //Console.println("UNAPP: need to typetest, arg.tpe = "+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 - - val typer1 = newTyper(unapplyContext) - val pattp = typer1.infer.inferTypedPattern(tree, unappFormal, arg.tpe, canRemedy = uncheckedTypeExtractor.nonEmpty) - - // 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) - 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))) - - if (fun1.tpe.isErroneous) duplErrTree - else { - val resTp = fun1.tpe.finalResultType.normalize - 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 itype = glb(List(pt1, arg.tpe)) - arg.tpe = 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 - // skip if the unapply's type is not a method type with (at least, but really it should be exactly) one argument - // also skip if we already wrapped a classtag extractor (so we don't keep doing that forever) - if (uncheckedTypeExtractor.isEmpty || fun1.symbol.owner.isNonBottomSubClass(ClassTagClass)) unapply - else wrapClassTagUnapply(unapply, uncheckedTypeExtractor.get, unappType.paramTypes.head) - } - } - } - - def wrapClassTagUnapply(uncheckedPattern: Tree, classTagExtractor: Tree, pt: Type): Tree = { - // TODO: disable when in unchecked match - // we don't create a new Context for a Match, so find the CaseDef, then go out one level and navigate back to the match that has this case - // val thisCase = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) - // val unchecked = thisCase.outer.tree.collect{case Match(selector, cases) if cases contains thisCase => selector} match { - // case List(Typed(_, tpt)) if tpt.tpe hasAnnotation UncheckedClass => true - // case t => println("outer tree: "+ (t, thisCase, thisCase.outer.tree)); false - // } - // println("wrapClassTagUnapply"+ (!isPastTyper && infer.containsUnchecked(pt), pt, uncheckedPattern)) - // println("wrapClassTagUnapply: "+ extractor) - // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true)) - - val args = List(uncheckedPattern) - val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) - // must call doTypedUnapply directly, as otherwise we get undesirable rewrites - // and re-typechecks of the target of the unapply call in PATTERNmode, - // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, - // but an arbitrary tree as is the case here - doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) - } - - // 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 { - // only look at top-level type, can't (reliably) do anything about unchecked type args (in general) - pt.normalize.typeConstructor 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))) => - None - - case ptCheckable if infer.containsUnchecked(ptCheckable) => - val classTagExtractor = resolveClassTag(pos, ptCheckable) - - if (classTagExtractor != EmptyTree && unapplyMember(classTagExtractor.tpe) != NoSymbol) - Some(classTagExtractor) - else None - - case _ => None - } - } - /** * 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), pt) + val const: Constant = ttree match { case l @ Literal(c) if !l.isErroneous => c case tree => tree.tpe match { case ConstantType(c) => c @@ -3581,29 +3280,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) if (typedFun.symbol.owner == ArrayModule.moduleClass && typedFun.symbol.name == nme.apply) pt match { case TypeRef(_, ArrayClass, targ :: _) => @@ -3631,44 +3337,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) + 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 { @@ -3698,47 +3402,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) => AnnotationInfo(annType, args, List()).setOriginal(typedAnn).setPos(t.pos) @@ -3762,36 +3452,33 @@ 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` */ - def packSymbols(hidden: List[Symbol], tp: Type): Type = global.packSymbols(hidden, tp, Some(context0.owner)) - - def isReferencedFrom(ctx: Context, sym: Symbol): Boolean = - ctx.owner.isTerm && - (ctx.scope.exists { dcl => dcl.isInitialized && (dcl.info contains sym) }) || - { - var ctx1 = ctx.outer - while ((ctx1 != NoContext) && (ctx1.scope eq ctx.scope)) ctx1 = ctx1.outer - (ctx1 != NoContext) && isReferencedFrom(ctx1, sym) - } - - def isCapturedExistential(sym: Symbol) = - (sym hasAllFlags (EXISTENTIAL | CAPTURED)) && { - val start = if (Statistics.canEnable) Statistics.startTimer(isReferencedNanos) else null - try !isReferencedFrom(context, sym) - finally if (Statistics.canEnable) Statistics.stopTimer(isReferencedNanos, start) - } + def packSymbols(hidden: List[Symbol], tp: Type): Type = global.packSymbols(hidden, tp, context0.owner) + + def isReferencedFrom(ctx: Context, sym: Symbol): Boolean = ( + ctx.owner.isTerm && (ctx.scope.exists { dcl => dcl.isInitialized && (dcl.info contains sym) }) || { + var ctx1 = ctx.outer + while ((ctx1 != NoContext) && (ctx1.scope eq ctx.scope)) + ctx1 = ctx1.outer + + (ctx1 != NoContext) && isReferencedFrom(ctx1, sym) + } + ) + + def isCapturedExistential(sym: Symbol) = ( + (sym hasAllFlags EXISTENTIAL | CAPTURED) && { + val start = if (Statistics.canEnable) Statistics.startTimer(isReferencedNanos) else null + try !isReferencedFrom(context, sym) + finally if (Statistics.canEnable) Statistics.stopTimer(isReferencedNanos, start) + } + ) def packCaptured(tpe: Type): Type = { val captured = mutable.Set[Symbol]() @@ -3803,11 +3490,15 @@ trait Typers extends Modes with Adaptations with Tags { /** convert local symbols and skolems to existentials */ def packedType(tree: Tree, owner: Symbol): Type = { - def defines(tree: Tree, sym: Symbol) = - sym.isExistentialSkolem && sym.unpackLocation == tree || - tree.isDef && tree.symbol == sym - def isVisibleParameter(sym: Symbol) = - sym.isParameter && (sym.owner == owner) && (sym.isType || !owner.isAnonymousFunction) + def defines(tree: Tree, sym: Symbol) = ( + sym.isExistentialSkolem && sym.unpackLocation == tree + || tree.isDef && tree.symbol == sym + ) + def isVisibleParameter(sym: Symbol) = ( + sym.isParameter + && (sym.owner == owner) + && (sym.isType || !owner.isAnonymousFunction) + ) def containsDef(owner: Symbol, sym: Symbol): Boolean = (!sym.hasPackageFlag) && { var o = sym.owner @@ -3822,7 +3513,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) @@ -3875,25 +3567,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 _ => { @@ -3907,7 +3599,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) @@ -3916,7 +3608,7 @@ trait Typers extends Modes with Adaptations with Tags { // as we don't know which alternative to choose... here we do map2Conserve(args, tparams) { //@M! the polytype denotes the expected kind - (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyClass.tpe)) + (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyTpe)) } } else // @M: there's probably something wrong when args.length != tparams.length... (triggered by bug #320) // Martin, I'm using fake trees, because, if you use args or arg.map(typedType), @@ -3932,12 +3624,12 @@ 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 { case Select(qual, _) => qual.tpe - case _ => AnyClass.tpe + case _ => AnyTpe } checkCheckable(tree, targs.head, scrutineeType, inPattern = false) } @@ -3996,7 +3688,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)) @@ -4025,7 +3717,7 @@ trait Typers extends Modes with Adaptations with Tags { * */ def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = { - log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") + debuglog(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") val treeInfo.Applied(treeSelection, _, _) = tree def isDesugaredApply = treeSelection match { case Select(`qual`, nme.apply) => true @@ -4041,20 +3733,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, _))) => @@ -4066,42 +3758,32 @@ 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 - @inline final def printTyping(s: => String) = { - if (printTypings) - println(context.typingIndent + s.replaceAll("\n", "\n" + context.typingIndent)) - } - @inline final def printInference(s: => String) = { - if (printInfers) - println(s) - } + def typed1(tree: Tree, mode: Mode, pt: Type): Tree = { + // Lookup in the given class using the root mirror. + def lookupInOwner(owner: Symbol, name: Name): Symbol = + if (mode.inQualMode) rootMirror.missingHook(owner, name) else NoSymbol - def typed1(tree: Tree, mode: Int, pt: Type): Tree = { - def isPatternMode = inPatternMode(mode) + // 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 - //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 - } + 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 @@ -4120,7 +3802,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 { @@ -4156,7 +3838,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 { @@ -4167,7 +3849,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 @@ -4191,7 +3873,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 = @@ -4199,7 +3881,9 @@ 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 (context.inPatAlternative) + VariableInPatternAlternativeError(tree) + namer.enterInScope(sym) } @@ -4224,16 +3908,16 @@ 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 = { // see SI-7617 for an explanation of why macro expansion is suppressed - def typedLhs(lhs: Tree) = typed(lhs, EXPRmode | LHSmode, WildcardType) + def typedLhs(lhs: Tree) = typed(lhs, EXPRmode | LHSmode) val lhs1 = unsuppressMacroExpansion(typedLhs(suppressMacroExpansion(lhs))) val varsym = lhs1.symbol @@ -4248,7 +3932,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) @@ -4259,52 +3943,54 @@ trait Typers extends Modes with Adaptations with Tags { // // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?! // (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) { if (varsym.isVariable || varsym.isValue && phase.erasedTypes) { - val rhs1 = typed(rhs, EXPRmode | BYVALmode, lhs1.tpe) - treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitClass.tpe + val rhs1 = typedByValueExpr(rhs, lhs1.tpe) + treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitTpe } else if(dyna.isDynamicallyUpdatable(lhs1)) { - val rhs1 = typed(rhs, EXPRmode | BYVALmode, WildcardType) + val rhs1 = typedByValueExpr(rhs) val t = Apply(lhs1, List(rhs1)) dyna.wrapErrors(t, _.typed1(t, mode, pt)) } else fail() } - def typedIf(tree: 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 + def typedIf(tree: If): If = { + val cond1 = checkDead(typedByValueExpr(tree.cond, BooleanTpe)) + // One-legged ifs don't need a lot of analysis + if (tree.elsep.isEmpty) + return treeCopy.If(tree, cond1, typed(tree.thenp, UnitTpe), tree.elsep) setType UnitTpe + + 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 = { @@ -4314,7 +4000,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), @@ -4341,9 +4027,9 @@ trait Typers extends Modes with Adaptations with Tags { val DefDef(_, name, _, _, restpt, _) = enclMethod.tree if (restpt.tpe eq null) { ReturnWithoutTypeError(tree, enclMethod.owner) - } else { - context.enclMethod.returnsSeen = true - val expr1: Tree = typed(expr, EXPRmode | BYVALmode | RETmode, restpt.tpe) + } + else { + val expr1 = context withinReturnExpr typedByValueExpr(expr, restpt.tpe) // Warn about returning a value if no value can be returned. if (restpt.tpe.typeSymbol == UnitClass) { // The typing in expr1 says expr is Unit (it has already been coerced if @@ -4353,7 +4039,7 @@ trait Typers extends Modes with Adaptations with Tags { unit.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded") } val res = treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner) - val tp = pluginsTypedReturn(NothingClass.tpe, this, res, restpt.tpe) + val tp = pluginsTypedReturn(NothingTpe, this, res, restpt.tpe) res.setType(tp) } } @@ -4370,7 +4056,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) @@ -4379,8 +4065,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 { @@ -4400,7 +4086,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 @@ -4430,36 +4116,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) @@ -4468,184 +4133,149 @@ 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 def onError(typeError: AbsTypeError): Tree = { - if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, start) - - // If the problem is with raw types, copnvert to existentials and try again. - // See #4712 for a case where this situation arises, - if ((fun.symbol ne null) && fun.symbol.isJavaDefined) { - val newtpe = rawToExistential(fun.tpe) - if (fun.tpe ne newtpe) { - // println("late cooking: "+fun+":"+fun.tpe) // DEBUG - return tryTypedApply(fun setType newtpe, args) - } + if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, start) + + // If the problem is with raw types, copnvert to existentials and try again. + // See #4712 for a case where this situation arises, + if ((fun.symbol ne null) && fun.symbol.isJavaDefined) { + val newtpe = rawToExistential(fun.tpe) + if (fun.tpe ne newtpe) { + // println("late cooking: "+fun+":"+fun.tpe) // DEBUG + return tryTypedApply(fun setType newtpe, args) } + } + def treesInResult(tree: Tree): List[Tree] = tree :: (tree match { + case Block(_, r) => treesInResult(r) + case Match(_, cases) => cases + case CaseDef(_, _, r) => treesInResult(r) + case Annotated(_, r) => treesInResult(r) + case If(_, t, e) => treesInResult(t) ++ treesInResult(e) + case Try(b, catches, _) => treesInResult(b) ++ catches + case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) + case _ => Nil + }) + def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == typeError.errPos) - def treesInResult(tree: Tree): List[Tree] = tree :: (tree match { - case Block(_, r) => treesInResult(r) - case Match(_, cases) => cases - case CaseDef(_, _, r) => treesInResult(r) - case Annotated(_, r) => treesInResult(r) - case If(_, t, e) => treesInResult(t) ++ treesInResult(e) - case Try(b, catches, _) => treesInResult(b) ++ catches - case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) - case _ => Nil - }) - def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == typeError.errPos) - - val retry = (typeError.errPos != null) && (fun :: tree :: args exists errorInResult) - printTyping { - val funStr = ptTree(fun) + " and " + (args map ptTree mkString ", ") - if (retry) "second try: " + funStr - else "no second try: " + funStr + " because error not in result: " + typeError.errPos+"!="+tree.pos - } - if (retry) { - val Select(qual, name) = fun - tryTypedArgs(args, forArgMode(fun, mode)) match { - case Some(args1) => - val qual1 = - if (!pt.isError) adaptToArguments(qual, name, args1, pt, true, true) - else qual - if (qual1 ne qual) { - val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos - return typed1(tree1, mode | SNDTRYmode, pt) - } - case _ => () - } + val retry = (typeError.errPos != null) && (fun :: tree :: args exists errorInResult) + typingStack.printTyping({ + val funStr = ptTree(fun) + " and " + (args map ptTree mkString ", ") + if (retry) "second try: " + funStr + else "no second try: " + funStr + " because error not in result: " + typeError.errPos+"!="+tree.pos + }) + if (retry) { + val Select(qual, name) = fun + tryTypedArgs(args, forArgMode(fun, mode)) match { + case Some(args1) => + val qual1 = + 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 + return context withinSecondTry typed1(tree1, mode, pt) + } + case _ => () } - issue(typeError) - setError(treeCopy.Apply(tree, fun, args)) + } + issue(typeError) + setError(treeCopy.Apply(tree, fun, args)) } - silent(_.doTypedApply(tree, fun, args, mode, pt)) match { - case SilentResultValue(t) => - t - case SilentTypeError(err) => - onError(err) - } + silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError } def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = { + // TODO: replace `fun.symbol.isStable` by `treeInfo.isStableIdentifierPattern(fun)` 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 _ => - 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 - } - 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 (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)}) - } + val funpt = if (mode.inPatternMode) 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 !mode.inPatternMode && 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 _ => + 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 + || context.inSecondTry + || (fun2.symbol ne null) && fun2.symbol.isConstructor + || isImplicitMethodType(fun2.tpe) + ) + val isFirstTry = fun2 match { + case Select(_, _) => !noSecondTry && mode.inExprMode + case _ => false + } + if (isFirstTry) + tryTypedApply(fun2, args) + else + doTypedApply(tree, fun2, args, mode, pt) + 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 = @@ -4699,8 +4329,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) @@ -4708,11 +4336,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 @@ -4730,7 +4353,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 (context.inSuperInit) clazz.info.firstParent else intersectionType(clazz.info.parents) ) treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) @@ -4744,14 +4367,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))) } @@ -4760,82 +4397,65 @@ 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.inAny(EXPRmode | 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) } val (result, accessibleError) = silent(_.makeAccessible(tree1, sym, qual.tpe, qual)) match { + case SilentTypeError(err: AccessTypeError) => + (tree1, Some(err)) case SilentTypeError(err) => - if (err.kind != ErrorKinds.Access) { - context issue err - return setError(tree) - } - else (tree1, Some(err)) + context issue err + return setError(tree) case SilentResultValue(treeAndPre) => (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 @@ -4850,7 +4470,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 @@ -4865,320 +4485,115 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedSelectOrSuperCall(tree: Select) = { - val qual = tree.qualifier - val name = tree.name - qual match { - case _: Super if name == nme.CONSTRUCTOR => - val qual1 = - typed(qual, EXPRmode | QUALmode | POLYmode | SUPERCONSTRmode, WildcardType) - // the qualifier type of a supercall constructor is its first parent class - typedSelect(tree, qual1, nme.CONSTRUCTOR) - case _ => - if (Statistics.canEnable) Statistics.incCounter(typedSelectCount) - var qual1 = checkDead(typedQualifier(qual, mode)) - if (name.isTypeName) qual1 = checkStable(qual1) - - 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(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { - case SilentResultValue(result2) => - unit.deprecationWarning( - tree.pos, "`withFilter' method does not yet exist on " + qual1.tpe.widen + - ", using `filter' method instead") - result2 - case SilentTypeError(err) => - WithFilterError(tree, err) - } - } - else - typedSelect(tree, qual1, name) - - if (tree.isInstanceOf[PostfixSelect]) - checkFeature(tree.pos, PostfixOpsFeature, name.decode) - if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember) - checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString) - - if (qual1.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name) - else tree1 + // temporarily use `filter` as an alternative for `withFilter` + def tryWithFilterAndFilter(tree: Select, qual: Tree): Tree = { + def warn() = unit.deprecationWarning(tree.pos, s"`withFilter' method does not yet exist on ${qual.tpe.widen}, using `filter' method instead") + silent(_ => typedSelect(tree, qual, nme.withFilter)) orElse { _ => + silent(_ => typed1(Select(qual, nme.filter) setPos tree.pos, mode, pt)) match { + case SilentResultValue(res) => warn() ; res + case SilentTypeError(err) => WithFilterError(tree, err) + } } } + def typedSelectOrSuperCall(tree: Select) = tree match { + case Select(qual @ Super(_, _), nme.CONSTRUCTOR) => + // the qualifier type of a supercall constructor is its first parent class + typedSelect(tree, typedSelectOrSuperQualifier(qual), nme.CONSTRUCTOR) + case Select(qual, name) => + if (Statistics.canEnable) Statistics.incCounter(typedSelectCount) + val qualTyped = checkDead(typedQualifier(qual, mode)) + val qualStableOrError = ( + if (qualTyped.isErrorTyped || !name.isTypeName || treeInfo.admitsTypeSelection(qualTyped)) + qualTyped + else + UnstableTreeError(qualTyped) + ) + val tree1 = name match { + case nme.withFilter => tryWithFilterAndFilter(tree, qualStableOrError) + case _ => typedSelect(tree, qualStableOrError, name) + } + def sym = tree1.symbol + if (tree.isInstanceOf[PostfixSelect]) + checkFeature(tree.pos, PostfixOpsFeature, name.decode) + if (sym != null && sym.isOnlyRefinementMember) + checkFeature(tree1.pos, ReflectiveCallsFeature, sym.toString) + + qualStableOrError.symbol match { + case s: Symbol if s.isRootPackage => treeCopy.Ident(tree1, name) + case _ => tree1 + } + } + + /* A symbol qualifies if: + * - it exists + * - it is not stale (stale symbols are made to disappear here) + * - if we are in a constructor pattern, 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) + && !(mode.typingConstructorPattern && sym.isMethod && !sym.isStable) + ) - /** Attribute an identifier consisting of a simple name or an outer reference. + /* 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} - """) - } - } - 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() + val startContext = if (mode.typingPatternOrTypePat) context.outer else context + 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) } - 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.typingPatternNotConstructor) || + (name == tpnme.WILDCARD && mode.inTypeMode)) tree setType makeFullyDefined(pt) else typedIdent(tree, name) @@ -5205,40 +4620,65 @@ trait Typers extends Modes with Adaptations with Tags { } def typedAppliedTypeTree(tree: AppliedTypeTree) = { - val tpt = tree.tpt - val args = tree.args - val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) + val tpt = tree.tpt + val args = tree.args + val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) + def isPoly = tpt1.tpe.isInstanceOf[PolyType] + def isComplete = tpt1.symbol.rawInfo.isComplete + if (tpt1.isErrorTyped) { tpt1 - } else if (!tpt1.hasSymbol) { + } else if (!tpt1.hasSymbolField) { AppliedTypeNoParametersError(tree, tpt1.tpe) } else { val tparams = tpt1.symbol.typeParams + if (sameLength(tparams, args)) { // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) val args1 = - if (!tpt1.symbol.rawInfo.isComplete) + if (!isComplete) args mapConserve (typedHigherKindedType(_, mode)) // if symbol hasn't been fully loaded, can't check kind-arity else map2Conserve(args, tparams) { (arg, tparam) => //@M! the polytype denotes the expected kind - typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyClass.tpe)) + typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyTpe)) } val argtypes = args1 map (_.tpe) - foreach2(args, tparams)((arg, tparam) => arg match { - // note: can't use args1 in selector, because Bind's got replaced - case Bind(_, _) => - if (arg.symbol.isAbstractType) - arg.symbol setInfo // XXX, feedback. don't trackSymInfo here! - TypeBounds( - lub(List(arg.symbol.info.bounds.lo, tparam.info.bounds.lo.subst(tparams, argtypes))), - glb(List(arg.symbol.info.bounds.hi, tparam.info.bounds.hi.subst(tparams, argtypes)))) - case _ => - }) + foreach2(args, tparams) { (arg, tparam) => + // note: can't use args1 in selector, because Binds got replaced + val asym = arg.symbol + def abounds = asym.info.bounds + def tbounds = tparam.info.bounds + def enhanceBounds(): Unit = { + val TypeBounds(lo0, hi0) = abounds + val TypeBounds(lo1, hi1) = tbounds.subst(tparams, argtypes) + val lo = lub(List(lo0, lo1)) + val hi = glb(List(hi0, hi1)) + if (!(lo =:= lo0 && hi =:= hi0)) + asym setInfo logResult(s"Updating bounds of ${asym.fullLocationString} in $tree from '$abounds' to")(TypeBounds(lo, hi)) + } + if (asym != null && asym.isAbstractType) { + // See pos/t1786 to follow what's happening here. + def canEnhanceIdent = ( + asym.hasCompleteInfo + && tparam.exists /* sometimes it is NoSymbol */ + && tparam.hasCompleteInfo /* SI-2940 */ + && !tparam.isFBounded /* SI-2251 */ + && !tparam.isHigherOrderTypeParameter + && !(abounds.hi <:< tbounds.hi) + && asym.isSynthetic /* this limits us to placeholder tparams, excluding named ones */ + ) + arg match { + case Bind(_, _) => enhanceBounds() + case Ident(name) if canEnhanceIdent => enhanceBounds() + case _ => + } + } + } val original = treeCopy.AppliedTypeTree(tree, tpt1, args1) val result = TypeTree(appliedType(tpt1.tpe, argtypes)) setOriginal original - if(tpt1.tpe.isInstanceOf[PolyType]) // did the type application (performed by appliedType) involve an unchecked beta-reduction? + if (isPoly) // did the type application (performed by appliedType) involve an unchecked beta-reduction? TypeTreeWithDeferredRefCheck(){ () => // wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap // we can't simply use original in refchecks because it does not contains types @@ -5251,7 +4691,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) } } @@ -5268,27 +4708,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). */ @@ -5299,111 +4719,89 @@ trait Typers extends Modes with Adaptations with Tags { } def typedAlternative(alt: Alternative) = { - val alts1 = alt.trees mapConserve (alt => typed(alt, mode | ALTmode, pt)) - treeCopy.Alternative(tree, alts1) setType pt + context withinPatAlternative ( + treeCopy.Alternative(tree, alt.trees mapConserve (alt => typed(alt, mode, pt))) setType pt + ) } - def typedStar(tree: Star) = { - if ((mode & STARmode) == 0 && !isPastTyper) + if (!context.starPatterns && !isPastTyper) StarPatternWithVarargParametersError(tree) - treeCopy.Star(tree, typed(tree.elem, mode, pt)) setType makeFullyDefined(pt) - } - def typedUnApply(tree: UnApply) = { - val fun1 = typed(tree.fun) - val tpes = formalTypes(unapplyTypeList(tree.fun.pos, tree.fun.symbol, fun1.tpe, tree.args), tree.args.length) - val args1 = map2(tree.args, tpes)(typedPattern) - treeCopy.UnApply(tree, fun1, args1) setType pt + treeCopy.Star(tree, typed(tree.elem, mode, pt)) setType makeFullyDefined(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, ThrowableTpe, pt) + val fin1 = if (fin.isEmpty) fin else typed(fin, UnitTpe) + + 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) = { - val expr1 = typed(tree.expr, EXPRmode | BYVALmode, ThrowableClass.tpe) - treeCopy.Throw(tree, expr1) setType NothingClass.tpe + val expr1 = typedByValueExpr(tree.expr, ThrowableTpe) + treeCopy.Throw(tree, expr1) setType NothingTpe } def typedTyped(tree: Typed) = { - val expr = tree.expr - val tpt = tree.tpt - tpt match { - case Function(List(), EmptyTree) => - // find out whether the programmer is trying to eta-expand a macro def - // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee) - // 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(suppressMacroExpansion(expr), mode, pt) - exprTyped match { - case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => - MacroEtaError(exprTyped) - case _ => - typedEta(checkDead(exprTyped)) - } - - case Ident(tpnme.WILDCARD_STAR) => - val exprTyped = typed(expr, onlyStickyModes(mode), WildcardType) - def subArrayType(pt: Type) = - if (isPrimitiveValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt) - else { - val tparam = context.owner freshExistential "" setInfo TypeBounds.upper(pt) - newExistentialType(List(tparam), arrayType(tparam.tpe)) - } - - val (exprAdapted, baseClass) = exprTyped.tpe.typeSymbol match { - case ArrayClass => (adapt(exprTyped, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) - case _ => (adapt(exprTyped, onlyStickyModes(mode), seqType(pt)), SeqClass) - } - exprAdapted.tpe.baseType(baseClass) match { - case TypeRef(_, _, List(elemtp)) => - treeCopy.Typed(tree, exprAdapted, tpt setType elemtp) setType elemtp - case _ => - setError(tree) + if (treeInfo isWildcardStarType tree.tpt) + typedStarInPattern(tree, mode.onlySticky, pt) + else if (mode.inPatternMode) + typedInPattern(tree, mode.onlySticky, pt) + else tree match { + // find out whether the programmer is trying to eta-expand a macro def + // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee) + // 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) + // + // Note: apparently `Function(Nil, EmptyTree)` is the secret parser marker + // which means trailing underscore. + case Typed(expr, Function(Nil, EmptyTree)) => + typed1(suppressMacroExpansion(expr), mode, pt) match { + case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef) + case exprTyped => typedEta(checkDead(exprTyped)) } - - case _ => - val tptTyped = typedType(tpt, mode) - val exprTyped = typed(expr, onlyStickyModes(mode), 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 ownType = inferTypedPattern(tptTyped, tptTyped.tpe, ptDefined, canRemedy = uncheckedTypeExtractor.nonEmpty) - treeTyped setType ownType - - uncheckedTypeExtractor match { - case None => treeTyped - case Some(extractor) => wrapClassTagUnapply(treeTyped, extractor, tptTyped.tpe) - } - } else - treeTyped setType tptTyped.tpe + case Typed(expr, tpt) => + val tpt1 = typedType(tpt, mode) // type the ascribed type first + val expr1 = typed(expr, mode.onlySticky, tpt1.tpe.deconst) // then type the expression with tpt1 as the expected type + treeCopy.Typed(tree, expr1, tpt1) setType tpt1.tpe } } @@ -5419,7 +4817,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) val tparams = fun1.symbol.typeParams //@M TODO: val undets_fun = context.undetparams ? @@ -5431,7 +4829,7 @@ trait Typers extends Modes with Adaptations with Tags { // @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds? val args1 = if (sameLength(args, tparams)) map2Conserve(args, tparams) { //@M! the polytype denotes the expected kind - (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyClass.tpe)) + (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyTpe)) } else { //@M this branch is correctly hit for an overloaded polymorphic type. It also has to handle erroneous cases. @@ -5449,10 +4847,9 @@ trait Typers extends Modes with Adaptations with Tags { def typedApplyDynamic(tree: ApplyDynamic) = { assert(phase.erasedTypes) - val reflectiveCalls = !(settings.refinementMethodDispatch.value == "invoke-dynamic") - val qual1 = typed(tree.qual, AnyRefClass.tpe) - val args1 = tree.args mapConserve (arg => if (reflectiveCalls) typed(arg, AnyRefClass.tpe) else typed(arg)) - treeCopy.ApplyDynamic(tree, qual1, args1) setType (if (reflectiveCalls) AnyRefClass.tpe else tree.symbol.info.resultType) + val qual1 = typed(tree.qual, AnyRefTpe) + val args1 = tree.args mapConserve (arg => typed(arg, AnyRefTpe)) + treeCopy.ApplyDynamic(tree, qual1, args1) setType AnyRefTpe } def typedReferenceToBoxed(tree: ReferenceToBoxed) = { @@ -5464,20 +4861,51 @@ 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) UnitTpe + else ConstantType(tree.value)) } def typedSingletonTypeTree(tree: SingletonTypeTree) = { - val ref1 = checkStable( - context.withImplicitsDisabled( - typed(tree.ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe) - ) - ) - tree setType ref1.tpe.resultType + val refTyped = + context.withImplicitsDisabled { + typed(tree.ref, MonoQualifierModes | mode.onlyTypePat, AnyRefTpe) + } + + if (!refTyped.isErrorTyped) + tree setType refTyped.tpe.resultType + + if (treeInfo.admitsTypeSelection(refTyped)) tree + else UnstableTreeError(refTyped) } def typedSelectFromTypeTree(tree: SelectFromTypeTree) = { @@ -5487,8 +4915,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) } @@ -5509,11 +4937,13 @@ trait Typers extends Modes with Adaptations with Tags { case _ => tree } } - else + else { // we should get here only when something before failed // and we try again (@see tryTypedApply). In that case we can assign // whatever type to tree; we just have to survive until a real error message is issued. - tree setType AnyClass.tpe + devWarning(tree.pos, s"Assigning Any type to TypeTree because tree.original is null: tree is $tree/${System.identityHashCode(tree)}, sym=${tree.symbol}, tpe=${tree.tpe}") + tree setType AnyTpe + } } def typedFunction(fun: Function) = { if (fun.symbol == NoSymbol) @@ -5522,104 +4952,126 @@ trait Typers extends Modes with Adaptations with Tags { typerWithLocalContext(context.makeNewScope(fun, fun.symbol))(_.typedFunction(fun, mode, pt)) } - // begin typed1 - //if (settings.debug.value && tree.isDef) log("typing definition of "+sym);//DEBUG - tree match { - case tree: Ident => typedIdentOrWildcard(tree) - case tree: Select => typedSelectOrSuperCall(tree) - case tree: Apply => typedApply(tree) + // Trees only allowed during pattern mode. + def typedInPatternMode(tree: Tree): Tree = tree match { + case tree: Alternative => typedAlternative(tree) + case tree: Star => typedStar(tree) + case _ => abort(s"unexpected tree in pattern mode: ${tree.getClass}\n$tree") + } + + def typedTypTree(tree: TypTree): Tree = tree match { case tree: TypeTree => typedTypeTree(tree) - case tree: Literal => typedLiteral(tree) - case tree: This => typedThis(tree) - case tree: ValDef => typedValDef(tree) - case tree: DefDef => defDefTyper(tree).typedDefDef(tree) - case tree: Block => typerWithLocalContext(context.makeNewScope(tree, context.owner))(_.typedBlock(tree, mode, pt)) - case tree: If => typedIf(tree) - case tree: TypeApply => typedTypeApply(tree) case tree: AppliedTypeTree => typedAppliedTypeTree(tree) - case tree: Bind => typedBind(tree) - case tree: Function => typedFunction(tree) - case tree: Match => typedVirtualizedMatch(tree) - case tree: New => typedNew(tree) - case tree: Assign => typedAssign(tree.lhs, tree.rhs) - case tree: AssignOrNamedArg => typedAssign(tree.lhs, tree.rhs) // called by NamesDefaults in silent typecheck - case tree: Super => typedSuper(tree) case tree: TypeBoundsTree => typedTypeBoundsTree(tree) - case tree: Typed => typedTyped(tree) - case tree: ClassDef => newTyper(context.makeNewScope(tree, sym)).typedClassDef(tree) - case tree: ModuleDef => newTyper(context.makeNewScope(tree, sym.moduleClass)).typedModuleDef(tree) - case tree: TypeDef => typedTypeDef(tree) - case tree: LabelDef => labelTyper(tree).typedLabelDef(tree) - case tree: PackageDef => typedPackageDef(tree) - case tree: DocDef => typedDocDef(tree) - case tree: Annotated => typedAnnotated(tree) case tree: SingletonTypeTree => typedSingletonTypeTree(tree) case tree: SelectFromTypeTree => typedSelectFromTypeTree(tree) case tree: CompoundTypeTree => typedCompoundTypeTree(tree) case tree: ExistentialTypeTree => typedExistentialTypeTree(tree) - case tree: Return => typedReturn(tree) - case tree: Try => typedTry(tree) - case tree: Throw => typedThrow(tree) - case tree: Alternative => typedAlternative(tree) - case tree: Star => typedStar(tree) - case tree: UnApply => typedUnApply(tree) - case tree: ArrayValue => typedArrayValue(tree) - 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") + case _ => abort(s"unexpected type-representing tree: ${tree.getClass}\n$tree") + } + + def typedMemberDef(tree: MemberDef): Tree = tree match { + case tree: ValDef => typedValDef(tree) + case tree: DefDef => defDefTyper(tree).typedDefDef(tree) + case tree: ClassDef => newTyper(context.makeNewScope(tree, sym)).typedClassDef(tree) + case tree: ModuleDef => newTyper(context.makeNewScope(tree, sym.moduleClass)).typedModuleDef(tree) + case tree: TypeDef => typedTypeDef(tree) + case tree: PackageDef => typedPackageDef(tree) + case _ => abort(s"unexpected member def: ${tree.getClass}\n$tree") + } + + // Trees not allowed during pattern mode. + def typedOutsidePatternMode(tree: Tree): Tree = tree match { + case tree: Block => typerWithLocalContext(context.makeNewScope(tree, context.owner))(_.typedBlock(tree, mode, pt)) + case tree: If => typedIf(tree) + case tree: TypeApply => typedTypeApply(tree) + case tree: Function => typedFunction(tree) + case tree: Match => typedVirtualizedMatch(tree) + case tree: New => typedNew(tree) + case tree: Assign => typedAssign(tree.lhs, tree.rhs) + case tree: AssignOrNamedArg => typedAssign(tree.lhs, tree.rhs) // called by NamesDefaults in silent typecheck + case tree: Super => typedSuper(tree) + case tree: Annotated => typedAnnotated(tree) + case tree: Return => typedReturn(tree) + case tree: Try => typedTry(tree) + case tree: Throw => typedThrow(tree) + case tree: ArrayValue => typedArrayValue(tree) + case tree: ApplyDynamic => typedApplyDynamic(tree) + case tree: ReferenceToBoxed => typedReferenceToBoxed(tree) + case tree: LabelDef => labelTyper(tree).typedLabelDef(tree) + case tree: DocDef => typedDocDef(tree, mode, pt) + case _ => abort(s"unexpected tree: ${tree.getClass}\n$tree") + } + + // Trees allowed in or out of pattern mode. + def typedInAnyMode(tree: Tree): Tree = tree match { + case tree: Ident => typedIdentOrWildcard(tree) + case tree: Bind => typedBind(tree) + case tree: Apply => typedApply(tree) + case tree: Select => typedSelectOrSuperCall(tree) + case tree: Literal => typedLiteral(tree) + case tree: Typed => typedTyped(tree) + case tree: This => typedThis(tree) // SI-6104 + case tree: UnApply => abort(s"unexpected UnApply $tree") // turns out UnApply never reaches here + case _ => + if (mode.inPatternMode) + typedInPatternMode(tree) + else + typedOutsidePatternMode(tree) + } + + // begin typed1 + tree match { + case tree: TypTree => typedTypTree(tree) + case tree: MemberDef => typedMemberDef(tree) + case _ => typedInAnyMode(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() - - val ptPlugins = pluginsPt(pt, this, tree, mode) - + def body = ( + if (printTypings && !phase.erasedTypes && !noPrintTyping(tree)) + typingStack.nextTyped(tree, mode, pt, context)(typedInternal(tree, mode, pt)) + else + typedInternal(tree, mode, pt) + ) val startByType = if (Statistics.canEnable) Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null if (Statistics.canEnable) Statistics.incCounter(visitsByType, tree.getClass) - try { - if (context.retyping && - (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))) { + try body + finally if (Statistics.canEnable) Statistics.popTimer(byTypeStack, startByType) + } + + private def typedInternal(tree: Tree, mode: Mode, pt: Type): Tree = { + val ptPlugins = pluginsPt(pt, this, tree, mode) + def retypingOk = ( + context.retyping + && (tree.tpe ne null) + && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins)) + ) + def runTyper(): Tree = { + if (retypingOk) { tree.tpe = null if (tree.hasSymbol) tree.symbol = NoSymbol } - val alreadyTyped = tree.tpe ne null - var 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), - "silent" -> context.bufferErrors, - "context.owner" -> context.owner - ) - ) - typed1(tree, mode, dropExistential(ptPlugins)) - } + val shouldPrint = !alreadyTyped && !phase.erasedTypes + val ptWild = if (mode.inPatternMode) + ptPlugins // SI-5022 don't widen pt for patterns as types flow from it to the case body. + else + dropExistential(ptPlugins) // FIXME: document why this is done. + val tree1: Tree = if (alreadyTyped) tree else typed1(tree, mode, ptWild) + if (shouldPrint) + typingStack.showTyped(tree1) + // Can happen during erroneous compilation - error(s) have been // reported, but we need to avoid causing an NPE with this tree if (tree1.tpe eq null) return setError(tree) - if (!alreadyTyped) { - printTyping("typed %s: %s%s".format( - ptTree(tree1), tree1.tpe, - if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "") - ) - } + tree1 modifyType (pluginsTyped(_, this, tree1, mode, ptPlugins)) - tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, ptPlugins) val result = if (tree1.isEmpty) tree1 else { @@ -5627,84 +5079,82 @@ trait Typers extends Modes with Adaptations with Tags { if (hasPendingMacroExpansions) macroExpandAll(this, result) else result } - if (!alreadyTyped) { - printTyping("adapted %s: %s to %s, %s".format( - tree1, tree1.tpe.widen, ptPlugins, context.undetparamsString) - ) //DEBUG - } - if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result) + if (shouldPrint) + typingStack.showAdapt(tree1, result, ptPlugins, context) + + if (!isPastTyper) + signalDone(context.asInstanceOf[analyzer.Context], tree, result) + result - } catch { + } + + try runTyper() 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 - + typingStack.printTyping(tree, "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 - Console.println("exception when typing "+tree+", pt = "+ptPlugins) + // @M causes cyclic reference error + devWarning(s"exception when typing $tree, pt=$ptPlugins") if (context != null && context.unit.exists && tree != null) logError("AT: " + (tree.pos).dbgString, ex) throw ex } - finally { - deindentTyping() - if (Statistics.canEnable) Statistics.popTimer(byTypeStack, startByType) - } } 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) + val ret = typed(tree, context.defaultModeForTyped, WildcardType) ret } - def typedPos(pos: Position, mode: Int, pt: Type)(tree: Tree) = typed(atPos(pos)(tree), mode, pt) + def typedByValueExpr(tree: Tree, pt: Type = WildcardType): Tree = typed(tree, EXPRmode | BYVALmode, 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) + typed(tree, context.defaultModeForTyped, pt) - /** Types qualifier <code>tree</code> of a select node. - * E.g. is tree occurs in a context like <code>tree.m</code>. + def typed(tree: Tree, mode: Mode): Tree = + typed(tree, mode, WildcardType) + + /** 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 = - 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 + def typedQualifier(tree: Tree, mode: Mode, pt: Type): Tree = + typed(tree, PolyQualifierModes | mode.onlyTypePat, 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) /** Types function part of an application */ - def typedOperator(tree: Tree): Tree = - typed(tree, EXPRmode | FUNmode | POLYmode | TAPPmode, WildcardType) + def typedOperator(tree: Tree): Tree = typed(tree, OperatorModes) + + // the qualifier type of a supercall constructor is its first parent class + private def typedSelectOrSuperQualifier(qual: Tree) = + context withinSuperInit typed(qual, PolyQualifierModes) - /** 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 @@ -5726,30 +5176,28 @@ trait Typers extends Modes with Adaptations with Tags { // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this? typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))) match { case tpt if tpt.isType => PatternMustBeValue(tpt, pt); tpt - case pat => pat + case pat => pat } } /** 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 = - typed(tree, HKmode, WildcardType) + else context withinTypeConstructorAllowed typed(tree, NOmode, pt) - def typedHigherKindedType(tree: Tree): Tree = typedHigherKindedType(tree, NOmode) + def typedHigherKindedType(tree: Tree, mode: Mode): Tree = + context withinTypeConstructorAllowed typed(tree) /** 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 { @@ -5770,7 +5218,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) @@ -5779,8 +5227,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] @@ -5798,40 +5246,29 @@ trait Typers extends Modes with Adaptations with Tags { val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous)) && tree1 != EmptyTree val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty - if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImpl(ddef, tree1.symbol) else AnyClass.tpe - } - - def transformedOr(tree: Tree, op: => Tree): Tree = transformed.get(tree) match { - case Some(tree1) => transformed -= tree; tree1 - case None => op + if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImplRef(ddef, tree1) else AnyTpe } - def transformedOrTyped(tree: Tree, mode: Int, pt: Type): Tree = transformed.get(tree) match { - case Some(tree1) => transformed -= tree; tree1 - case None => typed(tree, mode, pt) + def transformedOr(tree: Tree, op: => Tree): Tree = transformed remove tree match { + case Some(tree1) => tree1 + case _ => op } -/* - def convertToTypeTree(tree: Tree): Tree = tree match { - case TypeTree() => tree - case _ => TypeTree(tree.tpe) + def transformedOrTyped(tree: Tree, mode: Mode, pt: Type): Tree = transformed remove tree match { + case Some(tree1) => tree1 + case _ => typed(tree, mode, pt) } -*/ } } 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/TypersTracking.scala b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala new file mode 100644 index 0000000000..f44fd412fd --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala @@ -0,0 +1,180 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package typechecker + +import scala.collection.mutable +import scala.reflect.internal.util.{ BatchSourceFile, Statistics } +import mutable.ListBuffer +import symtab.Flags._ +import Mode._ + +trait TypersTracking { + self: Analyzer => + + import global._ + import definitions._ + import typeDebug._ + + // To enable decent error messages when the typer crashes. + // TODO - this only catches trees which go through def typed, + // but there are all kinds of back ways - typedClassDef, etc. etc. + // Funnel everything through one doorway. + var lastTreeToTyper: Tree = EmptyTree + + def fullSiteString(context: Context): String = { + def owner_long_s = ( + if (settings.debug.value) { + def flags_s = context.owner.debugFlagString match { + case "" => "" + case s => " with flags " + inLightMagenta(s) + } + s", a ${context.owner.shortSymbolClass}$flags_s" + } + else "" + ) + def marker = if (context.bufferErrors) "silent" else "site" + def undet_s = context.undetparams match { + case Nil => "" + case ps => ps.mkString(" solving: ", ",", "") + } + def implicits_s = ( + if (context.enrichmentEnabled) + if (context.implicitsEnabled) "" + else inLightRed("enrichment only") + else inLightRed("implicits disabled") + ) + + s"($marker$undet_s: ${context.siteString}$owner_long_s) $implicits_s" + } + + object typingStack { + val out = new java.io.PrintWriter(System.err, true) + def println(msg: Any) = out println "" + msg + + // TODO - account for colors so the color of a multiline string + // doesn't infect the connector lines + private def currentIndent = "| " * depth + + private var trees: List[Frame] = Nil + private var depth = 0 + private def atLowerIndent[T](body: => T): T = { + depth -= 1 + try body finally depth += 1 + } + private def resetIfEmpty(s: String) = if (trees.isEmpty) resetColor(s) else s + + private def truncAndOneLine(s: String): String = { + val s1 = s.replaceAll("\\s+", " ") + if (s1.length < 60 || settings.debug.value) s1 else s1.take(57) + "..." + } + + private val nextId = { var x = 1 ; () => try x finally x += 1 } + private class Frame(val tree: Tree) { + val stamp = System.nanoTime + val id = nextId() + } + private object NoFrame extends Frame(EmptyTree) { } + private def greenType(tp: Type): String = tpe_s(tp, inGreen) + private def greenType(tree: Tree): String = tree match { + case null => "[exception]" + case md: MemberDef if md.tpe == NoType => inBlue(s"[${md.keyword} ${md.name}]") + " " + greenType(md.symbol.tpe) + case _ if tree.tpe.isComplete => greenType(tree.tpe) + case _ => "<?>" + } + def indented(s: String): String = + if (s == "") "" else currentIndent + s.replaceAll("\n", "\n" + currentIndent) + + @inline final def runWith[T](t: Tree)(body: => T): T = { + push(t) + try body finally pop(t) + } + def push(t: Tree): Unit = { + trees ::= new Frame(t) + depth += 1 + } + def pop(t: Tree): Unit = { + val frame = trees.head + assert(frame.tree eq t, ((frame.tree, t))) + trees = trees.tail + depth -= 1 + } + def show(s: String) { if (s != "") out.println(s) } + + def showPush(tree: Tree, context: Context) { + showPush(tree, NOmode, WildcardType, context) + } + def showPush(tree: Tree, mode: Mode, pt: Type, context: Context) { + val alreadyTyped = tree.tpe ne null + def tree_s = truncAndOneLine(ptTree(tree)) + def pt_s = if (pt.isWildcard || context.inTypeConstructorAllowed) "" else s": pt=$pt" + def all_s = List(tree_s, pt_s, mode, fullSiteString(context)) filterNot (_ == "") mkString " " + + atLowerIndent(show(indented("""|-- """ + all_s))) + } + def showPop(typedTree: Tree): Tree = { + val s = greenType(typedTree) + show(resetIfEmpty(indented("""\-> """ + s))) + typedTree + } + def showAdapt(original: Tree, adapted: Tree, pt: Type, context: Context) { + if (!noPrintAdapt(original, adapted)) { + def tree_s1 = inLightCyan(truncAndOneLine(ptTree(original))) + def pt_s = if (pt.isWildcard) "" else s" based on pt $pt" + def tree_s2 = adapted match { + case tt: TypeTree => "is now a TypeTree(" + tpe_s(tt.tpe, inCyan) + ")" + case _ => "adapted to " + inCyan(truncAndOneLine(ptTree(adapted))) + pt_s + } + show(indented(s"[adapt] $tree_s1 $tree_s2")) + } + } + def showTyped(tree: Tree) { + def class_s = tree match { + case _: RefTree => "" + case _ => " " + tree.shortClass + } + if (!noPrintTyping(tree)) + show(indented(s"[typed$class_s] " + truncAndOneLine(ptTree(tree)))) + } + + def nextTyped(tree: Tree, mode: Mode, pt: Type, context: Context)(body: => Tree): Tree = + nextTypedInternal(tree, showPush(tree, mode, pt, context))(body) + + def nextTyped(tree: Tree, context: Context)(body: => Tree): Tree = + nextTypedInternal(tree, showPush(tree, context))(body) + + def nextTypedInternal(tree: Tree, pushFn: => Unit)(body: => Tree): Tree = ( + if (noPrintTyping(tree)) + body + else + runWith(tree) { pushFn ; showPop(body) } + ) + + @inline final def printTyping(tree: Tree, s: => String) = { + if (printTypings && !noPrintTyping(tree)) + show(indented(s)) + } + @inline final def printTyping(s: => String) = { + if (printTypings) + show(indented(s)) + } + } + def tpe_s(tp: Type, colorize: String => String): String = tp match { + case OverloadedType(pre, alts) => alts map (alt => tpe_s(pre memberType alt, colorize)) mkString " <and> " + case _ => colorize(tp.toLongString) + } + // def sym_s(s: Symbol) = if (s eq null) "" + s else s.getClass.getName split '.' last; + + // Some trees which are typed with mind-numbing frequency and + // which add nothing by being printed. Did () type to Unit? Let's + // gamble on yes. + private def printingOk(t: Tree) = printTypings && (settings.debug.value || !noPrint(t)) + def noPrintTyping(t: Tree) = (t.tpe ne null) || !printingOk(t) + def noPrintAdapt(tree1: Tree, tree2: Tree) = !printingOk(tree1) || ( + (tree1.tpe == tree2.tpe) + && (tree1.symbol == tree2.symbol) + ) +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 31c5a61a8c..5049fec65b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -12,8 +12,7 @@ import symtab.Flags._ * @author Martin Odersky * @version 1.0 */ -trait Unapplies extends ast.TreeDSL -{ +trait Unapplies extends ast.TreeDSL { self: Analyzer => import global._ @@ -21,8 +20,8 @@ trait Unapplies extends ast.TreeDSL import CODE.{ CASE => _, _ } import treeInfo.{ isRepeatedParamType, isByNameParamType } - private val unapplyParamName = nme.x_0 - + private def unapplyParamName = nme.x_0 + private def caseMods = Modifiers(SYNTHETIC | CASE) // In the typeCompleter (templateSig) of a case class (resp it's module), // synthetic `copy` (reps `apply`, `unapply`) methods are added. To compute @@ -31,42 +30,17 @@ trait Unapplies extends ast.TreeDSL // moduleClass symbol of the companion module. class ClassForCaseCompanionAttachment(val caseClass: ClassDef) - /** returns type list for return type of the extraction - * @see extractorFormalTypes + /** Returns unapply or unapplySeq if available, without further checks. */ - def unapplyTypeList(pos: Position, ufn: Symbol, ufntpe: Type, args: List[Tree]) = { - assert(ufn.isMethod, ufn) - val nbSubPats = args.length - //Console.println("utl "+ufntpe+" "+ufntpe.typeSymbol) - ufn.name match { - case nme.unapply | nme.unapplySeq => - val (formals, _) = extractorFormalTypes(pos, unapplyUnwrap(ufntpe), nbSubPats, ufn, treeInfo.effectivePatternArity(args)) - if (formals == null) throw new TypeError(s"$ufn of type $ufntpe cannot extract $nbSubPats sub-patterns") - else formals - case _ => throw new TypeError(ufn+" is not an unapply or unapplySeq") - } - } + def directUnapplyMember(tp: Type): Symbol = (tp member nme.unapply) orElse (tp member nme.unapplySeq) - /** returns type of the unapply method returning T_0...T_n - * for n == 0, boolean - * for n == 1, Some[T0] - * else Some[Product[Ti]] + /** Filters out unapplies with multiple (non-implicit) parameter lists, + * as they cannot be used as extractors */ - def unapplyReturnTypeExpected(argsLength: Int) = argsLength match { - case 0 => BooleanClass.tpe - case 1 => optionType(WildcardType) - case n => optionType(productType((List fill n)(WildcardType))) - } + def unapplyMember(tp: Type): Symbol = directUnapplyMember(tp) filter (sym => !hasMultipleNonImplicitParamLists(sym)) - /** returns unapply or unapplySeq if available */ - def unapplyMember(tp: Type): Symbol = (tp member nme.unapply) match { - case NoSymbol => tp member nme.unapplySeq - case unapp => unapp - } - /** returns unapply member's parameter type. */ - def unapplyParameterType(extractor: Symbol) = extractor.tpe.params match { - case p :: Nil => p.tpe.typeSymbol - case _ => NoSymbol + object ExtractorType { + def unapply(tp: Type): Option[Symbol] = unapplyMember(tp).toOption } def copyUntyped[T <: Tree](tree: T): T = @@ -97,25 +71,19 @@ trait Unapplies extends ast.TreeDSL */ private def caseClassUnapplyReturnValue(param: Name, caseclazz: ClassDef) = { def caseFieldAccessorValue(selector: ValDef): Tree = { - val accessorName = selector.name - val privateLocalParamAccessor = caseclazz.impl.body.collectFirst { - case dd: ValOrDefDef if dd.name == accessorName && dd.mods.isPrivateLocal => dd.symbol - } - privateLocalParamAccessor match { - case None => - // Selecting by name seems to be the most straight forward way here to - // avoid forcing the symbol of the case class in order to list the accessors. - val maybeRenamedAccessorName = caseAccessorName(caseclazz.symbol, accessorName) - Ident(param) DOT maybeRenamedAccessorName - case Some(sym) => - // But, that gives a misleading error message in neg/t1422.scala, where a case - // class has an illegal private[this] parameter. We can detect this by checking - // the modifiers on the param accessors. - // - // We just generate a call to that param accessor here, which gives us an inaccessible - // symbol error, as before. - Ident(param) DOT sym + // Selecting by name seems to be the most straight forward way here to + // avoid forcing the symbol of the case class in order to list the accessors. + def selectByName = Ident(param) DOT caseAccessorName(caseclazz.symbol, selector.name) + // But, that gives a misleading error message in neg/t1422.scala, where a case + // class has an illegal private[this] parameter. We can detect this by checking + // the modifiers on the param accessors. + // We just generate a call to that param accessor here, which gives us an inaccessible + // symbol error, as before. + def localAccessor = caseclazz.impl.body find { + case t @ ValOrDefDef(mods, selector.name, _, _) => mods.isPrivateLocal + case _ => false } + localAccessor.fold(selectByName)(Ident(param) DOT _.symbol) } // Working with trees, rather than symbols, to avoid cycles like SI-5082 @@ -128,11 +96,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,11 +122,9 @@ 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)) + gen.mkTemplate(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus)) } - private val caseMods = Modifiers(SYNTHETIC | CASE) - /** The apply method corresponding to a case class */ def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef): DefDef = { 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) - } -} |