Permalink
Checking mergeability…
Don’t worry, you can still create the pull request.
Comparing changes
Open a pull request
- 1 commit
- 46 files changed
- 0 commit comments
- 1 contributor
Unified
Split
Showing
with
837 additions
and 236 deletions.
- +23 −7 src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
- +6 −2 src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
- +2 −4 src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
- +6 −4 src/compiler/scala/tools/nsc/transform/Constructors.scala
- +3 −3 src/compiler/scala/tools/nsc/transform/Fields.scala
- +1 −2 src/compiler/scala/tools/nsc/transform/Flatten.scala
- +1 −0 src/compiler/scala/tools/nsc/transform/InfoTransform.scala
- +1 −2 src/compiler/scala/tools/nsc/transform/Statics.scala
- +1 −1 src/compiler/scala/tools/nsc/typechecker/Contexts.scala
- +1 −4 src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
- +63 −4 src/compiler/scala/tools/nsc/typechecker/Namers.scala
- +33 −18 src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
- +69 −3 src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
- +58 −12 src/compiler/scala/tools/nsc/typechecker/Typers.scala
- +1 −9 src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
- +1 −0 src/library/scala/Enumeration.scala
- +11 −0 src/library/scala/enum.scala
- +5 −1 src/reflect/scala/reflect/internal/Definitions.scala
- +1 −2 src/reflect/scala/reflect/internal/Symbols.scala
- +21 −4 src/reflect/scala/reflect/internal/TreeGen.scala
- +1 −0 src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
- +2 −2 test/files/jvm/serialization-new.check
- +2 −2 test/files/jvm/serialization.check
- +1 −0 test/files/jvm/t2214.check
- +1 −0 test/files/jvm/t2827.check
- +2 −1 test/files/neg/virtpatmat_unreach_select.check
- +2 −1 test/files/res/t831.check
- +16 −0 test/files/run/enumerations.check
- +161 −0 test/files/run/enumerations.scala
- +123 −14 test/files/run/enums.check
- +201 −130 test/files/run/enums.scala
- +1 −0 test/files/run/iterator-from.check
- +1 −0 test/files/run/manifests-new.check
- +1 −0 test/files/run/t1505.check
- +1 −0 test/files/run/t2111.check
- +1 −0 test/files/run/t3186.check
- +1 −0 test/files/run/t3616.check
- +2 −1 test/files/run/t3687.check
- +2 −1 test/files/run/t3719.check
- +2 −1 test/files/run/t3950.check
- +1 −0 test/files/run/t4570.check
- +1 −0 test/files/run/t5588.check
- +1 −0 test/files/run/t5612.check
- +1 −0 test/files/run/t8611b.check
- +0 −1 test/files/run/t8611b.flags
- +1 −0 test/files/run/t949.check
| @@ -2773,7 +2773,15 @@ self => | ||
| val annots = annotations(skipNewLines = true) | ||
| val pos = caseAwareTokenOffset | ||
| val mods = modifiers() withAnnotations annots | ||
| tmplDef(pos, mods) | ||
| // We need to come up with a more principled way to communicate the ability for | ||
| // exhaustiveness checks to the pattern matcher instead of abusing various flag | ||
| // combinations that need to be undone again in a later phase. | ||
| // It should be possible to address both Java-defined and Scala-defined enums with a simple | ||
| // check for the JAVA_ENUM flag. | ||
| val mods1 = | ||
| if (mods.hasAnnotationNamed("enum")) mods | Flags.JAVA_ENUM | Flags.SEALED | Flags.FINAL | ||
| else mods | ||
| tmplDef(pos, mods1) | ||
| } | ||
| /** {{{ | ||
| @@ -2828,9 +2836,8 @@ self => | ||
| val (constrMods, vparamss) = | ||
| if (mods.isTrait) (Modifiers(Flags.TRAIT), List()) | ||
| else (accessModifierOpt(), paramClauses(name, classContextBounds, ofCaseClass = mods.isCase)) | ||
| var mods1 = mods | ||
| val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart) | ||
| val result = gen.mkClassDef(mods1, name, tparams, template) | ||
| val template = templateOpt(mods, name, constrMods withAnnotations constrAnnots, vparamss, tstart) | ||
| val result = gen.mkClassDef(mods, name, tparams, template) | ||
| // Context bounds generate implicit parameters (part of the template) with types | ||
| // from tparams: we need to ensure these don't overlap | ||
| if (!classContextBounds.isEmpty) | ||
| @@ -2970,21 +2977,30 @@ self => | ||
| (List(), self, body) | ||
| } | ||
| ) | ||
| val newParents = | ||
| if (mods.hasFlag(Flags.JAVA_ENUM)) { | ||
| val enumParent = AppliedTypeTree(Ident(definitions.JavaEnumClass.name), List(Ident(name))) | ||
| enumParent :: parents | ||
| } else { | ||
| parents | ||
| } | ||
| def anyvalConstructor() = ( | ||
| // Not a well-formed constructor, has to be finished later - see note | ||
| // regarding AnyVal constructor in AddInterfaces. | ||
| DefDef(NoMods, nme.CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(Nil, literalUnit)) | ||
| ) | ||
| val parentPos = o2p(in.offset) | ||
| val tstart1 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart | ||
| val newConstrMods = constrMods | (mods & Flags.JAVA_ENUM).flags | ||
| atPos(tstart1) { | ||
| // Exclude only the 9 primitives plus AnyVal. | ||
| if (inScalaRootPackage && ScalaValueClassNames.contains(name)) | ||
| Template(parents, self, anyvalConstructor :: body) | ||
| else | ||
| gen.mkTemplate(gen.mkParents(mods, parents, parentPos), | ||
| self, constrMods, vparamss, body, o2p(tstart)) | ||
| gen.mkTemplate(gen.mkParents(mods, newParents, parentPos), | ||
| self, newConstrMods, vparamss, body, o2p(tstart)) | ||
| } | ||
| } | ||
| @@ -337,8 +337,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { | ||
| genLoadQualUnlessElidable() | ||
| genLoadModule(tree) | ||
| } else if (sym.isStaticMember) { | ||
| genLoadQualUnlessElidable() | ||
| fieldLoad(sym, receiverClass) | ||
| if (sym.isJavaEnum) { | ||
| fieldLoad(sym, receiverClass.companionClass) | ||
| } else { | ||
| genLoadQualUnlessElidable() | ||
| fieldLoad(sym, receiverClass) | ||
| } | ||
| } else { | ||
| genLoadQualifier(tree) | ||
| fieldLoad(sym, receiverClass) | ||
| @@ -385,18 +385,16 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { | ||
| /* | ||
| * must-single-thread | ||
| */ | ||
| def fieldSymbols(cls: Symbol): List[Symbol] = { | ||
| def fieldSymbols(cls: Symbol): List[Symbol] = | ||
| for (f <- cls.info.decls.toList ; | ||
| if !f.isMethod && f.isTerm && !f.isModule | ||
| ) yield f | ||
| } | ||
| /* | ||
| * can-multi-thread | ||
| */ | ||
| def methodSymbols(cd: ClassDef): List[Symbol] = { | ||
| def methodSymbols(cd: ClassDef): List[Symbol] = | ||
| cd.impl.body collect { case dd: DefDef => dd.symbol } | ||
| } | ||
| /* | ||
| * must-single-thread | ||
| @@ -555,7 +555,9 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | ||
| // 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) | ||
| val qual = | ||
| if (assignSym.isStaticMember) Select(Ident(clazz), assignSym) | ||
| else Select(This(clazz), assignSym) | ||
| if (assignSym.isSetter) Apply(qual, List(rhs)) | ||
| else Assign(qual, rhs) | ||
| } | ||
| @@ -598,7 +600,7 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | ||
| private def triage() = { | ||
| // Constant typed vals are not memoized. | ||
| def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType] | ||
| def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType] || sym.hasFlag(JAVA_ENUM) | ||
| // The early initialized field definitions of the class (these are the class members) | ||
| val presupers = treeInfo.preSuperFields(stats) | ||
| @@ -700,8 +702,8 @@ abstract class Constructors extends Statics with Transform with TypingTransforme | ||
| // TODO: this should omit fields for non-memoized (constant-typed, unit-typed vals need no storage -- | ||
| // all the action is in the getter) | ||
| def omittableSym(sym: Symbol) = omittableAccessor(sym) | ||
| def omittableStat(stat: Tree) = omittableSym(stat.symbol) | ||
| def omittableSym(sym: Symbol): Boolean = omittableAccessor(sym) | ||
| def omittableStat(stat: Tree): Boolean = omittableSym(stat.symbol) | ||
| // The parameter accessor fields which are members of the class | ||
| val paramAccessors = | ||
| @@ -84,7 +84,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor | ||
| else synthFieldsAndAccessors(tp) | ||
| // TODO: drop PRESUPER support when we implement trait parameters in 2.13 | ||
| private def excludedAccessorOrFieldByFlags(statSym: Symbol): Boolean = statSym hasFlag PRESUPER | ||
| private def excludedAccessorOrFieldByFlags(statSym: Symbol): Boolean = statSym hasFlag PRESUPER | JAVA_ENUM | ||
| // used for internal communication between info and tree transform of this phase -- not pickled, not in initialflags | ||
| // TODO: reuse MIXEDIN for NEEDS_TREES? | ||
| @@ -158,7 +158,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor | ||
| // Note that a strict unit-typed val does receive a field, because we cannot omit the write to the field | ||
| // (well, we could emit it for non-@volatile ones, if I understand the memory model correctly, | ||
| // but that seems pretty edge-casey) | ||
| val constantTyped = tp.isInstanceOf[ConstantType] | ||
| val constantTyped = tp.isInstanceOf[ConstantType] && tp.asInstanceOf[ConstantType].value.tag != EnumTag | ||
| } | ||
| private def fieldTypeForGetterIn(getter: Symbol, pre: Type): Type = getter.info.finalResultType.asSeenFrom(pre, getter.owner) | ||
| @@ -351,7 +351,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor | ||
| } | ||
| } | ||
| if (newDecls nonEmpty) { | ||
| if (newDecls.nonEmpty) { | ||
| val allDecls = newScope | ||
| origDecls foreach allDecls.enter | ||
| newDecls foreach allDecls.enter | ||
| @@ -17,8 +17,7 @@ abstract class Flatten extends InfoTransform { | ||
| /** the following two members override abstract members in Transform */ | ||
| val phaseName: String = "flatten" | ||
| /** Updates the owning scope with the given symbol, unlinking any others. | ||
| */ | ||
| /** Updates the owning scope with the given symbol, unlinking any others. */ | ||
| private def replaceSymbolInCurrentScope(sym: Symbol): Unit = exitingFlatten { | ||
| removeSymbolInCurrentScope(sym) | ||
| sym.owner.info.decls enter sym | ||
| @@ -33,6 +33,7 @@ trait InfoTransform extends Transform { | ||
| if (infoTransformers.nextFrom(id).pid != id) { | ||
| // this phase is not yet in the infoTransformers | ||
| val infoTransformer = new InfoTransformer { | ||
| override def toString = InfoTransform.this.getClass.toString | ||
| val pid = id | ||
| val changesBaseClasses = InfoTransform.this.changesBaseClasses | ||
| def transform(sym: Symbol, tpe: Type): Type = transformInfo(sym, tpe) | ||
| @@ -5,8 +5,7 @@ abstract class Statics extends Transform with ast.TreeDSL { | ||
| import global._ | ||
| trait StaticsTransformer extends Transformer { | ||
| /** generate a static constructor with symbol fields inits, or an augmented existing static ctor | ||
| */ | ||
| /** 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 = | ||
| body.collectFirst { | ||
| // If there already was a static ctor - augment existing one | ||
| @@ -436,7 +436,7 @@ trait Contexts { self: Analyzer => | ||
| * Construct a child context. The parent and child will share the report buffer. | ||
| * Compare with `makeSilent`, in which the child has a fresh report buffer. | ||
| * | ||
| * If `tree` is an `Import`, that import will be avaiable at the head of | ||
| * If `tree` is an `Import`, that import will be available at the head of | ||
| * `Context#imports`. | ||
| */ | ||
| def make(tree: Tree = tree, owner: Symbol = owner, | ||
| @@ -10,17 +10,14 @@ import symtab.Flags._ | ||
| import scala.reflect.internal.util.StringOps.ojoin | ||
| import scala.reflect.internal.util.ListOfNil | ||
| /** Logic related to method synthesis which involves cooperation between | ||
| * Namer and Typer. | ||
| */ | ||
| /** Logic related to method synthesis which involves cooperation between Namer and Typer. */ | ||
| trait MethodSynthesis { | ||
| self: Analyzer => | ||
| import global._ | ||
| import definitions._ | ||
| import CODE._ | ||
| class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { | ||
| def mkThis = This(clazz) setPos clazz.pos.focus | ||
| def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)( | ||
| @@ -665,9 +665,14 @@ trait Namers extends MethodSynthesis { | ||
| if (isScala && deriveAccessors(tree)) enterGetterSetter(tree) | ||
| else assignAndEnterFinishedSymbol(tree) | ||
| val sym = tree.symbol | ||
| if (isEnumConstant(tree)) { | ||
| tree.symbol setInfo ConstantType(Constant(tree.symbol)) | ||
| tree.symbol.owner.linkedClassOfClass addChild tree.symbol | ||
| sym setInfo ConstantType(Constant(sym)) | ||
| if (sym.isJavaDefined) | ||
| sym.owner.linkedClassOfClass addChild sym | ||
| else | ||
| sym.owner addChild sym | ||
| } | ||
| } | ||
| @@ -698,6 +703,38 @@ trait Namers extends MethodSynthesis { | ||
| tree.symbol = enterClassSymbol(tree) | ||
| tree.symbol setInfo completerOf(tree) | ||
| if (mods.hasAnnotationNamed(TypeName("enum"))) { | ||
| var ordinal = 0 | ||
| def newEnumItemOrdinalAttachment() = { | ||
| val att = new EnumConstantOrdinalAttachment(ordinal) | ||
| ordinal += 1 | ||
| att | ||
| } | ||
| def isPossibleEnumConstantTree(tree: Tree): Boolean = { val result = tree.isInstanceOf[Ident] || tree.isInstanceOf[Apply]; println(s"$tree is enum? $result"); result } | ||
| def enumConstantName(enumConstant: Tree): TermName = enumConstant match { | ||
| case Ident(termName: TermName) => termName | ||
| case Apply(Ident(termName: TermName), _) => termName | ||
| case Apply(Apply(Ident(termName: TermName), _), _) => termName | ||
| } | ||
| val enumConstants: List[TermName] = | ||
| tree.impl.body.iterator | ||
| .dropWhile(tree => !isPossibleEnumConstantTree(tree)) | ||
| .takeWhile(isPossibleEnumConstantTree) | ||
| .map(const => { | ||
| const.updateAttachment(newEnumItemOrdinalAttachment()) | ||
| const.symbol.setFlag(JAVA_ENUM) | ||
| enumConstantName(const)}) | ||
| .toList | ||
| tree.symbol.setFlag(JAVA_ENUM) | ||
| tree.symbol.updateAttachment(EnumConstantsAttachment(enumConstants)) | ||
| // We keep this for the moment, otherwise the compiler doesn't see the members. | ||
| ensureCompanionObject(tree) | ||
| } | ||
| if (mods.isCase) { | ||
| val m = ensureCompanionObject(tree, caseModuleDef) | ||
| m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) | ||
| @@ -718,7 +755,7 @@ trait Namers extends MethodSynthesis { | ||
| // Suggested location only. | ||
| if (mods.isImplicit) { | ||
| if (primaryConstructorArity == 1) { | ||
| log("enter implicit wrapper "+tree+", owner = "+owner) | ||
| log(s"enter implicit wrapper $tree, owner = $owner") | ||
| enterImplicitWrapper(tree) | ||
| } | ||
| else reporter.error(tree.pos, "implicit classes must accept exactly one primary constructor parameter") | ||
| @@ -1028,6 +1065,7 @@ trait Namers extends MethodSynthesis { | ||
| private def templateSig(templ: Template): Type = { | ||
| val clazz = context.owner | ||
| def checkParent(tpt: Tree): Type = { | ||
| if (tpt.tpe.isError) AnyRefTpe | ||
| else tpt.tpe | ||
| @@ -1041,6 +1079,10 @@ trait Namers extends MethodSynthesis { | ||
| val templateNamer = newNamer(context.make(templ, clazz, decls)) | ||
| templateNamer enterSyms templ.body | ||
| if (clazz.hasJavaEnumFlag) | ||
| clazz.attachments.get[EnumConstantsAttachment] | ||
| .map(att => addEnumMembers(att.constants, templateNamer)) | ||
| // add apply and unapply methods to companion objects of case classes, | ||
| // unless they exist already; here, "clazz" is the module class | ||
| if (clazz.isModuleClass) { | ||
| @@ -1617,7 +1659,6 @@ trait Namers extends MethodSynthesis { | ||
| } | ||
| } | ||
| /** Given a case class | ||
| * case class C[Ts] (ps: Us) | ||
| * Add the following methods to toScope: | ||
| @@ -1643,6 +1684,24 @@ trait Namers extends MethodSynthesis { | ||
| caseClassCopyMeth(cdef) foreach namer.enterSyntheticSym | ||
| } | ||
| /** Given an enum class §EnumClass with enum constants §EnumConstant1 ... §EnumConstantN, | ||
| * add the following members to scope: | ||
| * - a private static field $VALUES containing the enum constants | ||
| * `<static> private[this] val $VALUES: Array[§EnumCass]` | ||
| * - a public static method that returns a copy of $VALUES | ||
| * `<static> def values: Array[§EnumCass]` | ||
| * a public static method that returns the enum constant denoted by the string if such an enum constant exists | ||
| * - `<static> def valueOf(name: String): §EnumCass` | ||
| * | ||
| * @param constants a list of the enum constants defined by this enum | ||
| * @param namer the namer of this class | ||
| */ | ||
| def addEnumMembers(constants: List[TermName], namer: Namer) = { | ||
| namer.enterSyntheticSym(enumValuesField(owner, constants)) | ||
| namer.enterSyntheticSym(enumValuesMethod(owner)) | ||
| namer.enterSyntheticSym(enumValueOfMethod(owner)) | ||
| } | ||
| /** | ||
| * TypeSig is invoked by monoTypeCompleters. It returns the type of a definition which | ||
| * is then assigned to the corresponding symbol (typeSig itself does not need to assign | ||
| @@ -107,24 +107,6 @@ trait StdAttachments { | ||
| }) | ||
| ) | ||
| /** After being synthesized by the parser, primary constructors aren't fully baked yet. | ||
| * A call to super in such constructors is just a fill-me-in-later dummy resolved later | ||
| * by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and | ||
| * allows them to complete the synthesis. | ||
| */ | ||
| case class SuperArgsAttachment(argss: List[List[Tree]]) | ||
| /** Convenience method for `SuperArgsAttachment`. | ||
| * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, | ||
| * so it really benefits from a dedicated extractor. | ||
| */ | ||
| def superArgs(tree: Tree): Option[List[List[Tree]]] = | ||
| tree.attachments.get[SuperArgsAttachment] collect { case SuperArgsAttachment(argss) => argss } | ||
| /** Determines whether the given tree has an associated SuperArgsAttachment. | ||
| */ | ||
| def hasSuperArgs(tree: Tree): Boolean = superArgs(tree).nonEmpty | ||
| /** @see markMacroImplRef | ||
| */ | ||
| case object MacroImplRefAttachment | ||
| @@ -152,6 +134,39 @@ trait StdAttachments { | ||
| */ | ||
| def isMacroImplRef(tree: Tree): Boolean = tree.hasAttachment[MacroImplRefAttachment.type] | ||
| /** After being synthesized by the parser, primary constructors aren't fully baked yet. | ||
| * A call to super in such constructors is just a fill-me-in-later dummy resolved later | ||
| * by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and | ||
| * allows them to complete the synthesis. | ||
| */ | ||
| case class SuperArgsAttachment(argss: List[List[Tree]]) | ||
| /** Convenience method for `SuperArgsAttachment`. | ||
| * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, | ||
| * so it really benefits from a dedicated extractor. | ||
| */ | ||
| def superArgs(tree: Tree): Option[List[List[Tree]]] = | ||
| tree.attachments.get[SuperArgsAttachment] collect { case SuperArgsAttachment(argss) => argss } | ||
| /** Determines whether the given tree has an associated SuperArgsAttachment. | ||
| */ | ||
| def hasSuperArgs(tree: Tree): Boolean = superArgs(tree).nonEmpty | ||
| /** In the typeCompleter (templateSig) of a case class (resp it's module), | ||
| * synthetic `copy` (reps `apply`, `unapply`) methods are added. To compute | ||
| * their signatures, the corresponding ClassDef is needed. During naming (in | ||
| * `enterClassDef`), the case class ClassDef is added as an attachment to the | ||
| * moduleClass symbol of the companion module. | ||
| */ | ||
| case class ClassForCaseCompanionAttachment(caseClass: ClassDef) | ||
| /** We need to pass the tree of the class to the companion object as the methods there need to know which methods where defined. */ | ||
| case class EnumConstantsAttachment(constants: List[TermName]) | ||
| /** We use this to keep track of the right ordinal value in typedStat as we read one enum constant after another. */ | ||
| case class EnumConstantOrdinalAttachment(value: Int) | ||
| /** Since mkInvoke, the applyDynamic/selectDynamic/etc desugarer, is disconnected | ||
| * from typedNamedApply, the applyDynamicNamed argument rewriter, the latter | ||
| * doesn’t know whether it needs to apply the rewriting because the application | ||
Oops, something went wrong.