diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/RefChecks.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 342 |
1 files changed, 154 insertions, 188 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 0b44566108..86a1d3f2e4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -6,15 +6,17 @@ package scala.tools.nsc package typechecker -import symtab.Flags._ -import scala.collection.{ mutable, immutable } -import transform.InfoTransform -import scala.collection.mutable.ListBuffer import scala.language.postfixOps + +import scala.collection.mutable +import scala.collection.mutable.ListBuffer import scala.tools.nsc.settings.ScalaVersion -import scala.tools.nsc.settings.AnyScalaVersion import scala.tools.nsc.settings.NoScalaVersion +import symtab.Flags._ +import transform.Transform + + /** <p> * Post-attribution checking and transformation. * </p> @@ -41,42 +43,20 @@ import scala.tools.nsc.settings.NoScalaVersion * * @todo Check whether we always check type parameter bounds. */ -abstract class RefChecks extends InfoTransform with scala.reflect.internal.transform.RefChecks { +abstract class RefChecks extends Transform { val global: Global // need to repeat here because otherwise last mixin defines global as // SymbolTable. If we had DOT this would not be an issue import global._ import definitions._ - import typer.{typed, typedOperator, atOwner} + import typer.typed /** the following two members override abstract members in Transform */ val phaseName: String = "refchecks" - override def phaseNewFlags: Long = lateMETHOD def newTransformer(unit: CompilationUnit): RefCheckTransformer = new RefCheckTransformer(unit) - override def changesBaseClasses = false - - override def transformInfo(sym: Symbol, tp: Type): Type = { - // !!! 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) - } val toJavaRepeatedParam = new SubstSymMap(RepeatedParamClass -> JavaRepeatedParamClass) val toScalaRepeatedParam = new SubstSymMap(JavaRepeatedParamClass -> RepeatedParamClass) @@ -86,17 +66,24 @@ 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.dealiasWiden, tp2.dealiasWiden) match { + def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type, isModuleOverride: Boolean): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match { case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => rtp1 <:< rtp2 case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => rtp1 <:< rtp2 - case (TypeRef(_, sym, _), _) if sym.isModuleClass => - overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix) + + // all this module business would be so much simpler if we moduled^w modelled a module as a class and an accessor, like we do for fields + case (TypeRef(_, sym, _), _) if sym.isModuleClass => + overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix, isModuleOverride) + case (_, TypeRef(_, sym, _)) if sym.isModuleClass => + overridesTypeInPrefix(tp1, NullaryMethodType(tp2), prefix, isModuleOverride) + case _ => def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner) - - (tp1 <:< tp2) || ( // object override check + (tp1 <:< tp2) || isModuleOverride && ( + // Object override check. This requires that both the overridden and the overriding member are object + // definitions. The overriding module type is allowed to replace the original one with the same name + // as long as it conform to the original non-singleton type. tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && { val cb1 = classBoundAsSeen(tp1) val cb2 = classBoundAsSeen(tp2) @@ -108,6 +95,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans ) } + private val separatelyCompiledScalaSuperclass = perRunCaches.newAnyRefMap[Symbol, Unit]() + final def isSeparatelyCompiledScalaSuperclass(sym: Symbol) = if (globalPhase.refChecked){ + separatelyCompiledScalaSuperclass.contains(sym) + } else { + // conservative approximation in case someone in pre-refchecks phase asks for `exitingFields(someClass.info)` + // and we haven't run the refchecks tree transform which populates `separatelyCompiledScalaSuperclass` + false + } + class RefCheckTransformer(unit: CompilationUnit) extends Transformer { var localTyper: analyzer.Typer = typer @@ -168,12 +164,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // This has become noisy with implicit classes. if (settings.warnPolyImplicitOverload && settings.developer) { - clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym => + clazz.info.decls.foreach(sym => if (sym.isImplicit && sym.typeParams.nonEmpty) { // implicit classes leave both a module symbol and a method symbol as residue val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule) if (alts.size > 1) alts foreach (x => reporter.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds")) - } + }) } } @@ -294,16 +290,29 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz) def infoStringWithLocation(sym: Symbol) = infoString0(sym, true) - def infoString0(sym: Symbol, showLocation: Boolean) = { - val sym1 = analyzer.underlyingSymbol(sym) - sym1.toString() + + def infoString0(member: Symbol, showLocation: Boolean) = { + val underlying = // not using analyzer.underlyingSymbol(member) because we should get rid of it + if (!(member hasFlag ACCESSOR)) member + else member.accessed match { + case field if field.exists => field + case _ if member.isSetter => member.getterIn(member.owner) + case _ => member + } + + def memberInfo = + self.memberInfo(underlying) match { + case getterTp if underlying.isGetter => getterTp.resultType + case tp => tp + } + + underlying.toString() + (if (showLocation) - sym1.locationString + - (if (sym1.isAliasType) ", which equals "+self.memberInfo(sym1) - else if (sym1.isAbstractType) " with bounds"+self.memberInfo(sym1) - else if (sym1.isModule) "" - else if (sym1.isTerm) " of type "+self.memberInfo(sym1) - else "") + underlying.locationString + + (if (underlying.isAliasType) s", which equals $memberInfo" + else if (underlying.isAbstractType) s" with bounds$memberInfo" + else if (underlying.isModule) "" + else if (underlying.isTerm) s" of type $memberInfo" + else "") else "") } @@ -314,10 +323,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans import pair._ val member = low val other = high - def memberTp = lowType - def otherTp = highType - debuglog("Checking validity of %s overriding %s".format(member.fullLocationString, other.fullLocationString)) +// debuglog(s"Checking validity of ${member.fullLocationString} overriding ${other.fullLocationString}") def noErrorType = !pair.isErroneous def isRootOrNone(sym: Symbol) = sym != null && sym.isRoot || sym == NoSymbol @@ -342,9 +349,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans analyzer.foundReqMsg(member.tpe, other.tpe) else "" - "overriding %s;\n %s %s%s".format( - infoStringWithLocation(other), infoString(member), msg, addendum - ) + s"overriding ${infoStringWithLocation(other)};\n ${infoString(member)} $msg$addendum" } def emitOverrideError(fullmsg: String) { if (member.owner == clazz) reporter.error(member.pos, fullmsg) @@ -421,7 +426,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans overrideError("cannot be used here - classes can only override abstract types") } else if (other.isEffectivelyFinal) { // (1.2) overrideError("cannot override final member") - } else if (!other.isDeferredOrJavaDefault && !other.hasFlag(JAVA_DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*) + } else if (!other.isDeferred && !member.isAnyOverride && !member.isSynthetic) { // (*) // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. if (isNeitherInClass && !(other.owner isSubClass member.owner)) @@ -435,9 +440,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } else if (other.isAbstractOverride && other.isIncompleteIn(clazz) && !member.isAbstractOverride) { overrideError("needs `abstract override' modifiers") } - 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. + else if (member.isAnyOverride && (other hasFlag ACCESSOR) && !(other hasFlag STABLE | DEFERRED)) { + // The check above used to look at `field` == `other.accessed`, ensuring field.isVariable && !field.isLazy, + // which I think is identical to the more direct `!(other hasFlag STABLE)` (given that `other` is a method). + // Also, we're moving away from (looking at) underlying fields (vals in traits no longer have them, to begin with) + // TODO: this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here. if (!settings.overrideVars) overrideError("cannot override a mutable variable") } @@ -450,9 +457,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } else if (other.isStable && !member.isStable) { // (1.4) overrideError("needs to be a stable, immutable value") } else if (member.isValue && member.isLazy && - other.isValue && !other.isSourceMethod && !other.isDeferred && !other.isLazy) { + other.isValue && other.hasFlag(STABLE) && !(other.isDeferred || other.isLazy)) { overrideError("cannot override a concrete non-lazy value") - } else if (other.isValue && other.isLazy && !other.isSourceMethod && !other.isDeferred && + } else if (other.isValue && other.isLazy && 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) @@ -463,7 +470,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkOverrideTypes() checkOverrideDeprecated() if (settings.warnNullaryOverride) { - if (other.paramss.isEmpty && !member.paramss.isEmpty) { + if (other.paramss.isEmpty && !member.paramss.isEmpty && !member.isJavaDefined) { reporter.warning(member.pos, "non-nullary method overrides nullary method") } } @@ -518,7 +525,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } def checkOverrideTerm() { other.cookJavaRawInfo() // #2454 - if (!overridesTypeInPrefix(lowType, highType, rootType)) { // 8 + if (!overridesTypeInPrefix(lowType, highType, rootType, low.isModuleOrModuleClass && high.isModuleOrModuleClass)) { // 8 overrideTypeError() explainTypes(lowType, highType) } @@ -543,10 +550,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } def checkOverrideDeprecated() { - if (other.hasDeprecatedOverridingAnnotation && !member.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) { - val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse "" - val msg = s"overriding ${other.fullLocationString} is deprecated$suffix" - currentRun.reporting.deprecationWarning(member.pos, other, msg) + if (other.hasDeprecatedOverridingAnnotation && !(member.hasDeprecatedOverridingAnnotation || member.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation))) { + val version = other.deprecatedOverridingVersion.getOrElse("") + val since = if (version.isEmpty) version else s" (since $version)" + val message = other.deprecatedOverridingMessage map (msg => s": $msg") getOrElse "" + val report = s"overriding ${other.fullLocationString} is deprecated$since$message" + currentRun.reporting.deprecationWarning(member.pos, other, report, version) } } } @@ -604,10 +613,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def checkNoAbstractMembers(): Unit = { // Avoid spurious duplicates: first gather any missing members. def memberList = clazz.info.nonPrivateMembersAdmitting(VBRIDGE) - val (missing, rest) = memberList partition (m => m.isDeferredNotJavaDefault && !ignoreDeferred(m)) + val (missing, rest) = memberList partition (m => m.isDeferred && !ignoreDeferred(m)) // Group missing members by the name of the underlying symbol, // to consolidate getters and setters. - val grouped = missing groupBy (sym => analyzer.underlyingSymbol(sym).name) + val grouped = missing groupBy (_.name.getterName) val missingMethods = grouped.toList flatMap { case (name, syms) => if (syms exists (_.isSetter)) syms filterNot (_.isGetter) @@ -645,19 +654,20 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans for (member <- missing) { def undefined(msg: String) = abstractClassError(false, infoString(member) + " is not defined" + msg) - val underlying = analyzer.underlyingSymbol(member) + val underlying = analyzer.underlyingSymbol(member) // TODO: don't use this method // Give a specific error message for abstract vars based on why it fails: // It could be unimplemented, have only one accessor, or be uninitialized. - if (underlying.isVariable) { - val isMultiple = grouped.getOrElse(underlying.name, Nil).size > 1 + val groupedAccessors = grouped.getOrElse(member.name.getterName, Nil) + val isMultiple = groupedAccessors.size > 1 + if (groupedAccessors.exists(_.isSetter) || (member.isGetter && !isMultiple && member.setterIn(member.owner).exists)) { // If both getter and setter are missing, squelch the setter error. if (member.isSetter && isMultiple) () else undefined( if (member.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)" else if (member.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)" - else analyzer.abstractVarMessage(member) + else "\n(Note that variables need to be initialized to be defined)" ) } else if (underlying.isMethod) { @@ -851,6 +861,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // println("validate base type "+tp) val baseClass = tp.typeSymbol if (baseClass.isClass) { + if (!baseClass.isTrait && !baseClass.isJavaDefined && !currentRun.compiles(baseClass) && !separatelyCompiledScalaSuperclass.contains(baseClass)) + separatelyCompiledScalaSuperclass.update(baseClass, ()) val index = clazz.info.baseTypeIndex(baseClass) if (index >= 0) { if (seenTypes(index) forall (tp1 => !(tp1 <:< tp))) @@ -917,17 +929,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans var index = -1 for (stat <- stats) { index = index + 1 - def enterSym(sym: Symbol) = if (sym.isLocalToBlock) { - currentLevel.scope.enter(sym) - symIndex(sym) = index - } stat match { - case DefDef(_, _, _, _, _, _) if stat.symbol.isLazy => - enterSym(stat.symbol) - case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => - //assert(stat.symbol != NoSymbol, stat);//debug - enterSym(stat.symbol.lazyAccessorOrSelf) + case _ : MemberDef if stat.symbol.isLocalToBlock => + currentLevel.scope.enter(stat.symbol) + symIndex(stat.symbol) = index case _ => } } @@ -1095,7 +1101,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 (ObjectTpe <:< common && !(ObjectTpe <:< actual.tpe && ObjectTpe <:< receiver.tpe)) + if (ObjectTpe <:< common && !(ObjectTpe <:< actual.tpe) && !(ObjectTpe <:< receiver.tpe)) unrelatedTypes() } // warn if actual has a case parent that is not same as receiver's; @@ -1122,27 +1128,21 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } /** Sensibility check examines flavors of equals. */ 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) && !currentOwner.isSynthetic => + case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) && (!currentOwner.isSynthetic || currentOwner.isAnonymousFunction) => checkSensibleEquals(pos, qual, name, fn.symbol, args.head) case _ => } - // SI-6276 warn for `def foo = foo` or `val bar: X = bar`, which come up more frequently than you might think. - def checkInfiniteLoop(valOrDef: ValOrDefDef) { - def callsSelf = valOrDef.rhs match { - case t @ (Ident(_) | Select(This(_), _)) => - t hasSymbolWhich (_.accessedOrSelf == valOrDef.symbol) - case _ => false + // SI-6276 warn for trivial recursion, such as `def foo = foo` or `val bar: X = bar`, which come up more frequently than you might think. + // TODO: Move to abide rule. Also, this does not check that the def is final or not overridden, for example + def checkInfiniteLoop(sym: Symbol, rhs: Tree): Unit = + if (!sym.isValueParameter && sym.paramss.isEmpty) { + rhs match { + case t@(Ident(_) | Select(This(_), _)) if t hasSymbolWhich (_.accessedOrSelf == sym) => + reporter.warning(rhs.pos, s"${sym.fullLocationString} does nothing other than call itself recursively") + case _ => + } } - val trivialInfiniteLoop = ( - !valOrDef.isErroneous - && !valOrDef.symbol.isValueParameter - && valOrDef.symbol.paramss.isEmpty - && callsSelf - ) - if (trivialInfiniteLoop) - reporter.warning(valOrDef.rhs.pos, s"${valOrDef.symbol.fullLocationString} does nothing other than call itself recursively") - } // Transformation ------------------------------------------------------------ @@ -1150,11 +1150,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def toConstructor(pos: Position, tpe: Type): Tree = { val rtpe = tpe.finalResultType assert(rtpe.typeSymbol hasFlag CASE, tpe) - localTyper.typedOperator { + val tree = localTyper.typedOperator { atPos(pos) { Select(New(TypeTree(rtpe)), rtpe.typeSymbol.primaryConstructor) } } + checkUndesiredProperties(rtpe.typeSymbol, tree.pos) + checkUndesiredProperties(rtpe.typeSymbol.primaryConstructor, tree.pos) + tree } override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { @@ -1167,74 +1170,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans finally popLevel() } - /** 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: - * - * 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(moduleDef: Tree): List[Tree] = exitingRefchecks { - val ModuleDef(_, _, impl) = moduleDef - val module = moduleDef.symbol - val moduleClass = module.moduleClass - 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(moduleClass, impl) setType NoType - - // This code is related to the fix of SI-9375, which stops adding `readResolve` methods to - // non-static (nested) modules. Before the fix, the method would cause the module accessor - // to become notPrivate. To prevent binary changes in the 2.11.x branch, we mimic that behavior. - // There is a bit of code duplication between here and SyntheticMethods. We cannot call - // makeNotPrivate already in SyntheticMethod: that is during type checking, and not all references - // are resolved yet, so we cannot rename a definition. This code doesn't exist in the 2.12.x branch. - def hasConcreteImpl(name: Name) = moduleClass.info.member(name).alternatives exists (m => !m.isDeferred) - val hadReadResolveBeforeSI9375 = moduleClass.isSerializable && !hasConcreteImpl(nme.readResolve) - if (hadReadResolveBeforeSI9375) - moduleClass.sourceModule.makeNotPrivate(moduleClass.sourceModule.owner) - - // 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 (site.isTerm) NoSymbol - else site.info decl nme.moduleVarName(moduleName) - ) - vsym orElse (site newModuleVarSymbol module) - } - 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 matchingInnerObject() = { - val newFlags = (module.flags | STABLE) & ~MODULE - val newInfo = NullaryMethodType(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 - newInnerObject() - ) - transformTrees(newTrees map localTyper.typedPos(moduleDef.pos)) - } def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case t if treeInfo.isSelfConstrCall(t) => @@ -1245,15 +1181,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans debuglog("refsym = " + currentLevel.refsym) reporter.error(currentLevel.refpos, "forward reference not allowed from self constructor invocation") } - case ModuleDef(_, _, _) => eliminateModuleDefs(tree) case ValDef(_, _, _, _) => val tree1 = transform(tree) // important to do before forward reference check if (tree1.symbol.isLazy) tree1 :: Nil else { - val lazySym = tree.symbol.lazyAccessorOrSelf - if (lazySym.isLocalToBlock && index <= currentLevel.maxindex) { + val sym = tree.symbol + if (sym.isLocalToBlock && index <= currentLevel.maxindex) { debuglog("refsym = " + currentLevel.refsym) - reporter.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym) + reporter.error(currentLevel.refpos, "forward reference extends over definition of " + sym) } tree1 :: Nil } @@ -1421,7 +1356,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans currentRun.reporting.deprecationWarning( tree.pos, symbol, - s"${symbol.toString} overrides concrete, non-deprecated symbol(s): ${concrOvers.map(_.name.decode).mkString(", ")}") + s"${symbol.toString} overrides concrete, non-deprecated symbol(s): ${concrOvers.map(_.name.decode).mkString(", ")}", "") } } private def isRepeatedParamArg(tree: Tree) = currentApplication match { @@ -1476,17 +1411,26 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans transformTrees(annots flatMap (_.args)) } + def checkIsElisible(sym: Symbol) = if (sym ne null) sym.elisionLevel.foreach { level => + if (!sym.isMethod || sym.isAccessor || sym.isLazy || sym.isDeferred) + reporter.error(sym.pos, s"${sym.name}: Only methods can be marked @elidable.") + } + if (settings.isScala213) checkIsElisible(tree.symbol) + tree match { case m: MemberDef => val sym = m.symbol applyChecks(sym.annotations) - // validate implicitNotFoundMessage - analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn => - reporter.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn") - } + + def messageWarning(name: String)(warn: String) = + reporter.warning(tree.pos, f"Invalid $name message for ${sym}%s${sym.locationString}%s:%n$warn") + + // validate implicitNotFoundMessage and implicitAmbiguousMessage + analyzer.ImplicitNotFoundMsg.check(sym) foreach messageWarning("implicitNotFound") + analyzer.ImplicitAmbiguousMsg.check(sym) foreach messageWarning("implicitAmbiguous") case tpt@TypeTree() => - if(tpt.original != null) { + if (tpt.original != null) { tpt.original foreach { case dc@TypeTreeWithDeferredRefCheck() => applyRefchecksToAnnotations(dc.check()) // #2416 @@ -1518,25 +1462,35 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans ) } - sym.isSourceMethod && + sym.name == nme.apply && + !(sym hasFlag STABLE) && // ??? sym.isCase && - sym.name == nme.apply && isClassTypeAccessible(tree) && !tree.tpe.finalResultType.typeSymbol.primaryConstructor.isLessAccessibleThan(tree.symbol) } private def transformCaseApply(tree: Tree) = { + def loop(t: Tree): Unit = t match { + case Ident(_) => + checkUndesiredProperties(t.symbol, t.pos) + case Select(qual, _) => + checkUndesiredProperties(t.symbol, t.pos) + loop(qual) + case _ => + } + tree foreach { case i@Ident(_) => enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()` case _ => } + loop(tree) toConstructor(tree.pos, tree.tpe) } private def transformApply(tree: Apply): Tree = tree match { case Apply( - Select(qual, nme.filter | nme.withFilter), + Select(qual, nme.withFilter), List(Function( List(ValDef(_, pname, tpt, _)), Match(_, CaseDef(pat1, _, _) :: _)))) @@ -1643,13 +1597,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // inside annotations. applyRefchecksToAnnotations(tree) var result: Tree = tree match { - case DefDef(_, _, _, _, _, EmptyTree) if sym hasAnnotation NativeAttr => - sym resetFlag DEFERRED - transform(deriveDefDef(tree)(_ => typed(gen.mkSysErrorCall("native method stub")))) - - case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) => + // NOTE: a val in a trait is now a DefDef, with the RHS being moved to an Assign in Constructors + case tree: ValOrDefDef => checkDeprecatedOvers(tree) - checkInfiniteLoop(tree.asInstanceOf[ValOrDefDef]) + if (!tree.isErroneous) + checkInfiniteLoop(tree.symbol, tree.rhs) + if (settings.warnNullaryUnit) checkNullaryMethodReturnType(sym) if (settings.warnInaccessible) { @@ -1657,10 +1610,22 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkAccessibilityOfReferencedTypes(tree) } tree match { - case dd: DefDef => checkByNameRightAssociativeDef(dd) - case _ => + case dd: DefDef => + checkByNameRightAssociativeDef(dd) + + if (sym hasAnnotation NativeAttr) { + if (sym.owner.isTrait) { + reporter.error(tree.pos, "A trait cannot define a native method.") + tree + } else if (dd.rhs == EmptyTree) { + // pretend it had a stub implementation + sym resetFlag DEFERRED + deriveDefDef(dd)(_ => typed(gen.mkSysErrorCall("native method stub"))) + } else tree + } else tree + + case _ => tree } - tree case Template(parents, self, body) => localTyper = localTyper.atOwner(tree, currentOwner) @@ -1668,7 +1633,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkOverloadedRestrictions(currentOwner, currentOwner) // SI-7870 default getters for constructors live in the companion module checkOverloadedRestrictions(currentOwner, currentOwner.companionModule) - val bridges = addVarargBridges(currentOwner) + val bridges = addVarargBridges(currentOwner) // TODO: do this during uncurry? checkAllOverrides(currentOwner) checkAnyValSubclass(currentOwner) if (currentOwner.isDerivedValueClass) @@ -1694,7 +1659,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case tp @ ExistentialType(tparams, tpe) => existentialParams ++= tparams case ann: AnnotatedType if ann.hasAnnotation(UncheckedBoundsClass) => - // SI-7694 Allow code synthetizers to disable checking of bounds for TypeTrees based on inferred LUBs + // SI-7694 Allow code synthesizers to disable checking of bounds for TypeTrees based on inferred LUBs // which might not conform to the constraints. skipBounds = true case tp: TypeRef => @@ -1786,7 +1751,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } result match { case ClassDef(_, _, _, _) - | TypeDef(_, _, _, _) => + | TypeDef(_, _, _, _) + | ModuleDef(_, _, _) => if (result.symbol.isLocalToBlock || result.symbol.isTopLevel) varianceValidator.traverse(result) case tt @ TypeTree() if tt.original != null => |