diff options
103 files changed, 2064 insertions, 794 deletions
diff --git a/spec/05-classes-and-objects.md b/spec/05-classes-and-objects.md index 69828ec7fe..f92e88788a 100644 --- a/spec/05-classes-and-objects.md +++ b/spec/05-classes-and-objects.md @@ -344,8 +344,8 @@ $M'$: - If $M$ and $M'$ are both concrete value definitions, then either none of them is marked `lazy` or both must be marked `lazy`. -A stable member can only be overridden by a stable member. -For example, this is not allowed: +- A stable member can only be overridden by a stable member. + For example, this is not allowed: ```scala class X { val stable = 1} diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index d4c2896c5c..c2d92ce7f9 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -476,10 +476,22 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with TailCalls + // phaseName = "fields" + object fields extends { + val global: Global.this.type = Global.this + // after refchecks, so it doesn't have to make weird exceptions for synthetic accessors + // after uncurry as it produces more work for the fields phase as well as being confused by it: + // - sam expansion synthesizes classes, which may need trait fields mixed in + // - the fields phase adds synthetic abstract methods to traits that should not disqualify them from being a SAM type + // before erasure: correct signatures & bridges for accessors + val runsAfter = List("uncurry") + val runsRightAfter = None + } with Fields + // phaseName = "explicitouter" object explicitOuter extends { val global: Global.this.type = Global.this - val runsAfter = List("tailcalls") + val runsAfter = List("fields") val runsRightAfter = None } with ExplicitOuter @@ -595,7 +607,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * This implementation creates a description map at the same time. */ protected def computeInternalPhases(): Unit = { - // Note: this fits -Xshow-phases into 80 column width, which it is + // Note: this fits -Xshow-phases into 80 column width, which is // desirable to preserve. val phs = List( syntaxAnalyzer -> "parse source into ASTs, perform simple desugaring", @@ -608,6 +620,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) pickler -> "serialize symbol tables", refChecks -> "reference/override checking, translate nested objects", uncurry -> "uncurry, translate function values to anonymous classes", + fields -> "synthesize accessors and fields", tailCalls -> "replace tail calls by jumps", specializeTypes -> "@specialized-driven class and method specialization", explicitOuter -> "this refs to outer pointers", @@ -1239,6 +1252,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val picklerPhase = phaseNamed("pickler") val refchecksPhase = phaseNamed("refchecks") val uncurryPhase = phaseNamed("uncurry") + // val fieldsPhase = phaseNamed("fields") // val tailcallsPhase = phaseNamed("tailcalls") val specializePhase = phaseNamed("specialize") val explicitouterPhase = phaseNamed("explicitouter") diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index bb695500cc..5dddf30c96 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -290,6 +290,16 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { } + // the result type of a function or corresponding SAM type + private def functionResultType(tp: Type): Type = { + val dealiased = tp.dealiasWiden + if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.last + else samOf(tp) match { + case samSym if samSym.exists => tp.memberInfo(samSym).resultType.deconst + case _ => NoType + } + } + /** * Lift a Function's body to a method. For use during Uncurry, where Function nodes have type FunctionN[T1, ..., Tn, R] * diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index d779490ba8..27a4cbd134 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -164,7 +164,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { def enclosingMethod(sym: Symbol): Option[Symbol] = { if (sym.isClass || sym == NoSymbol) None - else if (sym.isMethod) { + else if (sym.isMethod && !sym.isGetter) { if (doesNotExist(sym)) None else Some(sym) } else enclosingMethod(nextEnclosing(sym)) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 383347a0d3..836893a98b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -549,7 +549,10 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { if (classSym.isEffectivelyFinal) None else { // Phase travel necessary. For example, nullary methods (getter of an abstract val) get an - // empty parameter list in later phases and would therefore be picked as SAM. + // empty parameter list in uncurry and would therefore be picked as SAM. + // Similarly, the fields phases adds abstract trait setters, which should not be considered + // abstract for SAMs (they do disqualify the SAM from LMF treatment, + // but an anonymous subclasss can be spun up by scalac after making just the single abstract method concrete) val samSym = exitingPickler(definitions.samOf(classSym.tpe)) if (samSym == NoSymbol) None else Some(samSym.javaSimpleName.toString + methodBTypeFromSymbol(samSym).descriptor) @@ -724,7 +727,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym)) && !sym.enclClass.isTrait && !sym.isClassConstructor - && !sym.isMutable // lazy vals and vars both + && (!sym.isMutable || nme.isTraitSetterName(sym.name)) // lazy vals and vars and their setters cannot be final, but trait setters are ) // Primitives are "abstract final" to prohibit instantiation diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 9a8eca152f..104e2e8c93 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -50,12 +50,6 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => } } - private def mkAssign(clazz: Symbol, assignSym: Symbol, rhs: Tree): Tree = { - val qual = Select(This(clazz), assignSym) - if (assignSym.isSetter) Apply(qual, List(rhs)) - else Assign(qual, rhs) - } - /** Add calls to supermixin constructors * `super[mix].$init$()` * to tree, which is assumed to be the body of a constructor of class clazz. diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 971a55f763..ec8dc68834 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -13,7 +13,7 @@ import symtab.Flags._ /** This phase converts classes with parameters into Java-like classes with * fields, which are assigned to from constructors. */ -abstract class Constructors extends Statics with Transform with ast.TreeDSL { +abstract class Constructors extends Statics with Transform with TypingTransformers with ast.TreeDSL { import global._ import definitions._ @@ -26,7 +26,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = perRunCaches.newMap[Symbol, List[Tree]]() private val ctorParams: mutable.Map[Symbol, List[Symbol]] = perRunCaches.newMap[Symbol, List[Symbol]]() - class ConstructorTransformer(unit: CompilationUnit) extends Transformer { + class ConstructorTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { /* * Inspect for obvious out-of-order initialization; concrete, eager vals or vars, declared in this class, * for which a reference to the member precedes its definition. @@ -80,7 +80,10 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { else { checkUninitializedReads(cd) val tplTransformer = new TemplateTransformer(unit, impl0) - treeCopy.ClassDef(cd, mods0, name0, tparams0, tplTransformer.transformed) + tplTransformer.localTyper = this.localTyper + tplTransformer.atOwner(impl0, cd.symbol) { + treeCopy.ClassDef(cd, mods0, name0, tparams0, tplTransformer.transformed) + } } case _ => super.transform(tree) @@ -442,13 +445,14 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { } // GuardianOfCtorStmts private class TemplateTransformer(val unit: CompilationUnit, val impl: Template) - extends StaticsTransformer + extends TypingTransformer(unit) + with StaticsTransformer with DelayedInitHelper with OmittablesHelper - with GuardianOfCtorStmts { + with GuardianOfCtorStmts + { val clazz = impl.symbol.owner // the transformed class - val localTyper = typer.atOwner(impl, clazz) val isDelayedInitSubclass = clazz isSubClass DelayedInitClass @@ -544,12 +548,15 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { else transform(tree.changeOwner(oldOwner -> newOwner)) } - // Create an assignment to class field `to` with rhs `from` - def mkAssign(to: Symbol, from: Tree): Tree = - localTyper.typedPos(to.pos) { - Assign(Select(This(clazz), to), from) + // Assign `rhs` to class field / trait setter `assignSym` + def mkAssign(assignSym: Symbol, rhs: Tree): Tree = + localTyper.typedPos(assignSym.pos) { + val qual = Select(This(clazz), assignSym) + if (assignSym.isSetter) Apply(qual, List(rhs)) + else Assign(qual, rhs) } + // Create code to copy parameter to parameter accessor field. // If parameter is $outer, check that it is not null so that we NPE // here instead of at some unknown future $outer access. @@ -565,9 +572,6 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { } } - // Constant typed vals are not memoized. - def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType] - /** Triage definitions and statements in this template into the following categories. * The primary constructor is treated separately, as it is assembled in part from these pieces. * @@ -577,84 +581,113 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { * - `constrStats`: statements that go into the constructor after and including the superclass constructor call * - `classInitStats`: statements that go into the class initializer */ - def triageStats = { - val defBuf, auxConstructorBuf, constrPrefixBuf, constrStatBuf, classInitStatBuf = new mutable.ListBuffer[Tree] - - // The early initialized field definitions of the class (these are the class members) - val presupers = treeInfo.preSuperFields(stats) - - // generate code to copy pre-initialized fields - for (stat <- primaryConstrBody.stats) { - constrStatBuf += stat - stat match { - case ValDef(mods, name, _, _) if mods.hasFlag(PRESUPER) => - // stat is the constructor-local definition of the field value - val fields = presupers filter (_.getterName == name) - assert(fields.length == 1, s"expected exactly one field by name $name in $presupers of $clazz's early initializers") - val to = fields.head.symbol - - if (memoizeValue(to)) constrStatBuf += mkAssign(to, Ident(stat.symbol)) - case _ => + class Triage { + private val defBuf, auxConstructorBuf, constrPrefixBuf, constrStatBuf, classInitStatBuf = new mutable.ListBuffer[Tree] + + triage() + + val defs = defBuf.toList + val auxConstructors = auxConstructorBuf.toList + val constructorPrefix = constrPrefixBuf.toList + val constructorStats = constrStatBuf.toList + val classInitStats = classInitStatBuf.toList + + private def triage() = { + // Constant typed vals are not memoized. + def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType] + + // The early initialized field definitions of the class (these are the class members) + val presupers = treeInfo.preSuperFields(stats) + + // generate code to copy pre-initialized fields + for (stat <- primaryConstrBody.stats) { + constrStatBuf += stat + stat match { + case ValDef(mods, name, _, _) if mods.hasFlag(PRESUPER) => // TODO trait presupers + // stat is the constructor-local definition of the field value + val fields = presupers filter (_.getterName == name) + assert(fields.length == 1, s"expected exactly one field by name $name in $presupers of $clazz's early initializers") + val to = fields.head.symbol + + if (memoizeValue(to)) constrStatBuf += mkAssign(to, Ident(stat.symbol)) + case _ => + } } - } - for (stat <- stats) { - val statSym = stat.symbol - - // Move the RHS of a ValDef to the appropriate part of the ctor. - // If the val is an early initialized or a parameter accessor, - // it goes before the superclass constructor call, otherwise it goes after. - // A lazy val's effect is not moved to the constructor, as it is delayed. - // Returns `true` when a `ValDef` is needed. - def moveEffectToCtor(mods: Modifiers, rhs: Tree, assignSym: Symbol): Unit = { - val initializingRhs = - if ((assignSym eq NoSymbol) || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val) - else if (!mods.hasStaticFlag) intoConstructor(statSym, primaryConstr.symbol)(rhs) - else rhs - - if (initializingRhs ne EmptyTree) { - val initPhase = - if (mods hasFlag STATIC) classInitStatBuf - else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf - else constrStatBuf - - initPhase += mkAssign(assignSym, initializingRhs) + val primaryConstrSym = primaryConstr.symbol + + for (stat <- stats) { + val statSym = stat.symbol + + // Move the RHS of a ValDef to the appropriate part of the ctor. + // If the val is an early initialized or a parameter accessor, + // it goes before the superclass constructor call, otherwise it goes after. + // A lazy val's effect is not moved to the constructor, as it is delayed. + // Returns `true` when a `ValDef` is needed. + def moveEffectToCtor(mods: Modifiers, rhs: Tree, assignSym: Symbol): Unit = { + val initializingRhs = + if ((assignSym eq NoSymbol) || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val) + else if (!mods.hasStaticFlag) intoConstructor(statSym, primaryConstrSym)(rhs) + else rhs + + if (initializingRhs ne EmptyTree) { + val initPhase = + if (mods hasFlag STATIC) classInitStatBuf + else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf + else constrStatBuf + + initPhase += mkAssign(assignSym, initializingRhs) + } } - } - stat match { - // recurse on class definition, store in defBuf - case _: ClassDef if !stat.symbol.isInterface => defBuf += new ConstructorTransformer(unit).transform(stat) - - // Triage methods -- they all end up in the template -- - // regular ones go to `defBuf`, secondary contructors go to `auxConstructorBuf`. - // The primary constructor is dealt with separately (we're massaging it here). - case _: DefDef if statSym.isPrimaryConstructor || statSym.isMixinConstructor => () - case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat - case _: DefDef => defBuf += stat - - // If a val needs a field, an empty valdef goes into the template. - // Except for lazy and ConstantTyped vals, the field is initialized by an assignment in: - // - the class initializer (static), - // - the constructor, before the super call (early initialized or a parameter accessor), - // - the constructor, after the super call (regular val). - case ValDef(mods, _, _, rhs) => - if (rhs ne EmptyTree) { - val emitField = memoizeValue(statSym) - moveEffectToCtor(mods, rhs, if (emitField) statSym else NoSymbol) - if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree) - } else defBuf += stat - - // all other statements go into the constructor - case _ => constrStatBuf += intoConstructor(impl.symbol, primaryConstr.symbol)(stat) + stat match { + // recurse on class definition, store in defBuf + case _: ClassDef if !statSym.isInterface => + defBuf += new ConstructorTransformer(unit).transform(stat) + + // primary constructor is already tracked as `primaryConstr` + // non-primary constructors go to auxConstructorBuf + // mixin constructors are suppressed (!?!?) + case _: DefDef if statSym.isConstructor => + if ((statSym ne primaryConstrSym) && !statSym.isMixinConstructor) auxConstructorBuf += stat + + // If a val needs a field, an empty valdef goes into the template. + // Except for lazy and ConstantTyped vals, the field is initialized by an assignment in: + // - the class initializer (static), + // - the constructor, before the super call (early initialized or a parameter accessor), + // - the constructor, after the super call (regular val). + case vd: ValDef => + if (vd.rhs eq EmptyTree) { defBuf += vd } + else { + val emitField = memoizeValue(statSym) + + if (emitField) { + moveEffectToCtor(vd.mods, vd.rhs, statSym) + defBuf += deriveValDef(stat)(_ => EmptyTree) + } + } + + case dd: DefDef => + // either move the RHS to ctor (for getter of stored field) or just drop it (for corresponding setter) + def shouldMoveRHS = + clazz.isTrait && statSym.isAccessor && !statSym.isLazy && (statSym.isSetter || memoizeValue(statSym)) + + if ((dd.rhs eq EmptyTree) || !shouldMoveRHS) { defBuf += dd } + else { + if (statSym.isGetter) moveEffectToCtor(dd.mods, dd.rhs, statSym.asTerm.referenced orElse statSym.setterIn(clazz)) + defBuf += deriveDefDef(stat)(_ => EmptyTree) + } + + // all other statements go into the constructor + case _ => + constrStatBuf += intoConstructor(impl.symbol, primaryConstrSym)(stat) + } } } - - (defBuf.toList, auxConstructorBuf.toList, constrPrefixBuf.toList, constrStatBuf.toList, classInitStatBuf.toList) } def transformed = { - val (defs, auxConstructors, constructorPrefix, constructorStats, classInitStats) = triageStats + val triage = new Triage; import triage._ // omit unused outers val omittableAccessor: Set[Symbol] = diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index db8e203c1c..289ac0cc02 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1156,6 +1156,8 @@ abstract class Erasure extends AddInterfaces treeCopy.ArrayValue( tree1, elemtpt setType specialScalaErasure.applyInArray(elemtpt.tpe), trees map transform).clearType() case DefDef(_, _, _, _, tpt, _) => + fields.dropFieldAnnotationsFromGetter(tree.symbol) // TODO: move this in some post-processing transform in the fields phase? + try super.transform(tree1).clearType() finally tpt setType specialErasure(tree1.symbol)(tree1.symbol.tpe).resultType case ApplyDynamic(qual, Literal(Constant(boostrapMethodRef: Symbol)) :: _) => diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala new file mode 100644 index 0000000000..0dd7b1fee0 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -0,0 +1,449 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author + */ + +package scala.tools.nsc +package transform + +import scala.annotation.tailrec +import symtab.Flags._ + + +/** Synthesize accessors and field for each (strict) val owned by a trait. + * + * For traits: + * + * - Namers translates a definition `val x = rhs` into a getter `def x = rhs` -- no underlying field is created. + * - This phase synthesizes accessors and fields for any vals mixed into a non-trait class. + * - Constructors will move the rhs to an assignment in the template body. + * and those statements then move to the template into the constructor, + * which means it will initialize the fields defined in this template (and execute the corresponding side effects). + * We need to maintain the connection between getter and rhs until after specialization so that it can duplicate vals. + * + * Runs before erasure (to get bridges), and thus before lambdalift/flatten, so that nested functions/definitions must be considered. + * We run after uncurry because it can introduce subclasses of traits with fields (SAMs with vals). + * Lambdalift also introduces new fields (paramaccessors for captured vals), but runs too late in the pipeline + * (mixins still synthesizes implementations for accessors that need to be mixed into subclasses of local traits that capture). + * + * In the future, would like to get closer to dotty, which lifts a val's RHS (a similar thing is done for template-level statements) + * to a method `$_initialize_$1$x` instead of a block, which is used in the constructor to initialize the val. + * This makes for a nice unification of strict and lazy vals, in that the RHS is lifted to a method for both, + * with the corresponding compute method called at the appropriate time.) + * + * This only reduces the required number of methods per field declaration in traits, + * if we encode the name (and place in initialisation order) of the field + * in the name of its initializing method, to allow separate compilation. + * (The name mangling must include ordering, and thus complicate incremental compilation: + * ideally, we'd avoid renumbering unchanged methods, but that would result in + * different bytecode between clean recompiles and incremental ones). + * + * In the even longer term (Scala 3?), I agree with @DarkDimius that it would make sense + * to hide the difference between strict and lazy vals. All vals are lazy, + * but the memoization overhead is removed when we statically know they are forced during initialiation. + * We could still expose the low-level field semantics through `private[this] val`s. + * + * In any case, the current behavior of overriding vals is pretty surprising. + * An overridden val's side-effect is still performed. + * The only change due to overriding is that its value is never written to the field + * (the overridden val's value is, of course, stored in the field in addition to its side-effect being performed). + */ +abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransformers { + + import global._ + import definitions._ + + /** the following two members override abstract members in Transform */ + val phaseName: String = "fields" + + protected def newTransformer(unit: CompilationUnit): Transformer = new FieldsTransformer(unit) + override def transformInfo(sym: Symbol, tp: Type): Type = + if (sym.isJavaDefined || sym.isPackageClass || !sym.isClass) tp + else synthFieldsAndAccessors(tp) + + // we leave lazy vars/accessors and early-init vals alone for now + private def excludedAccessorOrFieldByFlags(statSym: Symbol): Boolean = statSym hasFlag LAZY | PRESUPER + + // used for internal communication between info and tree transform of this phase -- not pickled, not in initialflags + // TODO: reuse MIXEDIN for NEEDS_TREES? + override def phaseNewFlags: Long = NEEDS_TREES | OVERRIDDEN_TRAIT_SETTER + + private final val OVERRIDDEN_TRAIT_SETTER = TRANS_FLAG + + final val TRAIT_SETTER_FLAGS = NEEDS_TREES | DEFERRED | ProtectedLocal + + private def accessorImplementedInSubclass(accessor: Symbol) = + (accessor hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS) && (accessor hasFlag (ACCESSOR)) + + private def concreteOrSynthImpl(sym: Symbol): Boolean = !(sym hasFlag DEFERRED) || (sym hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS) + + private def synthesizeImplInSubclasses(accessor: Symbol): Unit = + accessor setFlag lateDEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS + + private def setClonedTraitSetterFlags(clazz: Symbol, correspondingGetter: Symbol, cloneInSubclass: Symbol): Unit = { + val overridden = isOverriddenAccessor(correspondingGetter, clazz) + if (overridden) cloneInSubclass setFlag OVERRIDDEN_TRAIT_SETTER + else if (correspondingGetter.isEffectivelyFinal) cloneInSubclass setFlag FINAL + } + + // TODO: add MIXEDIN (see e.g., `accessed` on `Symbol`) + private def setMixedinAccessorFlags(orig: Symbol, cloneInSubclass: Symbol): Unit = + cloneInSubclass setFlag OVERRIDE | NEEDS_TREES resetFlag DEFERRED | lateDEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS + + private def setFieldFlags(accessor: Symbol, fieldInSubclass: TermSymbol): Unit = + fieldInSubclass setFlag (NEEDS_TREES | + PrivateLocal + | (accessor getFlag MUTABLE | LAZY) + | (if (accessor hasFlag STABLE) 0 else MUTABLE) + ) + + + def checkAndClearOverridden(setter: Symbol) = checkAndClear(OVERRIDDEN_TRAIT_SETTER)(setter) + def checkAndClearNeedsTrees(setter: Symbol) = checkAndClear(NEEDS_TREES)(setter) + def checkAndClear(flag: Long)(sym: Symbol) = + sym.hasFlag(flag) match { + case overridden => + sym resetFlag flag + overridden + } + + + private def isOverriddenAccessor(member: Symbol, site: Symbol): Boolean = { + val pre = site.thisType + @tailrec def loop(bcs: List[Symbol]): Boolean = { + // println(s"checking ${bcs.head} for member overriding $member (of ${member.owner})") + bcs.nonEmpty && bcs.head != member.owner && (matchingAccessor(pre, member, bcs.head) != NoSymbol || loop(bcs.tail)) + } + + member.exists && loop(site.info.baseClasses) + } + + + def matchingAccessor(pre: Type, member: Symbol, clazz: Symbol) = { + val res = member.matchingSymbol(clazz, pre) filter (sym => (sym hasFlag ACCESSOR) && concreteOrSynthImpl(sym)) + // if (res != NoSymbol) println(s"matching accessor for $member in $clazz = $res (under $pre)") + // else println(s"no matching accessor for $member in $clazz (under $pre) among ${clazz.info.decls}") + res + } + + + class FieldMemoization(accessorOrField: Symbol, site: Symbol) { + val tp = fieldTypeOfAccessorIn(accessorOrField, site.thisType) + // not stored, no side-effect + val pureConstant = tp.isInstanceOf[ConstantType] + + // if !stored, may still have a side-effect + // (currently not distinguished -- used to think we could drop unit-typed vals, + // but the memory model cares about writes to unit-typed fields) + val stored = !pureConstant // || isUnitType(tp)) + } + + private def fieldTypeForGetterIn(getter: Symbol, pre: Type): Type = getter.info.finalResultType.asSeenFrom(pre, getter.owner) + private def fieldTypeForSetterIn(setter: Symbol, pre: Type): Type = setter.info.paramTypes.head.asSeenFrom(pre, setter.owner) + + // TODO: is there a more elegant way? + def fieldTypeOfAccessorIn(accessor: Symbol, pre: Type) = + if (accessor.isSetter) fieldTypeForSetterIn(accessor, pre) + else fieldTypeForGetterIn(accessor, pre) + + + // Constant/unit typed vals are not memoized (their value is so cheap it doesn't make sense to store it in a field) + // for a unit-typed getter, we perform the effect at the appropriate time (constructor for eager ones, lzyCompute for lazy), + // and have the getter just return Unit (who does that!?) + // NOTE: this only considers type, filter on flags first! + def fieldMemoizationIn(accessorOrField: Symbol, site: Symbol) = new FieldMemoization(accessorOrField, site) + + // drop field-targeting annotations from getters + // (in traits, getters must also hold annotations that target the underlying field, + // because the latter won't be created until the trait is mixed into a class) + // TODO do bean getters need special treatment to suppress field-targeting annotations in traits? + def dropFieldAnnotationsFromGetter(sym: Symbol) = + if (sym.isGetter && sym.owner.isTrait) { + sym setAnnotations (sym.annotations filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false)) + } + + private object synthFieldsAndAccessors extends TypeMap { + private def newTraitSetter(getter: Symbol, clazz: Symbol) = { + // Add setter for an immutable, memoizing getter + // (can't emit during namers because we don't yet know whether it's going to be memoized or not) + val setterFlags = (getter.flags & ~(STABLE | PrivateLocal | OVERRIDE | IMPLICIT | FINAL)) | MUTABLE | ACCESSOR | TRAIT_SETTER_FLAGS + val setterName = nme.expandedSetterName(getter.name.setterName, clazz) + val setter = clazz.newMethod(setterName, getter.pos.focus, setterFlags) + val fieldTp = fieldTypeForGetterIn(getter, clazz.thisType) + // println(s"newTraitSetter in $clazz for $getter = $setterName : $fieldTp") + + getter.asTerm.referenced = setter + + setter setInfo MethodType(List(setter.newSyntheticValueParam(fieldTp)), UnitTpe) + setter + } + + def apply(tp0: Type): Type = tp0 match { + // TODO: make less destructive (name changes, decl additions, flag setting -- + // none of this is actually undone when travelling back in time using atPhase) + case tp@ClassInfoType(parents, decls, clazz) if clazz.isTrait => + // setters for trait vars or module accessor + val newDecls = collection.mutable.ListBuffer[Symbol]() + val origDecls = decls.toList + + // strict, memoized accessors will receive an implementation in first real class to extend this trait + origDecls.foreach { member => + if (member hasFlag ACCESSOR) { + val fieldMemoization = fieldMemoizationIn(member, clazz) + // check flags before calling makeNotPrivate + val accessorUnderConsideration = !(member hasFlag (DEFERRED | LAZY)) + + // destructively mangle accessor's name (which may cause rehashing of decls), also sets flags + if (member hasFlag PRIVATE) member makeNotPrivate clazz + + // Need to mark as notPROTECTED, so that it's carried over to the synthesized member in subclasses, + // since the trait member will receive this flag later in ExplicitOuter, but the synthetic subclass member will not. + // If we don't add notPROTECTED to the synthesized one, the member will not be seen as overriding the trait member. + // Therefore, addForwarders's call to membersBasedOnFlags would see the deferred member in the trait, + // instead of the concrete (desired) one in the class + // TODO: encapsulate as makeNotProtected, similar to makeNotPrivate (also do moduleClass, e.g.) + if (member hasFlag PROTECTED) member setFlag notPROTECTED + + // must not reset LOCAL, as we must maintain protected[this]ness to allow that variance hole + // (not sure why this only problem only arose when we started setting the notPROTECTED flag) + + // derive trait setter after calling makeNotPrivate (so that names are mangled consistently) + if (accessorUnderConsideration && fieldMemoization.stored) { + synthesizeImplInSubclasses(member) + + if (member hasFlag STABLE) // TODO: check isGetter? + newDecls += newTraitSetter(member, clazz) + } + } + } + + if (newDecls nonEmpty) { + val allDecls = newScope + origDecls foreach allDecls.enter + newDecls foreach allDecls.enter + ClassInfoType(parents, allDecls, clazz) + } else tp + + // mix in fields & accessors for all mixed in traits + + case tp@ClassInfoType(parents, oldDecls, clazz) if !clazz.isPackageClass => + val site = clazz.thisType + // TODO (1): improve logic below, which is used to avoid mixing in anything that would result in an error in refchecks + // (a reason to run after refchecks? we should run before pickler, though, I think, so that the synthesized stats are pickled) + + val membersNeedingSynthesis = clazz.mixinClasses.flatMap { mixin => + // afterOwnPhase, so traits receive trait setters for vals + afterOwnPhase {mixin.info}.decls.toList.filter(accessorImplementedInSubclass) + } + +// println(s"mixing in for $clazz: $membersNeedingSynthesis from ${clazz.mixinClasses}") + + // TODO: setter conflicts? + def accessorConflictsExistingVal(accessor: Symbol): Boolean = { + val existingGetter = oldDecls.lookup(accessor.name.getterName) + // println(s"$existingGetter from $accessor to ${accessor.name.getterName}") + val tp = fieldTypeOfAccessorIn(accessor, site) + (existingGetter ne NoSymbol) && (tp matches (site memberInfo existingGetter).resultType) // !existingGetter.isDeferred && -- see (3) + } + + // mixin field accessors -- + // invariant: (accessorsMaybeNeedingImpl, mixedInAccessorAndFields).zipped.forall(case (acc, clone :: _) => `clone` is clone of `acc` case _ => true) + val synthAccessorAndFields = membersNeedingSynthesis map { member => + def cloneAccessor() = { + val clonedAccessor = (member cloneSymbol clazz) setPos clazz.pos + setMixedinAccessorFlags(member, clonedAccessor) + + if (clonedAccessor.isGetter) + clonedAccessor setAnnotations (clonedAccessor.annotations filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false)) + + // if we don't cloneInfo, method argument symbols are shared between trait and subclasses --> lambalift proxy crash + // TODO: use derive symbol variant? + // println(s"cloning accessor $accessor to $clazz / $clonedInfo -> $relativeInfo") + clonedAccessor setInfo ((clazz.thisType memberType member) cloneInfo clonedAccessor) // accessor.info.cloneInfo(clonedAccessor).asSeenFrom(clazz.thisType, accessor.owner) + } + + // when considering whether to mix in the trait setter, forget about conflicts -- they will be reported for the getter + // a trait setter for an overridden val will receive a unit body in the tree transform + if (nme.isTraitSetterName(member.name)) { + val getter = member.getterIn(member.owner) + val clone = cloneAccessor() + + setClonedTraitSetterFlags(clazz, getter, clone) + // println(s"mixed in trait setter ${clone.defString}") + + List(clone) + } + // avoid creating early errors in case of conflicts (wait until refchecks); + // also, skip overridden accessors contributed by supertraits (only act on the last overriding one) + else if (accessorConflictsExistingVal(member) || isOverriddenAccessor(member, clazz)) Nil + else if (member.isGetter && fieldMemoizationIn(member, clazz).stored) { + // add field if needed + val field = clazz.newValue(member.localName, member.pos) setInfo fieldTypeForGetterIn(member, clazz.thisType) + + setFieldFlags(member, field) + + // filter getter's annotations to exclude those only meant for the field + // we must keep them around long enough to see them here, though, when we create the field + field setAnnotations (member.annotations filter AnnotationInfo.mkFilter(FieldTargetClass, defaultRetention = true)) + + List(cloneAccessor(), field) + } else List(cloneAccessor()) + } + + // println(s"new decls for $clazz: $mixedInAccessorAndFields") + + // omit fields that are not memoized, retain all other members + def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && !fieldMemoizationIn(sym, clazz).stored + + val newDecls = + if (synthAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField) + else { + // must not alter `decls` directly + val newDecls = newScope + val enter = newDecls enter (_: Symbol) + val enterAll = (_: List[Symbol]) foreach enter + + oldDecls foreach { d => if (!omittableField(d)) enter(d) } + synthAccessorAndFields foreach enterAll + + newDecls + } + + // println(s"new decls: $newDecls") + + if (newDecls eq oldDecls) tp + else ClassInfoType(parents, newDecls, clazz) + + case tp => mapOver(tp) + } + } + + + + class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + def mkTypedUnit(pos: Position) = localTyper.typedPos(pos)(CODE.UNIT) + def deriveUnitDef(stat: Tree) = deriveDefDef(stat)(_ => mkTypedUnit(stat.pos)) + + def mkAccessor(accessor: Symbol)(body: Tree) = localTyper.typedPos(accessor.pos)(DefDef(accessor, body)).asInstanceOf[DefDef] + + def mkField(sym: Symbol) = localTyper.typedPos(sym.pos)(ValDef(sym)).asInstanceOf[ValDef] + + + // synth trees for accessors/fields and trait setters when they are mixed into a class + def fieldsAndAccessors(exprOwner: Symbol): List[ValOrDefDef] = { + if (exprOwner.isLocalDummy) { + val clazz = exprOwner.owner + def fieldAccess(accessor: Symbol): Option[Tree] = { + val fieldName = accessor.localName + val field = clazz.info.decl(fieldName) + // The `None` result denotes an error, but we defer to refchecks to report it. + // This is the result of overriding a val with a def, so that no field is found in the subclass. + if (field.exists) Some(Select(This(clazz), field)) + else None + } + + def getterBody(getter: Symbol): Option[Tree] = { + val fieldMemoization = fieldMemoizationIn(getter, clazz) + if (fieldMemoization.pureConstant) Some(gen.mkAttributedQualifier(fieldMemoization.tp)) // TODO: drop when we no longer care about producing identical bytecode + else fieldAccess(getter) + } + + // println(s"accessorsAndFieldsNeedingTrees for $templateSym: $accessorsAndFieldsNeedingTrees") + def setterBody(setter: Symbol): Option[Tree] = { + // trait setter in trait + if (clazz.isTrait) Some(EmptyTree) + // trait setter for overridden val in class + else if (checkAndClearOverridden(setter)) Some(mkTypedUnit(setter.pos)) + // trait val/var setter mixed into class + else fieldAccess(setter) map (fieldSel => Assign(fieldSel, Ident(setter.firstParam))) + } + + + clazz.info.decls.toList.filter(checkAndClearNeedsTrees) flatMap { + case setter if setter.isSetter => setterBody(setter) map mkAccessor(setter) + case getter if getter.isAccessor => getterBody(getter) map mkAccessor(getter) + case field if !(field hasFlag METHOD) => Some(mkField(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES) + case _ => None + } + } else { +// println(s"$exprOwner : ${exprOwner.info} --> ${exprOwner.info.decls}") + Nil + } + } + + def rhsAtOwner(stat: ValOrDefDef, newOwner: Symbol): Tree = + atOwner(newOwner)(super.transform(stat.rhs.changeOwner(stat.symbol -> newOwner))) + + private def transformStat(exprOwner: Symbol)(stat: Tree): List[Tree] = { + val clazz = currentOwner + val statSym = stat.symbol + + // println(s"transformStat $statSym in ${exprOwner.ownerChain}") + // currentRun.trackerFactory.snapshot() + + /* + For traits, the getter has the val's RHS, which is already constant-folded. There is no valdef. + For classes, we still have the classic scheme of private[this] valdef + getter & setter that read/assign to the field. + + There are two axes: (1) is there a side-effect to the val (2) does the val need storage? + For a ConstantType, both answers are "no". (For a unit-typed field, there's a side-effect, but no storage needed.) + + All others (getter for trait field, valdef for class field) have their rhs moved to an initialization statement. + Trait accessors for stored fields are made abstract (there can be no field in a trait). + (In some future version, accessors for non-stored, but effectful fields, + would receive a constant rhs, as the effect is performed by the initialization statement. + We could do this for unit-typed fields, but have chosen not to for backwards compatibility.) + */ + stat match { + // TODO: consolidate with ValDef case + case stat@DefDef(_, _, _, _, _, rhs) if (statSym hasFlag ACCESSOR) && !excludedAccessorOrFieldByFlags(statSym) => + /* TODO: defer replacing ConstantTyped tree by the corresponding constant until erasure + (until then, trees should not be constant-folded -- only their type tracks the resulting constant) + TODO: also remove ACCESSOR flag since there won't be an underlying field to access? + */ + def statInlinedConstantRhs = + if (clazz.isTrait) stat // we've already done this for traits.. the asymmetry will be solved by the above todo + else deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe)) + + if (rhs ne EmptyTree) { + val fieldMemoization = fieldMemoizationIn(statSym, clazz) + + // if we decide to have non-stored fields with initialization effects, the stat's RHS should be replaced by unit + // if (!fieldMemoization.stored) deriveUnitDef(stat) else stat + + if (fieldMemoization.pureConstant) statInlinedConstantRhs :: Nil + else super.transform(stat) :: Nil + } else { + stat :: Nil + } + + case stat@ValDef(mods, _, _, rhs) if !excludedAccessorOrFieldByFlags(statSym) => + if (rhs ne EmptyTree) { + val fieldMemoization = fieldMemoizationIn(statSym, clazz) + + // drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields + if (fieldMemoization.pureConstant) Nil // (a) + else super.transform(stat) :: Nil // if (fieldMemoization.stored) + // else rhsAtOwner(transformStat, exprOwner) :: Nil // (b) -- not used currently + } else { + stat :: Nil + } + + + case tree => List( + if (exprOwner != currentOwner && tree.isTerm) atOwner(exprOwner)(super.transform(tree)) + else super.transform(tree) + ) + } + } + + // TODO flatMapConserve or something like it + // TODO use thicket encoding of multi-tree transformStat? + // if (!currentOwner.isClass || currentOwner.isPackageClass || currentOwner.isInterface) stats flatMap transformStat(exprOwner) // for the ModuleDef case, the only top-level case in that method + // else + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = + afterOwnPhase { + fieldsAndAccessors(exprOwner) ++ (stats flatMap transformStat(exprOwner)) + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index b5084cffe1..d98daf0ffb 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -45,8 +45,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * methods in the impl class (because they can have arbitrary initializers) */ private def isImplementedStatically(sym: Symbol) = ( - sym.isMethod - && (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED)) + sym.isMethod + && notDeferredOrLate(sym) && sym.owner.isTrait && (!sym.isModule || sym.hasFlag(PRIVATE | LIFTED)) && (!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isLazy) @@ -109,16 +109,16 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // --------- type transformation ----------------------------------------------- - def isConcreteAccessor(member: Symbol) = - member.hasAccessorFlag && (!member.isDeferred || (member hasFlag lateDEFERRED)) + private def notDeferredOrLate(sym: Symbol) = !sym.hasFlag(DEFERRED) || sym.hasFlag(lateDEFERRED) /** Is member overridden (either directly or via a bridge) in base class sequence `bcs`? */ def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = beforeOwnPhase { def hasOverridingAccessor(clazz: Symbol) = { clazz.info.nonPrivateDecl(member.name).alternatives.exists( sym => - isConcreteAccessor(sym) && + sym.hasFlag(ACCESSOR) && !sym.hasFlag(MIXEDIN) && + notDeferredOrLate(sym) && matchesType(sym.tpe, member.tpe, alwaysMatchSimple = true)) } ( bcs.head != member.owner @@ -126,6 +126,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { ) } + private def isUnitGetter(sym: Symbol) = sym.tpe.resultType.typeSymbol == UnitClass + /** Add given member to given class, and mark member as mixed-in. */ def addMember(clazz: Symbol, member: Symbol): Symbol = { @@ -202,6 +204,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { clazz.info // make sure info is up to date, so that implClass is set. + // TODO: is this needed? can there be fields in a class that don't have accessors yet but need them??? + // can we narrow this down to just getters for lazy vals? param accessors? for (member <- clazz.info.decls) { if (!member.isMethod && !member.isModule && !member.isModuleVar) { assert(member.isTerm && !member.isDeferred, member) @@ -297,49 +301,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def mixinTraitMembers(mixinClass: Symbol) { // For all members of a trait's interface do: for (mixinMember <- mixinClass.info.decls) { - if (isConcreteAccessor(mixinMember)) { - if (isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) - devWarning(s"Overridden concrete accessor: ${mixinMember.fullLocationString}") - else { - // mixin field accessors - val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember) - if (mixinMember.isLazy) { - initializer(mixedInAccessor) = ( - mixinClass.info.decl(mixinMember.name) - orElse abort("Could not find initializer for " + mixinMember.name) - ) - } - if (!mixinMember.isSetter) - mixinMember.tpe match { - case MethodType(Nil, ConstantType(_)) => - // mixinMember is a constant; only getter is needed - ; - case MethodType(Nil, TypeRef(_, UnitClass, _)) => - // mixinMember is a value of type unit. No field needed - ; - case _ => // otherwise mixin a field as well - // enteringPhase: the private field is moved to the implementation class by erasure, - // so it can no longer be found in the mixinMember's owner (the trait) - val accessed = enteringPickler(mixinMember.accessed) - // #3857, need to retain info before erasure when cloning (since cloning only - // carries over the current entry in the type history) - val sym = enteringErasure { - // so we have a type history entry before erasure - clazz.newValue(mixinMember.localName, mixinMember.pos).setInfo(mixinMember.tpe.resultType) - } - sym updateInfo mixinMember.tpe.resultType // info at current phase - - val newFlags = ( - ( PrivateLocal ) - | ( mixinMember getFlag MUTABLE | LAZY) - | ( if (mixinMember.hasStableFlag) 0 else MUTABLE ) - ) - - addMember(clazz, sym setFlag newFlags setAnnotations accessed.annotations) - } - } - } - else if (mixinMember.isSuperAccessor) { // mixin super accessors + if (mixinMember.hasFlag(SUPERACCESSOR)) { // mixin super accessors val superAccessor = addMember(clazz, mixinMember.cloneSymbol(clazz)) setPos clazz.pos assert(superAccessor.alias != NoSymbol, superAccessor) @@ -355,10 +317,53 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { superAccessor.asInstanceOf[TermSymbol] setAlias alias1 } } - else if (mixinMember.isMethod && mixinMember.isModule && mixinMember.hasNoFlags(LIFTED | BRIDGE)) { + else if (mixinMember.hasAllFlags(METHOD | MODULE) && mixinMember.hasNoFlags(LIFTED | BRIDGE)) { // mixin objects: todo what happens with abstract objects? addMember(clazz, mixinMember.cloneSymbol(clazz, mixinMember.flags & ~(DEFERRED | lateDEFERRED)) setPos clazz.pos) } + else if (mixinMember.hasFlag(ACCESSOR) && notDeferredOrLate(mixinMember) + && (mixinMember hasFlag (LAZY | PARAMACCESSOR)) + && !isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) { + // pick up where `fields` left off -- it already mixed in fields and accessors for regular vals. + // but has ignored lazy vals and constructor parameter accessors + // TODO: captures added by lambdalift for local traits? + // + // mixin accessor for lazy val or constructor parameter + // (note that a paramaccessor cannot have a constant type as it must have a user-defined type) + val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember) + val name = mixinMember.name + + if (mixinMember.isLazy) + initializer(mixedInAccessor) = + (mixinClass.info.decl(name) orElse abort(s"Could not find initializer for lazy val $name!")) + + // Add field while we're mixing in the getter (unless it's a Unit-typed lazy val) + // + // lazy val of type Unit doesn't need a field -- the bitmap is enough. + // TODO: constant-typed lazy vals... it's an extreme corner case, but we could also suppress the field in: + // `trait T { final lazy val a = "a" }; class C extends T`, but who writes code like that!? :) + // we'd also have to change the lazyvals logic if we do this + if (!nme.isSetterName(name) && !(mixinMember.isLazy && isUnitGetter(mixinMember))) { + // enteringPhase: the private field is moved to the implementation class by erasure, + // so it can no longer be found in the mixinMember's owner (the trait) + val accessed = enteringPickler(mixinMember.accessed) + // #3857, need to retain info before erasure when cloning (since cloning only + // carries over the current entry in the type history) + val sym = enteringErasure { + // so we have a type history entry before erasure + clazz.newValue(mixinMember.localName, mixinMember.pos).setInfo(mixinMember.tpe.resultType) + } + sym updateInfo mixinMember.tpe.resultType // info at current phase + + val newFlags = ( + (PrivateLocal) + | (mixinMember getFlag MUTABLE | LAZY) + | (if (mixinMember.hasStableFlag) 0 else MUTABLE) + ) + + addMember(clazz, sym setFlag newFlags setAnnotations accessed.annotations) + } + } } } @@ -478,8 +483,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { tree - case _ => - tree + case _ => tree } } @@ -763,13 +767,12 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def addCheckedGetters(clazz: Symbol, stats: List[Tree]): List[Tree] = { def dd(stat: DefDef) = { val sym = stat.symbol - def isUnit = sym.tpe.resultType.typeSymbol == UnitClass def isEmpty = stat.rhs == EmptyTree if (!clazz.isTrait && sym.isLazy && !isEmpty) { assert(fieldOffset contains sym, sym) deriveDefDef(stat) { - case t if isUnit => mkLazyDef(clazz, sym, List(t), UNIT, fieldOffset(sym)) + case t if isUnitGetter(sym) => mkLazyDef(clazz, sym, List(t), UNIT, fieldOffset(sym)) case Block(stats, res) => mkLazyDef(clazz, sym, stats, Select(This(clazz), res.symbol), fieldOffset(sym)) @@ -781,8 +784,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { assert(fieldOffset contains sym, sym) deriveDefDef(stat)(rhs => (mkCheckedAccessor(clazz, _: Tree, fieldOffset(sym), stat.pos, sym))( - if (sym.tpe.resultType.typeSymbol == UnitClass) UNIT - else rhs + if (isUnitGetter(sym)) UNIT else rhs ) ) } @@ -908,7 +910,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } } - def isUnitGetter(getter: Symbol) = getter.tpe.resultType.typeSymbol == UnitClass def fieldAccess(accessor: Symbol) = Select(This(clazz), accessor.accessed) def isOverriddenSetter(sym: Symbol) = @@ -924,7 +925,12 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { addDefDef(sym) } else { // if class is not a trait add accessor definitions - if (isConcreteAccessor(sym)) { + // used to include `sym` with `sym hasFlag lateDEFERRED` as not deferred, + // but I don't think MIXEDIN members ever get this flag + assert(!sym.hasFlag(lateDEFERRED), s"mixedin $sym from $clazz has lateDEFERRED flag?!") + if (sym.hasFlag(ACCESSOR) && !sym.hasFlag(DEFERRED)) { + assert(sym hasFlag (LAZY | PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?") + // add accessor definitions addDefDef(sym, { if (sym.isSetter) { @@ -1006,20 +1012,14 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val parents1 = currentOwner.info.parents map (t => TypeTree(t) setPos tree.pos) // mark fields which can be nulled afterward lazyValNullables = nullableFields(templ) withDefaultValue Set() - // Remove bodies of accessors in traits - TODO: after PR #5141 (fields refactoring), this might be a no-op - val bodyEmptyAccessors = if (!sym.enclClass.isTrait) body else body mapConserve { - case dd: DefDef if dd.symbol.isAccessor && !dd.symbol.isLazy => - deriveDefDef(dd)(_ => EmptyTree) - case tree => tree - } // add all new definitions to current class or interface - val body1 = addNewDefs(currentOwner, bodyEmptyAccessors) - body1 foreach { + val statsWithNewDefs = addNewDefs(currentOwner, body) + statsWithNewDefs foreach { case dd: DefDef if isTraitMethodRequiringStaticImpl(dd) => dd.symbol.updateAttachment(NeedStaticImpl) case _ => } - treeCopy.Template(tree, parents1, self, body1) + treeCopy.Template(tree, parents1, self, statsWithNewDefs) case Select(qual, name) if sym.owner.isTrait && !sym.isMethod => // refer to fields in some trait an abstract getter in the interface. diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index e4082eb376..a861115cab 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -18,8 +18,6 @@ abstract class OverridingPairs extends SymbolPairs { import global._ class Cursor(base: Symbol) extends super.Cursor(base) { - lazy val relatively = new RelativeTo(base.thisType) - /** Symbols to exclude: Here these are constructors and private/artifact symbols, * including bridges. But it may be refined in subclasses. */ @@ -37,7 +35,7 @@ abstract class OverridingPairs extends SymbolPairs { (lo.owner != high.owner) // don't try to form pairs from overloaded members && !high.isPrivate // private or private[this] members never are overridden && !exclude(lo) // this admits private, as one can't have a private member that matches a less-private member. - && relatively.matches(lo, high) + && ((self memberType lo) matches (self memberType high)) ) // TODO we don't call exclude(high), should we? } } diff --git a/src/compiler/scala/tools/nsc/transform/Statics.scala b/src/compiler/scala/tools/nsc/transform/Statics.scala index 9ab00f1a83..776805fd9f 100644 --- a/src/compiler/scala/tools/nsc/transform/Statics.scala +++ b/src/compiler/scala/tools/nsc/transform/Statics.scala @@ -4,7 +4,7 @@ package transform abstract class Statics extends Transform with ast.TreeDSL { import global._ - class StaticsTransformer extends Transformer { + trait StaticsTransformer extends Transformer { /** generate a static constructor with symbol fields inits, or an augmented existing static ctor */ def staticConstructor(body: List[Tree], localTyper: analyzer.Typer, pos: Position)(newStaticInits: List[Tree]): Tree = diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 81a465ef2f..fcfcc8feb9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -13,6 +13,7 @@ import scala.reflect.macros.runtime.AbortMacroException import scala.util.control.NonFatal import scala.tools.nsc.util.stackTraceString import scala.reflect.io.NoAbstractFile +import scala.reflect.internal.util.NoSourceFile trait ContextErrors { self: Analyzer => @@ -757,22 +758,18 @@ trait ContextErrors { } def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = { + val addPref = s";\n the conflicting $sym1 was defined" + val bugNote = "\n Note: this may be due to a bug in the compiler involving wildcards in package objects" + // Most of this hard work is associated with SI-4893. val isBug = sym0.isAbstractType && sym1.isAbstractType && (sym0.name startsWith "_$") - 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 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 - ) - val addendum = addendums.flatten match { - case Nil => "" - case xs => xs.mkString("\n ", "\n ", "") - } + val addendum = ( + if (sym0.pos.source eq sym1.pos.source) s"$addPref at line ${sym1.pos.line}:${sym1.pos.column}" + else if (sym1.pos.source ne NoSourceFile) s"$addPref at line ${sym1.pos.line}:${sym1.pos.column} of '${sym1.pos.source.path}'" + else if (sym1.associatedFile ne NoAbstractFile) s"$addPref in '${sym1.associatedFile.canonicalPath}'" + else "") + (if (isBug) bugNote else "") - issueSymbolTypeError(sym0, sym1+" is defined twice" + addendum) + issueSymbolTypeError(sym0, s"$sym0 is defined twice$addendum") } // cyclic errors diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index c03094bc6a..408b457d5b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -121,6 +121,7 @@ trait MethodSynthesis { } // TODO: see if we can link symbol creation & tree derivation by sharing the Field/Getter/Setter factories + // maybe we can at least reuse some variant of standardAccessors? def enterGetterSetter(tree: ValDef): Unit = { tree.symbol = if (tree.mods.isLazy) { @@ -131,15 +132,14 @@ trait MethodSynthesis { val getterSym = getter.createAndEnterSymbol() // Create the setter if necessary. - if (getter.needsSetter) - Setter(tree).createAndEnterSymbol() + if (getter.needsSetter) Setter(tree).createAndEnterSymbol() - // If the getter's abstract the tree gets the getter's symbol, - // otherwise, create a field (assume the getter requires storage). + // If the getter's abstract, the tree gets the getter's symbol, + // otherwise, create a field (we have to assume the getter requires storage for now). // NOTE: we cannot look at symbol info, since we're in the process of deriving them // (luckily, they only matter for lazy vals, which we've ruled out in this else branch, // and `doNotDeriveField` will skip them if `!mods.isLazy`) - if (Field.noFieldFor(tree)) getterSym setPos tree.pos + if (Field.noFieldFor(tree)) getterSym setPos tree.pos // TODO: why do setPos? `createAndEnterSymbol` already gave `getterSym` the position `tree.pos.focus` else enterStrictVal(tree) } @@ -282,14 +282,15 @@ trait MethodSynthesis { final def enclClass = basisSym.enclClass - /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ - final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter) + // There's no reliable way to detect all kinds of setters from flags or name!!! + // A BeanSetter's name does not end in `_=` -- it does begin with "set", but so could the getter + // for a regular Scala field... TODO: can we add a flag to distinguish getter/setter accessors? + final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, this.isInstanceOf[DerivedSetter]) final def fieldSelection = Select(This(enclClass), basisSym) def derivedSym: Symbol = tree.symbol def derivedTree: Tree = EmptyTree - def isSetter = false def isDeferred = mods.isDeferred def validate() { } def createAndEnterSymbol(): MethodSymbol = { @@ -304,6 +305,7 @@ trait MethodSynthesis { result } + final def derive(initial: List[AnnotationInfo]): Tree = { validate() @@ -311,7 +313,9 @@ trait MethodSynthesis { // 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. + // - val/var member annotations solely end up on the underlying field, except in traits (@since 2.12), + // where there is no field, and the getter thus holds annotations targetting both getter & field. + // As soon as there is a field/getter (in subclasses mixing in the trait), 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) @@ -319,9 +323,11 @@ trait MethodSynthesis { case _: Param => annotationFilter(ParamTargetClass, defaultRetention = true) // By default annotations go to the field, except if the field is generated for a class parameter (PARAMACCESSOR). case _: Field => annotationFilter(FieldTargetClass, defaultRetention = !mods.isParamAccessor) + case _: BaseGetter if owner.isTrait => annotationFilter(List(FieldTargetClass, GetterTargetClass), defaultRetention = true) case _: BaseGetter => annotationFilter(GetterTargetClass, defaultRetention = false) case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false) case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false) + // TODO do bean getters need special treatment to collect field-targeting annotations in traits? case _: AnyBeanGetter => annotationFilter(BeanGetterTargetClass, defaultRetention = false) } @@ -329,21 +335,23 @@ trait MethodSynthesis { // should be propagated to this kind of accessor. derivedSym setAnnotations (initial filter annotFilter) + if (derivedSym.isSetter && owner.isTrait && !isDeferred) + derivedSym addAnnotation TraitSetterAnnotationClass + logDerived(derivedTree) } } + sealed trait DerivedGetter extends DerivedFromValDef { - // A getter must be accompanied by a setter if the ValDef is mutable. def needsSetter = mods.isMutable } sealed trait DerivedSetter extends DerivedFromValDef { - override def isSetter = true - private def setterParam = derivedSym.paramss match { + protected def setterParam = derivedSym.paramss match { case (p :: Nil) :: _ => p case _ => NoSymbol } - private def setterRhs = { + protected def setterRhs = { assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for $basisSym in $enclClass") if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree else Assign(fieldSelection, Ident(setterParam)) @@ -390,6 +398,7 @@ trait MethodSynthesis { override def derivedSym = if (Field.noFieldFor(tree)) basisSym else basisSym.getterIn(enclClass) private def derivedRhs = if (Field.noFieldFor(tree)) tree.rhs else fieldSelection + // TODO: more principled approach -- this is a bit bizarre private def derivedTpt = { // For existentials, don't specify a type for the getter, even one derived // from the symbol! This leads to incompatible existentials for the field and @@ -457,6 +466,7 @@ trait MethodSynthesis { def flagsMask = SetterFlags def flagsExtra = ACCESSOR + // TODO: double check logic behind need for name expansion in context of new fields phase override def derivedSym = basisSym.setterIn(enclClass) } @@ -464,17 +474,25 @@ trait MethodSynthesis { // No field for these vals (either never emitted or eliminated later on): // - abstract vals have no value we could store (until they become concrete, potentially) // - lazy vals of type Unit - // - [Emitted, later removed during AddInterfaces/Mixins] concrete vals in traits can't have a field - // - [Emitted, later removed during Constructors] a concrete val with a statically known value (Unit / ConstantType) + // - concrete vals in traits don't yield a field here either (their getter's RHS has the initial value) + // Constructors will move the assignment to the constructor, abstracting over the field using the field setter, + // and Fields will add a field to the class that mixes in the trait, implementing the accessors in terms of it + // - [Emitted, later removed during Constructors] a concrete val with a statically known value (ConstantType) // performs its side effect according to lazy/strict semantics, but doesn't need to store its value // each access will "evaluate" the RHS (a literal) again // We would like to avoid emitting unnecessary fields, but the required knowledge isn't available until after typer. // The only way to avoid emitting & suppressing, is to not emit at all until we are sure to need the field, as dotty does. // NOTE: do not look at `vd.symbol` when called from `enterGetterSetter` (luckily, that call-site implies `!mods.isLazy`), + // similarly, the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite) // as the symbol info is in the process of being created then. // TODO: harmonize tree & symbol creation - // TODO: the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite) - def noFieldFor(vd: ValDef) = vd.mods.isDeferred || (vd.mods.isLazy && isUnitType(vd.symbol.info)) + // the middle `&& !owner.isTrait` is needed after `isLazy` because non-unit-typed lazy vals in traits still get a field -- see neg/t5455.scala + def noFieldFor(vd: ValDef) = (vd.mods.isDeferred + || (vd.mods.isLazy && !owner.isTrait && isUnitType(vd.symbol.info)) + || (owner.isTrait && !traitFieldFor(vd))) + + // TODO: never emit any fields in traits -- only use getter for lazy/presuper ones as well + private def traitFieldFor(vd: ValDef): Boolean = vd.mods.hasFlag(PRESUPER | LAZY) } case class Field(tree: ValDef) extends DerivedFromValDef { @@ -482,6 +500,9 @@ trait MethodSynthesis { def flagsMask = FieldFlags def flagsExtra = PrivateLocal + // TODO: override def createAndEnterSymbol (currently never called on Field) + // and do `enterStrictVal(tree)`, so that enterGetterSetter and addDerivedTrees can share some logic... + // handle lazy val first for now (we emit a Field even though we probably shouldn't...) override def derivedTree = if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus) @@ -528,7 +549,10 @@ trait MethodSynthesis { } case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { } case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { } - case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter + case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter { + // TODO: document, motivate + override protected def setterRhs = Apply(Ident(tree.name.setterName), List(Ident(setterParam))) + } // No Symbols available. private def beanAccessorsFromNames(tree: ValDef) = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index caad4a907b..784b43ab84 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -6,6 +6,7 @@ package scala.tools.nsc package typechecker +import scala.annotation.tailrec import scala.collection.mutable import symtab.Flags._ import scala.language.postfixOps @@ -116,10 +117,10 @@ trait Namers extends MethodSynthesis { } // 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.isClass && deriveAccessorsInClass(vd)) + 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.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 @@ -773,28 +774,31 @@ trait Namers extends MethodSynthesis { // 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) - - 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 - } + + // logDefinition(sym) { + val tp = typeSig(tree) + + findCyclicalLowerBound(tp) andAlso { sym => if (needsCycleCheck) { - log(s"Needs cycle check: ${sym.debugLocationString}") - if (!typer.checkNonCyclic(tree.pos, tp)) - sym setInfo ErrorType + // 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 + } + if (needsCycleCheck) { + log(s"Needs cycle check: ${sym.debugLocationString}") + if (!typer.checkNonCyclic(tree.pos, tp)) + sym setInfo ErrorType + } + //} + + validate(sym) } def moduleClassTypeCompleter(tree: ModuleDef) = { @@ -807,15 +811,18 @@ trait Namers extends MethodSynthesis { /* 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) - } - } + // typeSig calls valDefSig (because tree: ValDef) + // sym is an accessor, while tree is the field (which may have the same symbol as the getter, or maybe it's the field) + val sig = accessorSigFromFieldTp(sym, isSetter, typeSig(tree)) + + sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym) + + validate(sym) } + private def accessorSigFromFieldTp(sym: global.Symbol, isSetter: Boolean, tp: global.Type): global.Type with Product with Serializable = { + if (isSetter) MethodType(List(sym.newSyntheticValueParam(tp)), UnitTpe) else NullaryMethodType(tp) + } def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => val selftpe = typer.typedType(tree).tpe sym setInfo { @@ -992,6 +999,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`. * @@ -1009,166 +1029,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) @@ -1176,13 +1170,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 @@ -1193,27 +1183,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 // NOTEL must check `.info.isInstanceOf[MethodType]`, not `.isMethod`! + else vparamSymss + + val methSig = deskolemizedPolySig(vparamSymssOrEmptyParamsFromOverride, resTp) + pluginsTypeSig(methSig, typer, ddef, resTpGiven) } /** @@ -1369,19 +1367,76 @@ 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 (valOwner.isClass && settings.isScala212) { + // 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 + } else WildcardType + } + + 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) = { @@ -1560,10 +1615,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 diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index d1764ea482..0eae1ce419 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -298,16 +298,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 "") } @@ -321,7 +334,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans 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 @@ -346,9 +359,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) @@ -439,9 +450,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)) { + // 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") } @@ -456,7 +469,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } else if (member.isValue && member.isLazy && other.isValue && !other.isSourceMethod && !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 && !other.isSourceMethod && !other.isDeferred && // !(other.hasFlag(MODULE) && other.hasFlag(PACKAGE | JAVA)) && other.hasFlag(LAZY) && (!other.isMethod || other.hasFlag(STABLE)) && !other.hasFlag(DEFERRED) 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) @@ -547,7 +560,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } def checkOverrideDeprecated() { - if (other.hasDeprecatedOverridingAnnotation && !member.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) { + 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 "" @@ -651,7 +664,7 @@ 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. @@ -1133,22 +1146,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans 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 ------------------------------------------------------------ @@ -1659,16 +1666,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // inside annotations. applyRefchecksToAnnotations(tree) var result: Tree = tree match { - case vod: ValOrDefDef => + // 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(vod) + if (!tree.isErroneous) + checkInfiniteLoop(tree.symbol, tree.rhs) + if (settings.warnNullaryUnit) checkNullaryMethodReturnType(sym) if (settings.warnInaccessible) { if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.isSynthetic) checkAccessibilityOfReferencedTypes(tree) } - vod match { + tree match { case dd: DefDef => checkByNameRightAssociativeDef(dd) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 5f2643cb25..bee327c760 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -133,12 +133,14 @@ trait TypeDiagnostics { alternatives(tree) map (x => " " + methodTypeErrorString(x)) mkString ("", " <and>\n", "\n") /** The symbol which the given accessor represents (possibly in part). - * This is used for error messages, where we want to speak in terms - * of the actual declaration or definition, not in terms of the generated setters - * and getters. - */ + * This is used for error messages, where we want to speak in terms + * of the actual declaration or definition, not in terms of the generated setters + * and getters. + * + * TODO: is it wise to create new symbols simply to generate error message? is this safe in interactive/resident mode? + */ def underlyingSymbol(member: Symbol): Symbol = - if (!member.hasAccessorFlag) member + if (!member.hasAccessorFlag || member.owner.isTrait) member else if (!member.isDeferred) member.accessed else { val getter = if (member.isSetter) member.getterIn(member.owner) else member @@ -532,8 +534,8 @@ trait TypeDiagnostics { val what = ( if (sym.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 || sym.isLazy) "val" + else if (sym.isVar || sym.isGetter && (sym.accessed.isVar || (sym.owner.isTrait && !sym.hasFlag(STABLE)))) "var" + else if (sym.isVal || sym.isGetter && (sym.accessed.isVal || (sym.owner.isTrait && sym.hasFlag(STABLE))) || sym.isLazy) "val" else if (sym.isSetter) "setter" else if (sym.isMethod) "method" else if (sym.isModule) "object" diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ba104fb7a6..2bbf8ed74e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1360,7 +1360,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.") else if (stat.symbol != null && stat.symbol.isParamAccessor) notAllowed("additional parameter") + // concrete accessor (getter) in trait corresponds to a field definition (neg/anytrait.scala) + // TODO: only reject accessors that actually give rise to field (e.g., a constant-type val is fine) + else if (!isValueClass && stat.symbol.isAccessor && !stat.symbol.isDeferred) + notAllowed("field definition") checkEphemeralDeep.traverse(rhs) + // for value class or "exotic" vals in traits + // (traits don't receive ValDefs for regular vals until fields phase -- well, except for early initialized/lazy vals) case _: ValDef => notAllowed("field definition") case _: ModuleDef => @@ -4219,7 +4225,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // if (varsym.isVariable || // // 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) { + if (varsym.isVariable || varsym.isValue && phase.assignsFields) { val rhs1 = typedByValueExpr(rhs, lhs1.tpe) treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitTpe } diff --git a/src/library/scala/deprecatedInheritance.scala b/src/library/scala/deprecatedInheritance.scala index bd5daf5de0..994eac9ed8 100644 --- a/src/library/scala/deprecatedInheritance.scala +++ b/src/library/scala/deprecatedInheritance.scala @@ -8,6 +8,8 @@ package scala +import scala.annotation.meta._ + /** An annotation that designates that inheriting from a class is deprecated. * * This is usually done to warn about a non-final class being made final in a future version. @@ -41,4 +43,5 @@ package scala * @see [[scala.deprecatedOverriding]] * @see [[scala.deprecatedName]] */ +@getter @setter @beanGetter @beanSetter class deprecatedInheritance(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation diff --git a/src/library/scala/deprecatedOverriding.scala b/src/library/scala/deprecatedOverriding.scala index 46639986c0..5be6830b27 100644 --- a/src/library/scala/deprecatedOverriding.scala +++ b/src/library/scala/deprecatedOverriding.scala @@ -8,6 +8,8 @@ package scala +import scala.annotation.meta._ + /** An annotation that designates that overriding a member is deprecated. * * Overriding such a member in a sub-class then generates a warning. @@ -42,4 +44,5 @@ package scala * @see [[scala.deprecatedInheritance]] * @see [[scala.deprecatedName]] */ +@getter @setter @beanGetter @beanSetter class deprecatedOverriding(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index fa19103d0c..d58cabf3d7 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -305,10 +305,13 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => } /** The default kind of members to which this annotation is attached. - * For instance, for scala.deprecated defaultTargets = - * List(getter, setter, beanGetter, beanSetter). - */ - def defaultTargets = symbol.annotations map (_.symbol) filter isMetaAnnotation + * For instance, for scala.deprecated defaultTargets = + * List(getter, setter, beanGetter, beanSetter). + * + * NOTE: have to call symbol.initialize, since we won't get any annotations if the symbol hasn't yet been completed + */ + def defaultTargets = symbol.initialize.annotations map (_.symbol) filter isMetaAnnotation + // Test whether the typeSymbol of atp conforms to the given class. def matches(clazz: Symbol) = !symbol.isInstanceOf[StubSymbol] && (symbol isNonBottomSubClass clazz) // All subtrees of all args are considered. diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 3dec73da58..35ec80901e 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -674,8 +674,10 @@ trait Definitions extends api.StandardDefinitions { // Note that these call .dealiasWiden and not .normalize, the latter of which // tends to change the course of events by forcing types. def isFunctionType(tp: Type) = isFunctionTypeDirect(tp.dealiasWiden) + // the number of arguments expected by the function described by `tp` (a FunctionN or SAM type), // or `-1` if `tp` does not represent a function type or SAM + // for use during typers (after fields, samOf will be confused by abstract accessors for trait fields) def functionArityFromType(tp: Type) = { val dealiased = tp.dealiasWiden if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.length - 1 @@ -685,16 +687,6 @@ trait Definitions extends api.StandardDefinitions { } } - // the result type of a function or corresponding SAM type - def functionResultType(tp: Type): Type = { - val dealiased = tp.dealiasWiden - if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.last - else samOf(tp) match { - case samSym if samSym.exists => tp.memberInfo(samSym).resultType.deconst - case _ => NoType - } - } - // the SAM's parameters and the Function's formals must have the same length // (varargs etc don't come into play, as we're comparing signatures, not checking an application) def samMatchesFunctionBasedOnArity(sam: Symbol, formals: List[Any]): Boolean = diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index e06decea6d..d088150db6 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -169,6 +169,11 @@ class Flags extends ModifierFlags { final val SYNCHRONIZED = 1L << 45 // symbol is a method which should be marked ACC_SYNCHRONIZED + final val SYNTHESIZE_IMPL_IN_SUBCLASS = 1L << 50 // used in fields phase to indicate this accessor should receive an implementation in a subclass + + // flags used strictly internally in the Fields phase (info/tree transform): + final val NEEDS_TREES = 1L << 59 // this symbol needs a tree. (distinct from SYNTHESIZE_IMPL_IN_SUBCLASS) + // ------- shift definitions ------------------------------------------------------- // // Flags from 1L to (1L << 50) are normal flags. @@ -257,7 +262,8 @@ class Flags extends ModifierFlags { /** These modifiers appear in TreePrinter output. */ final val PrintableFlags = ExplicitFlags | BridgeFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | MACRO | - ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | ARTIFACT + ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | ARTIFACT | + SYNTHESIZE_IMPL_IN_SUBCLASS | NEEDS_TREES /** When a symbol for a field is created, only these flags survive * from Modifiers. Others which may be applied at creation time are: @@ -442,7 +448,7 @@ class Flags extends ModifierFlags { case JAVA_DEFAULTMETHOD => "<defaultmethod>" // (1L << 47) case JAVA_ENUM => "<enum>" // (1L << 48) case JAVA_ANNOTATION => "<annotation>" // (1L << 49) - case 0x4000000000000L => "" // (1L << 50) + case SYNTHESIZE_IMPL_IN_SUBCLASS => "<sub_synth>" // (1L << 50) case `lateDEFERRED` => "<latedeferred>" // (1L << 51) case `lateFINAL` => "<latefinal>" // (1L << 52) case `lateMETHOD` => "<latemethod>" // (1L << 53) @@ -451,7 +457,7 @@ class Flags extends ModifierFlags { case `notPROTECTED` => "<notprotected>" // (1L << 56) case `notOVERRIDE` => "<notoverride>" // (1L << 57) case `notPRIVATE` => "<notprivate>" // (1L << 58) - case 0x800000000000000L => "" // (1L << 59) + case NEEDS_TREES => "<needs_trees>" // (1L << 59) case 0x1000000000000000L => "" // (1L << 60) case 0x2000000000000000L => "" // (1L << 61) case 0x4000000000000000L => "" // (1L << 62) diff --git a/src/reflect/scala/reflect/internal/Phase.scala b/src/reflect/scala/reflect/internal/Phase.scala index a761f686e6..f56c41d71c 100644 --- a/src/reflect/scala/reflect/internal/Phase.scala +++ b/src/reflect/scala/reflect/internal/Phase.scala @@ -47,6 +47,8 @@ abstract class Phase(val prev: Phase) { final val specialized: Boolean = ((prev ne null) && (prev ne NoPhase)) && (prev.name == "specialize" || prev.specialized) final val refChecked: Boolean = ((prev ne null) && (prev ne NoPhase)) && (prev.name == "refchecks" || prev.refChecked) + // are we past the fields phase, so that we should allow writing to vals (as part of type checking trait setters) + final val assignsFields: Boolean = ((prev ne null) && (prev ne NoPhase)) && (prev.name == "fields" || prev.assignsFields) /** This is used only in unsafeTypeParams, and at this writing is * overridden to false in parser, namer, typer, and erasure. (And NoPhase.) diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index 30f2efd7e3..33ca78b439 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -285,6 +285,7 @@ trait ReificationSupport { self: SymbolTable => val (gvdefs, etdefs) = rawEdefs.partition(treeInfo.isEarlyValDef) val (fieldDefs, UnCtor(ctorMods, ctorVparamss, lvdefs) :: body) = rest.splitAt(indexOfCtor(rest)) val evdefs = gvdefs.zip(lvdefs).map { + // TODO: in traits, early val defs are defdefs case (gvdef @ ValDef(_, _, tpt: TypeTree, _), ValDef(_, _, _, rhs)) => copyValDef(gvdef)(tpt = tpt.original, rhs = rhs) case (tr1, tr2) => diff --git a/src/reflect/scala/reflect/internal/SymbolPairs.scala b/src/reflect/scala/reflect/internal/SymbolPairs.scala index a52d2d8510..320c814696 100644 --- a/src/reflect/scala/reflect/internal/SymbolPairs.scala +++ b/src/reflect/scala/reflect/internal/SymbolPairs.scala @@ -30,27 +30,6 @@ abstract class SymbolPairs { val global: SymbolTable import global._ - /** Type operations relative to a prefix. All operations work on Symbols, - * and the types are the member types of those symbols in the prefix. - */ - class RelativeTo(val prefix: Type) { - def this(clazz: Symbol) = this(clazz.thisType) - import scala.language.implicitConversions // geez, it even has to hassle me when it's private - private implicit def symbolToType(sym: Symbol): Type = prefix memberType sym - - def erasureOf(sym: Symbol): Type = erasure.erasure(sym)(sym: Type) - def signature(sym: Symbol): String = sym defStringSeenAs (sym: Type) - def erasedSignature(sym: Symbol): String = sym defStringSeenAs erasureOf(sym) - - def isSameType(sym1: Symbol, sym2: Symbol): Boolean = sym1 =:= sym2 - def isSubType(sym1: Symbol, sym2: Symbol): Boolean = sym1 <:< sym2 - def isSuperType(sym1: Symbol, sym2: Symbol): Boolean = sym2 <:< sym1 - def isSameErasure(sym1: Symbol, sym2: Symbol): Boolean = erasureOf(sym1) =:= erasureOf(sym2) - def matches(sym1: Symbol, sym2: Symbol): Boolean = (sym1: Type) matches (sym2: Type) - - override def toString = s"RelativeTo($prefix)" - } - /** Are types tp1 and tp2 equivalent seen from the perspective * of `baseClass`? For instance List[Int] and Seq[Int] are =:= * when viewed from IterableClass. @@ -58,10 +37,11 @@ abstract class SymbolPairs { def sameInBaseClass(baseClass: Symbol)(tp1: Type, tp2: Type) = (tp1 baseType baseClass) =:= (tp2 baseType baseClass) - case class SymbolPair(base: Symbol, low: Symbol, high: Symbol) { + final case class SymbolPair(base: Symbol, low: Symbol, high: Symbol) { + private[this] val self = base.thisType + def pos = if (low.owner == base) low.pos else if (high.owner == base) high.pos else base.pos - def self: Type = base.thisType - def rootType: Type = base.thisType + def rootType: Type = self def lowType: Type = self memberType low def lowErased: Type = erasure.specialErasure(base)(low.tpe) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index ab52a875f8..af1cdafcda 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -96,8 +96,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isByNameParam: Boolean = this.isValueParameter && (this hasFlag BYNAMEPARAM) def isImplementationArtifact: Boolean = (this hasFlag BRIDGE) || (this hasFlag VBRIDGE) || (this hasFlag ARTIFACT) def isJava: Boolean = isJavaDefined - def isVal: Boolean = isTerm && !isModule && !isMethod && !isMutable - def isVar: Boolean = isTerm && !isModule && !isMethod && !isLazy && isMutable + + def isField: Boolean = isTerm && !isModule && (!isMethod || owner.isTrait && isAccessor) + def isMutableVal = if (owner.isTrait) !hasFlag(STABLE) else isMutable + def isVal: Boolean = isField && !isMutableVal + def isVar: Boolean = isField && !isLazy && isMutableVal + def isAbstract: Boolean = isAbstractClass || isDeferred || isAbstractType def isPrivateThis = (this hasFlag PRIVATE) && (this hasFlag LOCAL) def isProtectedThis = (this hasFlag PROTECTED) && (this hasFlag LOCAL) @@ -1532,7 +1536,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def setInfo(info: Type): this.type = { info_=(info); this } /** Modifies this symbol's info in place. */ def modifyInfo(f: Type => Type): this.type = setInfo(f(info)) - /** Substitute second list of symbols for first in current info. */ + /** Substitute second list of symbols for first in current info. + * + * NOTE: this discards the type history (uses setInfo) + */ def substInfo(syms0: List[Symbol], syms1: List[Symbol]): this.type = if (syms0.isEmpty) this else modifyInfo(_.substSym(syms0, syms1)) @@ -2048,7 +2055,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert(hasAccessorFlag, this) val localField = owner.info decl localName - if (localField == NoSymbol && this.hasFlag(MIXEDIN)) { + if (localField == NoSymbol && this.hasFlag(MIXEDIN)) { // TODO: fields phase does not (yet?) add MIXEDIN in setMixedinAccessorFlags // SI-8087: private[this] fields don't have a `localName`. When searching the accessed field // for a mixin accessor of such a field, we need to look for `name` instead. // The phase travel ensures that the field is found (`owner` is the trait class symbol, the @@ -2088,8 +2095,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** If this is a lazy value, the lazy accessor; otherwise this symbol. */ def lazyAccessorOrSelf: Symbol = if (isLazy) lazyAccessor else this - /** If this is an accessor, the accessed symbol. Otherwise, this symbol. */ - def accessedOrSelf: Symbol = if (hasAccessorFlag) accessed else this + /** `accessed`, if this is an accessor that should have an underlying field. Otherwise, `this`. + * Note that a "regular" accessor in a trait does not have a field, as an interface cannot define a field. + * "non-regular" vals are: early initialized or lazy vals. + * Eventually, we should delay introducing symbols for all val/vars until the fields (or lazyvals) phase, + * as they are an implementation detail that's irrelevant to type checking. + */ + def accessedOrSelf: Symbol = + if (hasAccessorFlag && (!owner.isTrait || hasFlag(PRESUPER | LAZY))) accessed + else this /** For an outer accessor: The class from which the outer originates. * For all other symbols: NoSymbol @@ -2532,30 +2546,34 @@ trait Symbols extends api.Symbols { self: SymbolTable => private def symbolKind: SymbolKind = { var kind = - if (isTermMacro) ("term macro", "macro method", "MACM") - else if (isInstanceOf[FreeTermSymbol]) ("free term", "free term", "FTE") - else if (isInstanceOf[FreeTypeSymbol]) ("free type", "free type", "FTY") - else if (isPackageClass) ("package class", "package", "PKC") - else if (hasPackageFlag) ("package", "package", "PK") - else if (isPackageObject) ("package object", "package", "PKO") - else if (isPackageObjectClass) ("package object class", "package", "PKOC") - else if (isAnonymousClass) ("anonymous class", "anonymous class", "AC") - else if (isRefinementClass) ("refinement class", "", "RC") - else if (isModule) ("module", "object", "MOD") - else if (isModuleClass) ("module class", "object", "MODC") - else if (isGetter) ("getter", if (isSourceMethod) "method" else "value", "GET") - else if (isSetter) ("setter", if (isSourceMethod) "method" else "value", "SET") - else if (isTerm && isLazy) ("lazy value", "lazy value", "LAZ") - else if (isVariable) ("field", "variable", "VAR") - else if (isTrait) ("trait", "trait", "TRT") - else if (isClass) ("class", "class", "CLS") - else if (isType) ("type", "type", "TPE") - else if (isClassConstructor && (owner.hasCompleteInfo && isPrimaryConstructor)) ("primary constructor", "constructor", "PCTOR") - else if (isClassConstructor) ("constructor", "constructor", "CTOR") - else if (isSourceMethod) ("method", "method", "METH") - else if (isTerm) ("value", "value", "VAL") - else ("", "", "???") + if (isTermMacro) ("term macro", "macro method", "MACM") + else if (isInstanceOf[FreeTermSymbol]) ("free term", "free term", "FTE") + else if (isInstanceOf[FreeTypeSymbol]) ("free type", "free type", "FTY") + else if (isPackageClass) ("package class", "package", "PKC") + else if (hasPackageFlag) ("package", "package", "PK") + else if (isPackageObject) ("package object", "package", "PKO") + else if (isPackageObjectClass) ("package object class", "package", "PKOC") + else if (isAnonymousClass) ("anonymous class", "anonymous class", "AC") + else if (isRefinementClass) ("refinement class", "", "RC") + else if (isModule) ("module", "object", "MOD") + else if (isModuleClass) ("module class", "object", "MODC") + else if (isAccessor && + !hasFlag(STABLE | LAZY)) ("setter", "variable", "SET") + else if (isAccessor && !hasFlag(LAZY)) ("getter", "value", "GET") + else if (isTerm && hasFlag(LAZY)) ("lazy value", "lazy value", "LAZ") + else if (isVariable) ("field", "variable", "VAR") + else if (isTrait) ("trait", "trait", "TRT") + else if (isClass) ("class", "class", "CLS") + else if (isType) ("type", "type", "TPE") + else if (isClassConstructor && (owner.hasCompleteInfo && + isPrimaryConstructor)) ("primary constructor", "constructor", "PCTOR") + else if (isClassConstructor) ("constructor", "constructor", "CTOR") + else if (isMethod) ("method", "method", "METH") + else if (isTerm) ("value", "value", "VAL") + else ("", "", "???") + if (isSkolem) kind = (kind._1, kind._2, kind._3 + "#SKO") + SymbolKind(kind._1, kind._2, kind._3) } @@ -2623,12 +2641,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. */ override def toString: String = { - if (isPackageObjectOrClass && !settings.debug) - s"package object ${owner.decodedName}" - else compose( - kindString, - if (hasMeaninglessName) owner.decodedName + idString else nameString - ) + val simplifyNames = !settings.debug + if (isPackageObjectOrClass && simplifyNames) s"package object ${owner.decodedName}" + else { + val kind = kindString + val _name: String = + if (hasMeaninglessName) owner.decodedName + idString + else if (simplifyNames && (kind == "variable" || kind == "value")) unexpandedName.getterName.decode.toString // TODO: make condition less gross? + else nameString + + compose(kind, _name) + } } /** String representation of location. @@ -2764,18 +2787,21 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) ***/ override def isValueParameter = this hasFlag PARAM - override def isSetterParameter = isValueParameter && owner.isSetter - override def isAccessor = this hasFlag ACCESSOR - override def isGetter = isAccessor && !isSetter + override def isDefaultGetter = name containsName nme.DEFAULT_GETTER_STRING - override def isSetter = isAccessor && nme.isSetterName(name) // todo: make independent of name, as this can be forged. + + override def isAccessor = this hasFlag ACCESSOR + override def isGetter = isAccessor && !nme.isSetterName(name) // TODO: make independent of name, as this can be forged. + override def isSetter = isAccessor && nme.isSetterName(name) // TODO: make independent of name, as this can be forged. + override def isLocalDummy = nme.isLocalDummyName(name) + override def isClassConstructor = name == nme.CONSTRUCTOR override def isMixinConstructor = name == nme.MIXIN_CONSTRUCTOR - override def isConstructor = nme.isConstructorName(name) + override def isConstructor = isClassConstructor || isMixinConstructor - override def isPackageObject = isModule && (name == nme.PACKAGE) + override def isPackageObject = isModule && (name == nme.PACKAGE) // The name in comments is what it is being disambiguated from. // TODO - rescue CAPTURED from BYNAMEPARAM so we can see all the names. @@ -2900,10 +2926,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isVarargsMethod = this hasFlag VARARGS override def isLiftedMethod = this hasFlag LIFTED - // TODO - this seems a strange definition for "isSourceMethod", given that - // it does not make any specific effort to exclude synthetics. Figure out what - // this method is really for and what logic makes sense. - override def isSourceMethod = !(this hasFlag STABLE) // exclude all accessors + // TODO: this definition of isSourceMethod makes no sense -- inline it and re-evaluate at each call site. + // I'm guessing it meant "method written by user, and not generated by the compiler" + // (And then assuming those generated by the compiler don't require certain transformations?) + // Use SYNTHETIC/ARTIFACT instead as an indicator? I don't see how it makes sense to only exclude getters. + // Note also that trait vals are modelled as getters, and thus that user-supplied code appears in their rhs. + // Originally, it may have been an optimization to skip methods that were not user-defined (getters), + // but it doesn't even exclude setters, contrary to its original comment (// exclude all accessors) + override def isSourceMethod = !(this hasFlag STABLE) + // unfortunately having the CASEACCESSOR flag does not actually mean you // are a case accessor (you can also be a field.) override def isCaseAccessorMethod = isCaseAccessor diff --git a/test/files/neg/overloaded-unapply.check b/test/files/neg/overloaded-unapply.check index 68a826bac2..3951166de5 100644 --- a/test/files/neg/overloaded-unapply.check +++ b/test/files/neg/overloaded-unapply.check @@ -7,8 +7,8 @@ match argument types (List[a]) overloaded-unapply.scala:22: error: cannot resolve overloaded unapply case List(x, xs) => 7 ^ -overloaded-unapply.scala:12: error: method unapply is defined twice - conflicting symbols both originated in file 'overloaded-unapply.scala' +overloaded-unapply.scala:12: error: method unapply is defined twice; + the conflicting method unapply was defined at line 7:7 def unapply[a](xs: List[a]): Option[Null] = xs match { ^ three errors found diff --git a/test/files/neg/t1960.check b/test/files/neg/t1960.check index 5238141c4e..de0907b4a9 100644 --- a/test/files/neg/t1960.check +++ b/test/files/neg/t1960.check @@ -1,4 +1,4 @@ -t1960.scala:5: error: parameter 'p' requires field but conflicts with method p in trait TBase +t1960.scala:5: error: parameter 'p' requires field but conflicts with variable p in trait TBase class Aclass (p: Int) extends TBase { def g() { f(p) } } ^ one error found diff --git a/test/files/neg/t200.check b/test/files/neg/t200.check index b6b1a32267..f0c5e77772 100644 --- a/test/files/neg/t200.check +++ b/test/files/neg/t200.check @@ -1,5 +1,5 @@ -t200.scala:7: error: method foo is defined twice - conflicting symbols both originated in file 't200.scala' +t200.scala:7: error: method foo is defined twice; + the conflicting method foo was defined at line 6:7 def foo: Int; ^ one error found diff --git a/test/files/neg/t2779.check b/test/files/neg/t2779.check index 0ab4c50d0f..9881d5182c 100644 --- a/test/files/neg/t2779.check +++ b/test/files/neg/t2779.check @@ -1,5 +1,5 @@ -t2779.scala:16: error: method f is defined twice - conflicting symbols both originated in file 't2779.scala' +t2779.scala:16: error: method f is defined twice; + the conflicting method f was defined at line 15:18 override def f = List(M1) ^ one error found diff --git a/test/files/neg/t278.check b/test/files/neg/t278.check index 405f7d225c..940b8edcef 100644 --- a/test/files/neg/t278.check +++ b/test/files/neg/t278.check @@ -4,8 +4,8 @@ t278.scala:5: error: overloaded method value a with alternatives: does not take type parameters println(a[A]) ^ -t278.scala:4: error: method a is defined twice - conflicting symbols both originated in file 't278.scala' +t278.scala:4: error: method a is defined twice; + the conflicting method a was defined at line 3:7 def a = (p:A) => () ^ two errors found diff --git a/test/files/neg/t3871.check b/test/files/neg/t3871.check index b920357ee6..c9667abfb6 100644 --- a/test/files/neg/t3871.check +++ b/test/files/neg/t3871.check @@ -1,5 +1,5 @@ t3871.scala:4: error: variable foo in class Sub2 cannot be accessed in Sub2 - Access to protected method foo not permitted because + Access to protected variable foo not permitted because enclosing class Base is not a subclass of class Sub2 where target is defined s.foo = true diff --git a/test/files/neg/t4541.check b/test/files/neg/t4541.check index 7bd8ff78f9..7ee0cc6414 100644 --- a/test/files/neg/t4541.check +++ b/test/files/neg/t4541.check @@ -1,5 +1,5 @@ t4541.scala:11: error: variable data in class Sparse cannot be accessed in Sparse[Int] - Access to protected method data not permitted because + Access to protected variable data not permitted because prefix type Sparse[Int] does not conform to class Sparse$mcI$sp where the access take place that.data diff --git a/test/files/neg/t4541b.check b/test/files/neg/t4541b.check index 8a52fd97f4..2aae95f6b9 100644 --- a/test/files/neg/t4541b.check +++ b/test/files/neg/t4541b.check @@ -1,5 +1,5 @@ t4541b.scala:13: error: variable data in class SparseArray cannot be accessed in SparseArray[Int] - Access to protected method data not permitted because + Access to protected variable data not permitted because prefix type SparseArray[Int] does not conform to class SparseArray$mcI$sp where the access take place use(that.data.clone) diff --git a/test/files/neg/t5429.check b/test/files/neg/t5429.check index 4350696bc8..fb2d9c2e47 100644 --- a/test/files/neg/t5429.check +++ b/test/files/neg/t5429.check @@ -134,7 +134,7 @@ t5429.scala:87: error: overriding value value in class A0 of type Any; lazy value value cannot override a concrete non-lazy value override lazy val value = 0 // fail (strict over lazy) ^ -t5429.scala:91: error: value oneArg overrides nothing. +t5429.scala:91: error: lazy value oneArg overrides nothing. Note: the super classes of class F0 contain the following, non final members named oneArg: def oneArg(x: String): Any override lazy val oneArg = 15 // fail diff --git a/test/files/neg/t591.check b/test/files/neg/t591.check index d33f6d7a2f..c0bade0814 100644 --- a/test/files/neg/t591.check +++ b/test/files/neg/t591.check @@ -1,5 +1,5 @@ -t591.scala:38: error: method input_= is defined twice - conflicting symbols both originated in file 't591.scala' +t591.scala:40: error: method input_= is defined twice; + the conflicting variable input was defined at line 35:18 def input_=(in : Input) = {} ^ one error found diff --git a/test/files/neg/t591.scala b/test/files/neg/t591.scala index 0f0b02395c..14fb256a69 100644 --- a/test/files/neg/t591.scala +++ b/test/files/neg/t591.scala @@ -35,7 +35,8 @@ trait BaseFlow extends BaseList { private var input : Input = _; private var output : Output = _; + // the error message is a bit confusing, as it points here, + // but the symbol it reports is `input`'s actual setter (the one we synthesized) def input_=(in : Input) = {} - } } diff --git a/test/files/neg/t6335.check b/test/files/neg/t6335.check index 1727a05eb2..d118440f75 100644 --- a/test/files/neg/t6335.check +++ b/test/files/neg/t6335.check @@ -1,9 +1,9 @@ -t6335.scala:6: error: method Z is defined twice - conflicting symbols both originated in file 't6335.scala' +t6335.scala:6: error: method Z is defined twice; + the conflicting method Z was defined at line 5:7 implicit class Z[A](val i: A) { def zz = i } ^ -t6335.scala:3: error: method X is defined twice - conflicting symbols both originated in file 't6335.scala' +t6335.scala:3: error: method X is defined twice; + the conflicting method X was defined at line 2:7 implicit class X(val x: Int) { def xx = x } ^ two errors found diff --git a/test/files/neg/t6446-additional.check b/test/files/neg/t6446-additional.check index e56a67b28b..45db63317c 100644 --- a/test/files/neg/t6446-additional.check +++ b/test/files/neg/t6446-additional.check @@ -10,18 +10,19 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - ploogin 24 A sample phase that does so many things it's kind of hard... - terminal 25 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + ploogin 25 A sample phase that does so many things it's kind of hard... + terminal 26 the last phase during a compilation run diff --git a/test/files/neg/t6446-missing.check b/test/files/neg/t6446-missing.check index 15f0ceb6e3..04523d18e6 100644 --- a/test/files/neg/t6446-missing.check +++ b/test/files/neg/t6446-missing.check @@ -11,17 +11,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check index 280a4f43d5..03f8273c17 100644 --- a/test/files/neg/t6446-show-phases.check +++ b/test/files/neg/t6446-show-phases.check @@ -10,17 +10,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run diff --git a/test/files/neg/t6666.check b/test/files/neg/t6666.check index 43c8252753..090ef72770 100644 --- a/test/files/neg/t6666.check +++ b/test/files/neg/t6666.check @@ -1,7 +1,7 @@ t6666.scala:23: error: Implementation restriction: access of method x$2 in object O1 from <$anon: Function0>, would require illegal premature access to object O1 F.byname(x) ^ -t6666.scala:30: error: Implementation restriction: access of value x$3 in object O2 from <$anon: Function0>, would require illegal premature access to object O2 +t6666.scala:30: error: Implementation restriction: access of method x$3 in object O2 from <$anon: Function0>, would require illegal premature access to object O2 F.byname(x) ^ t6666.scala:37: error: Implementation restriction: access of method x$4 in object O3 from <$anon: Function0>, would require illegal premature access to object O3 @@ -10,7 +10,7 @@ t6666.scala:37: error: Implementation restriction: access of method x$4 in objec t6666.scala:50: error: Implementation restriction: access of method x$6 in class C1 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C1 F.byname(x) ^ -t6666.scala:54: error: Implementation restriction: access of value x$7 in class C2 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C2 +t6666.scala:54: error: Implementation restriction: access of method x$7 in class C2 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C2 F.byname(x) ^ t6666.scala:58: error: Implementation restriction: access of method x$8 in class C3 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C3 diff --git a/test/files/neg/t7494-no-options.check b/test/files/neg/t7494-no-options.check index a4c4a1ad5b..bb143e8644 100644 --- a/test/files/neg/t7494-no-options.check +++ b/test/files/neg/t7494-no-options.check @@ -11,18 +11,19 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - ploogin 24 A sample phase that does so many things it's kind of hard... - terminal 25 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + ploogin 25 A sample phase that does so many things it's kind of hard... + terminal 26 the last phase during a compilation run diff --git a/test/files/neg/t7602.check b/test/files/neg/t7602.check index 5bb1450d7d..5ce3776790 100644 --- a/test/files/neg/t7602.check +++ b/test/files/neg/t7602.check @@ -1,5 +1,5 @@ -t7602.scala:16: error: method foo is defined twice - conflicting symbols both originated in file 't7602.scala' +t7602.scala:16: error: method foo is defined twice; + the conflicting method foo was defined at line 15:7 def foo : Device ^ one error found diff --git a/test/files/neg/t7622-cyclic-dependency.check b/test/files/neg/t7622-cyclic-dependency.check index 3546964f5f..81e3ecc6a4 100644 --- a/test/files/neg/t7622-cyclic-dependency.check +++ b/test/files/neg/t7622-cyclic-dependency.check @@ -1 +1 @@ -error: Cycle in phase dependencies detected at cyclicdependency1, created phase-cycle.dot +error: Cycle in phase dependencies detected at cyclicdependency2, created phase-cycle.dot diff --git a/test/files/neg/t800.check b/test/files/neg/t800.check index 8ba95fddde..238b8dd27d 100644 --- a/test/files/neg/t800.check +++ b/test/files/neg/t800.check @@ -1,16 +1,16 @@ t800.scala:4: error: qualification is already defined as value qualification val qualification = false; ^ -t800.scala:8: error: method qualification is defined twice - conflicting symbols both originated in file 't800.scala' +t800.scala:8: error: value qualification is defined twice; + the conflicting variable qualification was defined at line 7:7 val qualification = false; ^ -t800.scala:12: error: value qualification is defined twice - conflicting symbols both originated in file 't800.scala' +t800.scala:12: error: variable qualification is defined twice; + the conflicting value qualification was defined at line 11:7 var qualification = false; ^ -t800.scala:16: error: method qualification is defined twice - conflicting symbols both originated in file 't800.scala' +t800.scala:16: error: variable qualification is defined twice; + the conflicting variable qualification was defined at line 15:7 var qualification = false; ^ four errors found diff --git a/test/files/neg/t8849.check b/test/files/neg/t8849.check index 15b00aee8b..1d5b4164b2 100644 --- a/test/files/neg/t8849.check +++ b/test/files/neg/t8849.check @@ -1,5 +1,5 @@ t8849.scala:8: error: ambiguous implicit values: - both value global in object Implicits of type => scala.concurrent.ExecutionContext + both lazy value global in object Implicits of type => scala.concurrent.ExecutionContext and value dummy of type scala.concurrent.ExecutionContext match expected type scala.concurrent.ExecutionContext require(implicitly[ExecutionContext] eq dummy) diff --git a/test/files/neg/trait_fields_conflicts.check b/test/files/neg/trait_fields_conflicts.check new file mode 100644 index 0000000000..696d0284c1 --- /dev/null +++ b/test/files/neg/trait_fields_conflicts.check @@ -0,0 +1,273 @@ +trait_fields_conflicts.scala:5: error: overriding value x in trait Val of type Int; + value x needs `override' modifier +trait ValForVal extends Val { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:6: error: overriding value x in trait Val of type Int; + variable x needs `override' modifier +trait VarForVal extends Val { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:7: error: overriding value x in trait Val of type Int; + method x needs `override' modifier +trait DefForVal extends Val { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:8: error: overriding variable x in trait Var of type Int; + value x needs `override' modifier +trait ValForVar extends Var { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:9: error: overriding variable x in trait Var of type Int; + variable x needs `override' modifier +trait VarForVar extends Var { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:10: error: overriding variable x in trait Var of type Int; + method x needs `override' modifier +trait DefForVar extends Var { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:11: error: overriding lazy value x in trait Lazy of type Int; + value x needs `override' modifier +trait ValForLazy extends Lazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:12: error: overriding lazy value x in trait Lazy of type Int; + variable x needs `override' modifier +trait VarForLazy extends Lazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:13: error: overriding lazy value x in trait Lazy of type Int; + method x needs `override' modifier +trait DefForLazy extends Lazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:16: error: overriding value x in trait Val of type Int; + variable x needs to be a stable, immutable value +trait VarForValOvr extends Val { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:17: error: overriding value x in trait Val of type Int; + method x needs to be a stable, immutable value +trait DefForValOvr extends Val { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:18: error: overriding variable x in trait Var of type Int; + value x cannot override a mutable variable +trait ValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:19: error: overriding variable x in trait Var of type Int; + variable x cannot override a mutable variable +trait VarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:20: error: overriding variable x in trait Var of type Int; + method x cannot override a mutable variable +trait DefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:21: error: overriding lazy value x in trait Lazy of type Int; + value x must be declared lazy to override a concrete lazy value +trait ValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:22: error: overriding lazy value x in trait Lazy of type Int; + variable x needs to be a stable, immutable value +trait VarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:23: error: overriding lazy value x in trait Lazy of type Int; + method x needs to be a stable, immutable value +trait DefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:25: error: overriding value x in trait Val of type Int; + value x needs `override' modifier +class CValForVal extends Val { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:26: error: overriding value x in trait Val of type Int; + variable x needs `override' modifier +class CVarForVal extends Val { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:27: error: overriding value x in trait Val of type Int; + method x needs `override' modifier +class CDefForVal extends Val { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:28: error: overriding variable x in trait Var of type Int; + value x needs `override' modifier +class CValForVar extends Var { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:29: error: overriding variable x in trait Var of type Int; + variable x needs `override' modifier +class CVarForVar extends Var { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:30: error: overriding variable x in trait Var of type Int; + method x needs `override' modifier +class CDefForVar extends Var { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:31: error: overriding lazy value x in trait Lazy of type Int; + value x needs `override' modifier +class CValForLazy extends Lazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:32: error: overriding lazy value x in trait Lazy of type Int; + variable x needs `override' modifier +class CVarForLazy extends Lazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:33: error: overriding lazy value x in trait Lazy of type Int; + method x needs `override' modifier +class CDefForLazy extends Lazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:36: error: overriding value x in trait Val of type Int; + variable x needs to be a stable, immutable value +class CVarForValOvr extends Val { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:37: error: overriding value x in trait Val of type Int; + method x needs to be a stable, immutable value +class CDefForValOvr extends Val { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:38: error: overriding variable x in trait Var of type Int; + value x cannot override a mutable variable +class CValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:39: error: overriding variable x in trait Var of type Int; + variable x cannot override a mutable variable +class CVarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:40: error: overriding variable x in trait Var of type Int; + method x cannot override a mutable variable +class CDefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:41: error: overriding lazy value x in trait Lazy of type Int; + value x must be declared lazy to override a concrete lazy value +class CValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:42: error: overriding lazy value x in trait Lazy of type Int; + variable x needs to be a stable, immutable value +class CVarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:43: error: overriding lazy value x in trait Lazy of type Int; + method x needs to be a stable, immutable value +class CDefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:49: error: overriding value x in class CVal of type Int; + value x needs `override' modifier +trait ValForCVal extends CVal { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:50: error: overriding value x in class CVal of type Int; + variable x needs `override' modifier +trait VarForCVal extends CVal { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:51: error: overriding value x in class CVal of type Int; + method x needs `override' modifier +trait DefForCVal extends CVal { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:52: error: overriding variable x in class CVar of type Int; + value x needs `override' modifier +trait ValForCVar extends CVar { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:53: error: overriding variable x in class CVar of type Int; + variable x needs `override' modifier +trait VarForCVar extends CVar { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:54: error: overriding variable x in class CVar of type Int; + method x needs `override' modifier +trait DefForCVar extends CVar { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:55: error: overriding lazy value x in class CLazy of type Int; + value x needs `override' modifier +trait ValForCLazy extends CLazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:56: error: overriding lazy value x in class CLazy of type Int; + variable x needs `override' modifier +trait VarForCLazy extends CLazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:57: error: overriding lazy value x in class CLazy of type Int; + method x needs `override' modifier +trait DefForCLazy extends CLazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:60: error: overriding value x in class CVal of type Int; + variable x needs to be a stable, immutable value +trait VarForCValOvr extends CVal { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:61: error: overriding value x in class CVal of type Int; + method x needs to be a stable, immutable value +trait DefForCValOvr extends CVal { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:62: error: overriding variable x in class CVar of type Int; + value x cannot override a mutable variable +trait ValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:63: error: overriding variable x in class CVar of type Int; + variable x cannot override a mutable variable +trait VarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:64: error: overriding variable x in class CVar of type Int; + method x cannot override a mutable variable +trait DefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:65: error: overriding lazy value x in class CLazy of type Int; + value x must be declared lazy to override a concrete lazy value +trait ValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:66: error: overriding lazy value x in class CLazy of type Int; + variable x needs to be a stable, immutable value +trait VarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:67: error: overriding lazy value x in class CLazy of type Int; + method x needs to be a stable, immutable value +trait DefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:69: error: overriding value x in class CVal of type Int; + value x needs `override' modifier +class CValForCVal extends CVal { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:70: error: overriding value x in class CVal of type Int; + variable x needs `override' modifier +class CVarForCVal extends CVal { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:71: error: overriding value x in class CVal of type Int; + method x needs `override' modifier +class CDefForCVal extends CVal { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:72: error: overriding variable x in class CVar of type Int; + value x needs `override' modifier +class CValForCVar extends CVar { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:73: error: overriding variable x in class CVar of type Int; + variable x needs `override' modifier +class CVarForCVar extends CVar { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:74: error: overriding variable x in class CVar of type Int; + method x needs `override' modifier +class CDefForCVar extends CVar { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:75: error: overriding lazy value x in class CLazy of type Int; + value x needs `override' modifier +class CValForCLazy extends CLazy { val x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:76: error: overriding lazy value x in class CLazy of type Int; + variable x needs `override' modifier +class CVarForCLazy extends CLazy { var x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:77: error: overriding lazy value x in class CLazy of type Int; + method x needs `override' modifier +class CDefForCLazy extends CLazy { def x: Int = 1 } // needs override + ^ +trait_fields_conflicts.scala:80: error: overriding value x in class CVal of type Int; + variable x needs to be a stable, immutable value +class CVarForCValOvr extends CVal { override var x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:81: error: overriding value x in class CVal of type Int; + method x needs to be a stable, immutable value +class CDefForCValOvr extends CVal { override def x: Int = 1 } // bad override + ^ +trait_fields_conflicts.scala:82: error: overriding variable x in class CVar of type Int; + value x cannot override a mutable variable +class CValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes + ^ +trait_fields_conflicts.scala:83: error: overriding variable x in class CVar of type Int; + variable x cannot override a mutable variable +class CVarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:84: error: overriding variable x in class CVar of type Int; + method x cannot override a mutable variable +class CDefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:85: error: overriding lazy value x in class CLazy of type Int; + value x must be declared lazy to override a concrete lazy value +class CValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:86: error: overriding lazy value x in class CLazy of type Int; + variable x needs to be a stable, immutable value +class CVarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? + ^ +trait_fields_conflicts.scala:87: error: overriding lazy value x in class CLazy of type Int; + method x needs to be a stable, immutable value +class CDefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? + ^ +68 errors found diff --git a/test/files/neg/trait_fields_conflicts.scala b/test/files/neg/trait_fields_conflicts.scala new file mode 100644 index 0000000000..92fc106e44 --- /dev/null +++ b/test/files/neg/trait_fields_conflicts.scala @@ -0,0 +1,87 @@ +trait Val { val x: Int = 123 } +trait Var { var x: Int = 123 } +trait Lazy { lazy val x: Int = 123 } + +trait ValForVal extends Val { val x: Int = 1 } // needs override +trait VarForVal extends Val { var x: Int = 1 } // needs override +trait DefForVal extends Val { def x: Int = 1 } // needs override +trait ValForVar extends Var { val x: Int = 1 } // needs override +trait VarForVar extends Var { var x: Int = 1 } // needs override +trait DefForVar extends Var { def x: Int = 1 } // needs override +trait ValForLazy extends Lazy { val x: Int = 1 } // needs override +trait VarForLazy extends Lazy { var x: Int = 1 } // needs override +trait DefForLazy extends Lazy { def x: Int = 1 } // needs override + +trait ValForValOvr extends Val { override val x: Int = 1 } // override ok +trait VarForValOvr extends Val { override var x: Int = 1 } // bad override +trait DefForValOvr extends Val { override def x: Int = 1 } // bad override +trait ValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +trait VarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? +trait DefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? +trait ValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? +trait VarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? +trait DefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + +class CValForVal extends Val { val x: Int = 1 } // needs override +class CVarForVal extends Val { var x: Int = 1 } // needs override +class CDefForVal extends Val { def x: Int = 1 } // needs override +class CValForVar extends Var { val x: Int = 1 } // needs override +class CVarForVar extends Var { var x: Int = 1 } // needs override +class CDefForVar extends Var { def x: Int = 1 } // needs override +class CValForLazy extends Lazy { val x: Int = 1 } // needs override +class CVarForLazy extends Lazy { var x: Int = 1 } // needs override +class CDefForLazy extends Lazy { def x: Int = 1 } // needs override + +class CValForValOvr extends Val { override val x: Int = 1 } // override ok +class CVarForValOvr extends Val { override var x: Int = 1 } // bad override +class CDefForValOvr extends Val { override def x: Int = 1 } // bad override +class CValForVarOvr extends Var { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +class CVarForVarOvr extends Var { override var x: Int = 1 } // bad override -- why? +class CDefForVarOvr extends Var { override def x: Int = 1 } // bad override -- why? +class CValForLazyOvr extends Lazy { override val x: Int = 1 } // bad override -- why? +class CVarForLazyOvr extends Lazy { override var x: Int = 1 } // bad override -- why? +class CDefForLazyOvr extends Lazy { override def x: Int = 1 } // bad override -- why? + +class CVal { val x: Int = 123 } +class CVar { var x: Int = 123 } +class CLazy { lazy val x: Int = 123 } + +trait ValForCVal extends CVal { val x: Int = 1 } // needs override +trait VarForCVal extends CVal { var x: Int = 1 } // needs override +trait DefForCVal extends CVal { def x: Int = 1 } // needs override +trait ValForCVar extends CVar { val x: Int = 1 } // needs override +trait VarForCVar extends CVar { var x: Int = 1 } // needs override +trait DefForCVar extends CVar { def x: Int = 1 } // needs override +trait ValForCLazy extends CLazy { val x: Int = 1 } // needs override +trait VarForCLazy extends CLazy { var x: Int = 1 } // needs override +trait DefForCLazy extends CLazy { def x: Int = 1 } // needs override + +trait ValForCValOvr extends CVal { override val x: Int = 1 } // override ok +trait VarForCValOvr extends CVal { override var x: Int = 1 } // bad override +trait DefForCValOvr extends CVal { override def x: Int = 1 } // bad override +trait ValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +trait VarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? +trait DefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? +trait ValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? +trait VarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? +trait DefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? + +class CValForCVal extends CVal { val x: Int = 1 } // needs override +class CVarForCVal extends CVal { var x: Int = 1 } // needs override +class CDefForCVal extends CVal { def x: Int = 1 } // needs override +class CValForCVar extends CVar { val x: Int = 1 } // needs override +class CVarForCVar extends CVar { var x: Int = 1 } // needs override +class CDefForCVar extends CVar { def x: Int = 1 } // needs override +class CValForCLazy extends CLazy { val x: Int = 1 } // needs override +class CVarForCLazy extends CLazy { var x: Int = 1 } // needs override +class CDefForCLazy extends CLazy { def x: Int = 1 } // needs override + +class CValForCValOvr extends CVal { override val x: Int = 1 } // override ok +class CVarForCValOvr extends CVal { override var x: Int = 1 } // bad override +class CDefForCValOvr extends CVal { override def x: Int = 1 } // bad override +class CValForCVarOvr extends CVar { override val x: Int = 1 } // bad override -- unsound if used in path and var changes +class CVarForCVarOvr extends CVar { override var x: Int = 1 } // bad override -- why? +class CDefForCVarOvr extends CVar { override def x: Int = 1 } // bad override -- why? +class CValForCLazyOvr extends CLazy { override val x: Int = 1 } // bad override -- why? +class CVarForCLazyOvr extends CLazy { override var x: Int = 1 } // bad override -- why? +class CDefForCLazyOvr extends CLazy { override def x: Int = 1 } // bad override -- why? diff --git a/test/files/neg/trait_fields_deprecated_overriding.check b/test/files/neg/trait_fields_deprecated_overriding.check new file mode 100644 index 0000000000..89dfa5c295 --- /dev/null +++ b/test/files/neg/trait_fields_deprecated_overriding.check @@ -0,0 +1,6 @@ +trait_fields_deprecated_overriding.scala:8: warning: overriding value x in trait DeprecatedOverriding is deprecated + override val x = 2 + ^ +error: No warnings can be incurred under -Xfatal-warnings. +one warning found +one error found diff --git a/test/files/neg/trait_fields_deprecated_overriding.flags b/test/files/neg/trait_fields_deprecated_overriding.flags new file mode 100644 index 0000000000..c6bfaf1f64 --- /dev/null +++ b/test/files/neg/trait_fields_deprecated_overriding.flags @@ -0,0 +1 @@ +-deprecation -Xfatal-warnings diff --git a/test/files/neg/trait_fields_deprecated_overriding.scala b/test/files/neg/trait_fields_deprecated_overriding.scala new file mode 100644 index 0000000000..e7d722c92f --- /dev/null +++ b/test/files/neg/trait_fields_deprecated_overriding.scala @@ -0,0 +1,11 @@ +package scala + +trait DeprecatedOverriding { + @deprecatedOverriding val x = 1 +} + +class COverride extends DeprecatedOverriding { + override val x = 2 +} + +class CSynthImpl extends DeprecatedOverriding
\ No newline at end of file diff --git a/test/files/neg/val_infer.check b/test/files/neg/val_infer.check new file mode 100644 index 0000000000..711450add9 --- /dev/null +++ b/test/files/neg/val_infer.check @@ -0,0 +1,6 @@ +val_infer.scala:3: error: type mismatch; + found : String("") + required: Int + trait Sub extends Base { def foo = "" } + ^ +one error found diff --git a/test/files/neg/val_infer.scala b/test/files/neg/val_infer.scala new file mode 100644 index 0000000000..7fe8393749 --- /dev/null +++ b/test/files/neg/val_infer.scala @@ -0,0 +1,4 @@ +class Test { + trait Base { def foo: Int } + trait Sub extends Base { def foo = "" } +}
\ No newline at end of file diff --git a/test/files/neg/val_sig_infer_match.check b/test/files/neg/val_sig_infer_match.check new file mode 100644 index 0000000000..704c99cf84 --- /dev/null +++ b/test/files/neg/val_sig_infer_match.check @@ -0,0 +1,4 @@ +val_sig_infer_match.scala:21: error: value y is not a member of A + def m = f.y // doesn't compile anymore + ^ +one error found diff --git a/test/files/neg/val_sig_infer_match.scala b/test/files/neg/val_sig_infer_match.scala new file mode 100644 index 0000000000..fb8aa66d56 --- /dev/null +++ b/test/files/neg/val_sig_infer_match.scala @@ -0,0 +1,22 @@ +class A + +class B extends A { + def y: Int = 0 +} + +class B1 extends B +class B2 extends B + +class C { + def f: A = null +} + +class D extends C { + def s = "" + override final val f = s match { + case "" => new B1 + case _ => new B2 + } + + def m = f.y // doesn't compile anymore +}
\ No newline at end of file diff --git a/test/files/neg/val_sig_infer_struct.check b/test/files/neg/val_sig_infer_struct.check new file mode 100644 index 0000000000..26efbbc3f4 --- /dev/null +++ b/test/files/neg/val_sig_infer_struct.check @@ -0,0 +1,4 @@ +val_sig_infer_struct.scala:7: error: value foo is not a member of Object + def bar = f.foo + ^ +one error found diff --git a/test/files/neg/val_sig_infer_struct.scala b/test/files/neg/val_sig_infer_struct.scala new file mode 100644 index 0000000000..e88340337c --- /dev/null +++ b/test/files/neg/val_sig_infer_struct.scala @@ -0,0 +1,8 @@ +class C { + def f: Object = this +} + +class D extends C { + override val f = new Object { def foo = 1 } + def bar = f.foo +}
\ No newline at end of file diff --git a/test/files/neg/warn-unused-privates.check b/test/files/neg/warn-unused-privates.check index 4876ed8fc2..2e93f338bb 100644 --- a/test/files/neg/warn-unused-privates.check +++ b/test/files/neg/warn-unused-privates.check @@ -19,10 +19,7 @@ warn-unused-privates.scala:36: warning: private val in class Boppy is never used warn-unused-privates.scala:43: warning: private var in trait Accessors is never used private var v1: Int = 0 // warn ^ -warn-unused-privates.scala:43: warning: private setter in trait Accessors is never used - private var v1: Int = 0 // warn - ^ -warn-unused-privates.scala:44: warning: private setter in trait Accessors is never used +warn-unused-privates.scala:44: warning: private var in trait Accessors is never used private var v2: Int = 0 // warn, never set ^ warn-unused-privates.scala:45: warning: private var in trait Accessors is never used @@ -65,5 +62,5 @@ warn-unused-privates.scala:103: warning: local type OtherThing is never used type OtherThing = String // warn ^ error: No warnings can be incurred under -Xfatal-warnings. -22 warnings found +21 warnings found one error found diff --git a/test/files/presentation/t4287c.flags b/test/files/pos/infer_override_def_args.flags index d1a8244169..d1a8244169 100644 --- a/test/files/presentation/t4287c.flags +++ b/test/files/pos/infer_override_def_args.flags diff --git a/test/files/pos/infer_override_def_args.scala b/test/files/pos/infer_override_def_args.scala new file mode 100644 index 0000000000..ac10720c81 --- /dev/null +++ b/test/files/pos/infer_override_def_args.scala @@ -0,0 +1,5 @@ +abstract class A { def foo(a: Int): A } +class B extends A { + implicit def spackle(x: Int): A = new B + def foo(a) = a +}
\ No newline at end of file diff --git a/test/files/pos/trait_fields_dependent_conflict.scala b/test/files/pos/trait_fields_dependent_conflict.scala new file mode 100644 index 0000000000..afb6f4b0c5 --- /dev/null +++ b/test/files/pos/trait_fields_dependent_conflict.scala @@ -0,0 +1,20 @@ +// derived from test/files/pos/S5.scala + +// compile with -uniqid to see a hint of the trouble +trait N { + // the symbol for self does not get rebound when synthesizing members in C + val self: N = ??? + val n: self.type = self +} + +abstract class M { + val self: N + val n: self.type +} + +abstract class MConflict extends N { + val self: N + val n: self.type +} + +class C extends M with N diff --git a/test/files/pos/trait_fields_dependent_rebind.scala b/test/files/pos/trait_fields_dependent_rebind.scala new file mode 100644 index 0000000000..e2cf4c43c3 --- /dev/null +++ b/test/files/pos/trait_fields_dependent_rebind.scala @@ -0,0 +1,15 @@ +// derived from test/files/pos/S5.scala + +// compile with -uniqid to see a hint of the trouble +trait N { + // the symbol for self does not get rebound when synthesizing members in C + val self: N = ??? + val n: self.type = self +} + +abstract class M { + val self: N + val n: self.type +} + +class C extends M with N diff --git a/test/files/pos/trait_fields_inherit_double_def.scala b/test/files/pos/trait_fields_inherit_double_def.scala new file mode 100644 index 0000000000..8703d6312c --- /dev/null +++ b/test/files/pos/trait_fields_inherit_double_def.scala @@ -0,0 +1,20 @@ +// done +// test/files/trait-defaults/fields.scala:24: error: double definition: +// def signalDelegate_=(x$1: Signalling): Unit at line 24 and +// def signalDelegate_=(x$1: Signalling): Unit at line 24 +// have same type +// class SUB extends IterableSplitter +// ^ +// one error found + +trait Signalling + +trait DelegatedSignalling extends Signalling { + var signalDelegate: Signalling +} + +trait IterableSplitter extends DelegatedSignalling { + var signalDelegate: Signalling = ??? +} + +class SUB extends IterableSplitter
\ No newline at end of file diff --git a/test/files/pos/trait_fields_lambdalift.scala b/test/files/pos/trait_fields_lambdalift.scala new file mode 100644 index 0000000000..62304a5268 --- /dev/null +++ b/test/files/pos/trait_fields_lambdalift.scala @@ -0,0 +1,22 @@ +class Lift { + def foo = { + // this will be captured by the MouseHandler trait, + // which gives rise to a new trait field during LambdaLift + var Clicked = "Clicked" + + def bar = Clicked + + trait MouseHandler { + def mouseClicked = Clicked + bar + } + + class CC extends MouseHandler + + // new C {} + (new CC).mouseClicked + } +} + +object O extends Lift with App { + println(foo) +} diff --git a/test/files/pos/trait_fields_nested_private_object.scala b/test/files/pos/trait_fields_nested_private_object.scala new file mode 100644 index 0000000000..8efc1cb3fa --- /dev/null +++ b/test/files/pos/trait_fields_nested_private_object.scala @@ -0,0 +1,8 @@ +trait NestedObj { + private object O { println("NO") } +} + + +class C extends NestedObj { + def O = ??? +}
\ No newline at end of file diff --git a/test/files/pos/trait_fields_nested_public_object.scala b/test/files/pos/trait_fields_nested_public_object.scala new file mode 100644 index 0000000000..016487fb8a --- /dev/null +++ b/test/files/pos/trait_fields_nested_public_object.scala @@ -0,0 +1,5 @@ +trait NestedObj { + object O { println("NO") } +} + +class C extends NestedObj
\ No newline at end of file diff --git a/test/files/pos/trait_fields_owners.scala b/test/files/pos/trait_fields_owners.scala new file mode 100644 index 0000000000..6aa5572171 --- /dev/null +++ b/test/files/pos/trait_fields_owners.scala @@ -0,0 +1,19 @@ +trait V { + // ok + // error: java.lang.IllegalArgumentException: Could not find proxy for val f: Function1 in List(value f, value v, trait V, package <empty>, package <root>) (currentOwner= value <local V$class> ) + val v = { val f = (x: Int) => x + 1; f(2) } + + // ok + // assertion failed: + // Trying to access the this of another class: tree.symbol = trait V, class symbol = object V$class compilation unit: fields.scala + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + def scalaPropOrNone(name: String): Option[String] = ??? +} + +object O extends V
\ No newline at end of file diff --git a/test/files/pos/trait_fields_private_this.scala b/test/files/pos/trait_fields_private_this.scala new file mode 100644 index 0000000000..8065cc89e6 --- /dev/null +++ b/test/files/pos/trait_fields_private_this.scala @@ -0,0 +1,5 @@ +trait Chars { + private[this] val char2uescapeArray: String = ??? +} + +object Chars extends Chars
\ No newline at end of file diff --git a/test/files/pos/trait_fields_static_fwd.scala b/test/files/pos/trait_fields_static_fwd.scala new file mode 100644 index 0000000000..af2cdad9ff --- /dev/null +++ b/test/files/pos/trait_fields_static_fwd.scala @@ -0,0 +1,10 @@ +trait T { + // Need to mark the synthesized member in the object's module class as notPROTECTED, + // since the trait member will receive this flag later. + // If we don't add notPROTECTED to the synthesized one, the member will not be seen as overriding the trait member. + // Therefore, addForwarders's call to membersBasedOnFlags would see the deferred member in the trait, + // instead of the concrete (desired) one in the class, and thus not create the static forwarder. + protected val propFilename: String = "/" +} + +object P extends T diff --git a/test/files/pos/val_infer.scala b/test/files/pos/val_infer.scala new file mode 100644 index 0000000000..5f82da8393 --- /dev/null +++ b/test/files/pos/val_infer.scala @@ -0,0 +1,5 @@ +class Test { + implicit def s2i(s: String): Int = s.length + trait Base { def foo: Int } + trait Sub extends Base { val foo = "" } +} diff --git a/test/files/presentation/doc/doc.scala b/test/files/presentation/doc/doc.scala index 8c60af557b..08c6ebf059 100644 --- a/test/files/presentation/doc/doc.scala +++ b/test/files/presentation/doc/doc.scala @@ -37,7 +37,7 @@ object Test extends InteractiveTest { prepre + docComment(nTags) + prepost + post } - override lazy val compiler = { + override lazy val compiler: Global { def getComment(sym: Symbol, source: SourceFile, fragments: List[(Symbol,SourceFile)]): Option[Comment] } = { prepareSettings(settings) new Global(settings, compilerReporter) with MemberLookupBase with CommentFactoryBase with doc.ScaladocGlobalTrait { outer => diff --git a/test/files/presentation/scope-completion-3.check b/test/files/presentation/scope-completion-3.check index b70a7d5c6b..f2510127fb 100644 --- a/test/files/presentation/scope-completion-3.check +++ b/test/files/presentation/scope-completion-3.check @@ -3,7 +3,7 @@ reload: Completions.scala askScopeCompletion at Completions.scala(75,2) ================================================================================ [response] askScopeCompletion at (75,2) -retrieved 37 members +retrieved 38 members abstract class Base1 extends AnyRef abstract trait Trait1 extends AnyRef class Cb1 extends AnyRef @@ -14,6 +14,8 @@ def <init>(): test.Completion1 def fb1: Int def fc1: Int def ft1: Int +def rt1: Int +def rt1_=(x$1: Int): Unit object Completion2 object Ob1 object Oc1 @@ -30,23 +32,22 @@ private[this] val vb1: Int private[this] val vb3: Int private[this] val vc1: Int private[this] val vc2: Int -private[this] val vt1: Int private[this] val vt3: Int private[this] var rb1: Int private[this] var rb3: Int private[this] var rc1: Int private[this] var rc2: Int -private[this] var rt1: Int private[this] var rt3: Int type tb1 = Completion1.this.tb1 type tc1 = Completion1.this.tc1 type tt1 = Completion1.this.tt1 +val vt1: Int ================================================================================ askScopeCompletion at Completions.scala(104,2) ================================================================================ [response] askScopeCompletion at (104,2) -retrieved 37 members +retrieved 38 members abstract class Base1 extends AnyRef abstract trait Trait1 extends AnyRef class Cb1 extends AnyRef @@ -57,6 +58,8 @@ def <init>(): test.Completion2.type def fb1: Int def fo1: Int def ft1: Int +def rt1: Int +def rt1_=(x$1: Int): Unit object Completion2 object Ob1 object Oo1 @@ -73,15 +76,14 @@ private[this] val vb1: Int private[this] val vb3: Int private[this] val vo1: Int private[this] val vo2: Int -private[this] val vt1: Int private[this] val vt3: Int private[this] var rb1: Int private[this] var rb3: Int private[this] var ro1: Int private[this] var ro2: Int -private[this] var rt1: Int private[this] var rt3: Int type tb1 = test.Completion2.tb1 type to1 = test.Completion2.to1 type tt1 = test.Completion2.tt1 +val vt1: Int ================================================================================ diff --git a/test/files/presentation/t4287c.check b/test/files/presentation/t4287c.check deleted file mode 100644 index 42fc30997d..0000000000 --- a/test/files/presentation/t4287c.check +++ /dev/null @@ -1,11 +0,0 @@ -reload: Foo.scala - -askHyperlinkPos for `A` at (1,18) Foo.scala -================================================================================ -[response] found askHyperlinkPos for `A` at (3,8) Foo.scala -================================================================================ - -askHyperlinkPos for `a` at (1,25) Foo.scala -================================================================================ -[response] found askHyperlinkPos for `a` at (4,7) Foo.scala -================================================================================ diff --git a/test/files/presentation/t4287c/Test.scala b/test/files/presentation/t4287c/Test.scala deleted file mode 100644 index bec1131c4c..0000000000 --- a/test/files/presentation/t4287c/Test.scala +++ /dev/null @@ -1,3 +0,0 @@ -import scala.tools.nsc.interactive.tests.InteractiveTest - -object Test extends InteractiveTest
\ No newline at end of file diff --git a/test/files/presentation/t4287c/src/Foo.scala b/test/files/presentation/t4287c/src/Foo.scala deleted file mode 100644 index 26870b5021..0000000000 --- a/test/files/presentation/t4287c/src/Foo.scala +++ /dev/null @@ -1,9 +0,0 @@ -class A(a: Int = A/*#*/.a/*#*/) - -object A { - val a = 2 -} - -class B extends A { - def this(a) = this() -}
\ No newline at end of file diff --git a/test/files/run/SymbolsTest.scala b/test/files/run/SymbolsTest.scala index d5948ea168..7c185b0e09 100644 --- a/test/files/run/SymbolsTest.scala +++ b/test/files/run/SymbolsTest.scala @@ -137,16 +137,16 @@ object Test { // } // val an2 = () => { // object nested { - // val m = 'mfsa + // val m = 'mfsa // } // nested.m // } // val an3 = () => { // object nested { - // val f = () => { - // 'layered - // } - // def gets = f() + // val f = () => { + // 'layered + // } + // def gets = f() // } // nested.gets // } @@ -204,8 +204,8 @@ object Test { val s1 = 's1 def s2 = 's2 object inner { - val s3 = 's3 - val s4 = 's4 + val s3 = 's3 + val s4 = 's4 } } @@ -223,8 +223,8 @@ object Test { val s5 = 's5 def s6 = 's6 object inner2 { - val s7 = 's7 - def s8 = 's8 + val s7 = 's7 + def s8 = 's8 } } assert(Local.s5 == 's5) diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index 1bb7c6ceab..ca0005ea4d 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -21,7 +21,6 @@ lub(List(Int @testAnn, Int)) [1] pluginsPt(?, Trees$Annotated) [7] pluginsPt(?, Trees$Apply) [11] pluginsPt(?, Trees$ApplyImplicitView) [2] -pluginsPt(?, Trees$Assign) [7] pluginsPt(?, Trees$Block) [4] pluginsPt(?, Trees$ClassDef) [2] pluginsPt(?, Trees$DefDef) [14] @@ -31,9 +30,9 @@ pluginsPt(?, Trees$Literal) [16] pluginsPt(?, Trees$New) [5] pluginsPt(?, Trees$PackageDef) [1] pluginsPt(?, Trees$Return) [1] -pluginsPt(?, Trees$Select) [50] +pluginsPt(?, Trees$Select) [43] pluginsPt(?, Trees$Super) [2] -pluginsPt(?, Trees$This) [20] +pluginsPt(?, Trees$This) [13] pluginsPt(?, Trees$TypeApply) [3] pluginsPt(?, Trees$TypeBoundsTree) [2] pluginsPt(?, Trees$TypeDef) [1] @@ -47,23 +46,19 @@ pluginsPt(Boolean @testAnn, Trees$Literal) [1] pluginsPt(Boolean @testAnn, Trees$Select) [1] pluginsPt(Boolean, Trees$Apply) [1] pluginsPt(Boolean, Trees$Ident) [1] -pluginsPt(Boolean, Trees$Literal) [1] pluginsPt(Double, Trees$Select) [1] pluginsPt(Int @testAnn, Trees$Literal) [1] pluginsPt(Int, Trees$Apply) [1] -pluginsPt(Int, Trees$Ident) [2] -pluginsPt(Int, Trees$If) [1] -pluginsPt(Int, Trees$Literal) [5] +pluginsPt(Int, Trees$Ident) [1] +pluginsPt(Int, Trees$Literal) [4] pluginsPt(Int, Trees$Select) [3] -pluginsPt(List, Trees$Apply) [1] pluginsPt(List[Any], Trees$Select) [1] pluginsPt(String @testAnn, Trees$Select) [1] pluginsPt(String, Trees$Apply) [1] pluginsPt(String, Trees$Block) [2] -pluginsPt(String, Trees$Ident) [4] +pluginsPt(String, Trees$Ident) [3] pluginsPt(String, Trees$Literal) [1] pluginsPt(String, Trees$Select) [1] -pluginsPt(String, Trees$Typed) [1] pluginsPt(Unit, Trees$Assign) [1] pluginsPt(testAnn, Trees$Apply) [5] pluginsTypeSig(<none>, Trees$Template) [2] @@ -119,7 +114,7 @@ pluginsTyped(=> Int, Trees$TypeApply) [1] pluginsTyped(=> String @testAnn, Trees$Select) [1] pluginsTyped(A, Trees$Apply) [1] pluginsTyped(A, Trees$Ident) [2] -pluginsTyped(A, Trees$This) [8] +pluginsTyped(A, Trees$This) [1] pluginsTyped(A, Trees$TypeTree) [4] pluginsTyped(A.super.type, Trees$Super) [1] pluginsTyped(A.this.type, Trees$This) [11] @@ -128,25 +123,23 @@ pluginsTyped(AnyRef, Trees$Select) [4] pluginsTyped(Array[Any], Trees$ArrayValue) [1] pluginsTyped(Boolean @testAnn, Trees$Select) [1] pluginsTyped(Boolean @testAnn, Trees$TypeTree) [4] -pluginsTyped(Boolean(false), Trees$Literal) [2] +pluginsTyped(Boolean(false), Trees$Literal) [1] pluginsTyped(Boolean, Trees$Apply) [1] -pluginsTyped(Boolean, Trees$Select) [4] +pluginsTyped(Boolean, Trees$Select) [3] pluginsTyped(Char('c'), Trees$Literal) [2] pluginsTyped(Double, Trees$Apply) [3] pluginsTyped(Double, Trees$Select) [6] pluginsTyped(Int @testAnn, Trees$TypeTree) [2] pluginsTyped(Int @testAnn, Trees$Typed) [2] -pluginsTyped(Int(0), Trees$Literal) [3] +pluginsTyped(Int(0), Trees$Literal) [2] pluginsTyped(Int(1) @testAnn, Trees$Typed) [1] pluginsTyped(Int(1), Trees$Literal) [8] pluginsTyped(Int(2), Trees$Literal) [1] pluginsTyped(Int, Trees$Apply) [1] -pluginsTyped(Int, Trees$Ident) [2] -pluginsTyped(Int, Trees$If) [2] -pluginsTyped(Int, Trees$Select) [15] +pluginsTyped(Int, Trees$Ident) [1] +pluginsTyped(Int, Trees$If) [1] +pluginsTyped(Int, Trees$Select) [12] pluginsTyped(Int, Trees$TypeTree) [13] -pluginsTyped(List, Trees$Apply) [1] -pluginsTyped(List, Trees$Select) [1] pluginsTyped(List[Any], Trees$Apply) [1] pluginsTyped(List[Any], Trees$Select) [1] pluginsTyped(List[Any], Trees$TypeTree) [3] @@ -159,15 +152,13 @@ pluginsTyped(String(""), Trees$Literal) [2] pluginsTyped(String("huhu"), Trees$Literal) [1] pluginsTyped(String("str") @testAnn, Trees$Typed) [1] pluginsTyped(String("str"), Trees$Literal) [1] -pluginsTyped(String("str"), Trees$Typed) [1] pluginsTyped(String("two"), Trees$Literal) [2] pluginsTyped(String, Trees$Apply) [2] pluginsTyped(String, Trees$Block) [2] -pluginsTyped(String, Trees$Ident) [1] -pluginsTyped(String, Trees$Select) [9] +pluginsTyped(String, Trees$Select) [7] pluginsTyped(String, Trees$TypeTree) [7] pluginsTyped(Unit, Trees$Apply) [2] -pluginsTyped(Unit, Trees$Assign) [8] +pluginsTyped(Unit, Trees$Assign) [1] pluginsTyped(Unit, Trees$Block) [4] pluginsTyped(Unit, Trees$If) [1] pluginsTyped(Unit, Trees$Literal) [5] diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index 280a4f43d5..03f8273c17 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -10,17 +10,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - tailcalls 11 replace tail calls by jumps - specialize 12 @specialized-driven class and method specialization - explicitouter 13 this refs to outer pointers - erasure 14 erase types, add interfaces for traits - posterasure 15 clean up erased inline classes - lazyvals 16 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 17 move nested functions to top level - constructors 18 move field definitions into constructors - flatten 19 eliminate inner classes - mixin 20 mixin composition - cleanup 21 platform-specific cleanups, generate reflective calls - delambdafy 22 remove lambdas - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + fields 11 synthesize accessors and fields + tailcalls 12 replace tail calls by jumps + specialize 13 @specialized-driven class and method specialization + explicitouter 14 this refs to outer pointers + erasure 15 erase types, add interfaces for traits + posterasure 16 clean up erased inline classes + lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 18 move nested functions to top level + constructors 19 move field definitions into constructors + flatten 20 eliminate inner classes + mixin 21 mixin composition + cleanup 22 platform-specific cleanups, generate reflective calls + delambdafy 23 remove lambdas + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run diff --git a/test/files/run/reflection-fieldsymbol-navigation.check b/test/files/run/reflection-fieldsymbol-navigation.check index ae0597a045..fd06c78a18 100644 --- a/test/files/run/reflection-fieldsymbol-navigation.check +++ b/test/files/run/reflection-fieldsymbol-navigation.check @@ -1,6 +1,6 @@ -method x +variable x false variable x true -method x -method x_= +variable x +variable x diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check index 21fbe34d96..1217e8d8c2 100644 --- a/test/files/run/repl-colon-type.check +++ b/test/files/run/repl-colon-type.check @@ -35,7 +35,7 @@ Int scala> :type protected lazy val f = 5 <console>:5: error: lazy value f cannot be accessed in object $iw - Access to protected value f not permitted because + Access to protected lazy value f not permitted because enclosing object $eval in package $line13 is not a subclass of object $iw where target is defined lazy val $result = f diff --git a/test/files/run/showdecl.check b/test/files/run/showdecl.check index b8d7f94c57..d431c36f6d 100644 --- a/test/files/run/showdecl.check +++ b/test/files/run/showdecl.check @@ -8,7 +8,7 @@ initialized y: lazy val y: Int uninitialized z: def z: <?> initialized z: def z: Int uninitialized t: def t: <?> -initialized t: def t[T <: Int](x: D)(y: x.W): Int +initialized t: def t[T <: <?>](x: D)(y: x.W): Int uninitialized W: type W = String initialized W: type W = String uninitialized C: class C extends diff --git a/test/files/run/showdecl/Macros_1.scala b/test/files/run/showdecl/Macros_1.scala index c68dd275de..89b8e8d3c2 100644 --- a/test/files/run/showdecl/Macros_1.scala +++ b/test/files/run/showdecl/Macros_1.scala @@ -9,7 +9,7 @@ object Macros { import c.universe._ def test(sym: Symbol): Unit = { println(s"uninitialized ${sym.name}: ${showDecl(sym)}") - sym.info + sym.info // NOTE: not fullyInitializeSymbol, so some parts may still be LazyTypes println(s"initialized ${sym.name}: ${showDecl(sym)}") } diff --git a/test/files/run/showraw_mods.check b/test/files/run/showraw_mods.check index 4d34160422..ff77d22adf 100644 --- a/test/files/run/showraw_mods.check +++ b/test/files/run/showraw_mods.check @@ -1 +1 @@ -Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), TypeName("C"), List(), Template(List(Ident(TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), TermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), ValDef(Modifiers(PRIVATE | LOCAL), TermName("x"), TypeTree(), Literal(Constant(2))), ValDef(Modifiers(MUTABLE), TermName("y"), TypeTree(), Select(This(TypeName("C")), TermName("x"))), ValDef(Modifiers(LAZY), TermName("z"), TypeTree(), Select(This(TypeName("C")), TermName("y"))))))), Literal(Constant(()))) +Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), TypeName("C"), List(), Template(List(Ident(TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), TermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), DefDef(Modifiers(PRIVATE | METHOD | LOCAL | STABLE | ACCESSOR), TermName("x"), List(), List(), TypeTree(), Literal(Constant(2))), DefDef(Modifiers(METHOD | ACCESSOR), TermName("y"), List(), List(), TypeTree(), Select(This(TypeName("C")), TermName("x"))), DefDef(Modifiers(METHOD | ACCESSOR), TermName("y_$eq"), List(), List(List(ValDef(Modifiers(PARAM | SYNTHETIC), TermName("x$1"), TypeTree(), EmptyTree))), TypeTree(), EmptyTree), ValDef(Modifiers(LAZY), TermName("z"), TypeTree(), Select(This(TypeName("C")), TermName("y"))))))), Literal(Constant(()))) diff --git a/test/files/run/t4287inferredMethodTypes.check b/test/files/run/t4287inferredMethodTypes.check deleted file mode 100644 index 56e9c097cc..0000000000 --- a/test/files/run/t4287inferredMethodTypes.check +++ /dev/null @@ -1,30 +0,0 @@ -[[syntax trees at end of typer]] // newSource1.scala -[0:92]package [0:0]<empty> { - [0:21]class A extends [7:21][23]scala.AnyRef { - [8:16]<paramaccessor> private[this] val a: [8]Int = _; - <8:20>def <init>(<8:20>a: [11]<type: [11]scala.Int> = [17:20]A.a): [7]A = <8:20>{ - <8:20><8:20><8:20>A.super.<init>(); - <8:20>() - } - }; - [23:47]object A extends [32:47][49]scala.AnyRef { - [49]def <init>(): [32]A.type = [49]{ - [49][49][49]A.super.<init>(); - [32]() - }; - [36:45]private[this] val a: [40]Int = [44:45]2; - [40]<stable> <accessor> def a: [40]Int = [40][40]A.this.a; - [8]<synthetic> def <init>$default$1: [8]Int = [19]A.a - }; - [49:92]class B extends [57:92][65:66]A { - [65]def <init>(): [57]B = [65]{ - [65][65][65]B.super.<init>([65]A.<init>$default$1); - [57]() - }; - [70:90]def <init>([79:80]a: [79]Int): [74]B = [84:90]{ - [84:90][84:90][84]B.this.<init>(); - [84]() - } - } -} - diff --git a/test/files/run/t4287inferredMethodTypes.scala b/test/files/run/t4287inferredMethodTypes.scala deleted file mode 100644 index f14e672da8..0000000000 --- a/test/files/run/t4287inferredMethodTypes.scala +++ /dev/null @@ -1,25 +0,0 @@ -import scala.tools.partest.DirectTest - -object Test extends DirectTest { - - override def extraSettings: String = - s"-usejavacp -Yinfer-argument-types -Xprint-pos -Xprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" - - override def code = """ -class A(a: Int = A.a) - -object A { - val a = 2 -} - -class B extends A { - def this(a) = this() -} - """.trim - - override def show(): Unit = { - Console.withErr(System.out) { - compile() - } - } -}
\ No newline at end of file diff --git a/test/files/run/t6733.check b/test/files/run/t6733.check index aeb595fbfd..7062301c56 100644 --- a/test/files/run/t6733.check +++ b/test/files/run/t6733.check @@ -2,23 +2,22 @@ method $init$: isPrivateThis = false, isProtectedThis = false value pri1a: isPrivateThis = true, isProtectedThis = false method pri2a: isPrivateThis = true, isProtectedThis = false variable pri3a: isPrivateThis = true, isProtectedThis = false -value pri4a: isPrivateThis = true, isProtectedThis = false +variable pri3a: isPrivateThis = true, isProtectedThis = false +lazy value pri4a: isPrivateThis = true, isProtectedThis = false lazy value pri4a: isPrivateThis = true, isProtectedThis = false type Pri5a: isPrivateThis = true, isProtectedThis = false class Pri6: isPrivateThis = true, isProtectedThis = false trait Pri7: isPrivateThis = true, isProtectedThis = false object Pri8: isPrivateThis = true, isProtectedThis = false value pro1a: isPrivateThis = false, isProtectedThis = true -value pro1a: isPrivateThis = true, isProtectedThis = false value pro1b: isPrivateThis = false, isProtectedThis = true method pro2a: isPrivateThis = false, isProtectedThis = true method pro2b: isPrivateThis = false, isProtectedThis = true -method pro3a: isPrivateThis = false, isProtectedThis = true -method pro3a_=: isPrivateThis = false, isProtectedThis = true -variable pro3a: isPrivateThis = true, isProtectedThis = false -method pro3b: isPrivateThis = false, isProtectedThis = true -method pro3b_=: isPrivateThis = false, isProtectedThis = true -value pro4a: isPrivateThis = false, isProtectedThis = true +variable pro3a: isPrivateThis = false, isProtectedThis = true +variable pro3a: isPrivateThis = false, isProtectedThis = true +variable pro3b: isPrivateThis = false, isProtectedThis = true +variable pro3b: isPrivateThis = false, isProtectedThis = true +lazy value pro4a: isPrivateThis = false, isProtectedThis = true lazy value pro4a: isPrivateThis = true, isProtectedThis = false type Pro5a: isPrivateThis = false, isProtectedThis = true type Pro5b: isPrivateThis = false, isProtectedThis = true diff --git a/test/files/run/t7533.check b/test/files/run/t7533.check index fa5b3edc8f..61fd4657bd 100644 --- a/test/files/run/t7533.check +++ b/test/files/run/t7533.check @@ -1,30 +1,29 @@ Testing Symbol.isAbstract... =======class C======= -class C => true -constructor C => false -value x1 => true -value x2 => false -value x2 => false -method y1 => true -method y2 => false -type T1 => true -type T2 => false +class C => abstract +constructor C => concrete +value xAbs => abstract +value x => concrete +value x => concrete +method yAbs => abstract +method y => concrete +type TAbs => abstract +type T => concrete =======trait T======= -trait T => true -method $init$ => false -value z1 => true -value z2 => false -value z2 => false -method w1 => true -method w2 => false -type U1 => true -type U2 => false -=======class D======= -class D => false -constructor D => false -value x1 => false -value x1 => false -method y1 => false +trait T => abstract +method $init$ => concrete +value zAbs => abstract +value z => concrete +method wAbs => abstract +method w => concrete +type UAbs => abstract +type U => concrete +=======class AllConcrete======= +class AllConcrete => concrete +constructor AllConcrete => concrete +value xAbs => concrete +value xAbs => concrete +method yAbs => concrete =======object M======= -object M => false -constructor M => false +object M => concrete +constructor M => concrete diff --git a/test/files/run/t7533.scala b/test/files/run/t7533.scala index c7bd8e8d43..65c5c26b42 100644 --- a/test/files/run/t7533.scala +++ b/test/files/run/t7533.scala @@ -1,24 +1,24 @@ import scala.reflect.runtime.universe._ abstract class C { - val x1: Int - val x2: Int = 2 - def y1: Int - def y2: Int = 2 - type T1 <: Int - type T2 = Int + val xAbs: Int + val x: Int = 2 + def yAbs: Int + def y: Int = 2 + type TAbs <: Int + type T = Int } trait T { - val z1: Int - val z2: Int = 2 - def w1: Int - def w2: Int = 2 - type U1 <: Int - type U2 = Int + val zAbs: Int + val z: Int = 2 + def wAbs: Int + def w: Int = 2 + type UAbs <: Int + type U = Int } -class D extends C { - val x1 = 3 - def y1 = 3 +class AllConcrete extends C { + val xAbs = 3 + def yAbs = 3 } object M @@ -27,12 +27,12 @@ object Test extends App { def test[T: TypeTag] = { val sym = typeOf[T].typeSymbol println(s"=======$sym=======") - def printAbstract(sym: Symbol) = println(s"$sym => ${sym.isAbstract}") + def printAbstract(sym: Symbol) = println(s"$sym => ${if (sym.isAbstract) "abstract" else "concrete"}") printAbstract(sym) sym.info.decls.sorted.foreach(printAbstract) } test[C] test[T] - test[D] + test[AllConcrete] test[M.type] }
\ No newline at end of file diff --git a/test/files/run/t8549.scala b/test/files/run/t8549.scala index da7a731459..7a38491231 100644 --- a/test/files/run/t8549.scala +++ b/test/files/run/t8549.scala @@ -79,7 +79,7 @@ object Test extends App { } } - // Generated on 20160715-08:27:53 with Scala version 2.12.0-20160715-012500-f5a80bd) + // Generated on 20160720-18:56:11 with Scala version 2.12.0-local-5815f9a) overwrite.foreach(updateComment) check(Some(1))("rO0ABXNyAApzY2FsYS5Tb21lESLyaV6hi3QCAAFMAAV2YWx1ZXQAEkxqYXZhL2xhbmcvT2JqZWN0O3hyAAxzY2FsYS5PcHRpb27+aTf92w5mdAIAAHhwc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAE=") @@ -174,7 +174,7 @@ object Test extends App { // check(mutable.ArraySeq(1, 2, 3))( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTZXEVPD3SKEkOcwIAAkkABmxlbmd0aFsABWFycmF5dAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAA3VyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABQAAAAJzcQB+AAUAAAAD") check(mutable.AnyRefMap("a" -> "A"))( "rO0ABXNyACJzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQW55UmVmTWFwAAAAAAAAAAECAAdJAAVfc2l6ZUkAB192YWNhbnRJAARtYXNrTAAMZGVmYXVsdEVudHJ5dAARTHNjYWxhL0Z1bmN0aW9uMTtbACtzY2FsYSRjb2xsZWN0aW9uJG11dGFibGUkQW55UmVmTWFwJCRfaGFzaGVzdAACW0lbAClzY2FsYSRjb2xsZWN0aW9uJG11dGFibGUkQW55UmVmTWFwJCRfa2V5c3QAE1tMamF2YS9sYW5nL09iamVjdDtbACtzY2FsYSRjb2xsZWN0aW9uJG11dGFibGUkQW55UmVmTWFwJCRfdmFsdWVzcQB+AAN4cAAAAAEAAAAAAAAAB3NyADNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQW55UmVmTWFwJEV4Y2VwdGlvbkRlZmF1bHQAAAAAAAAAAQIAAHhwdXIAAltJTbpgJnbqsqUCAAB4cAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+UkA2AAAAAHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAhwcHBwcHB0AAFhcHVxAH4ACQAAAAhwcHBwcHB0AAFBcA==") check(mutable.ArrayStack(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTdGFja3bdxXbcnLBeAgACSQAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJEFycmF5U3RhY2skJGluZGV4WwAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJEFycmF5U3RhY2skJHRhYmxldAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAA3VyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAA3NxAH4ABQAAAAJzcQB+AAUAAAAB") - check(mutable.DoubleLinkedList(1, 2, 3))( "rO0ABXNyAClzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuRG91YmxlTGlua2VkTGlzdI73LKsKRr1RAgADTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL1NlcTtMAARwcmV2cQB+AAJ4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAAc3EAfgAEAAAAAnNxAH4AAHNxAH4ABAAAAANzcQB+AABwcQB+AAtxAH4ACXEAfgAHcQB+AANw") + check(mutable.DoubleLinkedList(1, 2, 3))( "rO0ABXNyAClzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuRG91YmxlTGlua2VkTGlzdI73LKsKRr1RAgADTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0ACtMc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL0RvdWJsZUxpbmtlZExpc3Q7TAAEcHJldnEAfgACeHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAHNxAH4ABAAAAAJzcQB+AABzcQB+AAQAAAADc3EAfgAAcHEAfgALcQB+AAlxAH4AB3EAfgADcA==") check(mutable.HashMap())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAAAAAAABAB4") check(mutable.HashMap(1 -> 1))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXEAfgAEeA==") @@ -189,7 +189,7 @@ object Test extends App { // check(new mutable.History())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGlzdG9yeUhuXxDIFJrsAgACSQAKbWF4SGlzdG9yeUwAA2xvZ3QAIExzY2FsYS9jb2xsZWN0aW9uL211dGFibGUvUXVldWU7eHAAAAPoc3IAHnNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5RdWV1ZbjMURVfOuHHAgAAeHIAJHNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5NdXRhYmxlTGlzdFJpnjJ+gFbAAgADSQADbGVuTAAGZmlyc3QwdAAlTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9MaW5rZWRMaXN0O0wABWxhc3QwcQB+AAV4cAAAAABzcgAjc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkxpbmtlZExpc3Sak+nGCZHaUQIAAkwABGVsZW10ABJMamF2YS9sYW5nL09iamVjdDtMAARuZXh0dAAeTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9TZXE7eHBwcQB+AApxAH4ACg==") check(mutable.LinkedHashMap(1 -> 2))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJ4") check(mutable.LinkedHashSet(1, 2, 3))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkSGFzaFNldAAAAAAAAAABAwAAeHB3DQAAAu4AAAADAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJzcQB+AAIAAAADeA==") - check(mutable.LinkedList(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkTGlzdJqT6cYJkdpRAgACTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL1NlcTt4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAAc3EAfgAEAAAAAnNxAH4AAHNxAH4ABAAAAANzcQB+AABwcQB+AAs=") + check(mutable.LinkedList(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkTGlzdJqT6cYJkdpRAgACTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0ACVMc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL0xpbmtlZExpc3Q7eHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAHNxAH4ABAAAAAJzcQB+AABzcQB+AAQAAAADc3EAfgAAcHEAfgAL") // TODO SI-8576 unstable under -Xcheckinit // check(mutable.ListBuffer(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlzdEJ1ZmZlci9y9I7QyWzGAwAEWgAIZXhwb3J0ZWRJAANsZW5MAAVsYXN0MHQAKUxzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS8kY29sb24kY29sb247TAAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJExpc3RCdWZmZXIkJHN0YXJ0dAAhTHNjYWxhL2NvbGxlY3Rpb24vaW1tdXRhYmxlL0xpc3Q7eHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABAAAAAJzcQB+AAQAAAADc3IALHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3RTZXJpYWxpemVFbmQkilxjW/dTC20CAAB4cHcFAAAAAAN4") diff --git a/test/files/run/trait_fields_bytecode.scala b/test/files/run/trait_fields_bytecode.scala new file mode 100644 index 0000000000..d87412f43e --- /dev/null +++ b/test/files/run/trait_fields_bytecode.scala @@ -0,0 +1,23 @@ +trait TFinal { final val bla: Int = 123 } + +// bla should be final in C +class CFinal extends TFinal + + +trait TConst { final val C = "S" } +// there should be a C method in `T$class`! +class CConst extends TConst { } + + +object Test { + def main(args: Array[String]): Unit = { + val f1 = classOf[CFinal].getDeclaredMethod("bla") + import java.lang.reflect.Modifier._ + assert(isFinal(f1.getModifiers), f1) + + classOf[CConst].getMethod("C") + + import language.reflectiveCalls + assert(new CConst().asInstanceOf[{def C: String}].C == "S") + } +} diff --git a/test/files/run/trait_fields_final.scala b/test/files/run/trait_fields_final.scala new file mode 100644 index 0000000000..8b32e5b47d --- /dev/null +++ b/test/files/run/trait_fields_final.scala @@ -0,0 +1,21 @@ +// TODO: clarify meaning of final in traits +// In the new compiler, there's no final modifier after mixin for `meh`'s setter, +// whereas 2.12.0-M3 makes meh's trait setter final. +// NOTE: bytecode is identical, but the scalasignature is different +trait Foo { self: Meh => + def bar(x: String) = x == "a" + private final val meh = bar("a") +} + +abstract class Meh extends Foo + +object Test { + def main(args: Array[String]): Unit = { + val setter = classOf[Meh].getDeclaredMethod("Foo$_setter_$Foo$$meh_$eq", java.lang.Boolean.TYPE) + val getter = classOf[Meh].getDeclaredMethod("Foo$$meh") + import java.lang.reflect.Modifier._ + assert(isFinal(setter.getModifiers), setter) + assert(isFinal(getter.getModifiers), getter) + } + +} diff --git a/test/files/run/trait_fields_init.check b/test/files/run/trait_fields_init.check new file mode 100644 index 0000000000..84c1a2ead9 --- /dev/null +++ b/test/files/run/trait_fields_init.check @@ -0,0 +1,21 @@ +x +y +z +abstract +public +protected +abstract protected +private +private[this] +abstract +public +protected +abstract protected +private +private[this] +abstract +public +protected +abstract protected +private +private[this] diff --git a/test/files/run/trait_fields_init.scala b/test/files/run/trait_fields_init.scala new file mode 100644 index 0000000000..496911d538 --- /dev/null +++ b/test/files/run/trait_fields_init.scala @@ -0,0 +1,55 @@ +trait T { + val abs: String + protected val protabs: String + val pub = "public" + protected val prot = "protected" + private val privvy = "private" + private[this] val privateThis = "private[this]" + // TODO: + // final val const = "const" + + trait Nested { println(abs + privateThis) } + + object NO { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } + + trait NT { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } + + class NC { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } +} + +class C extends AnyRef with T { + println("x") + val abs = "abstract" + println("y") + val protabs = "abstract protected" + final val const = "const" + println("z") +} + +object Test extends C { + def main(args: Array[String]): Unit = { + NO + new NT{} + new NC +}}
\ No newline at end of file diff --git a/test/files/run/trait_fields_repl.check b/test/files/run/trait_fields_repl.check new file mode 100644 index 0000000000..d03a565c7b --- /dev/null +++ b/test/files/run/trait_fields_repl.check @@ -0,0 +1,11 @@ + +scala> trait B { val y = "a" } +defined trait B + +scala> trait T extends B { val x: y.type = y } +defined trait T + +scala> println((new T{}).x) +a + +scala> :quit diff --git a/test/files/run/trait_fields_repl.scala b/test/files/run/trait_fields_repl.scala new file mode 100644 index 0000000000..311477b7d2 --- /dev/null +++ b/test/files/run/trait_fields_repl.scala @@ -0,0 +1,10 @@ +// TODO: fix AME when this runs in REPL +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ +trait B { val y = "a" } +trait T extends B { val x: y.type = y } +println((new T{}).x) +""" +} diff --git a/test/files/run/trait_fields_three_layer_overrides.check b/test/files/run/trait_fields_three_layer_overrides.check new file mode 100644 index 0000000000..8bb45803c5 --- /dev/null +++ b/test/files/run/trait_fields_three_layer_overrides.check @@ -0,0 +1,2 @@ +the real universe.TypeTag +1 diff --git a/test/files/run/trait_fields_three_layer_overrides.scala b/test/files/run/trait_fields_three_layer_overrides.scala new file mode 100644 index 0000000000..9d7aa94341 --- /dev/null +++ b/test/files/run/trait_fields_three_layer_overrides.scala @@ -0,0 +1,25 @@ +// interesting hierarchies/overrides distilled from reflect/compiler + +trait Aliases { + val TypeTag = "universe.TypeTag" +} +trait AliasesOverrides extends Aliases { // or self: Aliases => + override val TypeTag = "the real universe.TypeTag" +} +class Context extends Aliases with AliasesOverrides + + + +trait SymbolTable { + def currentRunId: Int = -1 +} +trait ReflectSetup extends SymbolTable { + override val currentRunId = 1 +} +class G extends SymbolTable with ReflectSetup + + +object Test extends App { + println((new Context).TypeTag) + println((new G).currentRunId) +}
\ No newline at end of file diff --git a/test/files/run/trait_fields_volatile.scala b/test/files/run/trait_fields_volatile.scala new file mode 100644 index 0000000000..eedb6de1c2 --- /dev/null +++ b/test/files/run/trait_fields_volatile.scala @@ -0,0 +1,13 @@ +// bytecode should reflect volatile annotation +trait VolatileAbort { + @volatile private var abortflag = false +} +class DefaultSignalling extends VolatileAbort + +object Test { + def main(args: Array[String]): Unit = { + val field = classOf[DefaultSignalling].getDeclaredFields.find(_.getName.contains("abortflag")).get + assert(java.lang.reflect.Modifier.isVolatile(field.getModifiers), field) + } + +} diff --git a/test/junit/scala/reflect/internal/PrintersTest.scala b/test/junit/scala/reflect/internal/PrintersTest.scala index d581ca8cf4..6f9b711b34 100644 --- a/test/junit/scala/reflect/internal/PrintersTest.scala +++ b/test/junit/scala/reflect/internal/PrintersTest.scala @@ -151,7 +151,7 @@ class BasePrintTest { |else | ((a.toString): String)""", typedCode=sm""" - |val a = 1; + |val a: Int = 1; |if (PrintersContext.this.a.>(1)) | ((PrintersContext.this.a): scala.Int) |else @@ -864,7 +864,7 @@ class TraitPrintTest { @Test def testTraitWithSelf2 = assertPrintedCode(sm""" |trait X { self: scala.Cloneable with scala.Serializable => - | val x: scala.Int = 1 + | val x: Int = 1 |}""") @Test def testTraitTypeParams = assertPrintedCode("trait X[A, B]") @@ -903,7 +903,7 @@ class TraitPrintTest { | type Foo; | type XString = scala.Predef.String |} with scala.Serializable { - | val z = 7 + | val z: Int = 7 |}""") @Test def testTraitWithSingletonTypeTree = assertPrintedCode(sm""" diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala index 54f4c805c1..d24b4e518b 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala @@ -104,6 +104,7 @@ class ScalaInlineInfoTest extends BytecodeTesting { ("x4()I", MethodInlineInfo(false,false,false)), ("x4$(LT;)I", MethodInlineInfo(true ,false,false)), ("x5()I", MethodInlineInfo(true, false,false)), + ("x5$(LT;)I", MethodInlineInfo(true ,false,false)), ("L$lzycompute$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)), ("L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true ,false,false)), ("nest$1()I", MethodInlineInfo(true, false,false)), @@ -127,7 +128,7 @@ class ScalaInlineInfoTest extends BytecodeTesting { "x3_$eq(I)V" -> MethodInlineInfo(false,false,false), "x4$lzycompute()I" -> MethodInlineInfo(true ,false,false), "x4()I" -> MethodInlineInfo(false,false,false), - "x5()I" -> MethodInlineInfo(true ,false,false), +// "x5()I" -> MethodInlineInfo(true ,false,false), -- there is no x5 in the class as it's implemented fully in the interface "T$$super$toString()Ljava/lang/String;" -> MethodInlineInfo(true ,false,false), "<init>()V" -> MethodInlineInfo(false,false,false)), None) diff --git a/test/pending/run/origins.check b/test/pending/run/origins.check index b12cb6e38f..af94b549d3 100644 --- a/test/pending/run/origins.check +++ b/test/pending/run/origins.check @@ -1,6 +1,4 @@ ->> Origins tag 'boop' logged 65 calls from 3 distinguished sources. +>> Origins tag 'boop' logged 65 calls from 1 distinguished sources. - 50 Test$$anonfun$f3$1.apply(origins.scala:16) - 10 Test$$anonfun$f2$1.apply(origins.scala:15) - 5 Test$$anonfun$f1$1.apply(origins.scala:14) + 65 null |