From 2a6d77dda7fae7faee7a9c4d55efab6e31db5f8c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 26 May 2018 18:05:18 +0200 Subject: [PATCH 01/17] Piggyback unrelated test case --- tests/neg/t10035.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/neg/t10035.scala 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 + } + }) +} From 69c7712f43c0a9e570a90b39870cb8e52d88c65f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 28 May 2018 11:41:44 +0200 Subject: [PATCH 02/17] Drop EnumCase modifier Use combination of Enum and Case instead. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 8 ++++---- compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 6 +++--- compiler/src/dotty/tools/dotc/ast/untpd.scala | 5 +++-- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Checking.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 14930d89295a..e90993d80d06 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, 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/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index c3b1cb309935..2a540584fc9d 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -133,8 +133,6 @@ 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) } /** Modifiers and annotations for definitions @@ -192,6 +190,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { val cls = implicitly[ClassTag[T]].runtimeClass mods.exists(mod => cls.isAssignableFrom(mod.getClass)) } + + def isEnumCase = hasMod[Mod.Enum] && is(Case) + def isEnumClass = hasMod[Mod.Enum] && !is(Case) } @sharable val EmptyModifiers: Modifiers = new Modifiers() diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 92bfb2cb9c7d..ca5a364ed8e5 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 = mods.withAddedMod(atPos(in.offset)(Mod.Enum())) | Case accept(CASE) in.adjustSepRegions(ARROW) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 37d45fd3d45d..caab5de88411 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -855,7 +855,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/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 400cdf758525..b486d6a15312 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1904,7 +1904,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 _ => } From ee5bfedf7218b1328cc0f1fe7fc6e81eb8d90d03 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 28 May 2018 11:56:50 +0200 Subject: [PATCH 03/17] Liberate Enum flag so that it can be used for Scala enums as well. --- .../dotty/tools/backend/jvm/DottyBackendInterface.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Flags.scala | 9 +++++++++ .../tools/dotc/core/classfile/ClassfileParser.scala | 4 ++-- compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala | 4 ++-- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/Namer.scala | 2 +- 6 files changed, 19 insertions(+), 10 deletions(-) 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/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 80630f3afa18..e51c1c916c54 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -620,6 +620,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/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/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/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/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 */ From aa2c63c973ddbaae49267cd7521de7c7e3c09fde Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 28 May 2018 14:54:51 +0200 Subject: [PATCH 04/17] Re-use Enum flag for Scala enums --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 8 +++++--- compiler/src/dotty/tools/dotc/core/Flags.scala | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 2a540584fc9d..b397cf476170 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -132,7 +132,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 Enum() extends Mod(Flags.Enum) } /** Modifiers and annotations for definitions @@ -191,8 +191,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { mods.exists(mod => cls.isAssignableFrom(mod.getClass)) } - def isEnumCase = hasMod[Mod.Enum] && is(Case) - def isEnumClass = hasMod[Mod.Enum] && !is(Case) + private def isEnum = hasMod[Mod.Enum] || is(Enum, butNot = JavaDefined) + + def isEnumCase = isEnum && is(Case) + def isEnumClass = isEnum && !is(Case) } @sharable val EmptyModifiers: Modifiers = new Modifiers() diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index e51c1c916c54..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 From 973347991181b75f055aa141569e854c723710c8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 May 2018 12:31:30 +0200 Subject: [PATCH 05/17] Pickle Enum flag It's useful to know something was an enum when it started, and it's necessary to pickle this when serializing untyped trees. --- .../tools/dotc/core/tasty/TastyFormat.scala | 32 ++++++++++--------- .../tools/dotc/core/tasty/TreePickler.scala | 1 + .../tools/dotc/core/tasty/TreeUnpickler.scala | 1 + 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 6e25c37c0f71..e389e9e081c9 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -184,6 +184,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 @@ -282,21 +283,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 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 4c4ab3c72147..279d49d9d834 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -592,6 +592,7 @@ class TreePickler(pickler: TastyPickler) { 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) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 5b35002530f7..b249049c43c9 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -591,6 +591,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) From c73a21ba2af1e9fff20073236f7163b80d77be78 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 May 2018 15:37:13 +0200 Subject: [PATCH 06/17] More debug options for printing - Mark splices in code under -print-debug - Print owners of all definitions under -print-debug-owners --- .../dotty/tools/dotc/config/ScalaSettings.scala | 2 +- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 376f89e4f96c..c420d9e8e23a 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/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7b333e3a4c51..c5472b951263 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -444,7 +444,8 @@ 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 tree @ ModuleDef(name, impl) => withEnclosingDef(tree) { modText(tree.mods, NoSymbol, keywordStr("object")) ~~ nameIdText(tree) ~ toTextTemplate(impl) @@ -610,11 +611,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 From 401e1d920d0f7b8be60f7f7c9de2fefb2f4b795c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 5 Jun 2018 13:36:11 +0200 Subject: [PATCH 07/17] Tweaks to Tasty pickling and unpickling - some fixes to format docs - some refactorings for better legibility --- .../tools/dotc/core/tasty/TastyFormat.scala | 6 +- .../tools/dotc/core/tasty/TreePickler.scala | 24 ++--- .../tools/dotc/core/tasty/TreeUnpickler.scala | 100 ++++++++++-------- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index e389e9e081c9..b135b8a427a5 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -65,11 +65,11 @@ Standard-Section: "ASTs" TopLevelStat* // 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 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 279d49d9d834..32816fcbc98f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -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) } @@ -569,6 +560,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) @@ -600,7 +600,7 @@ class TreePickler(pickler: TastyPickler) { if (sym.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) @@ -608,7 +608,7 @@ class TreePickler(pickler: TastyPickler) { 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 Label) writeByte(LABEL) } else { if (flags is Sealed) writeByte(SEALED) if (flags is Abstract) writeByte(ABSTRACT) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index b249049c43c9..c77006510a06 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,7 +511,7 @@ 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) pickling.println(i"creating symbol $name at $start with flags $givenFlags") @@ -562,7 +563,7 @@ class TreeUnpickler(reader: TastyReader, */ def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Annotation], Symbol) = { var flags: FlagSet = EmptyFlags - var annots = new mutable.ListBuffer[Annotation] + var annots: List[Annotation] = Nil var privateWithin: Symbol = NoSymbol while (currentAddr.index != end.index) { def addFlag(flag: FlagSet) = { @@ -613,18 +614,23 @@ class TreeUnpickler(reader: TastyReader, addFlag(Protected) privateWithin = readType().typeSymbol 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 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 @@ -721,7 +727,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) = @@ -786,7 +792,7 @@ class TreeUnpickler(reader: TastyReader, } case PARAM => val tpt = readTpt()(localCtx) - if (noRhs(end)) { + if (nothingButMods(end)) { sym.info = tpt.tpe ValDef(tpt) } From 0e82b01928590b81872a90b2009f2da7cf2c182b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 5 Jun 2018 13:38:49 +0200 Subject: [PATCH 08/17] Fix pickler printing of TERMREFin, TYPEREFin --- compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index fd94296fd93d..c4ee8cffcf05 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -65,7 +65,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { printName(); printName() case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => printName(); printTrees() - case REFINEDtype => + case REFINEDtype | TERMREFin | TYPEREFin => printName(); printTree(); printTrees() case RETURN | HOLE => printNat(); printTrees() From 2796d110bf9c22fbd6532fdc089aea313d456397 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 5 Jun 2018 13:40:37 +0200 Subject: [PATCH 09/17] Enforce that mods in Modifiers don't add new information Everything should be reflected in flags and privateWithin already. --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 25 +++++++++++++------ .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index b397cf476170..63fd300b88a0 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -163,15 +163,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) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ca5a364ed8e5..0024a285a025 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.Enum())) | Case + val mods1 = addMod(mods, atPos(in.offset)(Mod.Enum())) | Case accept(CASE) in.adjustSepRegions(ARROW) From 559306eb905d79bfb5186a1e07ca11ee8489d6a7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 5 Jun 2018 13:41:38 +0200 Subject: [PATCH 10/17] Don't forget Mode.Printing in RefinedPrinter --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c5472b951263..d9c5aa83f582 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 From 087cf9f09f350b677ede98f6c6650bf4a48a00fc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 2 Jun 2018 19:05:32 +0200 Subject: [PATCH 11/17] Streamline trace implementation Avoid creation of `op1` methods if tracing is not enabled. --- .../dotty/tools/dotc/reporting/trace.scala | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) 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 = From 5e4a3405c0e33a204bdee1ebbe41107a28ab7ec4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Jun 2018 11:23:30 +0200 Subject: [PATCH 12/17] Relax assertion to allow printing trees with overloaded denotations after typer --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 304aacca2d1e..57f24a917f45 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1826,7 +1826,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) From 66727e387a5ec5fa2761f42662b11877d2e52aa3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 5 Jun 2018 13:45:49 +0200 Subject: [PATCH 13/17] Don't treat inline closures specially. Rely on call-by-name parameters instead. Fix "unused defs" logic so that referred-to cbn parameters are not eliminated. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 9 +- .../src/dotty/tools/dotc/typer/Checking.scala | 3 +- .../src/dotty/tools/dotc/typer/Inliner.scala | 157 ++++++++++++++---- .../src/dotty/tools/dotc/typer/Typer.scala | 5 +- tests/run/i4431/quoted_1.scala | 2 +- tests/run/inlineForeach.scala | 4 +- tests/run/inlinedAssign.scala | 2 +- tests/run/lst/Lst.scala | 14 +- 8 files changed, 143 insertions(+), 53 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e90993d80d06..5fc49c277f5c 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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(), isInlineable: Boolean, isImplicit: Boolean)(implicit ctx: Context) = { - var mods = synthetic | Artifact - if (isInlineable) mods |= Inline + def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), isImplicit: Boolean)(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), if (isImplicit) ImplicitEmptyTree else EmptyTree)) - } /** If `nparams` == 1, expand partial function * diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index caab5de88411..fdfa66031f50 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -681,9 +681,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) } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index a6b782f26ce3..0fa26e94bb72 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,99 @@ 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 => + 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) { + (bindings, tree) + } + else { + val expanded = inlineBindings.transform(tree) + dropUnusedDefs(retained, expanded) + } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b486d6a15312..f05e7c61818c 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] && @@ -923,8 +923,7 @@ class Typer extends Namer else cpy.ValDef(param)( tpt = untpd.TypeTree( inferredParamType(param, protoFormal(i)).underlyingIfRepeated(isJava = false))) - val isInlineable = pt.hasAnnotation(defn.InlineParamAnnot) - desugar.makeClosure(inferredParams, fnBody, resultTpt, isInlineable, isImplicit) + desugar.makeClosure(inferredParams, fnBody, resultTpt, isImplicit) } typed(desugared, pt) } 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/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] From 5c74218b753c68c863611c1ed81c31c3d6eb4d09 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Jun 2018 14:46:49 +0200 Subject: [PATCH 14/17] Drop Modifiers.hasMod Since all Mod values are now reflected in flags and privateWithin, there is no need anymore to test whether a Mod exists. --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 63fd300b88a0..5d68de1bb284 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -197,12 +197,8 @@ 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 = hasMod[Mod.Enum] || is(Enum, butNot = JavaDefined) + private def isEnum = is(Enum, butNot = JavaDefined) def isEnumCase = isEnum && is(Case) def isEnumClass = isEnum && !is(Case) From 8800d7430cbccfa08e891997889f612cda6e5fba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Jun 2018 14:54:52 +0200 Subject: [PATCH 15/17] Address reviewer comment on previous PR --- compiler/src/dotty/tools/dotc/transform/AccessProxies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From d2c19ce0f44cfb5e8c5b748897e832ac74cb552b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 5 Jun 2018 17:18:57 +0200 Subject: [PATCH 16/17] Bump Tasty version Also, polishing and new test --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- .../tools/dotc/core/tasty/TastyFormat.scala | 2 +- .../src/dotty/tools/dotc/typer/Inliner.scala | 2 +- tests/run/inlineByName.scala | 37 +++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 tests/run/inlineByName.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5fc49c277f5c..c7017a8f547c 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -811,7 +811,7 @@ object desugar { * def $anonfun(params) = body * Closure($anonfun) */ - def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), isImplicit: Boolean)(implicit ctx: Context) = { + def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), isImplicit: Boolean)(implicit ctx: Context) = Block( DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic | Artifact), Closure(Nil, Ident(nme.ANON_FUN), if (isImplicit) ImplicitEmptyTree else EmptyTree)) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index b135b8a427a5..751304b31c4c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -226,7 +226,7 @@ Standard Section: "Positions" Assoc* object TastyFormat { final val header = Array(0x5C, 0xA1, 0xAB, 0x1F) - val MajorVersion = 8 + val MajorVersion = 9 val MinorVersion = 0 /** Tags used to serialize names */ diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 0fa26e94bb72..20d334136c87 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -230,7 +230,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { 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 isByName = paramtp.dealias.isInstanceOf[ExprType] val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags val (bindingFlags, bindingType) = if (isByName) (Method, ExprType(argtpe.widen)) 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 + } + } + } +} From 0c7ef1354e5ee16e4f4e2f0b4d6719ef49a3fb11 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 6 Jun 2018 11:42:02 +0200 Subject: [PATCH 17/17] Address review comments --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 20d334136c87..3db32b4a08f3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -468,8 +468,19 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context) = { + /** Rewrite an application + * + * ((x1, ..., sn) => b)(e1, ..., en) + * + * to + * + * val/def x1 = e1; ...; val/def xn = en; b + * + * where `def` is used for call-by-name parameters. However, we shortcut any NoPrefix + * refs among the ei's directly without creating an intermediate binding. + */ def betaReduce(tree: Tree) = tree match { - case Apply(Select(cl @ closureDef(ddef), nme.apply), args) => + case Apply(Select(cl @ closureDef(ddef), nme.apply), args) if defn.isFunctionType(cl.tpe) => ddef.tpe.widen match { case mt: MethodType if ddef.vparamss.head.length == args.length => val bindingsBuf = new mutable.ListBuffer[ValOrDefDef]