diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Namers.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 911 |
1 files changed, 533 insertions, 378 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 81299dc425..f69d1d5254 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -6,8 +6,8 @@ package scala.tools.nsc package typechecker -import scala.collection.mutable import scala.annotation.tailrec +import scala.collection.mutable import symtab.Flags._ import scala.language.postfixOps import scala.reflect.internal.util.ListOfNil @@ -61,6 +61,11 @@ trait Namers extends MethodSynthesis { private lazy val innerNamer = if (isTemplateContext(context)) createInnerNamer() else this + // Cached as a val because `settings.isScala212` parses the Scala version each time... + // Not in Namers because then we need to go to outer first to check this. + // I do think it's ok to check every time we create a Namer instance (so, not a lazy val). + private[this] val isScala212 = settings.isScala212 + def createNamer(tree: Tree): Namer = { val sym = tree match { case ModuleDef(_, _, _) => tree.symbol.moduleClass @@ -98,14 +103,10 @@ trait Namers extends MethodSynthesis { else newNamer(cx) } - def enterValueParams(vparamss: List[List[ValDef]]): List[List[Symbol]] = { + def enterValueParams(vparamss: List[List[ValDef]]): List[List[Symbol]] = mmap(vparamss) { param => - val sym = assignSymbol(param, param.name, mask = ValueParameterFlags) - setPrivateWithin(param, sym) - enterInScope(sym) - sym setInfo monoTypeCompleter(param) + enterInScope(assignMemberSymbol(param, mask = ValueParameterFlags)) setInfo monoTypeCompleter(param) } - } protected def owner = context.owner def contextFile = context.unit.source.file @@ -115,21 +116,15 @@ trait Namers extends MethodSynthesis { TypeSigError(tree, ex) alt } - // PRIVATE | LOCAL are fields generated for primary constructor arguments - // @PP: ...or fields declared as private[this]. PARAMACCESSOR marks constructor arguments. - // Neither gets accessors so the code is as far as I know still correct. - def noEnterGetterSetter(vd: ValDef) = !vd.mods.isLazy && ( - !owner.isClass - || (vd.mods.isPrivateLocal && !vd.mods.isCaseAccessor) - || (vd.name startsWith nme.OUTER) - || (context.unit.isJava) - || isEnumConstant(vd) - ) - def noFinishGetterSetter(vd: ValDef) = ( - (vd.mods.isPrivateLocal && !vd.mods.isLazy) // all lazy vals need accessors, even private[this] - || vd.symbol.isModuleVar - || isEnumConstant(vd)) + // All lazy vals need accessors, including those owned by terms (e.g., in method) or private[this] in a class + def deriveAccessors(vd: ValDef) = (vd.mods.isLazy || owner.isTrait || (owner.isClass && deriveAccessorsInClass(vd))) + + private def deriveAccessorsInClass(vd: ValDef) = + !vd.mods.isPrivateLocal && // note, private[this] lazy vals do get accessors -- see outer disjunction of deriveAccessors + !(vd.name startsWith nme.OUTER) && // outer accessors are added later, in explicitouter + !isEnumConstant(vd) // enums can only occur in classes, so only check here + /** Determines whether this field holds an enum constant. * To qualify, the following conditions must be met: @@ -143,7 +138,7 @@ trait Namers extends MethodSynthesis { val ownerHasEnumFlag = // Necessary to check because scalac puts Java's static members into the companion object // while Scala's enum constants live directly in the class. - // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal + // We don't check for clazz.superClass == JavaEnumClass, because this causes an illegal // cyclic reference error. See the commit message for details. if (context.unit.isJava) owner.companionClass.hasJavaEnumFlag else owner.hasJavaEnumFlag vd.mods.hasAllFlags(JAVA_ENUM | STABLE | STATIC) && ownerHasEnumFlag @@ -170,13 +165,9 @@ trait Namers extends MethodSynthesis { def updatePosFlags(sym: Symbol, pos: Position, flags: Long): Symbol = { debuglog("[overwrite] " + sym) val newFlags = (sym.flags & LOCKED) | flags - sym.rawInfo match { - case tr: TypeRef => - // !!! needed for: pos/t5954d; the uniques type cache will happily serve up the same TypeRef - // over this mutated symbol, and we witness a stale cache for `parents`. - tr.invalidateCaches() - case _ => - } + // !!! needed for: pos/t5954d; the uniques type cache will happily serve up the same TypeRef + // over this mutated symbol, and we witness a stale cache for `parents`. + invalidateCaches(sym.rawInfo, sym :: sym.moduleClass :: Nil) sym reset NoType setFlag newFlags setPos pos sym.moduleClass andAlso (updatePosFlags(_, pos, moduleClassFlags(flags))) @@ -225,7 +216,10 @@ trait Namers extends MethodSynthesis { private def inCurrentScope(m: Symbol): Boolean = { if (owner.isClass) owner == m.owner - else m.owner.isClass && context.scope == m.owner.info.decls + else context.scope.lookupSymbolEntry(m) match { + case null => false + case entry => entry.owner eq context.scope + } } /** Enter symbol into context's scope and return symbol itself */ @@ -288,9 +282,7 @@ trait Namers extends MethodSynthesis { case tree @ DefDef(_, _, _, _, _, _) => enterDefDef(tree) case tree @ TypeDef(_, _, _, _) => enterTypeDef(tree) case DocDef(_, defn) => enterSym(defn) - case tree @ Import(_, _) => - assignSymbol(tree) - returnContext = context.make(tree) + case tree @ Import(_, _) => enterImport(tree); returnContext = context.make(tree) case _ => } returnContext @@ -301,40 +293,23 @@ trait Namers extends MethodSynthesis { } } - /** Creates a new symbol and assigns it to the tree, returning the symbol - */ - def assignSymbol(tree: Tree): Symbol = - logAssignSymbol(tree, tree match { - case PackageDef(pid, _) => createPackageSymbol(tree.pos, pid) - case Import(_, _) => createImportSymbol(tree) - case mdef: MemberDef => createMemberSymbol(mdef, mdef.name, -1L) - case _ => abort("Unexpected tree: " + tree) - }) - def assignSymbol(tree: MemberDef, name: Name, mask: Long): Symbol = - logAssignSymbol(tree, createMemberSymbol(tree, name, mask)) - - def assignAndEnterSymbol(tree: MemberDef): Symbol = { - val sym = assignSymbol(tree, tree.name, -1L) + def assignMemberSymbol(tree: MemberDef, mask: Long = -1L): Symbol = { + val sym = createMemberSymbol(tree, tree.name, mask) setPrivateWithin(tree, sym) - enterInScope(sym) + tree.symbol = sym + sym } + def assignAndEnterFinishedSymbol(tree: MemberDef): Symbol = { - val sym = assignAndEnterSymbol(tree) + val sym = enterInScope(assignMemberSymbol(tree)) sym setInfo completerOf(tree) // log("[+info] " + sym.fullLocationString) sym } - private def logAssignSymbol(tree: Tree, sym: Symbol): Symbol = { - if (isPastTyper) sym.name.toTermName match { - case nme.IMPORT | nme.OUTER | nme.ANON_CLASS_NAME | nme.ANON_FUN_NAME | nme.CONSTRUCTOR => () - case _ => - tree match { - case md: DefDef => log("[+symbol] " + sym.debugLocationString) - case _ => - } - } - tree.symbol = sym + def createMethod(accessQual: MemberDef, name: TermName, pos: Position, flags: Long): MethodSymbol = { + val sym = owner.newMethod(name, pos, flags) + setPrivateWithin(accessQual, sym) sym } @@ -361,11 +336,9 @@ trait Namers extends MethodSynthesis { else owner.newValue(name.toTermName, pos, flags) } } - def createFieldSymbol(tree: ValDef): TermSymbol = - owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) - def createImportSymbol(tree: Tree) = - NoSymbol.newImport(tree.pos) setInfo completerOf(tree) + def createImportSymbol(tree: Import) = + NoSymbol.newImport(tree.pos) setInfo (namerOf(tree.symbol) importTypeCompleter tree) /** All PackageClassInfoTypes come from here. */ def createPackageSymbol(pos: Position, pid: RefTree): Symbol = { @@ -417,7 +390,7 @@ trait Namers extends MethodSynthesis { clearRenamedCaseAccessors(existing) existing } - else assignAndEnterSymbol(tree) setFlag inConstructorFlag + else enterInScope(assignMemberSymbol(tree)) setFlag inConstructorFlag } clazz match { case csym: ClassSymbol if csym.isTopLevel => enterClassSymbol(tree, csym) @@ -443,6 +416,7 @@ trait Namers extends MethodSynthesis { && !(module isCoDefinedWith clazz) && module.exists && clazz.exists + && (currentRun.compiles(clazz) == currentRun.compiles(module)) ) if (fails) { reporter.error(tree.pos, ( @@ -463,9 +437,10 @@ trait Namers extends MethodSynthesis { /** Enter a module symbol. */ def enterModuleSymbol(tree : ModuleDef): Symbol = { - var m: Symbol = context.scope lookupModule tree.name val moduleFlags = tree.mods.flags | MODULE - if (m.isModule && !m.hasPackageFlag && inCurrentScope(m) && (currentRun.canRedefine(m) || m.isSynthetic)) { + + val existingModule = context.scope lookupModule tree.name + if (existingModule.isModule && !existingModule.hasPackageFlag && inCurrentScope(existingModule) && (currentRun.canRedefine(existingModule) || existingModule.isSynthetic)) { // This code accounts for the way the package objects found in the classpath are opened up // early by the completer of the package itself. If the `packageobjects` phase then finds // the same package object in sources, we have to clean the slate and remove package object @@ -473,21 +448,24 @@ trait Namers extends MethodSynthesis { // // TODO SI-4695 Pursue the approach in https://github.com/scala/scala/pull/2789 that avoids // opening up the package object on the classpath at all if one exists in source. - if (m.isPackageObject) { - val packageScope = m.enclosingPackageClass.rawInfo.decls - packageScope.filter(_.owner != m.enclosingPackageClass).toList.foreach(packageScope unlink _) + if (existingModule.isPackageObject) { + val packageScope = existingModule.enclosingPackageClass.rawInfo.decls + packageScope.foreach(mem => if (mem.owner != existingModule.enclosingPackageClass) packageScope unlink mem) } - updatePosFlags(m, tree.pos, moduleFlags) - setPrivateWithin(tree, m) - m.moduleClass andAlso (setPrivateWithin(tree, _)) - context.unit.synthetics -= m - tree.symbol = m + updatePosFlags(existingModule, tree.pos, moduleFlags) + setPrivateWithin(tree, existingModule) + existingModule.moduleClass andAlso (setPrivateWithin(tree, _)) + context.unit.synthetics -= existingModule + tree.symbol = existingModule } else { - m = assignAndEnterSymbol(tree) + enterInScope(assignMemberSymbol(tree)) + val m = tree.symbol m.moduleClass setFlag moduleClassFlags(moduleFlags) setPrivateWithin(tree, m.moduleClass) } + + val m = tree.symbol if (m.isTopLevel && !m.hasPackageFlag) { m.moduleClass.associatedFile = contextFile currentRun.symSource(m) = m.moduleClass.sourceFile @@ -610,24 +588,11 @@ trait Namers extends MethodSynthesis { noDuplicates(selectors map (_.rename), AppearsTwice) } - class CompleterWrapper(completer: TypeCompleter) extends TypeCompleter { - // override important when completer.isInstanceOf[PolyTypeCompleter]! - override val typeParams = completer.typeParams - - val tree = completer.tree - - override def complete(sym: Symbol): Unit = { - completer.complete(sym) - } - } - def copyMethodCompleter(copyDef: DefDef): TypeCompleter = { - 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` */ - def assignParamTypes() { + * copy method. See comment in `Unapplies.caseClassCopyMeth` + */ + def assignParamTypes(copyDef: DefDef, sym: Symbol) { val clazz = sym.owner val constructorType = clazz.primaryConstructor.tpe val subst = new SubstSymMap(clazz.typeParams, copyDef.tparams map (_.symbol)) @@ -640,9 +605,11 @@ trait Namers extends MethodSynthesis { ) } - mkTypeCompleter(copyDef) { sym => - assignParamTypes() - lazyType complete sym + new CompleterWrapper(completerOf(copyDef)) { + override def complete(sym: Symbol): Unit = { + assignParamTypes(tree.asInstanceOf[DefDef], sym) + super.complete(sym) + } } } @@ -654,6 +621,12 @@ trait Namers extends MethodSynthesis { super.complete(sym) + // don't propagate e.g. @volatile annot to apply's argument + def retainOnlyParamAnnots(param: Symbol) = + param setAnnotations (param.annotations filter AnnotationInfo.mkFilter(ParamTargetClass, defaultRetention = false)) + + sym.info.paramss.foreach(_.foreach(retainOnlyParamAnnots)) + // owner won't be locked val ownerInfo = companionContext.owner.info @@ -691,19 +664,16 @@ trait Namers extends MethodSynthesis { if (suppress) { sym setInfo ErrorType + // There are two ways in which we exclude the symbol from being added in typedStats::addSynthetics, // because we don't know when the completer runs with respect to this loop in addSynthetics // for (sym <- scope) // for (tree <- context.unit.synthetics.get(sym) if shouldAdd(sym)) { // if (!sym.initialize.hasFlag(IS_ERROR)) // newStats += typedStat(tree) - // (1) If we're already in the loop, set the IS_ERROR flag and trigger the condition - // `sym.initialize.hasFlag(IS_ERROR)` in typedStats::addSynthetics, - // (2) Or, if we are not yet in the addSynthetics loop (and we're not going to emit an error anyway), - // we unlink the symbol from its scope. + // If we're already in the loop, set the IS_ERROR flag and trigger the condition `sym.initialize.hasFlag(IS_ERROR)` sym setFlag IS_ERROR - - // For good measure. Removing it from its owner's scope and setting the IS_ERROR flag is enough to exclude it from addSynthetics + // Or, if we are not yet in the addSynthetics loop, we can just retract our symbol from the synthetics for this unit. companionContext.unit.synthetics -= sym // Don't unlink in an error situation to generate less confusing error messages. @@ -715,12 +685,12 @@ trait Namers extends MethodSynthesis { // I hesitate to provide more info, because it would involve a WildCard or something for its result type, // which could upset other code paths) if (!scopePartiallyCompleted) - companionContext.scope.unlink(sym) // (2) + companionContext.scope.unlink(sym) } } } - def completerOf(tree: Tree): TypeCompleter = { + def completerOf(tree: MemberDef): TypeCompleter = { val mono = namerOf(tree.symbol) monoTypeCompleter tree val tparams = treeInfo.typeParameters(tree) if (tparams.isEmpty) mono @@ -738,52 +708,46 @@ trait Namers extends MethodSynthesis { } } - def enterValDef(tree: ValDef) { - if (noEnterGetterSetter(tree)) - assignAndEnterFinishedSymbol(tree) - else - enterGetterSetter(tree) + def enterValDef(tree: ValDef): Unit = { + val isScala = !context.unit.isJava + if (isScala) { + if (nme.isSetterName(tree.name)) ValOrVarWithSetterSuffixError(tree) + if (tree.mods.isPrivateLocal && tree.mods.isCaseAccessor) PrivateThisCaseClassParameterError(tree) + } + + if (isScala && deriveAccessors(tree)) enterGetterSetter(tree) + else assignAndEnterFinishedSymbol(tree) - if (isEnumConstant(tree)) + if (isEnumConstant(tree)) { tree.symbol setInfo ConstantType(Constant(tree.symbol)) + tree.symbol.owner.linkedClassOfClass addChild tree.symbol + } } - def enterLazyVal(tree: ValDef, lazyAccessor: Symbol): TermSymbol = { - // If the owner is not a class, this is a lazy val from a method, - // with no associated field. It has an accessor with $lzy appended to its name and - // its flags are set differently. The implicit flag is reset because otherwise - // a local implicit "lazy val x" will create an ambiguity with itself - // 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 | ARTIFACT) & ~IMPLICIT) - ) - enterValSymbol(tree, sym setFlag MUTABLE setLazyAccessor lazyAccessor) - } - def enterStrictVal(tree: ValDef): TermSymbol = { - enterValSymbol(tree, createFieldSymbol(tree)) - } - def enterValSymbol(tree: ValDef, sym: TermSymbol): TermSymbol = { - enterInScope(sym) - sym setInfo namerOf(sym).monoTypeCompleter(tree) - } def enterPackage(tree: PackageDef) { - val sym = assignSymbol(tree) + val sym = createPackageSymbol(tree.pos, tree.pid) + tree.symbol = sym newNamer(context.make(tree, sym.moduleClass, sym.info.decls)) enterSyms tree.stats } + + private def enterImport(tree: Import) = { + val sym = createImportSymbol(tree) + tree.symbol = sym + } + def enterTypeDef(tree: TypeDef) = assignAndEnterFinishedSymbol(tree) def enterDefDef(tree: DefDef): Unit = tree match { case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => assignAndEnterFinishedSymbol(tree) - case DefDef(mods, name, tparams, _, _, _) => + case DefDef(mods, name, _, _, _, _) => val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE | ARTIFACT else 0 - val sym = assignAndEnterSymbol(tree) setFlag bridgeFlag + val sym = enterInScope(assignMemberSymbol(tree)) setFlag bridgeFlag val completer = if (sym hasFlag SYNTHETIC) { if (name == nme.copy) copyMethodCompleter(tree) - else if (settings.isScala212 && (sym hasFlag CASE)) applyUnapplyMethodCompleter(tree, context) + else if (sym hasFlag CASE) applyUnapplyMethodCompleter(tree, context) else completerOf(tree) } else completerOf(tree) @@ -857,16 +821,20 @@ trait Namers extends MethodSynthesis { 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 = typeSig(tree) + def monoTypeCompleter(tree: MemberDef) = new MonoTypeCompleter(tree) + class MonoTypeCompleter(tree: MemberDef) extends TypeCompleterBase(tree) { + override def completeImpl(sym: Symbol): Unit = { + // 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 + + val annotations = annotSig(tree.mods.annotations) + + val tp = typeSig(tree, annotations) findCyclicalLowerBound(tp) andAlso { sym => if (needsCycleCheck) { @@ -877,42 +845,175 @@ trait Namers extends MethodSynthesis { sym.initialize } } - sym setInfo { - if (sym.isJavaDefined) RestrictJavaArraysMap(tp) - else tp - } + + sym.setInfo(if (!sym.isJavaDefined) tp else RestrictJavaArraysMap(tp)) + if (needsCycleCheck) { log(s"Needs cycle check: ${sym.debugLocationString}") if (!typer.checkNonCyclic(tree.pos, tp)) sym setInfo ErrorType } + + validate(sym) } } - def moduleClassTypeCompleter(tree: ModuleDef) = { - mkTypeCompleter(tree) { sym => + def moduleClassTypeCompleter(tree: ModuleDef) = new ModuleClassTypeCompleter(tree) + class ModuleClassTypeCompleter(tree: ModuleDef) extends TypeCompleterBase(tree) { + override def completeImpl(sym: Symbol): Unit = { val moduleSymbol = tree.symbol assert(moduleSymbol.moduleClass == sym, moduleSymbol.moduleClass) moduleSymbol.info // sets moduleClass info as a side effect. } } - /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ - def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym => - logAndValidate(sym) { - sym setInfo { - val tp = if (isSetter) MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitTpe) - else NullaryMethodType(typeSig(tree)) - pluginsTypeSigAccessor(tp, typer, tree, sym) + def importTypeCompleter(tree: Import) = new ImportTypeCompleter(tree) + class ImportTypeCompleter(imp: Import) extends TypeCompleterBase(imp) { + override def completeImpl(sym: Symbol): Unit = { + sym setInfo importSig(imp) + } + } + + import AnnotationInfo.{mkFilter => annotationFilter} + + def implicitFactoryMethodCompleter(tree: DefDef, classSym: Symbol) = new CompleterWrapper(completerOf(tree)) { + override def complete(methSym: Symbol): Unit = { + super.complete(methSym) + val annotations = classSym.initialize.annotations + + methSym setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false)) + classSym setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true)) + } + } + + // complete the type of a value definition (may have a method symbol, for those valdefs that never receive a field, + // as specified by Field.noFieldFor) + def valTypeCompleter(tree: ValDef) = new ValTypeCompleter(tree) + class ValTypeCompleter(tree: ValDef) extends TypeCompleterBase(tree) { + override def completeImpl(fieldOrGetterSym: Symbol): Unit = { + val mods = tree.mods + val isGetter = fieldOrGetterSym.isMethod + val annots = + if (mods.annotations.isEmpty) Nil + else { + val annotSigs = annotSig(mods.annotations) + if (isGetter) filterAccessorAnnots(annotSigs, tree) // if this is really a getter, retain annots targeting either field/getter + else annotSigs filter annotationFilter(FieldTargetClass, !mods.isParamAccessor) + } + + // must use typeSig, not memberSig (TODO: when do we need to switch namers?) + val sig = typeSig(tree, annots) + + fieldOrGetterSym setInfo (if (isGetter) NullaryMethodType(sig) else sig) + + validate(fieldOrGetterSym) + } + } + + // knowing `isBean`, we could derive `isSetter` from `valDef.name` + def accessorTypeCompleter(valDef: ValDef, missingTpt: Boolean, isBean: Boolean, isSetter: Boolean) = new AccessorTypeCompleter(valDef, missingTpt, isBean, isSetter) + class AccessorTypeCompleter(valDef: ValDef, missingTpt: Boolean, isBean: Boolean, isSetter: Boolean) extends TypeCompleterBase(valDef) { + override def completeImpl(accessorSym: Symbol): Unit = { + context.unit.synthetics get accessorSym match { + case Some(ddef: DefDef) => + // `accessorSym` is the accessor for which we're completing the info (tree == ddef), + // while `valDef` is the field definition that spawned the accessor + // NOTE: `valTypeCompleter` handles abstract vals, trait vals and lazy vals, where the ValDef carries the getter's symbol + + // reuse work done in valTypeCompleter if we already computed the type signature of the val + // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait) + val valSig = + if ((accessorSym ne valDef.symbol) && valDef.symbol.isInitialized) valDef.symbol.info + else typeSig(valDef, Nil) // don't set annotations for the valdef -- we just want to compute the type sig (TODO: dig deeper and see if we can use memberSig) + + // patch up the accessor's tree if the valdef's tpt was not known back when the tree was synthesized + // can't look at `valDef.tpt` here because it may have been completed by now (this is why we pass in `missingTpt`) + // HACK: a param accessor `ddef.tpt.tpe` somehow gets out of whack with `accessorSym.info`, so always patch it back... + // (the tpt is typed in the wrong namer, using the class as owner instead of the outer context, which is where param accessors should be typed) + if (missingTpt || accessorSym.isParamAccessor) { + if (!isSetter) ddef.tpt setType valSig + else if (ddef.vparamss.nonEmpty && ddef.vparamss.head.nonEmpty) ddef.vparamss.head.head.tpt setType valSig + else throw new TypeError(valDef.pos, s"Internal error: could not complete parameter/return type for $ddef from $accessorSym") + } + + val mods = valDef.mods + val annots = + if (mods.annotations.isEmpty) Nil + else filterAccessorAnnots(annotSig(mods.annotations), valDef, isSetter, isBean) + + // for a setter, call memberSig to attribute the parameter (for a bean, we always use the regular method sig completer since they receive method types) + // for a regular getter, make sure it gets a NullaryMethodType (also, no need to recompute it: we already have the valSig) + val sig = + if (isSetter || isBean) typeSig(ddef, annots) + else { + if (annots.nonEmpty) annotate(accessorSym, annots) + + NullaryMethodType(valSig) + } + + accessorSym setInfo pluginsTypeSigAccessor(sig, typer, valDef, accessorSym) + + if (!isBean && accessorSym.isOverloaded) + if (isSetter) ddef.rhs.setType(ErrorType) + else GetterDefinedTwiceError(accessorSym) + + validate(accessorSym) + + case _ => + throw new TypeError(valDef.pos, s"Internal error: no synthetic tree found for bean accessor $accessorSym") } } } - def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => - val selftpe = typer.typedType(tree).tpe - sym setInfo { - if (selftpe.typeSymbol isNonBottomSubClass sym.owner) selftpe - else intersectionType(List(sym.owner.tpe, selftpe)) + // see scala.annotation.meta's package class for more info + // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param. + // The defaults are: + // - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity. + // - val/var member annotations solely end up on the underlying field, except in traits and for all lazy vals (@since 2.12), + // where there is no field, and the getter thus holds annotations targeting both getter & field. + // As soon as there is a field/getter (in subclasses mixing in the trait, or after expanding the lazy val during the fields phase), + // we triage the annotations. + // + // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit? + // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter) + private def filterAccessorAnnots(annotSigs: List[global.AnnotationInfo], tree: global.ValDef, isSetter: Boolean = false, isBean: Boolean = false): List[AnnotationInfo] = { + val mods = tree.mods + if (!isBean) { + // neg/t3403: check that we didn't get a sneaky type alias/renamed import that we couldn't detect because we only look at names during synthesis + // (TODO: can we look at symbols earlier?) + if (!((mods hasAnnotationNamed tpnme.BeanPropertyAnnot) || (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot)) + && annotSigs.exists(ann => (ann.matches(BeanPropertyAttr)) || ann.matches(BooleanBeanPropertyAttr))) + BeanPropertyAnnotationLimitationError(tree) + } + + val canTriageAnnotations = isSetter || !fields.getterTreeAnnotationsTargetFieldAndGetter(owner, mods) + + def filterAccessorAnnotations: AnnotationInfo => Boolean = + if (canTriageAnnotations) + annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(GetterTargetClass, defaultRetention = true)(ann)) + + def filterBeanAccessorAnnotations: AnnotationInfo => Boolean = + if (canTriageAnnotations) + annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(BeanGetterTargetClass, defaultRetention = true)(ann)) + + annotSigs filter (if (isBean) filterBeanAccessorAnnotations else filterAccessorAnnotations) + } + + + def selfTypeCompleter(tree: Tree) = new SelfTypeCompleter(tree) + class SelfTypeCompleter(tree: Tree) extends TypeCompleterBase(tree) { + override def completeImpl(sym: Symbol): Unit = { + val selftpe = typer.typedType(tree).tpe + sym setInfo { + if (selftpe.typeSymbol isNonBottomSubClass sym.owner) selftpe + else intersectionType(List(sym.owner.tpe, selftpe)) + } } } @@ -948,13 +1049,14 @@ trait Namers extends MethodSynthesis { !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 + || sym.hasFlag(ACCESSOR) && !sym.hasFlag(STABLE) + || sym.isMethod && !sym.hasFlag(ACCESSOR) || isHidden(tpe) ) ) dropIllegalStarTypes( if (shouldWiden) tpe.widen - else if (sym.isFinal) tpe // "final val" allowed to retain constant type + else if (sym.isFinal && !sym.isLazy) tpe // "final val" allowed to retain constant type else tpe.deconst ) } @@ -1105,6 +1207,19 @@ trait Namers extends MethodSynthesis { clazz.tpe_* } + + // make a java method type if meth.isJavaDefined + private def methodTypeFor(meth: Symbol, vparamSymss: List[List[Symbol]], restpe: Type) = { + def makeJavaMethodType(vparams: List[Symbol], restpe: Type) = { + vparams foreach (p => p setInfo objToAny(p.tpe)) + JavaMethodType(vparams, restpe) + } + if (vparamSymss.isEmpty) NullaryMethodType(restpe) + else if (meth.isJavaDefined) vparamSymss.foldRight(restpe)(makeJavaMethodType) + else vparamSymss.foldRight(restpe)(MethodType(_, _)) + } + + /** * The method type for `ddef`. * @@ -1122,166 +1237,140 @@ trait Namers extends MethodSynthesis { * to the non-skolems. */ private def methodSig(ddef: DefDef): Type = { - - // DEPMETTODO: do we need to skolemize value parameter symbols? - val DefDef(_, _, tparams, vparamss, tpt, _) = ddef val meth = owner val methOwner = meth.owner - val site = methOwner.thisType /* tparams already have symbols (created in enterDefDef/completerOf), namely the skolemized ones (created * by the PolyTypeCompleter constructor, and assigned to tparams). reenterTypeParams enters the type skolems * into scope and returns the non-skolems. */ val tparamSyms = typer.reenterTypeParams(tparams) - val tparamSkolems = tparams.map(_.symbol) - /* since the skolemized tparams are in scope, the TypeRefs in types of vparamSymss refer to the type skolems - * note that for parameters with missing types, `methodSig` reassigns types of these symbols (the parameter - * types from the overridden method). - */ - 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. + * + * tparamSyms are deskolemized symbols -- TODO: check that their infos don't refer to method args? + * vparamss refer (if they do) to skolemized tparams */ - def thisMethodType(restpe: Type) = { - if (vparamSymss.lengthCompare(0) > 0) { // OPT fast path for methods of 0-1 parameter lists - val checkDependencies = new DependentTypeChecker(context)(this) - checkDependencies check vparamSymss - } - - val makeMethodType = (vparams: List[Symbol], restpe: Type) => { - // TODODEPMET: check that we actually don't need to do anything here - // new dependent method types: probably OK already, since 'enterValueParams' above - // enters them in scope, and all have a lazy type. so they may depend on other params. but: need to - // check that params only depend on ones in earlier sections, not the same. (done by checkDependencies, - // so re-use / adapt that) - if (meth.isJavaDefined) - // TODODEPMET necessary?? new dependent types: replace symbols in restpe with the ones in vparams - JavaMethodType(vparams map (p => p setInfo objToAny(p.tpe)), restpe) - else - MethodType(vparams, restpe) - } + def deskolemizedPolySig(vparamSymss: List[List[Symbol]], restpe: Type) = + GenPolyType(tparamSyms, methodTypeFor(meth, vparamSymss, restpe).substSym(tparamSkolems, tparamSyms)) - val res = GenPolyType( - tparamSyms, // deSkolemized symbols -- TODO: check that their infos don't refer to method args? - if (vparamSymss.isEmpty) NullaryMethodType(restpe) - // vparamss refer (if they do) to skolemized tparams - else (vparamSymss :\ restpe) (makeMethodType) - ) - res.substSym(tparamSkolems, tparamSyms) + if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { + tpt defineType context.enclClass.owner.tpe_* + tpt setPos meth.pos.focus } + /* since the skolemized tparams are in scope, the TypeRefs in types of vparamSymss refer to the type skolems + * note that for parameters with missing types, `methodSig` reassigns types of these symbols (the parameter + * types from the overridden method). + */ + val vparamSymss: List[List[Symbol]] = enterValueParams(vparamss) + + val resTpGiven = + if (tpt.isEmpty) WildcardType + else typer.typedType(tpt).tpe + + + // ignore missing types unless we can look to overridden method to recover the missing information + val canOverride = methOwner.isClass && !meth.isConstructor + val inferResTp = canOverride && tpt.isEmpty + val inferArgTp = canOverride && settings.YmethodInfer && mexists(vparamss)(_.tpt.isEmpty) + + /* - * 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 + * Find the overridden method that matches a schematic method type, + * which has WildcardTypes for unspecified return or parameter types. + * For instance, in `def f[T](a: T, b) = ...`, the type schema is * * PolyType(T, MethodType(List(a: T, b: WildcardType), WildcardType)) * * where T are non-skolems. + * + * NOTE: mutates info of symbol of vparamss that don't specify a type */ - def methodTypeSchema(resTp: Type) = { - // for all params without type set WildcaradType - mforeach(vparamss)(v => if (v.tpt.isEmpty) v.symbol setInfo WildcardType) - thisMethodType(resTp) - } - - def overriddenSymbol(resTp: Type) = { - lazy val schema: Type = methodTypeSchema(resTp) // OPT create once. Must be lazy to avoid cycles in neg/t5093.scala - intersectionType(methOwner.info.parents).nonPrivateMember(meth.name).filter { sym => - sym != NoSymbol && (site.memberType(sym) matches schema) + val methodSigApproxUnknownArgs: () => Type = + if (!inferArgTp) () => deskolemizedPolySig(vparamSymss, resTpGiven) + else () => { + // for all params without type set WildcardType + mforeach(vparamss)(v => if (v.tpt.isEmpty) v.symbol setInfo WildcardType) + // must wait to call deskolemizedPolySig until we've temporarily set the WildcardType info for the vparamSymss + // (Otherwise, valDefSig will complain about missing argument types.) + deskolemizedPolySig(vparamSymss, resTpGiven) } - } - // TODO: see whether this or something similar would work instead: - // def overriddenSymbol = meth.nextOverriddenSymbol + // Must be lazy about the schema to avoid cycles in neg/t5093.scala + val overridden = + if (!canOverride) NoSymbol + else safeNextOverriddenSymbolLazySchema(meth, methodSigApproxUnknownArgs) /* - * 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 + * If `meth` doesn't have an explicit return type, extract the return type from the method + * overridden by `meth` (if there's an unique one). This type is later used as the expected * type for computing the type of the rhs. The resulting type references type skolems for * type parameters (consistent with the result of `typer.typedType(tpt).tpe`). * - * As a first side effect, this method assigns a MethodType constructed using this - * return type to `meth`. This allows omitting the result type for recursive methods. + * If the result type is missing, assign a MethodType to `meth` that's constructed using this return type. + * This allows omitting the result type for recursive methods. * - * As another side effect, this method also assigns parameter types from the overridden - * method to parameters of `meth` that have missing types (the parser accepts missing - * parameter types under -Yinfer-argument-types). + * Missing parameter types are also recovered from the overridden method (by mutating the info of their symbols). + * (The parser accepts missing parameter types under -Yinfer-argument-types.) */ - def typesFromOverridden(methResTp: Type): Type = { - val overridden = overriddenSymbol(methResTp) - if (overridden == NoSymbol || overridden.isOverloaded) { - methResTp - } else { + val resTpFromOverride = + if (!(inferArgTp || inferResTp) || overridden == NoSymbol || overridden.isOverloaded) resTpGiven + else { overridden.cookJavaRawInfo() // #3404 xform java rawtypes into existentials - var overriddenTp = site.memberType(overridden) match { - case PolyType(tparams, rt) => rt.substSym(tparams, tparamSkolems) - case mt => mt + + val (overriddenTparams, overriddenTp) = + methOwner.thisType.memberType(overridden) match { + case PolyType(tparams, mt) => (tparams, mt.substSym(tparams, tparamSkolems)) + case mt => (Nil, mt) } - for (vparams <- vparamss) { - var overriddenParams = overriddenTp.params - for (vparam <- vparams) { + + // try to derive empty parameter types from the overridden method's argument types + if (inferArgTp) { + val overriddenSyms = overriddenTparams ++ overridden.paramss.flatten + val ourSyms = tparamSkolems ++ vparamSymss.flatten + foreach2(vparamss, overridden.paramss) { foreach2(_, _) { (vparam, overriddenParam) => + // println(s"infer ${vparam.symbol} from ${overriddenParam}? ${vparam.tpt}") if (vparam.tpt.isEmpty) { - val overriddenParamTp = overriddenParams.head.tpe + val overriddenParamTp = overriddenParam.tpe.substSym(overriddenSyms, ourSyms) + // println(s"inferred ${vparam.symbol} : $overriddenParamTp") // references to type parameters in overriddenParamTp link to the type skolems, so the // assigned type is consistent with the other / existing parameter types in vparamSymss. vparam.symbol setInfo overriddenParamTp vparam.tpt defineType overriddenParamTp setPos vparam.pos.focus } - overriddenParams = overriddenParams.tail - } - 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)) + @tailrec @inline def applyFully(tp: Type, paramss: List[List[Symbol]]): Type = + if (paramss.isEmpty) tp match { + case NullaryMethodType(rtpe) => rtpe + case MethodType(Nil, rtpe) => rtpe + case tp => tp + } + else applyFully(tp.resultType(paramss.head.map(_.tpe)), paramss.tail) - overriddenTp match { - case NullaryMethodType(rtpe) => overriddenTp = rtpe - case MethodType(List(), rtpe) => overriddenTp = rtpe - case _ => - } + if (inferResTp) { + // SI-7668 Substitute parameters from the parent method with those of the overriding method. + val overriddenResTp = applyFully(overriddenTp, vparamSymss).substSym(overriddenTparams, tparamSkolems) - if (tpt.isEmpty) { // provisionally assign `meth` a method type with inherited result type // that way, we can leave out the result type even if method is recursive. - meth setInfo thisMethodType(overriddenTp) - overriddenTp - } else { - methResTp - } + meth setInfo deskolemizedPolySig(vparamSymss, overriddenResTp) + overriddenResTp + } else resTpGiven } - } - - if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { - tpt defineType context.enclClass.owner.tpe_* - tpt setPos meth.pos.focus - } - - val methResTp = if (tpt.isEmpty) WildcardType else typer.typedType(tpt).tpe - val resTpFromOverride = if (methOwner.isClass && (tpt.isEmpty || mexists(vparamss)(_.tpt.isEmpty))) { - typesFromOverridden(methResTp) - } else { - methResTp - } - - // Add a () parameter section if this overrides some method with () parameters - if (methOwner.isClass && vparamss.isEmpty && - overriddenSymbol(methResTp).alternatives.exists(_.info.isInstanceOf[MethodType])) { - vparamSymss = ListOfNil - } // issue an error for missing parameter types + // (computing resTpFromOverride may have required inferring some, meanwhile) mforeach(vparamss) { vparam => if (vparam.tpt.isEmpty) { MissingParameterOrValTypeError(vparam) @@ -1289,13 +1378,9 @@ trait Namers extends MethodSynthesis { } } - val overridden = { - val isConstr = meth.isConstructor - if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol(methResTp) - } - val hasDefaults = mexists(vparamss)(_.symbol.hasDefault) || mexists(overridden.paramss)(_.hasDefault) - if (hasDefaults) - addDefaultGetters(meth, ddef, vparamss, tparams, overridden) + // If we, or the overridden method has defaults, add getters for them + if (mexists(vparamss)(_.symbol.hasDefault) || mexists(overridden.paramss)(_.hasDefault)) + addDefaultGetters(meth, ddef, vparamss, tparams, overridden) // fast track macros, i.e. macros defined inside the compiler, are hardcoded // hence we make use of that and let them have whatever right-hand side they need @@ -1306,27 +1391,35 @@ 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.isMacro) { - typer.computeMacroDefType(ddef, resTpFromOverride) + if (meth.isMacro) typer.computeMacroDefType(ddef, resTpFromOverride) // note: `pt` argument ignored in `computeMacroDefType` + + if (vparamSymss.lengthCompare(0) > 0) { // OPT fast path for methods of 0-1 parameter lists + val checkDependencies = new DependentTypeChecker(context)(this) + checkDependencies check vparamSymss } - val res = thisMethodType({ - val rt = ( - if (!tpt.isEmpty) { - methResTp - } else { - // return type is inferred, we don't just use resTpFromOverride. Here, C.f has type String: - // trait T { def f: Object }; class C <: T { def f = "" } - // using resTpFromOverride as expected type allows for the following (C.f has type A): - // trait T { def f: A }; class C <: T { implicit def b2a(t: B): A = ???; def f = new B } - assignTypeToTree(ddef, typer, resTpFromOverride) - }) + val resTp = { + // When return type is inferred, we don't just use resTpFromOverride -- it must be packed and widened. + // Here, C.f has type String: + // trait T { def f: Object }; class C extends T { def f = "" } + // using resTpFromOverride as expected type allows for the following (C.f has type A): + // trait T { def f: A }; class C extends T { implicit def b2a(t: B): A = ???; def f = new B } + val resTpComputedUnlessGiven = + if (tpt.isEmpty) assignTypeToTree(ddef, typer, resTpFromOverride) + else resTpGiven + // #2382: return type of default getters are always @uncheckedVariance - if (meth.hasDefault) - rt.withAnnotation(AnnotationInfo(uncheckedVarianceClass.tpe, List(), List())) - else rt - }) - pluginsTypeSig(res, typer, ddef, methResTp) + if (meth.hasDefault) resTpComputedUnlessGiven.withAnnotation(AnnotationInfo(uncheckedVarianceClass.tpe, List(), List())) + else resTpComputedUnlessGiven + } + + // Add a () parameter section if this overrides some method with () parameters + val vparamSymssOrEmptyParamsFromOverride = + if (overridden != NoSymbol && vparamSymss.isEmpty && overridden.alternatives.exists(_.info.isInstanceOf[MethodType])) ListOfNil // NOTE: must check `.info.isInstanceOf[MethodType]`, not `.isMethod`! + else vparamSymss + + val methSig = deskolemizedPolySig(vparamSymssOrEmptyParamsFromOverride, resTp) + pluginsTypeSig(methSig, typer, ddef, resTpGiven) } /** @@ -1459,7 +1552,7 @@ trait Namers extends MethodSynthesis { val defRhs = rvparam.rhs val defaultTree = atPos(vparam.pos.focus) { - DefDef(Modifiers(paramFlagsToDefaultGetter(meth.flags)) | oflag, name, defTparams, defVparamss, defTpt, defRhs) + DefDef(Modifiers(paramFlagsToDefaultGetter(meth.flags), ddef.mods.privateWithin) | oflag, name, defTparams, defVparamss, defTpt, defRhs) } if (!isConstr) methOwner.resetFlag(INTERFACE) // there's a concrete member now @@ -1482,19 +1575,78 @@ trait Namers extends MethodSynthesis { private def valDefSig(vdef: ValDef) = { val ValDef(_, _, tpt, rhs) = vdef - val result = if (tpt.isEmpty) { - if (rhs.isEmpty) { - MissingParameterOrValTypeError(tpt) - ErrorType - } - else assignTypeToTree(vdef, typer, WildcardType) - } else { - typer.typedType(tpt).tpe - } + val result = + if (tpt.isEmpty) { + if (rhs.isEmpty) { + MissingParameterOrValTypeError(tpt) + ErrorType + } else { + // enterGetterSetter assigns the getter's symbol to a ValDef when there's no underlying field + // (a deferred val or most vals defined in a trait -- see Field.noFieldFor) + val isGetter = vdef.symbol hasFlag ACCESSOR + + val pt = { + val valOwner = owner.owner + // there's no overriding outside of classes, and we didn't use to do this in 2.11, so provide opt-out + + if (!isScala212 || !valOwner.isClass) WildcardType + else { + // normalize to getter so that we correctly consider a val overriding a def + // (a val's name ends in a " ", so can't compare to def) + val overridingSym = if (isGetter) vdef.symbol else vdef.symbol.getterIn(valOwner) + + // We're called from an accessorTypeCompleter, which is completing the info for the accessor's symbol, + // which may or may not be `vdef.symbol` (see isGetter above) + val overridden = safeNextOverriddenSymbol(overridingSym) + + if (overridden == NoSymbol || overridden.isOverloaded) WildcardType + else valOwner.thisType.memberType(overridden).resultType + } + } + + def patchSymInfo(tp: Type): Unit = + if (pt ne WildcardType) // no patching up to do if we didn't infer a prototype + vdef.symbol setInfo (if (isGetter) NullaryMethodType(tp) else tp) + + patchSymInfo(pt) + + // derives the val's result type from type checking its rhs under the expected type `pt` + // vdef.tpt is mutated, and `vdef.tpt.tpe` is `assignTypeToTree`'s result + val tptFromRhsUnderPt = assignTypeToTree(vdef, typer, pt) + + // need to re-align with assignTypeToTree, as the type we're returning from valDefSig (tptFromRhsUnderPt) + // may actually go to the accessor, not the valdef (and if assignTypeToTree returns a subtype of `pt`, + // we would be out of synch between field and its accessors), and thus the type completer won't + // fix the symbol's info for us -- we set it to tmpInfo above, which may need to be improved to tptFromRhsUnderPt + if (!isGetter) patchSymInfo(tptFromRhsUnderPt) + + tptFromRhsUnderPt + } + } else typer.typedType(tpt).tpe + +// println(s"val: $result / ${vdef.tpt.tpe} / ") + pluginsTypeSig(result, typer, vdef, if (tpt.isEmpty) WildcardType else result) + } + // Pretend we're an erroneous symbol, for now, so that we match while finding the overridden symbol, + // but are not considered during implicit search. + private def safeNextOverriddenSymbol(sym: Symbol, schema: Type = ErrorType): Symbol = { + val savedInfo = sym.rawInfo + val savedFlags = sym.rawflags + try { + sym setInfo schema + sym.nextOverriddenSymbol + } finally { + sym setInfo savedInfo // setInfo resets the LOCKED flag, so restore saved flags as well + sym.rawflags = savedFlags + } } + private def safeNextOverriddenSymbolLazySchema(sym: Symbol, schema: () => Type): Symbol = + safeNextOverriddenSymbol(sym, new LazyType { override def complete(sym: Symbol): Unit = sym setInfo schema() }) + + //@M! an abstract type definition (abstract type member/type parameter) // may take type parameters, which are in scope in its bounds private def typeDefSig(tdef: TypeDef) = { @@ -1590,67 +1742,52 @@ trait Namers extends MethodSynthesis { * is then assigned to the corresponding symbol (typeSig itself does not need to assign * the type to the symbol, but it can if necessary). */ - 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. - */ - def annotate(annotated: Symbol) = { - // typeSig might be called multiple times, e.g. on a ValDef: val, getter, setter - // parse the annotations only once. - if (!annotated.isInitialized) tree match { - case defn: MemberDef => - val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => - val ctx = typer.context - val annCtx = ctx.makeNonSilent(ann) - // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. - AnnotationInfo lazily { - enteringTyper(newTyper(annCtx) typedAnnotation ann) - } - } - if (ainfos.nonEmpty) { - annotated setAnnotations ainfos - if (annotated.isTypeSkolem) - annotated.deSkolemize setAnnotations ainfos - } - case _ => + def typeSig(tree: Tree, annotSigs: List[AnnotationInfo]): Type = { + if (annotSigs.nonEmpty) annotate(tree.symbol, annotSigs) + + try tree match { + case member: MemberDef => createNamer(tree).memberSig(member) + case imp: Import => importSig(imp) + } catch typeErrorHandler(tree, ErrorType) + } + + /* 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 annotSig(annotations: List[Tree]): List[AnnotationInfo] = + annotations filterNot (_ eq null) map { ann => + val ctx = typer.context + // need to be lazy, #1782. enteringTyper to allow inferView in annotation args, SI-5892. + AnnotationInfo lazily { + enteringTyper { + newTyper(ctx.makeNonSilent(ann)) typedAnnotation ann + } } } - val sym: Symbol = tree.symbol + private def annotate(sym: Symbol, annotSigs: List[AnnotationInfo]): Unit = { + sym setAnnotations annotSigs // TODO: meta-annotations to indicate where module annotations should go (module vs moduleClass) - annotate(sym) - if (sym.isModule) annotate(sym.moduleClass) - - def getSig = tree match { - case cdef: ClassDef => - createNamer(tree).classSig(cdef) - - case mdef: ModuleDef => - createNamer(tree).moduleSig(mdef) - - case ddef: DefDef => - createNamer(tree).methodSig(ddef) - - case vdef: ValDef => - createNamer(tree).valDefSig(vdef) - - case tdef: TypeDef => - createNamer(tree).typeDefSig(tdef) //@M! + if (sym.isModule) sym.moduleClass setAnnotations annotSigs + else if (sym.isTypeSkolem) sym.deSkolemize setAnnotations annotSigs + } - case imp: Import => - importSig(imp) + // TODO OPT: move to method on MemberDef? + private def memberSig(member: MemberDef) = + member match { + case ddef: DefDef => methodSig(ddef) + case vdef: ValDef => valDefSig(vdef) + case tdef: TypeDef => typeDefSig(tdef) + case cdef: ClassDef => classSig(cdef) + case mdef: ModuleDef => moduleSig(mdef) + // skip PackageDef } - try getSig - catch typeErrorHandler(tree, ErrorType) - } - def includeParent(tpe: Type, parent: Symbol): Type = tpe match { case PolyType(tparams, restpe) => PolyType(tparams, includeParent(restpe, parent)) @@ -1673,10 +1810,6 @@ trait Namers extends MethodSynthesis { sym => "[define] >> " + sym.flagString + " " + sym.fullLocationString, sym => "[define] << " + sym ) - private def logAndValidate(sym: Symbol)(body: => Unit) { - logDefinition(sym)(body) - validate(sym) - } /** Convert Java generic array type T[] to (T with Object)[] * (this is necessary because such arrays have a representation which is incompatible @@ -1708,11 +1841,7 @@ trait Namers extends MethodSynthesis { import SymValidateErrors._ def fail(kind: SymValidateErrors.Value) = SymbolValidationError(sym, kind) - def checkWithDeferred(flag: Int) { - if (sym hasFlag flag) - AbstractMemberWithModiferError(sym, flag) - } - def checkNoConflict(flag1: Int, flag2: Int) { + def checkNoConflict(flag1: Int, flag2: Int) = { if (sym hasAllFlags flag1.toLong | flag2) IllegalModifierCombination(sym, flag1, flag2) } @@ -1751,6 +1880,10 @@ trait Namers extends MethodSynthesis { checkNoConflict(ABSTRACT, FINAL) if (sym.isDeferred) { + def checkWithDeferred(flag: Int) = { + if (sym hasFlag flag) + AbstractMemberWithModiferError(sym, flag) + } // Is this symbol type always allowed the deferred flag? def symbolAllowsDeferred = ( sym.isValueParameter @@ -1766,14 +1899,16 @@ trait Namers extends MethodSynthesis { ) if (sym hasAnnotation NativeAttr) sym resetFlag DEFERRED - else if (!symbolAllowsDeferred && ownerRequiresConcrete) - fail(AbstractVar) + else { + if (!symbolAllowsDeferred && ownerRequiresConcrete) fail(AbstractVar) - checkWithDeferred(PRIVATE) - checkWithDeferred(FINAL) + checkWithDeferred(PRIVATE) + checkWithDeferred(FINAL) + } } - checkNoConflict(FINAL, SEALED) + if (!sym.isJavaEnum) + checkNoConflict(FINAL, SEALED) checkNoConflict(PRIVATE, PROTECTED) // checkNoConflict(PRIVATE, OVERRIDE) // this one leads to bad error messages like #4174, so catch in refchecks // checkNoConflict(PRIVATE, FINAL) // can't do this because FINAL also means compile-time constant @@ -1794,11 +1929,15 @@ trait Namers extends MethodSynthesis { } } - def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter with FlagAgnosticCompleter { - val tree = t + @deprecated("Instantiate TypeCompleterBase (for monomorphic, non-wrapping completer) or CompleterWrapper directly.", "2.12.2") + def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new TypeCompleterBase(t) { def completeImpl(sym: Symbol) = c(sym) } + // NOTE: only meant for monomorphic definitions, + // do not use to wrap existing completers (see CompleterWrapper for that) + abstract class TypeCompleterBase[T <: Tree](val tree: T) extends LockingTypeCompleter with FlagAgnosticCompleter + trait LockingTypeCompleter extends TypeCompleter { def completeImpl(sym: Symbol): Unit @@ -1841,6 +1980,22 @@ trait Namers extends MethodSynthesis { } } + /** + * Wrap an existing completer to do some post/pre-processing of the completed type. + * + * @param completer + */ + class CompleterWrapper(completer: TypeCompleter) extends TypeCompleter { + // override important when completer.isInstanceOf[PolyTypeCompleter]! + override val typeParams = completer.typeParams + + val tree = completer.tree + + override def complete(sym: Symbol): Unit = { + completer.complete(sym) + } + } + // Can we relax these restrictions? For motivation, see // test/files/pos/depmet_implicit_oopsla_session_2.scala // neg/depmet_try_implicit.scala @@ -1881,18 +2036,18 @@ trait Namers extends MethodSynthesis { * bugs waiting to be reported? If not, why not? When exactly do we need to * call this method? */ - def companionSymbolOf(original: Symbol, ctx: Context): Symbol = { + def companionSymbolOf(original: Symbol, ctx: Context): Symbol = if (original == NoSymbol) NoSymbol else { 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, owner).suchThat(sym => - (original.isTerm || sym.hasModuleFlag) && - (sym isCoDefinedWith original) - ) - } + + if (original.isModuleClass) original.sourceModule + else if (!owner.isTerm && owner.hasCompleteInfo) + original.companionSymbol + else + ctx.lookupCompanionInIncompleteOwner(original) } /** A version of `Symbol#linkedClassOfClass` that works with local companions, ala `companionSymbolOf`. */ |