diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 32061e0d7561..c3ad297c860b 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -258,7 +258,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma case t: TypeApply if (t.fun.symbol == Predef_classOf) => av.visit(name, t.args.head.tpe.classSymbol.denot.info.toTypeKind(bcodeStore)(innerClasesStore).toASMType) case t: tpd.Select => - if (t.symbol.denot.owner.is(Flags.Enum)) { + if (t.symbol.denot.owner.is(Flags.JavaEnum)) { val edesc = innerClasesStore.typeDescriptor(t.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class. val evalue = t.symbol.name.mangledString // value the actual enumeration value. av.visitEnum(name, edesc, evalue) @@ -710,7 +710,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def isBottomClass: Boolean = (sym ne defn.NullClass) && (sym ne defn.NothingClass) def isBridge: Boolean = sym is Flags.Bridge def isArtifact: Boolean = sym is Flags.Artifact - def hasEnumFlag: Boolean = sym is Flags.Enum + def hasEnumFlag: Boolean = sym is Flags.JavaEnum def hasAccessBoundary: Boolean = sym.accessBoundary(defn.RootClass) ne defn.RootClass def isVarargsMethod: Boolean = sym is Flags.JavaVarargs def isDeprecated: Boolean = false diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index d4582ac695c0..248dec0ebaef 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -297,8 +297,8 @@ object desugar { val isCaseClass = mods.is(Case) && !mods.is(Module) val isCaseObject = mods.is(Case) && mods.is(Module) val isImplicit = mods.is(Implicit) - val isEnum = mods.hasMod[Mod.Enum] && !mods.is(Module) - val isEnumCase = mods.hasMod[Mod.EnumCase] + val isEnum = mods.isEnumClass && !mods.is(Module) + def isEnumCase = mods.isEnumCase val isValueClass = parents.nonEmpty && isAnyVal(parents.head) // This is not watertight, but `extends AnyVal` will be replaced by `inline` later. @@ -641,7 +641,7 @@ object desugar { val moduleName = checkNotReservedName(mdef).asTermName val impl = mdef.impl val mods = mdef.mods - lazy val isEnumCase = mods.hasMod[Mod.EnumCase] + def isEnumCase = mods.isEnumCase if (mods is Package) PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, impl).withMods(mods &~ Package) :: Nil) else if (isEnumCase) @@ -688,7 +688,7 @@ object desugar { */ def patDef(pdef: PatDef)(implicit ctx: Context): Tree = flatTree { val PatDef(mods, pats, tpt, rhs) = pdef - if (mods.hasMod[Mod.EnumCase]) + if (mods.isEnumCase) pats map { case id: Ident => expandSimpleEnumCase(id.name.asTermName, mods, @@ -810,16 +810,11 @@ object desugar { * ==> * def $anonfun(params) = body * Closure($anonfun) - * - * If `inlineable` is true, tag $anonfun with an @inline annotation. */ - def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = { - var mods = synthetic | Artifact - if (inlineable) mods |= Inline + def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree())(implicit ctx: Context) = Block( - DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods), + DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic | Artifact), Closure(Nil, Ident(nme.ANON_FUN), EmptyTree)) - } /** If `nparams` == 1, expand partial function * diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 925d3f74c415..ebb3157a5733 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -33,8 +33,8 @@ object DesugarEnums { /** Is `tree` an (untyped) enum case? */ def isEnumCase(tree: Tree)(implicit ctx: Context): Boolean = tree match { - case tree: MemberDef => tree.mods.hasMod[Mod.EnumCase] - case PatDef(mods, _, _, _) => mods.hasMod[Mod.EnumCase] + case tree: MemberDef => tree.mods.isEnumCase + case PatDef(mods, _, _, _) => mods.isEnumCase case _ => false } @@ -69,7 +69,7 @@ object DesugarEnums { /** Add implied flags to an enum class or an enum case */ def addEnumFlags(cdef: TypeDef)(implicit ctx: Context) = - if (cdef.mods.hasMod[Mod.Enum]) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Abstract | Sealed)) + if (cdef.mods.isEnumClass) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Abstract | Sealed)) else if (isEnumCase(cdef)) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Final)) else cdef diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index e52213cf0485..ccadcf15d4da 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -11,7 +11,7 @@ import core.tasty.TreePickler.Hole /** A map that applies three functions and a substitution together to a tree and * makes sure they are coordinated so that the result is well-typed. The functions are - * @param typeMap A function from Type to Type that gets applied to the + * @param typeMap A function from Type to Type that gets applied to the * type of every tree node and to all locally defined symbols, * followed by the substitution [substFrom := substTo]. * @param treeMap A transformer that translates all encountered subtrees in diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 1a0d52497085..ff15c23559eb 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1083,6 +1083,10 @@ object Trees { case tree: Annotated if (arg eq tree.arg) && (annot eq tree.annot) => tree case _ => finalize(tree, untpd.Annotated(arg, annot)) } + def UntypedSplice(tree: Tree)(splice: untpd.Tree) = tree match { + case tree: tpd.UntypedSplice if tree.splice `eq` splice => tree + case _ => finalize(tree, tpd.UntypedSplice(splice)) + } def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match { case tree: Thicket if trees eq tree.trees => tree case _ => finalize(tree, untpd.Thicket(trees)) @@ -1120,7 +1124,7 @@ object Trees { */ protected def inlineContext(call: Tree)(implicit ctx: Context): Context = ctx - abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { + abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { self => def transform(tree: Tree)(implicit ctx: Context): Tree = { Stats.record(s"TreeMap.transform $getClass") @@ -1219,8 +1223,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case _ if ctx.reporter.errorsReported => - tree + case _ => + transformMoreCases(tree) } } @@ -1232,9 +1236,26 @@ object Trees { transform(tree).asInstanceOf[Tr] def transformSub[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] = transform(trees).asInstanceOf[List[Tr]] + + protected def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match { + case tpd.UntypedSplice(usplice) => + // For a typed tree map: homomorphism on the untyped part with + // recursive mapping of typed splices. + // The case is overridden in UntypedTreeMap.## + val untpdMap = new untpd.UntypedTreeMap { + override def transform(tree: untpd.Tree)(implicit ctx: Context): untpd.Tree = tree match { + case untpd.TypedSplice(tsplice) => + untpd.cpy.TypedSplice(tree)(self.transform(tsplice).asInstanceOf[tpd.Tree]) + // the cast is safe, since the UntypedSplice case is overridden in UntypedTreeMap. + case _ => super.transform(tree) + } + } + cpy.UntypedSplice(tree)(untpdMap.transform(usplice)) + case _ if ctx.reporter.errorsReported => tree + } } - abstract class TreeAccumulator[X] { + abstract class TreeAccumulator[X] { self => // Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node. def apply(x: X, tree: Tree)(implicit ctx: Context): X @@ -1329,14 +1350,29 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) => - // In interactive mode, errors might come from previous runs. - // In case of errors it may be that typed trees point to untyped ones. - // The IDE can still traverse inside such trees, either in the run where errors - // are reported, or in subsequent ones. - x + case _ => + foldMoreCases(x, tree) } } + + def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match { + case tpd.UntypedSplice(usplice) => + // For a typed tree accumulator: skip the untyped part and fold all typed splices. + // The case is overridden in UntypedTreeAccumulator. + val untpdAcc = new untpd.UntypedTreeAccumulator[X] { + override def apply(x: X, tree: untpd.Tree)(implicit ctx: Context): X = tree match { + case untpd.TypedSplice(tsplice) => self(x, tsplice) + case _ => foldOver(x, tree) + } + } + untpdAcc(x, usplice) + case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) => + // In interactive mode, errors might come from previous runs. + // In case of errors it may be that typed trees point to untyped ones. + // The IDE can still traverse inside such trees, either in the run where errors + // are reported, or in subsequent ones. + x + } } abstract class TreeTraverser extends TreeAccumulator[Unit] { diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e77b56e4c27f..b2ecf2ca24a2 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -324,7 +324,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case pre: ThisType => tp.isType || pre.cls.isStaticOwner || - tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls + tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls // was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough // and was spuriously triggered in case inner class would inherit from outer one // eg anonymous TypeMap inside TypeMap.andThen @@ -471,6 +471,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } else foldOver(sym, tree) } + case class UntypedSplice(splice: untpd.Tree) extends Tree + override val cpy: TypedTreeCopier = // Type ascription needed to pick up any new members in TreeCopier (currently there are none) new TypedTreeCopier @@ -640,7 +642,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { override def skipTransform(tree: Tree)(implicit ctx: Context) = tree.tpe.isError - implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal { + implicit class TreeOps[ThisTree <: tpd.Tree](private val tree: ThisTree) extends AnyVal { def isValue(implicit ctx: Context): Boolean = tree.isTerm && tree.tpe.widen.isValueType diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index a7e291431ede..0fb3ddd9bf36 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -24,8 +24,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice * @param owner The current owner at the time the tree was defined */ - abstract case class TypedSplice(tree: tpd.Tree)(val owner: Symbol) extends ProxyTree { - def forwardTo = tree + abstract case class TypedSplice(splice: tpd.Tree)(val owner: Symbol) extends ProxyTree { + def forwardTo = splice } object TypedSplice { @@ -130,9 +130,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Inline() extends Mod(Flags.Inline) - case class Enum() extends Mod(Flags.EmptyFlags) - - case class EnumCase() extends Mod(Flags.EmptyFlags) + case class Enum() extends Mod(Flags.Enum) } /** Modifiers and annotations for definitions @@ -163,15 +161,26 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { if (this.flags == flags) this else copy(flags = flags) - def withAddedMod(mod: Mod): Modifiers = - if (mods.exists(_ eq mod)) this - else withMods(mods :+ mod) + def withAddedMod(mod: Mod): Modifiers = + if (mods.exists(_ eq mod)) this + else withMods(mods :+ mod) - def withMods(ms: List[Mod]): Modifiers = - if (mods eq ms) this - else copy(mods = ms) + /** Modifiers with given list of Mods. It is checked that + * all modifiers are already accounted for in `flags` and `privateWithin`. + */ + def withMods(ms: List[Mod]): Modifiers = { + if (mods eq ms) this + else { + if (ms.nonEmpty) + for (m <- ms) + assert(flags.is(m.flags) || + m.isInstanceOf[Mod.Private] && !privateWithin.isEmpty, + s"unaccounted modifier: $m in $this when adding $ms") + copy(mods = ms) + } + } - def withAddedAnnotation(annot: Tree): Modifiers = + def withAddedAnnotation(annot: Tree): Modifiers = if (annotations.exists(_ eq annot)) this else withAnnotations(annotations :+ annot) @@ -186,10 +195,11 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def hasFlags = flags != EmptyFlags def hasAnnotations = annotations.nonEmpty def hasPrivateWithin = privateWithin != tpnme.EMPTY - def hasMod[T: ClassTag] = { - val cls = implicitly[ClassTag[T]].runtimeClass - mods.exists(mod => cls.isAssignableFrom(mod.getClass)) - } + + private def isEnum = is(Enum, butNot = JavaDefined) + + def isEnumCase = isEnum && is(Case) + def isEnumClass = isEnum && !is(Case) } @sharable val EmptyModifiers: Modifiers = new Modifiers() @@ -482,10 +492,14 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs)) } + def TypedSplice(tree: Tree)(splice: tpd.Tree)(implicit ctx: Context) = tree match { + case tree: TypedSplice if splice `eq` tree.splice => tree + case _ => finalize(tree, untpd.TypedSplice(splice)) + } } abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) { - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { + override def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match { case ModuleDef(name, impl) => cpy.ModuleDef(tree)(name, transformSub(impl)) case ParsedTry(expr, handler, finalizer) => @@ -526,15 +540,17 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds)) case PatDef(mods, pats, tpt, rhs) => cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) + case tpd.UntypedSplice(splice) => + cpy.UntypedSplice(tree)(transform(splice)) case TypedSplice(_) => tree case _ => - super.transform(tree) + super.transformMoreCases(tree) } } - abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { - override def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = tree match { + abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { self => + override def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match { case ModuleDef(name, impl) => this(x, impl) case ParsedTry(expr, handler, finalizer) => @@ -575,10 +591,12 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(this(x, bounds), cxBounds) case PatDef(mods, pats, tpt, rhs) => this(this(this(x, pats), tpt), rhs) - case TypedSplice(tree) => - this(x, tree) + case TypedSplice(splice) => + this(x, splice) + case tpd.UntypedSplice(splice) => + this(x, splice) case _ => - super.foldOver(x, tree) + super.foldMoreCases(x, tree) } } diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index bbbfb88262f4..fdbfe3011a8d 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -87,7 +87,6 @@ class ScalaSettings extends Settings.SettingGroup { val YdebugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions") val YdebugMissingRefs = BooleanSetting("-Ydebug-missing-refs", "Print a stacktrace when a required symbol is missing") val YdebugNames = BooleanSetting("-Ydebug-names", "Show internal representation of names") - val YdebugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)") val YtermConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val Ylog = PhasesSetting("-Ylog", "Log operations during") val YemitTasty = BooleanSetting("-Yemit-tasty", "Generate tasty in separate *.tasty file.") @@ -113,6 +112,7 @@ class ScalaSettings extends Settings.SettingGroup { val YplainPrinter = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.") val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.") val YprintDebug = BooleanSetting("-Yprint-debug", "when printing trees, print some extra information useful for debugging.") + val YprintDebugOwners = BooleanSetting("-Yprint-debug-owners", "when printing trees, print owners of definitions.") val YshowPrintErrors = BooleanSetting("-Yshow-print-errors", "don't suppress exceptions thrown during tree printing.") val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler") val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.") diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 80630f3afa18..ad052f84c7fb 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -563,6 +563,9 @@ object Flags { /** An inline parameter */ final val InlineParam = allOf(Inline, Param) + /** An enum case */ + final val EnumCase = allOf(Enum, Case) + /** A term parameter or parameter accessor */ final val TermParamOrAccessor = Param | ParamAccessor @@ -620,6 +623,15 @@ object Flags { /** A Java companion object */ final val JavaProtected = allOf(JavaDefined, Protected) + /** A Java enum */ + final val JavaEnum = allOf(JavaDefined, Enum) + + /** A Java enum trait */ + final val JavaEnumTrait = allOf(JavaDefined, Enum) + + /** A Java enum value */ + final val JavaEnumValue = allOf(Stable, JavaStatic, JavaDefined, Enum) + /** Labeled private[this] */ final val PrivateLocal = allOf(Private, Local) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 79e4a469b00a..8a8388e3adfa 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1825,7 +1825,7 @@ object Types { private def setDenot(denot: Denotation)(implicit ctx: Context): Unit = { if (ctx.isAfterTyper) - assert(!denot.isOverloaded, this) + assert(!denot.isOverloaded || ctx.mode.is(Mode.Printing), this) if (Config.checkNoDoubleBindings) if (ctx.settings.YnoDoubleBindings.value) checkSymAssign(denot.symbol) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 00e73fcd4d1e..86d7866041ad 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -186,8 +186,8 @@ class ClassfileParser( if (isEnum) { instanceScope.toList.map(_.ensureCompleted()) staticScope.toList.map(_.ensureCompleted()) - classRoot.setFlag(Flags.Enum) - moduleRoot.setFlag(Flags.Enum) + classRoot.setFlag(Flags.JavaEnum) + moduleRoot.setFlag(Flags.JavaEnum) } result diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 6e25c37c0f71..4f7436680191 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -59,17 +59,18 @@ Standard-Section: "ASTs" TopLevelStat* DEFDEF Length NameRef TypeParam* Params* returnType_Term rhs_Term? Modifier* TYPEDEF Length NameRef (type_Term | Template) Modifier* + OBJECTDEF Length NameRef Template Modifier* IMPORT Length qual_Term Selector* Selector = IMPORTED name_NameRef RENAMED to_NameRef // Imports are for scala.meta, they are not used in the backend - TypeParam = TYPEPARAM Length NameRef Type Modifier* + TypeParam = TYPEPARAM Length NameRef type_Term Modifier* Params = PARAMS Length Param* - Param = PARAM Length NameRef Type rhs_Term? Modifier* // rhs_Term is present in the case of an aliased class parameter + Param = PARAM Length NameRef type_Term rhs_Term? Modifier* // rhs_Term is present in the case of an aliased class parameter Template = TEMPLATE Length TypeParam* Param* parent_Term* Self? Stat* // Stat* always starts with the primary constructor. - Self = SELFDEF selfName_NameRef selfType_Type + Self = SELFDEF selfName_NameRef selfType_Term Term = Path IDENT NameRef Type // used when term ident’s type is not a TermRef @@ -109,6 +110,7 @@ Standard-Section: "ASTs" TopLevelStat* EMPTYTREE SHAREDterm term_ASTRef HOLE Length idx_Nat arg_Tree* + UNTYPEDSPLICE Length splice_TermUntyped splice_Type CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? ImplicitArg = IMPLICITARG arg_Term @@ -184,6 +186,7 @@ Standard-Section: "ASTs" TopLevelStat* STATIC // mapped to static Java member OBJECT // an object or its class TRAIT // a trait + ENUM // a enum class or enum case LOCAL // private[this] or protected[this] SYNTHETIC // generated by Scala compiler ARTIFACT // to be tagged Java Synthetic @@ -201,6 +204,14 @@ Standard-Section: "ASTs" TopLevelStat* Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term +// --------------- untyped additions ------------------------------------------ + + TermUntyped = Term + TYPEDSPLICE Length splice_Term + FUNCTION Length body_Term arg_Term* + INFIXOP Length op_NameRef left_Term right_Term + PATDEF Length type_Term rhs_Term pattern_Term* Modifier* + Note: Tree tags are grouped into 5 categories that determine what follows, and thus allow to compute the size of the tagged tree in a generic way. Category 1 (tags 1-49) : tag @@ -282,21 +293,22 @@ object TastyFormat { final val STATIC = 17 final val OBJECT = 18 final val TRAIT = 19 - final val LOCAL = 20 - final val SYNTHETIC = 21 - final val ARTIFACT = 22 - final val MUTABLE = 23 - final val LABEL = 24 - final val FIELDaccessor = 25 - final val CASEaccessor = 26 - final val COVARIANT = 27 - final val CONTRAVARIANT = 28 - final val SCALA2X = 29 - final val DEFAULTparameterized = 30 - final val STABLE = 31 - final val MACRO = 32 - final val ERASED = 33 - final val PARAMsetter = 34 + final val ENUM = 20 + final val LOCAL = 21 + final val SYNTHETIC = 22 + final val ARTIFACT = 23 + final val MUTABLE = 24 + final val LABEL = 25 + final val FIELDaccessor = 26 + final val CASEaccessor = 27 + final val COVARIANT = 28 + final val CONTRAVARIANT = 29 + final val SCALA2X = 30 + final val DEFAULTparameterized = 31 + final val STABLE = 32 + final val MACRO = 33 + final val ERASED = 34 + final val PARAMsetter = 35 // Cat. 2: tag Nat @@ -318,6 +330,7 @@ object TastyFormat { final val IMPORTED = 65 final val RENAMED = 66 final val SYMBOLconst = 67 + final val UIDENT = 68 // Cat. 3: tag AST @@ -398,6 +411,7 @@ object TastyFormat { final val ANNOTATION = 172 final val TERMREFin = 173 final val TYPEREFin = 174 + final val OBJECTDEF = 175 // In binary: 101100EI // I = implicit method type @@ -407,6 +421,14 @@ object TastyFormat { final val ERASEDMETHODtype = 178 final val ERASEDIMPLICITMETHODtype = 179 + final val UNTYPEDSPLICE = 199 + + // Tags for untyped trees only: + final val TYPEDSPLICE = 200 + final val FUNCTION = 201 + final val INFIXOP = 202 + final val PATDEF = 203 + def methodType(isImplicit: Boolean = false, isErased: Boolean = false) = { val implicitOffset = if (isImplicit) 1 else 0 val erasedOffset = if (isErased) 2 else 0 @@ -548,6 +570,7 @@ object TastyFormat { case VALDEF => "VALDEF" case DEFDEF => "DEFDEF" case TYPEDEF => "TYPEDEF" + case OBJECTDEF => "OBJECTDEF" case IMPORT => "IMPORT" case TYPEPARAM => "TYPEPARAM" case PARAMS => "PARAMS" @@ -615,13 +638,19 @@ object TastyFormat { case PRIVATEqualified => "PRIVATEqualified" case PROTECTEDqualified => "PROTECTEDqualified" case HOLE => "HOLE" + + case UNTYPEDSPLICE => "UNTYPEDSPLICE" + case TYPEDSPLICE => "TYPEDSPLICE" + case FUNCTION => "FUNCTION" + case INFIXOP => "INFIXOP" + case PATDEF => "PATDEF" } /** @return If non-negative, the number of leading references (represented as nats) of a length/trees entry. * If negative, minus the number of leading non-reference trees. */ def numRefs(tag: Int) = tag match { - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | + case VALDEF | DEFDEF | TYPEDEF | OBJECTDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | HOLE => 1 case RENAMED | PARAMtype => 2 case POLYtype | METHODtype | TYPELAMBDAtype => -1 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index fd94296fd93d..1a1f2dadc4ba 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -63,9 +63,9 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { tag match { case RENAMED => printName(); printName() - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => + case VALDEF | DEFDEF | TYPEDEF | OBJECTDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => printName(); printTrees() - case REFINEDtype => + case REFINEDtype | TERMREFin | TYPEREFin => printName(); printTree(); printTrees() case RETURN | HOLE => printNat(); printTrees() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 4c4ab3c72147..679be32e3aa5 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -4,7 +4,7 @@ package core package tasty import ast.Trees._ -import ast.{untpd, tpd} +import ast.{untpd, tpd, desugar} import TastyFormat._ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ import collection.mutable @@ -504,16 +504,7 @@ class TreePickler(pickler: TastyPickler) { } case Import(expr, selectors) => writeByte(IMPORT) - withLength { - pickleTree(expr) - selectors foreach { - case Thicket((from @ Ident(_)) :: (to @ Ident(_)) :: Nil) => - pickleSelector(IMPORTED, from) - pickleSelector(RENAMED, to) - case id @ Ident(_) => - pickleSelector(IMPORTED, id) - } - } + withLength { pickleTree(expr); pickleSelectors(selectors) } case PackageDef(pid, stats) => writeByte(PACKAGE) withLength { pickleType(pid.tpe); pickleStats(stats) } @@ -545,7 +536,7 @@ class TreePickler(pickler: TastyPickler) { pickleTree(tp) case Annotated(tree, annot) => writeByte(ANNOTATEDtpt) - withLength { pickleTree(tree); pickleTree(annot.tree) } + withLength { pickleTree(tree); pickleTree(annot) } case LambdaTypeTree(tparams, body) => writeByte(LAMBDAtpt) withLength { pickleParams(tparams); pickleTree(body) } @@ -555,6 +546,9 @@ class TreePickler(pickler: TastyPickler) { pickleTree(lo); if (hi ne lo) pickleTree(hi) } + case tpd.UntypedSplice(splice) => + writeByte(UNTYPEDSPLICE) + withLength { pickleUntyped(splice); pickleType(tree.tpe) } case Hole(idx, args) => writeByte(HOLE) withLength { @@ -569,6 +563,15 @@ class TreePickler(pickler: TastyPickler) { } } + def pickleSelectors(selectors: List[untpd.Tree])(implicit ctx: Context): Unit = + selectors foreach { + case Thicket((from @ Ident(_)) :: (to @ Ident(_)) :: Nil) => + pickleSelector(IMPORTED, from) + pickleSelector(RENAMED, to) + case id @ Ident(_) => + pickleSelector(IMPORTED, id) + } + def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = { registerTreeAddr(id) writeByte(tag) @@ -577,37 +580,47 @@ class TreePickler(pickler: TastyPickler) { def pickleModifiers(sym: Symbol)(implicit ctx: Context): Unit = { import Flags._ - val flags = sym.flags + var flags = sym.flags val privateWithin = sym.privateWithin if (privateWithin.exists) { writeByte(if (flags is Protected) PROTECTEDqualified else PRIVATEqualified) pickleType(privateWithin.typeRef) + flags = flags &~ Protected } + if ((flags is ParamAccessor) && sym.isTerm && !sym.isSetter) + flags = flags &~ ParamAccessor // we only generate a tag for parameter setters + pickleFlags(flags, sym.isTerm) + sym.annotations.foreach(pickleAnnotation(sym, _)) + } + + def pickleFlags(flags: Flags.FlagSet, isTerm: Boolean)(implicit ctx: Context): Unit = { + import Flags._ if (flags is Private) writeByte(PRIVATE) - if (flags is Protected) if (!privateWithin.exists) writeByte(PROTECTED) - if ((flags is Final) && !(sym is Module)) writeByte(FINAL) + if (flags is Protected) writeByte(PROTECTED) + if (flags.is(Final, butNot = Module)) writeByte(FINAL) if (flags is Case) writeByte(CASE) if (flags is Override) writeByte(OVERRIDE) if (flags is Inline) writeByte(INLINE) if (flags is Macro) writeByte(MACRO) if (flags is JavaStatic) writeByte(STATIC) if (flags is Module) writeByte(OBJECT) + if (flags is Enum) writeByte(ENUM) if (flags is Local) writeByte(LOCAL) if (flags is Synthetic) writeByte(SYNTHETIC) if (flags is Artifact) writeByte(ARTIFACT) if (flags is Scala2x) writeByte(SCALA2X) - if (sym.isTerm) { + if (isTerm) { if (flags is Implicit) writeByte(IMPLICIT) if (flags is Erased) writeByte(ERASED) - if ((flags is Lazy) && !(sym is Module)) writeByte(LAZY) + if (flags.is(Lazy, butNot = Module)) writeByte(LAZY) if (flags is AbsOverride) { writeByte(ABSTRACT); writeByte(OVERRIDE) } if (flags is Mutable) writeByte(MUTABLE) if (flags is Accessor) writeByte(FIELDaccessor) if (flags is CaseAccessor) writeByte(CASEaccessor) if (flags is DefaultParameterized) writeByte(DEFAULTparameterized) if (flags is Stable) writeByte(STABLE) - if ((flags is ParamAccessor) && sym.isSetter) writeByte(PARAMsetter) - if ((flags is Label)) writeByte(LABEL) + if (flags is ParamAccessor) writeByte(PARAMsetter) + if (flags is Label) writeByte(LABEL) } else { if (flags is Sealed) writeByte(SEALED) if (flags is Abstract) writeByte(ABSTRACT) @@ -615,7 +628,6 @@ class TreePickler(pickler: TastyPickler) { if (flags is Covariant) writeByte(COVARIANT) if (flags is Contravariant) writeByte(CONTRAVARIANT) } - sym.annotations.foreach(pickleAnnotation(sym, _)) } private def isUnpicklable(owner: Symbol, ann: Annotation)(implicit ctx: Context) = ann match { @@ -634,6 +646,231 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) } } +// ---- pickling untyped trees ---------------------------------- + + def pickleUntyped(tree: untpd.Tree)(implicit ctx: Context): Unit = { + + def pickleDummyRef(): Unit = writeNat(0) + + def pickleDummyType(): Unit = { + writeByte(SHAREDtype) + pickleDummyRef() + } + + def pickleUnlessEmpty(tree: untpd.Tree): Unit = + if (!tree.isEmpty) pickleUntyped(tree) + + def pickleTpt(tree: untpd.Tree) = pickleUntyped(tree)(ctx.addMode(Mode.Type)) + def pickleTerm(tree: untpd.Tree) = pickleUntyped(tree)(ctx.retractMode(Mode.Type)) + + def pickleAllParams(tree: untpd.DefDef): Unit = { + pickleParams(tree.tparams) + for (vparams <- tree.vparamss) { + writeByte(PARAMS) + withLength { pickleParams(vparams) } + } + } + + def pickleParams(trees: List[untpd.Tree]): Unit = + trees.foreach(pickleParam) + + def pickleParam(tree: untpd.Tree): Unit = tree match { + case tree: untpd.ValDef => pickleDef(PARAM, tree, tree.tpt) + case tree: untpd.DefDef => pickleDef(PARAM, tree, tree.tpt, tree.rhs) + case tree: untpd.TypeDef => pickleDef(TYPEPARAM, tree, tree.rhs) + } + + def pickleParent(tree: untpd.Tree): Unit = tree match { + case _: untpd.Apply | _: untpd.TypeApply => pickleUntyped(tree) + case _ => pickleTpt(tree) + } + + def pickleDef(tag: Int, tree: untpd.MemberDef, tpt: untpd.Tree, rhs: untpd.Tree = untpd.EmptyTree, pickleParams: => Unit = ()) = { + import untpd.modsDeco + writeByte(tag) + withLength { + pickleName(tree.name) + pickleParams + pickleTpt(tpt) + pickleUnlessEmpty(rhs) + pickleModifiers(tree.mods, tree.name.isTermName) + } + } + + def pickleModifiers(mods: untpd.Modifiers, isTerm: Boolean): Unit = { + import Flags._ + var flags = mods.flags + val privateWithin = mods.privateWithin + if (!privateWithin.isEmpty) { + writeByte(if (flags is Protected) PROTECTEDqualified else PRIVATEqualified) + pickleUntyped(untpd.Ident(privateWithin)) + flags = flags &~ Protected + } + pickleFlags(flags, isTerm) + mods.annotations.foreach(pickleAnnotation) + } + + def pickleAnnotation(annotTree: untpd.Tree) = { + writeByte(ANNOTATION) + withLength { pickleDummyType(); pickleUntyped(annotTree) } + } + + try tree match { + case Ident(name) => + writeByte(if (name.isTypeName) TYPEREF else TERMREF) + pickleName(name) + pickleDummyType() + case This(qual) => + writeByte(QUALTHIS) + pickleUntyped(qual) + case Select(qual, name) => + writeByte(if (name.isTypeName) SELECTtpt else SELECT) + pickleName(name) + if (qual.isType) pickleTpt(qual) else pickleTerm(qual) + case Apply(fun, args) => + writeByte(APPLY) + withLength { + pickleUntyped(fun) + args.foreach(pickleUntyped) + } + case untpd.Throw(exc) => + writeByte(THROW) + pickleUntyped(exc) + case TypeApply(fun, args) => + writeByte(TYPEAPPLY) + withLength { + pickleUntyped(fun) + args.foreach(pickleTpt) + } + case Literal(const) => + pickleConstant(const) + case Super(qual, mix) => + writeByte(SUPER) + withLength { + pickleUntyped(qual); + if (!mix.isEmpty) pickleUntyped(mix) + } + case New(tpt) => + writeByte(NEW) + pickleTpt(tpt) + case Typed(expr, tpt) => + writeByte(TYPED) + withLength { pickleUntyped(expr); pickleTpt(tpt) } + case NamedArg(name, arg) => + writeByte(NAMEDARG) + pickleName(name) + pickleUntyped(arg) + case Assign(lhs, rhs) => + writeByte(ASSIGN) + withLength { pickleUntyped(lhs); pickleUntyped(rhs) } + case Block(stats, expr) => + writeByte(BLOCK) + withLength { pickleUntyped(expr); stats.foreach(pickleUntyped) } + case If(cond, thenp, elsep) => + writeByte(IF) + withLength { pickleUntyped(cond); pickleUntyped(thenp); pickleUntyped(elsep) } + case Match(selector, cases) => + writeByte(MATCH) + withLength { pickleUntyped(selector); cases.foreach(pickleUntyped) } + case CaseDef(pat, guard, rhs) => + writeByte(CASEDEF) + withLength { pickleUntyped(pat); pickleUntyped(rhs); pickleUnlessEmpty(guard) } + case Return(expr, from) => + writeByte(RETURN) + withLength { pickleDummyRef(); pickleUnlessEmpty(expr) } + case Try(block, cases, finalizer) => + writeByte(TRY) + withLength { pickleUntyped(block); cases.foreach(pickleUntyped); pickleUnlessEmpty(finalizer) } + case Bind(name, body) => + writeByte(BIND) + withLength { + pickleName(name); pickleDummyType(); pickleUntyped(body) + } + case Alternative(alts) => + writeByte(ALTERNATIVE) + withLength { alts.foreach(pickleUntyped) } + case tree: untpd.ValDef => + pickleDef(VALDEF, tree, tree.tpt, tree.rhs) + case tree: untpd.DefDef => + pickleDef(DEFDEF, tree, tree.tpt, tree.rhs, pickleAllParams(tree)) + case tree: untpd.TypeDef => + pickleDef(TYPEDEF, tree, tree.rhs) + case tree: untpd.ModuleDef => + pickleDef(OBJECTDEF, tree, tree.impl) + case tree: untpd.Template => + writeByte(TEMPLATE) + tree.parents.foreach(pickleParent) + if (!tree.self.isEmpty) { + writeByte(SELFDEF); pickleName(tree.self.name); pickleTpt(tree.self.tpt) + } + pickleUntyped(tree.constr) + tree.body.foreach(pickleUntyped) + case Import(expr, selectors) => + writeByte(IMPORT) + withLength { pickleUntyped(expr); pickleSelectors(selectors) } + case tree: untpd.TypeTree => + pickleDummyType() + case SingletonTypeTree(ref) => + writeByte(SINGLETONtpt) + pickleTerm(ref) + case RefinedTypeTree(parent, refinements) => + writeByte(REFINEDtpt) + withLength { pickleTpt(parent); refinements.foreach(pickleTerm) } + case AppliedTypeTree(tycon, args) => + writeByte(APPLIEDtpt) + withLength { pickleTpt(tycon); args.foreach(pickleTpt) } + case AndTypeTree(tp1, tp2) => + writeByte(ANDtpt) + withLength { pickleTpt(tp1); pickleTpt(tp2) } + case OrTypeTree(tp1, tp2) => + writeByte(ORtpt) + withLength { pickleTpt(tp1); pickleTpt(tp2) } + case ByNameTypeTree(tp) => + writeByte(BYNAMEtpt) + pickleTpt(tp) + case Annotated(tree, annot) => + writeByte(ANNOTATEDtpt) + withLength { pickleTpt(tree); pickleTerm(annot) } + case LambdaTypeTree(tparams, body) => + writeByte(LAMBDAtpt) + withLength { pickleParams(tparams); pickleTpt(body) } + case TypeBoundsTree(lo, hi) => + writeByte(TYPEBOUNDStpt) + withLength { + pickleTpt(lo); + if (hi ne lo) pickleTpt(hi) + } + case untpd.Function(args, body) => + writeByte(FUNCTION) + withLength { pickleUntyped(body); args.foreach(pickleUntyped) } + case untpd.InfixOp(l, op, r) => + writeByte(INFIXOP) + withLength { pickleUntyped(l); pickleUntyped(op); pickleUntyped(r) } + case untpd.PatDef(mods, pats, tpt, rhs) => + writeByte(PATDEF) + withLength { + pickleTpt(tpt) + pickleUntyped(rhs) + pats.foreach(pickleUntyped) + pickleModifiers(mods, isTerm = true) + } + case untpd.TypedSplice(splice) => + writeByte(TYPEDSPLICE) + withLength { pickleTree(splice) } + case Thicket(trees) => + trees.foreach(pickleUntyped) + case _ => + pickleUntyped(desugar(tree)) + } + catch { + case ex: AssertionError => + println(i"error when pickling tree $tree") + throw ex + } + } + +// ---- main entry points --------------------------------------- + def pickle(trees: List[Tree])(implicit ctx: Context) = { trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree)) def missing = forwardSymRefs.keysIterator.map(_.showLocated).toList diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 5b35002530f7..ac356a44e5d9 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -228,6 +228,39 @@ class TreeUnpickler(reader: TastyReader, createSymbol() } + def readConstant(tag: Int)(implicit ctx: Context): Constant = (tag: @switch) match { + case UNITconst => + Constant(()) + case TRUEconst => + Constant(true) + case FALSEconst => + Constant(false) + case BYTEconst => + Constant(readInt().toByte) + case SHORTconst => + Constant(readInt().toShort) + case CHARconst => + Constant(readNat().toChar) + case INTconst => + Constant(readInt()) + case LONGconst => + Constant(readLongInt()) + case FLOATconst => + Constant(java.lang.Float.intBitsToFloat(readInt())) + case DOUBLEconst => + Constant(java.lang.Double.longBitsToDouble(readLongInt())) + case STRINGconst => + Constant(readName().toString) + case NULLconst => + Constant(null) + case CLASSconst => + Constant(readType()) + case ENUMconst => + Constant(readTermRef().termSymbol) + case SYMBOLconst => + Constant(scala.Symbol(readName().toString)) + } + /** Read a type */ def readType()(implicit ctx: Context): Type = { val start = currentAddr @@ -313,10 +346,6 @@ class TreeUnpickler(reader: TastyReader, readTypeRef() match { case binder: LambdaType => binder.paramRefs(readNat()) } - case CLASSconst => - ConstantType(Constant(readType())) - case ENUMconst => - ConstantType(Constant(readTermRef().termSymbol)) case HOLE => readHole(end, isType = true).tpe } @@ -356,38 +385,10 @@ class TreeUnpickler(reader: TastyReader, case SHAREDtype => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) - case UNITconst => - ConstantType(Constant(())) - case TRUEconst => - ConstantType(Constant(true)) - case FALSEconst => - ConstantType(Constant(false)) - case BYTEconst => - ConstantType(Constant(readInt().toByte)) - case SHORTconst => - ConstantType(Constant(readInt().toShort)) - case CHARconst => - ConstantType(Constant(readNat().toChar)) - case INTconst => - ConstantType(Constant(readInt())) - case LONGconst => - ConstantType(Constant(readLongInt())) - case FLOATconst => - ConstantType(Constant(java.lang.Float.intBitsToFloat(readInt()))) - case DOUBLEconst => - ConstantType(Constant(java.lang.Double.longBitsToDouble(readLongInt()))) - case STRINGconst => - ConstantType(Constant(readName().toString)) - case NULLconst => - ConstantType(Constant(null)) - case CLASSconst => - ConstantType(Constant(readType())) - case ENUMconst => - ConstantType(Constant(readTermRef().termSymbol)) - case SYMBOLconst => - ConstantType(Constant(scala.Symbol(readName().toString))) case BYNAMEtype => ExprType(readType()) + case _ => + ConstantType(readConstant(tag)) } if (tag < firstLengthTreeTag) readSimpleType() else readLengthType() @@ -420,7 +421,7 @@ class TreeUnpickler(reader: TastyReader, // ------ Reading definitions ----------------------------------------------------- - private def noRhs(end: Addr): Boolean = + private def nothingButMods(end: Addr): Boolean = currentAddr == end || isModifierTag(nextByte) private def localContext(owner: Symbol)(implicit ctx: Context) = @@ -510,9 +511,9 @@ class TreeUnpickler(reader: TastyReader, val templateStart = currentAddr skipTree() // tpt val rhsStart = currentAddr - val rhsIsEmpty = noRhs(end) + val rhsIsEmpty = nothingButMods(end) if (!rhsIsEmpty) skipTree() - val (givenFlags, annots, privateWithin) = readModifiers(end) + val (givenFlags, annots, privateWithin) = readModifiers(end, readAnnot, readWithin, NoSymbol) pickling.println(i"creating symbol $name at $start with flags $givenFlags") val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty) def adjustIfModule(completer: LazyType) = @@ -560,10 +561,12 @@ class TreeUnpickler(reader: TastyReader, /** Read modifier list into triplet of flags, annotations and a privateWithin * boundary symbol. */ - def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Annotation], Symbol) = { + def readModifiers[WithinType, AnnotType] + (end: Addr, readAnnot: Context => AnnotType, readWithin: Context => WithinType, defaultWithin: WithinType) + (implicit ctx: Context): (FlagSet, List[AnnotType], WithinType) = { var flags: FlagSet = EmptyFlags - var annots = new mutable.ListBuffer[Annotation] - var privateWithin: Symbol = NoSymbol + var annots: List[AnnotType] = Nil + var privateWithin = defaultWithin while (currentAddr.index != end.index) { def addFlag(flag: FlagSet) = { flags |= flag @@ -591,6 +594,7 @@ class TreeUnpickler(reader: TastyReader, case STATIC => addFlag(JavaStatic) case OBJECT => addFlag(Module) case TRAIT => addFlag(Trait) + case ENUM => addFlag(Enum) case LOCAL => addFlag(Local) case SYNTHETIC => addFlag(Synthetic) case ARTIFACT => addFlag(Artifact) @@ -607,23 +611,31 @@ class TreeUnpickler(reader: TastyReader, addFlag(ParamAccessor) case PRIVATEqualified => readByte() - privateWithin = readType().typeSymbol + privateWithin = readWithin(ctx) case PROTECTEDqualified => addFlag(Protected) - privateWithin = readType().typeSymbol + privateWithin = readWithin(ctx) case ANNOTATION => - readByte() - val end = readEnd() - val tp = readType() - val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx)) - annots += Annotation.deferredSymAndTree( - implicit ctx => tp.typeSymbol, - implicit ctx => lazyAnnotTree.complete) + annots = readAnnot(ctx) :: annots case tag => assert(false, s"illegal modifier tag $tag at $currentAddr, end = $end") } } - (flags, annots.toList, privateWithin) + (flags, annots.reverse, privateWithin) + } + + private val readWithin: Context => Symbol = + implicit ctx => readType().typeSymbol + + private val readAnnot: Context => Annotation = { + implicit ctx => + readByte() + val end = readEnd() + val tp = readType() + val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx)) + Annotation.deferredSymAndTree( + implicit ctx => tp.typeSymbol, + implicit ctx => lazyAnnotTree.complete) } /** Create symbols for the definitions in the statement sequence between @@ -720,7 +732,7 @@ class TreeUnpickler(reader: TastyReader, val localCtx = localContext(sym) def readRhs(implicit ctx: Context) = - if (noRhs(end)) EmptyTree + if (nothingButMods(end)) EmptyTree else readLater(end, rdr => ctx => rdr.readTerm()(ctx.retractMode(Mode.InSuperCall))) def ValDef(tpt: Tree) = @@ -785,7 +797,7 @@ class TreeUnpickler(reader: TastyReader, } case PARAM => val tpt = readTpt()(localCtx) - if (noRhs(end)) { + if (nothingButMods(end)) { sym.info = tpt.tpe ValDef(tpt) } @@ -895,26 +907,27 @@ class TreeUnpickler(reader: TastyReader, readByte() readEnd() val expr = readTerm() - def readSelectors(): List[untpd.Tree] = nextByte match { - case IMPORTED => - val start = currentAddr - readByte() - val from = setPos(start, untpd.Ident(readName())) - nextByte match { - case RENAMED => - val start2 = currentAddr - readByte() - val to = setPos(start2, untpd.Ident(readName())) - untpd.Thicket(from, to) :: readSelectors() - case _ => - from :: readSelectors() - } - case _ => - Nil - } setPos(start, Import(expr, readSelectors())) } + def readSelectors()(implicit ctx: Context): List[untpd.Tree] = nextByte match { + case IMPORTED => + val start = currentAddr + readByte() + val from = setPos(start, untpd.Ident(readName())) + nextByte match { + case RENAMED => + val start2 = currentAddr + readByte() + val to = setPos(start2, untpd.Ident(readName())) + untpd.Thicket(from, to) :: readSelectors() + case _ => + from :: readSelectors() + } + case _ => + Nil + } + def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = until(end)(readIndexedStat(exprOwner)) @@ -1102,6 +1115,8 @@ class TreeUnpickler(reader: TastyReader, TypeBoundsTree(lo, hi) case HOLE => readHole(end, isType = false) + case UNTYPEDSPLICE => + tpd.UntypedSplice(readUntyped()).withType(readType()) case _ => readPathTerm() } @@ -1168,6 +1183,194 @@ class TreeUnpickler(reader: TastyReader, PickledQuotes.quotedExprToTree(quotedExpr) } } +// ------ Reading untyped trees -------------------------------------------- + + def readUntyped()(implicit ctx: Context): untpd.Tree = { + val start = currentAddr + val tag = readByte() + pickling.println(s"reading term ${astTagToString(tag)} at $start") + + def readDummyType(): Unit = { + assert(readByte() == SHAREDtype) + assert(readNat() == 0) + } + + def readIdent(): untpd.Ident = readUntyped().asInstanceOf[untpd.Ident] + + def readParams[T <: untpd.MemberDef](tag: Int): List[T] = + collectWhile(nextByte == tag)(readUntyped().asInstanceOf[T]) + + def readParamss(): List[List[untpd.ValDef]] = + collectWhile(nextByte == PARAMS) { + readByte() + readEnd() + readParams[untpd.ValDef](PARAM) + } + + def readCases(end: Addr): List[untpd.CaseDef] = + collectWhile((nextUnsharedTag == CASEDEF) && currentAddr != end) { + readUntyped().asInstanceOf[untpd.CaseDef] + } + + def readSimpleTerm(): untpd.Tree = (tag: @switch) match { + case TERMREF => + val name = readName() + readDummyType() + untpd.Ident(name) + case TYPEREF => + val name = readName().toTypeName + readDummyType() + untpd.Ident(name) + case SELECT => + val name = readName() + val qual = readUntyped() + untpd.Select(qual, name) + case SELECTtpt => + val name = readName().toTypeName + val qual = readUntyped() + untpd.Select(qual, name) + case QUALTHIS => + untpd.This(readIdent()) + case NEW => + untpd.New(readUntyped()) + case THROW => + untpd.Throw(readUntyped()) + case SINGLETONtpt => + untpd.SingletonTypeTree(readUntyped()) + case BYNAMEtpt => + untpd.ByNameTypeTree(readUntyped()) + case NAMEDARG => + untpd.NamedArg(readName(), readUntyped()) + case SHAREDtype => + assert(readNat() == 0) + untpd.TypeTree() + case _ => + untpd.Literal(readConstant(tag)) + } + + def readLengthTerm(): untpd.Tree = { + val end = readEnd() + + def readMods(): untpd.Modifiers = { + val (flags, annots, privateWithin) = + readModifiers(end, readUntypedAnnot, readUntypedWithin, EmptyTypeName) + untpd.Modifiers(flags, privateWithin, annots) + } + + def readRhs(): untpd.Tree = + if (nothingButMods(end)) untpd.EmptyTree else readUntyped() + + val result = (tag: @switch) match { + case SUPER => + val qual = readUntyped() + val mixId = ifBefore(end)(readIdent(), untpd.EmptyTypeIdent) + untpd.Super(qual, mixId) + case APPLY => + val fn = readUntyped() + untpd.Apply(fn, until(end)(readUntyped())) + case TYPEAPPLY => + untpd.TypeApply(readUntyped(), until(end)(readUntyped())) + case TYPED => + val expr = readUntyped() + val tpt = readUntyped() + untpd.Typed(expr, tpt) + case ASSIGN => + untpd.Assign(readUntyped(), readUntyped()) + case BLOCK => + val expr = readUntyped() + val stats = until(end)(readUntyped()) + untpd.Block(stats, expr) + case IF => + untpd.If(readUntyped(), readUntyped(), readUntyped()) + case MATCH => + untpd.Match(readUntyped(), readCases(end)) + case CASEDEF => + val pat = readUntyped() + val rhs = readUntyped() + val guard = ifBefore(end)(readUntyped(), untpd.EmptyTree) + untpd.CaseDef(pat, guard, rhs) + case RETURN => + readNat() + val expr = ifBefore(end)(readUntyped(), untpd.EmptyTree) + untpd.Return(expr, untpd.EmptyTree) + case TRY => + untpd.Try(readUntyped(), readCases(end), ifBefore(end)(readUntyped(), untpd.EmptyTree)) + case BIND => + val name = readName() + readDummyType() + untpd.Bind(name, readUntyped()) + case ALTERNATIVE => + untpd.Alternative(until(end)(readUntyped())) + case DEFDEF => + untpd.DefDef(readName(), readParams[TypeDef](TYPEPARAM), readParamss(), readUntyped(), readRhs()) + .withMods(readMods()) + case VALDEF | PARAM => + untpd.ValDef(readName(), readUntyped(), readRhs()) + .withMods(readMods()) + case TYPEDEF | TYPEPARAM => + untpd.TypeDef(readName().toTypeName, readUntyped()) + .withMods(readMods()) + case OBJECTDEF => + untpd.ModuleDef(readName(), readUntyped().asInstanceOf[untpd.Template]) + .withMods(readMods()) + case TEMPLATE => + val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF)(readUntyped()) + val self = + if (nextByte == SELFDEF) { + readByte() + untpd.ValDef(readName(), readUntyped(), untpd.EmptyTree) + } + else untpd.EmptyValDef + val constr = readUntyped().asInstanceOf[untpd.DefDef] + val body = until(end)(readUntyped()) + untpd.Template(constr, parents, self, body) + case IMPORT => + untpd.Import(readUntyped(), readSelectors()) + case REFINEDtpt => + untpd.RefinedTypeTree(readUntyped(), until(end)(readUntyped())) + case APPLIEDtpt => + untpd.AppliedTypeTree(readUntyped(), until(end)(readUntyped())) + case ANDtpt => + untpd.AndTypeTree(readUntyped(), readUntyped()) + case ORtpt => + untpd.OrTypeTree(readUntyped(), readUntyped()) + case ANNOTATEDtpt => + untpd.Annotated(readUntyped(), readUntyped()) + case LAMBDAtpt => + val tparams = readParams[TypeDef](TYPEPARAM) + val body = readUntyped() + untpd.LambdaTypeTree(tparams, body) + case TYPEBOUNDStpt => + val lo = readUntyped() + val hi = ifBefore(end)(lo, readUntyped()) + untpd.TypeBoundsTree(lo, hi) + case TYPEDSPLICE => + untpd.TypedSplice(readTerm()) + case FUNCTION => + val body = readUntyped() + val params = until(end)(readUntyped()) + untpd.Function(params, body) + case INFIXOP => + untpd.InfixOp(readUntyped(), readIdent(), readUntyped()) + case PATDEF => + val tpt = readUntyped() + val rhs = readUntyped() + val pats = collectWhile(!nothingButMods(end))(readUntyped()) + untpd.PatDef(readMods(), pats, tpt, rhs) + } + assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") + result + } + + val tree = if (tag < firstLengthTreeTag) readSimpleTerm() else readLengthTerm() + setPos(start, tree) + } + + private val readUntypedWithin: Context => TypeName = + implicit ctx => readName().toTypeName + + private val readUntypedAnnot: Context => untpd.Tree = + implicit ctx => readUntyped() // ------ Setting positions ------------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index a39c281d200f..5c203d123f4a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -842,7 +842,7 @@ object JavaParsers { List(Literal(Constant(null)),Literal(Constant(0)))) val enumclazz = atPos(start, nameOffset) { TypeDef(name, - makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.Enum) + makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.JavaEnum) } addCompanionObject(consts ::: statics ::: predefs, enumclazz) } @@ -861,7 +861,7 @@ object JavaParsers { skipAhead() accept(RBRACE) } - ValDef(name.toTermName, enumType, unimplementedExpr).withMods(Modifiers(Flags.Enum | Flags.Stable | Flags.JavaDefined | Flags.JavaStatic)) + ValDef(name.toTermName, enumType, unimplementedExpr).withMods(Modifiers(Flags.JavaEnum | Flags.Stable | Flags.JavaDefined | Flags.JavaStatic)) } } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 560699232edf..e35d23e10abd 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2270,7 +2270,7 @@ object Parsers { /** EnumCase = `case' (id ClassConstr [`extends' ConstrApps] | ids) */ def enumCase(start: Offset, mods: Modifiers): DefTree = { - val mods1 = mods.withAddedMod(atPos(in.offset)(Mod.EnumCase())) | Case + val mods1 = addMod(mods, atPos(in.offset)(Mod.Enum())) | Case accept(CASE) in.adjustSepRegions(ARROW) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 6fd5da3fc623..dff92ebfa800 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -37,7 +37,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { /** A stack of enclosing DefDef, TypeDef, or ClassDef, or ModuleDefs nodes */ private[this] var enclosingDef: untpd.Tree = untpd.EmptyTree - private[this] var myCtx: Context = _ctx + private[this] var myCtx: Context = super.ctx private[this] var printPos = ctx.settings.YprintPos.value private[this] val printLines = ctx.settings.printLines.value @@ -446,7 +446,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case EmptyTree => "" case TypedSplice(t) => - toText(t) + if (ctx.settings.YprintDebug.value) "[" ~ toText(t) ~ "]#TS#" + else toText(t) + case tpd.UntypedSplice(t) => + if (ctx.settings.YprintDebug.value) "[" ~ toText(t) ~ ":" ~ toText(tree.typeOpt) ~ "]#US#" + else toText(t) case tree @ ModuleDef(name, impl) => withEnclosingDef(tree) { modText(tree.mods, NoSymbol, keywordStr("object")) ~~ nameIdText(tree) ~ toTextTemplate(impl) @@ -544,6 +548,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { // add type to term nodes; replace type nodes with their types unless -Yprint-pos is also set. def tp = tree.typeOpt match { case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => tp.underlying + case tp: ConstantType if homogenizedView => + // constant folded types are forgotten in Tasty, are reconstituted subsequently in FirstTransform. + // Therefore we have to gloss over this when comparing before/after pickling by widening to + // underlying type `T`, or, if expression is a unary primitive operation, to `=> T`. + tree match { + case Select(qual, _) if qual.typeOpt.widen.typeSymbol.isPrimitiveValueClass => + ExprType(tp.widen) + case _ => tp.widen + } case tp => tp } if (!suppressTypes) @@ -603,11 +616,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else toText(tree.name) ~ idText(tree) } + private def toTextOwner(tree: Tree[_]) = + "[owner = " ~ tree.symbol.owner.show ~ "]" provided ctx.settings.YprintDebugOwners.value + protected def dclTextOr[T >: Untyped](tree: Tree[T])(treeText: => Text) = - if (useSymbol(tree)) - annotsText(tree.symbol) ~~ dclText(tree.symbol) ~ - ( " " provided ctx.settings.YdebugOwners.value) - else treeText + toTextOwner(tree) ~ { + if (useSymbol(tree)) annotsText(tree.symbol) ~~ dclText(tree.symbol) + else treeText + } def tparamsText[T >: Untyped](params: List[Tree[T]]): Text = "[" ~ toText(params, ", ") ~ "]" provided params.nonEmpty diff --git a/compiler/src/dotty/tools/dotc/reporting/trace.scala b/compiler/src/dotty/tools/dotc/reporting/trace.scala index d42008500bbf..20617bb2648d 100644 --- a/compiler/src/dotty/tools/dotc/reporting/trace.scala +++ b/compiler/src/dotty/tools/dotc/reporting/trace.scala @@ -14,25 +14,30 @@ object trace { conditionally(ctx.settings.YdebugTrace.value, question, false)(op) @inline - def conditionally[TC](cond: Boolean, question: => String, show: Boolean)(op: => TC)(implicit ctx: Context): TC = { - def op1 = op - if (Config.tracingEnabled && cond) apply[TC](question, Printers.default, show)(op1) - else op1 - } + def conditionally[TC](cond: Boolean, question: => String, show: Boolean)(op: => TC)(implicit ctx: Context): TC = + if (Config.tracingEnabled) { + def op1 = op + if (cond) apply[TC](question, Printers.default, show)(op1) + else op1 + } else op @inline - def apply[T](question: => String, printer: Printers.Printer, showOp: Any => String)(op: => T)(implicit ctx: Context): T = { - def op1 = op - if (!Config.tracingEnabled || printer.eq(config.Printers.noPrinter)) op1 - else doTrace[T](question, printer, showOp)(op1) - } + def apply[T](question: => String, printer: Printers.Printer, showOp: Any => String)(op: => T)(implicit ctx: Context): T = + if (Config.tracingEnabled) { + def op1 = op + if (printer.eq(config.Printers.noPrinter)) op1 + else doTrace[T](question, printer, showOp)(op1) + } + else op @inline - def apply[T](question: => String, printer: Printers.Printer, show: Boolean)(op: => T)(implicit ctx: Context): T = { - def op1 = op - if (!Config.tracingEnabled || printer.eq(config.Printers.noPrinter)) op1 - else doTrace[T](question, printer, if (show) showShowable(_) else alwaysToString)(op1) - } + def apply[T](question: => String, printer: Printers.Printer, show: Boolean)(op: => T)(implicit ctx: Context): T = + if (Config.tracingEnabled) { + def op1 = op + if (printer.eq(config.Printers.noPrinter)) op1 + else doTrace[T](question, printer, if (show) showShowable(_) else alwaysToString)(op1) + } + else op @inline def apply[T](question: => String, printer: Printers.Printer)(op: => T)(implicit ctx: Context): T = diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index f5b207926f8a..6824df459bde 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -66,7 +66,7 @@ abstract class AccessProxies { /** An accessor symbol, create a fresh one unless one exists already */ private def accessorSymbol(owner: Symbol, accessorName: TermName, accessorInfo: Type, accessed: Symbol)(implicit ctx: Context) = { - def refersToAccessed(sym: Symbol) = accessedBy.get(sym) == Some(accessed) + def refersToAccessed(sym: Symbol) = accessedBy.get(sym).contains(accessed) owner.info.decl(accessorName).suchThat(refersToAccessed).symbol.orElse { val acc = newAccessorSymbol(owner, accessorName, accessorInfo, accessed.pos) accessedBy(acc) = accessed diff --git a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala index 9d84a2adb66c..a2f27747d87b 100644 --- a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -58,6 +58,8 @@ abstract class MacroTransform extends Phase { transform(parents)(ctx.superCallContext), transformSelf(self), transformStats(impl.body, tree.symbol)) + case UntypedSplice(_) => + tree case _ => super.transform(tree) } diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 41989b04e66f..a3cfefb12d07 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -460,7 +460,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { ) case tp if tp.isRef(defn.UnitClass) => Typ(ConstantType(Constant(())), true) :: Nil - case tp if tp.classSymbol.is(Enum) => + case tp if tp.classSymbol.is(JavaEnum) => children.map(sym => Typ(sym.termRef, true)) case tp => val parts = children.map { sym => @@ -730,7 +730,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { }) || tp.isRef(defn.BooleanClass) || tp.isRef(defn.UnitClass) || - tp.classSymbol.is(allOf(Enum, Sealed)) // Enum value doesn't have Sealed flag + tp.classSymbol.is(JavaEnumTrait) debug.println(s"decomposable: ${tp.show} = $res") @@ -874,7 +874,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { isCheckable(and.tp1) || isCheckable(and.tp2) }) || tpw.isRef(defn.BooleanClass) || - tpw.typeSymbol.is(Enum) || + tpw.typeSymbol.is(JavaEnum) || canDecompose(tpw) || (defn.isTupleType(tpw) && tpw.argInfos.exists(isCheckable(_))) } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 7c5d0894a18a..968e291d37dc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -645,9 +645,8 @@ trait Checking { case tp: TermRef if tp.symbol.is(InlineParam) => // ok case tp => tp.widenTermRefExpr match { case tp: ConstantType if exprPurity(tree) >= purityLevel => // ok - case tp if defn.isFunctionType(tp) && exprPurity(tree) >= purityLevel => // ok case _ => - if (!ctx.erasedTypes) ctx.error(em"$what must be a constant expression or a function", tree.pos) + if (!ctx.erasedTypes) ctx.error(em"$what must be a constant expression", tree.pos) } } } @@ -819,7 +818,7 @@ trait Checking { cls.isAnonymousClass && cls.owner.isTerm && (cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW) - if (!cdef.mods.hasMod[untpd.Mod.EnumCase] && !isEnumAnonCls) + if (!cdef.mods.isEnumCase && !isEnumAnonCls) ctx.error(em"normal case $cls in ${cls.owner} cannot extend an enum", cdef.pos) } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 5e3b99cf1e2f..7b2fd0b16006 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -218,6 +218,31 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { private def newSym(name: Name, flags: FlagSet, info: Type): Symbol = ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos) + /** A binding for the parameter of an inlined method. This is a `val` def for + * by-value parameters and a `def` def for by-name parameters. `val` defs inherit + * inline annotations from their parameters. The generated `def` is appended + * to `bindingsBuf`. + * @param name the name of the parameter + * @param paramtp the type of the parameter + * @param arg the argument corresponding to the parameter + * @param bindingsBuf the buffer to which the definition should be appended + */ + private def paramBindingDef(name: Name, paramtp: Type, arg: Tree, + bindingsBuf: mutable.ListBuffer[ValOrDefDef]): ValOrDefDef = { + val argtpe = arg.tpe.dealias + def isByName = paramtp.dealias.isInstanceOf[ExprType] + val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags + val (bindingFlags, bindingType) = + if (isByName) (Method, ExprType(argtpe.widen)) + else (inlineFlag, argtpe.widen) + val boundSym = newSym(name, bindingFlags, bindingType).asTerm + val binding = + if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym)) + else ValDef(boundSym, arg) + bindingsBuf += binding + binding + } + /** Populate `paramBinding` and `bindingsBuf` by matching parameters with * corresponding arguments. `bindingbuf` will be further extended later by * proxies to this-references. @@ -230,20 +255,9 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { computeParamBindings(tp.resultType, Nil, argss) case tp: MethodType => (tp.paramNames, tp.paramInfos, argss.head).zipped.foreach { (name, paramtp, arg) => - def isByName = paramtp.dealias.isInstanceOf[ExprType] paramBinding(name) = arg.tpe.dealias match { case _: SingletonType if isIdempotentExpr(arg) => arg.tpe - case argtpe => - val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags - val (bindingFlags, bindingType) = - if (isByName) (inlineFlag | Method, ExprType(argtpe.widen)) - else (inlineFlag, argtpe.widen) - val boundSym = newSym(name, bindingFlags, bindingType).asTerm - val binding = - if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym)) - else ValDef(boundSym, arg) - bindingsBuf += binding - boundSym.termRef + case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol.termRef } } computeParamBindings(tp.resultType, targs, argss.tail) @@ -265,7 +279,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { * The proxy is not yet entered in `bindingsBuf`; that will come later. * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored * in `paramNames` under the parameter's name. This roundabout way to bind parameter - * references to proxies is done because we not known a priori what the parameter + * references to proxies is done because we don't know a priori what the parameter * references of a method are (we only know the method's type, but that contains TypeParamRefs * and MethodParams, not TypeRefs or TermRefs. */ @@ -374,16 +388,15 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details. val expansion1 = InlineTyper.typed(expansion, pt)(inlineCtx) - /** Does given definition bind a closure that will be inlined? */ - def bindsDeadInlineable(defn: ValOrDefDef) = Ident(defn.symbol.termRef) match { - case InlineableArg(_) => !InlineTyper.retainedInlineables.contains(defn.symbol) - case _ => false - } - /** All bindings in `bindingsBuf` except bindings of inlineable closures */ - val bindings = bindingsBuf.toList.filterNot(bindsDeadInlineable).map(_.withPos(call.pos)) + val bindings = bindingsBuf.toList.map(_.withPos(call.pos)) + + inlining.println(i"original bindings = $bindings%\n%") + inlining.println(i"original expansion = $expansion1") - tpd.Inlined(call, bindings, expansion1) + val (finalBindings, finalExpansion) = dropUnusedDefs(bindings, expansion1) + + tpd.Inlined(call, finalBindings, finalExpansion) } } @@ -414,8 +427,6 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { */ private object InlineTyper extends ReTyper { - var retainedInlineables = Set[Symbol]() - override def ensureAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = { tpe match { case tpe @ TypeRef(pre, _) if !tpe.symbol.isAccessibleFrom(pre, superAccess) => @@ -455,13 +466,103 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { } } - override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context) = - tree.asInstanceOf[tpd.Tree] match { - case Apply(Select(InlineableArg(closure(_, fn, _)), nme.apply), args) => - inlining.println(i"reducing $tree with closure $fn") - typed(fn.appliedToArgs(args), pt) - case _ => - super.typedApply(tree, pt) + override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context) = { + + def betaReduce(tree: Tree) = tree match { + case Apply(Select(cl @ closureDef(ddef), nme.apply), args) => + ddef.tpe.widen match { + case mt: MethodType if ddef.vparamss.head.length == args.length => + val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] + val argSyms = (mt.paramNames, mt.paramInfos, args).zipped.map { (name, paramtp, arg) => + arg.tpe.dealias match { + case ref @ TermRef(NoPrefix, _) => ref.symbol + case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol + } + } + val expander = new TreeTypeMap( + oldOwners = ddef.symbol :: Nil, + newOwners = ctx.owner :: Nil, + substFrom = ddef.vparamss.head.map(_.symbol), + substTo = argSyms) + Block(bindingsBuf.toList, expander.transform(ddef.rhs)) + case _ => tree + } + case _ => tree } + + betaReduce(super.typedApply(tree, pt)) + } + } + + /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings. + * Inline def bindings that are used only once. + */ + def dropUnusedDefs(bindings: List[ValOrDefDef], tree: Tree)(implicit ctx: Context): (List[ValOrDefDef], Tree) = { + val refCount = newMutableSymbolMap[Int] + val bindingOfSym = newMutableSymbolMap[ValOrDefDef] + def isInlineable(binding: ValOrDefDef) = binding match { + case DefDef(_, Nil, Nil, _, _) => true + case vdef @ ValDef(_, _, _) => isPureExpr(vdef.rhs) + case _ => false + } + for (binding <- bindings if isInlineable(binding)) { + refCount(binding.symbol) = 0 + bindingOfSym(binding.symbol) = binding + } + val countRefs = new TreeTraverser { + override def traverse(t: Tree)(implicit ctx: Context) = { + t match { + case t: RefTree => + refCount.get(t.symbol) match { + case Some(x) => refCount(t.symbol) = x + 1 + case none => + } + case _: New | _: TypeTree => + //println(i"refcount ${t.tpe}") + t.tpe.foreachPart { + case ref: TermRef => + refCount.get(ref.symbol) match { + case Some(x) => refCount(ref.symbol) = x + 2 + case none => + } + case _ => + } + case _ => + } + traverseChildren(t) + } + } + countRefs.traverse(tree) + for (binding <- bindings) countRefs.traverse(binding.rhs) + val inlineBindings = new TreeMap { + override def transform(t: Tree)(implicit ctx: Context) = + super.transform { + t match { + case t: RefTree => + val sym = t.symbol + refCount.get(sym) match { + case Some(1) if sym.is(Method) => + bindingOfSym(sym).rhs.changeOwner(sym, ctx.owner) + case none => t + } + case _ => t + } + } + } + def retain(binding: ValOrDefDef) = refCount.get(binding.symbol) match { + case Some(x) => x > 1 || x == 1 && !binding.symbol.is(Method) + case none => true + } + val retained = bindings.filterConserve(retain) + if (retained `eq` bindings) { + //println(i"DONE\n${bindings}%\n% ;;;\n ${tree}") + (bindings, tree) + } + else { + val expanded = inlineBindings.transform(tree) + //println(i"ref counts: ${refCount.toMap map { case (sym, count) => i"$sym -> $count" }}") + //println(i"""MAPPING\n${bindings}%\n% ;;;\n ${tree} \n------->\n${retained}%\n%;;;\n ${expanded} """) + dropUnusedDefs(retained, expanded) + } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 2d6ecae78fe4..592006e2b57f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -494,7 +494,7 @@ class Namer { typer: Typer => // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal // cyclic reference error. See the commit message for details. // if (ctx.compilationUnit.isJava) ctx.owner.companionClass.is(Enum) else ctx.owner.is(Enum) - vd.mods.is(allOf(Enum, Stable, JavaStatic, JavaDefined)) // && ownerHasEnumFlag + vd.mods.is(JavaEnumValue) // && ownerHasEnumFlag } /** Add java enum constants */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9e34deeb2555..ce0b762945f9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -417,7 +417,7 @@ class Typer extends Namer /** Check that a stable identifier pattern is indeed stable (SLS 8.1.5) */ - private def checkStableIdentPattern(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { + private def checkStableIdentPattern(tree: Tree, pt: Type)(implicit ctx: Context): tree.type = { if (ctx.mode.is(Mode.Pattern) && !tree.isType && !pt.isInstanceOf[ApplyingProto] && @@ -437,7 +437,7 @@ class Typer extends Namer val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) val select = typedSelect(tree, pt, qual1) - if (select.tpe ne TryDynamicCallType) checkStableIdentPattern(select, pt) + if (select.tpe ne TryDynamicCallType) ConstFold(checkStableIdentPattern(select, pt)) else if (pt.isInstanceOf[PolyProto] || pt.isInstanceOf[FunProto] || pt == AssignProto) select else typedDynamicSelect(tree, Nil, pt) } @@ -915,8 +915,7 @@ class Typer extends Namer else cpy.ValDef(param)( tpt = untpd.TypeTree( inferredParamType(param, protoFormal(i)).underlyingIfRepeated(isJava = false))) - val inlineable = pt.hasAnnotation(defn.InlineParamAnnot) - desugar.makeClosure(inferredParams, fnBody, resultTpt, inlineable) + desugar.makeClosure(inferredParams, fnBody, resultTpt) } typed(desugared, pt) } @@ -1648,7 +1647,7 @@ class Typer extends Namer } def typedTypedSplice(tree: untpd.TypedSplice)(implicit ctx: Context): Tree = - tree.tree match { + tree.splice match { case tree1: TypeTree => tree1 // no change owner necessary here ... case tree1: Ident => tree1 // ... or here, since these trees cannot contain bindings case tree1 => @@ -1886,7 +1885,7 @@ class Typer extends Namer case mdef1 => import untpd.modsDeco mdef match { - case mdef: untpd.TypeDef if mdef.mods.hasMod[untpd.Mod.Enum] => + case mdef: untpd.TypeDef if mdef.mods.isEnumClass => enumContexts(mdef1.symbol) = ctx case _ => } diff --git a/tests/neg/t10035.scala b/tests/neg/t10035.scala new file mode 100644 index 000000000000..f3f8444bed88 --- /dev/null +++ b/tests/neg/t10035.scala @@ -0,0 +1,11 @@ +trait Inner { + def f(): Outer +} + +class Outer(o: Set[Inner]) { + def this() = this(Set(1).map{ + case k => new Inner { + def f(): Outer = Outer.this // error: Outer is not an enclosing class + } + }) +} diff --git a/tests/run/i4431/quoted_1.scala b/tests/run/i4431/quoted_1.scala index b4e89dbbc512..8f0be7b23ef7 100644 --- a/tests/run/i4431/quoted_1.scala +++ b/tests/run/i4431/quoted_1.scala @@ -1,5 +1,5 @@ import scala.quoted._ object Macros { - inline def h(inline f: Int => String): String = ~ '(f(42)) + inline def h(f: => Int => String): String = ~ '(f(42)) } diff --git a/tests/run/inlineByName.scala b/tests/run/inlineByName.scala new file mode 100644 index 000000000000..94f276dcce3e --- /dev/null +++ b/tests/run/inlineByName.scala @@ -0,0 +1,37 @@ +object Test { + + class Range(from: Int, end: Int) { + inline def foreach(op: => Int => Unit): Unit = { + var i = from + while (i < end) { + op(i) + i += 1 + } + } + } + inline def twice(op: => Int => Unit): Unit = { + op(1) + op(2) + } + inline def thrice(op: => Unit): Unit = { + op + op + op + } + + def main(args: Array[String]) = { + var j = 0 + new Range(1, 10).foreach(j += _) + assert(j == 45, j) + twice { x => j = j - x } + thrice { j = j + 1 } + val f = new Range(1, 10).foreach + f(j -= _) + assert(j == 0, j) + new Range(1, 10).foreach { i1 => + new Range(2, 11).foreach { i2 => + j += i1 * i2 + } + } + } +} diff --git a/tests/run/inlineForeach.scala b/tests/run/inlineForeach.scala index 437e58cbfc5f..7f22f1cc3753 100644 --- a/tests/run/inlineForeach.scala +++ b/tests/run/inlineForeach.scala @@ -3,7 +3,7 @@ object Test { class Range(from: Int, end: Int) { inline - def foreach(inline op: Int => Unit): Unit = { + def foreach(op: => Int => Unit): Unit = { var i = from while (i < end) { op(i) @@ -36,7 +36,7 @@ object Test { } implicit class intArrayOps(arr: Array[Int]) { - inline def foreach(inline op: Int => Unit): Unit = { + inline def foreach(op: => Int => Unit): Unit = { var i = 0 while (i < arr.length) { op(arr(i)) diff --git a/tests/run/inlinedAssign.scala b/tests/run/inlinedAssign.scala index 1b524f92bf24..37e66833a0dc 100644 --- a/tests/run/inlinedAssign.scala +++ b/tests/run/inlinedAssign.scala @@ -1,6 +1,6 @@ object Test { - inline def swap[T](x: T, inline x_= : T => Unit, y: T, inline y_= : T => Unit) = { + inline def swap[T](x: T, x_= : => T => Unit, y: T, y_= : => T => Unit) = { x_=(y) y_=(x) } diff --git a/tests/run/lst/Lst.scala b/tests/run/lst/Lst.scala index f501bff72381..34c579b22b2c 100644 --- a/tests/run/lst/Lst.scala +++ b/tests/run/lst/Lst.scala @@ -25,7 +25,7 @@ class Lst[+T](val elems: Any) extends AnyVal { def isEmpty = elems == null def nonEmpty = elems != null - inline def foreach(inline op: T => Unit): Unit = { + inline def foreach(op: => T => Unit): Unit = { def sharedOp(x: T) = op(x) elems match { case null => @@ -39,7 +39,7 @@ class Lst[+T](val elems: Any) extends AnyVal { /** Like `foreach`, but completely inlines `op`, at the price of generating the code twice. * Should be used only of `op` is small */ - inline def foreachInlined(inline op: T => Unit): Unit = elems match { + inline def foreachInlined(op: => T => Unit): Unit = elems match { case null => case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] var i = 0 @@ -60,7 +60,7 @@ class Lst[+T](val elems: Any) extends AnyVal { } /** `f` is pulled out, not duplicated */ - inline def map[U](inline f: T => U): Lst[U] = { + inline def map[U](f: => T => U): Lst[U] = { def op(x: T) = f(x) elems match { case null => Empty @@ -144,7 +144,7 @@ class Lst[+T](val elems: Any) extends AnyVal { } def filterNot(p: T => Boolean): Lst[T] = filter(!p(_)) - inline def exists(inline p: T => Boolean): Boolean = { + inline def exists(p: => T => Boolean): Boolean = { def op(x: T) = p(x) elems match { case null => false @@ -157,7 +157,7 @@ class Lst[+T](val elems: Any) extends AnyVal { } } - inline def forall(inline p: T => Boolean): Boolean = { + inline def forall(p: => T => Boolean): Boolean = { def op(x: T) = p(x) elems match { case null => true @@ -180,7 +180,7 @@ class Lst[+T](val elems: Any) extends AnyVal { elem == x } - inline def foldLeft[U](z: U)(inline f: (U, T) => U) = { + inline def foldLeft[U](z: U)(f: => (U, T) => U) = { def op(x: U, y: T) = f(x, y) elems match { case null => z @@ -194,7 +194,7 @@ class Lst[+T](val elems: Any) extends AnyVal { } } - inline def /: [U](z: U)(inline op: (U, T) => U) = foldLeft(z)(op) + inline def /: [U](z: U)(op: => (U, T) => U) = foldLeft(z)(op) def reduceLeft[U >: T](op: (U, U) => U) = elems match { case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T]