diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 0d3fb5821501..8ee016117de3 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -55,7 +55,9 @@ class Compiler { new ExtensionMethods, // Expand methods of value classes with extension methods new ExpandSAMs, // Expand single abstract method closures to anonymous classes new TailRec, // Rewrite tail recursion to loops + new ByNameClosures, // Expand arguments to by-name parameters to closures new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods + new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope new ClassOf), // Expand `Predef.classOf` calls. List(new TryCatchPatterns, // Compile cases in try/catch new PatternMatcher, // Compile pattern matches @@ -69,7 +71,7 @@ class Compiler { new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods new Getters, // Replace non-private vals and vars with getter defs (fields are added later) - new ElimByName, // Expand by-name parameters and arguments + new ElimByName, // Expand by-name parameter references new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings new ResolveSuper, // Implement super accessors and add forwarders to trait methods new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index cd0115a99ac2..690f18509ebd 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -629,7 +629,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case tree: DefTree => val sym = tree.symbol val prevDenot = sym.denot(ctx.withPhase(trans)) - if (prevDenot.owner == from) { + if (prevDenot.effectiveOwner == from.skipWeakOwner) { val d = sym.copySymDenotation(owner = to) d.installAfter(trans) d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index eee6ba7850b4..4abaf3bc788c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -280,9 +280,11 @@ class Definitions { def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone, Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI) - /** Dummy method needed by elimByName */ - lazy val dummyApply = enterPolyMethod( - OpsPackageClass, nme.dummyApply, 1, + /** Marker method to indicate an argument to a call-by-name parameter. + * Created by byNameClosures and elimByName, eliminated by Erasure, + */ + lazy val cbnArg = enterPolyMethod( + OpsPackageClass, nme.cbnArg, 1, pt => MethodType(List(FunctionOf(Nil, TypeParamRef(pt, 0))), TypeParamRef(pt, 0))) /** Method representing a throw */ diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 84072cd50b85..c1929d882b21 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -372,9 +372,6 @@ object Flags { /** Symbol always defines a fresh named type */ final val Fresh = commonFlag(45, "") - /** Symbol is defined in a super call */ - final val InSuperCall = commonFlag(46, "") - /** Denotation is in train of being loaded and completed, used to catch cyclic dependencies */ final val Touched = commonFlag(48, "") @@ -451,7 +448,7 @@ object Flags { /** Flags guaranteed to be set upon symbol creation */ final val FromStartFlags = Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | - Scala2ExistentialCommon | Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | + Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | SuperAccessorOrScala2x | Inline @@ -511,8 +508,7 @@ object Flags { Accessor | AbsOverride | Stable | Captured | Synchronized /** Flags that can apply to a module class */ - final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | - InSuperCall | ImplClass + final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | ImplClass /** Packages and package classes always have these flags set */ final val PackageCreationFlags = diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 21e534a6b05f..95ff963653fa 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -221,6 +221,7 @@ object NameKinds { val ExceptionBinderName = new UniqueNameKind("ex") val SkolemName = new UniqueNameKind("?") val LiftedTreeName = new UniqueNameKind("liftedTree") + val SuperArgName = new UniqueNameKind("$superArg$") val UniqueExtMethName = new UniqueNameKind("$extension") { override def unmangle(name: SimpleTermName): TermName = { diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 92befdacbd9f..bf8a9924780d 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -378,6 +378,7 @@ object StdNames { val build : N = "build" val bytes: N = "bytes" val canEqual_ : N = "canEqual" + val cbnArg: N = "" val checkInitialized: N = "checkInitialized" val ClassManifestFactory: N = "ClassManifestFactory" val classOf: N = "classOf" @@ -391,7 +392,6 @@ object StdNames { val delayedInitArg: N = "delayedInit$body" val drop: N = "drop" val dynamics: N = "dynamics" - val dummyApply: N = "" val elem: N = "elem" val emptyValDef: N = "emptyValDef" val ensureAccessible : N = "ensureAccessible" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index e5cc94883dd1..f0d71a281504 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -851,7 +851,7 @@ object SymDenotations { /** The class containing this denotation. * If this denotation is already a class, return itself - * Definitions flagged with InSuperCall are treated specially. + * Definitions flagged with JavaStatic are treated specially. * Their enclosing class is not the lexically enclosing class, * but in turn the enclosing class of the latter. This reflects * the context created by `Context#superCallContext`, `Context#thisCallArgContext` @@ -862,7 +862,7 @@ object SymDenotations { */ final def enclosingClass(implicit ctx: Context): Symbol = { def enclClass(sym: Symbol, skip: Boolean): Symbol = { - def newSkip = sym.is(InSuperCall) || sym.is(JavaStaticTerm) + def newSkip = sym.is(JavaStaticTerm) if (!sym.exists) NoSymbol else if (sym.isClass) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index f03e279c65a8..b564c09e996d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -186,7 +186,6 @@ Standard-Section: "ASTs" TopLevelStat* CONTRAVARIANT // type param marked “-” SCALA2X // Imported from Scala2.x DEFAULTparameterized // Method with default params - INSUPERCALL // defined in the argument of a constructor supercall STABLE // Method that is assumed to be stable Annotation Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term @@ -278,8 +277,7 @@ object TastyFormat { final val CONTRAVARIANT = 28 final val SCALA2X = 29 final val DEFAULTparameterized = 30 - final val INSUPERCALL = 31 - final val STABLE = 32 + final val STABLE = 31 final val SHARED = 64 final val TERMREFdirect = 65 @@ -403,7 +401,6 @@ object TastyFormat { | CONTRAVARIANT | SCALA2X | DEFAULTparameterized - | INSUPERCALL | STABLE | ANNOTATION | PRIVATEqualified @@ -469,7 +466,6 @@ object TastyFormat { case CONTRAVARIANT => "CONTRAVARIANT" case SCALA2X => "SCALA2X" case DEFAULTparameterized => "DEFAULTparameterized" - case INSUPERCALL => "INSUPERCALL" case STABLE => "STABLE" case SHARED => "SHARED" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 5d33738c2b59..8535d0cc85cd 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -577,7 +577,6 @@ class TreePickler(pickler: TastyPickler) { if (flags is Synthetic) writeByte(SYNTHETIC) if (flags is Artifact) writeByte(ARTIFACT) if (flags is Scala2x) writeByte(SCALA2X) - if (flags is InSuperCall) writeByte(INSUPERCALL) if (sym.isTerm) { if (flags is Implicit) writeByte(IMPLICIT) if ((flags is Lazy) && !(sym is Module)) writeByte(LAZY) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 2908c541e5e9..460af7c0ceea 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -510,7 +510,6 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi case CONTRAVARIANT => addFlag(Contravariant) case SCALA2X => addFlag(Scala2x) case DEFAULTparameterized => addFlag(DefaultParameterized) - case INSUPERCALL => addFlag(InSuperCall) case STABLE => addFlag(Stable) case PRIVATEqualified => readByte() diff --git a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala new file mode 100644 index 000000000000..4f8c7cfceac8 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala @@ -0,0 +1,37 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core._ +import Symbols._ +import SymDenotations._ +import Contexts._ +import Types._ +import Flags._ +import Decorators._ +import DenotTransformers.IdentityDenotTransformer +import core.StdNames.nme + +/** This phase translates arguments to call-by-name parameters, using the rules + * + * x ==> x if x is a => parameter + * e.apply() ==> (e) if e is pure + * e ==> (() => e) for all other arguments + * + * where + * + * : [T](() => T): T + * + * is a synthetic method defined in Definitions. Erasure will later strip the wrappers. + */ +class ByNameClosures extends TransformByNameApply with IdentityDenotTransformer { thisTransformer => + import ast.tpd._ + + override def phaseName: String = "byNameClosures" + + override def mkByNameClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = { + val meth = ctx.newSymbol( + ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, argType)) + Closure(meth, _ => arg.changeOwnerAfter(ctx.owner, meth, thisTransformer)) + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index db850e944a25..59582b4d69d4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -30,8 +30,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th import tpd._ override def phaseName: String = "constructors" - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Memoize]) - + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Memoize], classOf[HoistSuperArgs]) // Collect all private parameter accessors and value definitions that need // to be retained. There are several reasons why a parameter accessor or @@ -103,7 +102,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th * outer link, so no parameter accessors need to be rewired to parameters */ private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) = - tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall) + tree.isDef && tree.symbol.isClass /** Class members that can be eliminated if referenced only from their own * constructor. diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 06c48902952d..9b3008f9147f 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -3,7 +3,7 @@ package transform import TreeTransforms._ import core._ -import DenotTransformers._ +import DenotTransformers.InfoTransformer import Symbols._ import SymDenotations._ import Contexts._ @@ -15,32 +15,16 @@ import util.Attachment import core.StdNames.nme import ast.Trees._ -/** This phase eliminates ExprTypes `=> T` as types of function parameters, and replaces them by +/** This phase eliminates ExprTypes `=> T` as types of method parameter references, and replaces them b * nullary function types. More precisely: * * For the types of parameter symbols: * - * => T ==> () => T + * => T ==> () => T * - * Note that `=> T` types are not eliminated in MethodTypes. This is done later at erasure. - * Terms are rewritten as follows: + * For cbn parameter values * - * x ==> x.apply() if x is a parameter that had type => T - * - * Arguments to call-by-name parameters are translated as follows. First, the argument is - * rewritten by the rules - * - * e.apply() ==> e if e.apply() is an argument to a call-by-name parameter - * expr ==> () => expr if other expr is an argument to a call-by-name parameter - * - * This makes the argument compatible with a parameter type of () => T, which will be the - * formal parameter type at erasure. But to be -Ycheckable until then, any argument - * ARG rewritten by the rules above is again wrapped in an application DummyApply(ARG) - * where - * - * DummyApply: [T](() => T): T - * - * is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers. + * x ==> x() * * Note: This scheme to have inconsistent types between method types (whose formal types are still * ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two @@ -53,61 +37,18 @@ import ast.Trees._ * Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been * tried yet. */ -class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer => +class ElimByName extends TransformByNameApply with InfoTransformer { thisTransformer => import ast.tpd._ override def phaseName: String = "elimByName" override def runsAfterGroupsOf = Set(classOf[Splitter]) - // assumes idents and selects have symbols; interferes with splitter distribution - // that's why it's "after group". - - /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */ - private def originalDenotation(tree: Tree)(implicit ctx: Context) = - tree.symbol.denot(ctx.withPhase(thisTransformer)) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { - - def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { - case formalExpr: ExprType => - var argType = arg.tpe.widenIfUnstable - if (defn.isBottomType(argType)) argType = formal.widenExpr - val argFun = arg match { - case Apply(Select(qual, nme.apply), Nil) - if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => - qual - case _ => - val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags - val meth = ctx.newSymbol( - ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType)) - Closure(meth, _ => - atGroupEnd { implicit ctx: Context => - arg.changeOwner(ctx.owner, meth) - } - ) - } - ref(defn.dummyApply).appliedToType(argType).appliedTo(argFun) - case _ => - arg - } - - val mt @ MethodType(_) = tree.fun.tpe.widen - val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg) - cpy.Apply(tree)(tree.fun, args1) - } - - /** If denotation had an ExprType before, it now gets a function type */ - private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = - (symd is Param) || (symd is (ParamAccessor, butNot = Method)) + // I got errors running this phase in an earlier group, but I did not track them down. /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ - private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = { - val origDenot = originalDenotation(ftree) - if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) - tree.select(defn.Function0_apply).appliedToNone + private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = + if (isByNameRef(ftree)) tree.select(defn.Function0_apply).appliedToNone else tree - } override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = applyIfFunction(tree, tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 4cee0d0de721..245b153ba9a6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -452,7 +452,7 @@ object Erasure extends TypeTestsCasts{ */ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree - if (fun.symbol == defn.dummyApply) + if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) else typedExpr(fun, FunProto(args, pt, this)) match { case fun1: Apply => // arguments passed in prototype were already passed diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 7ad7fb348447..60086031edff 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -46,7 +46,7 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf /** List of names of phases that should have finished their processing of all compilation units * before this phase starts */ - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PatternMatcher]) + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PatternMatcher], classOf[HoistSuperArgs]) /** Add outer accessors if a class always needs an outer pointer */ override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { diff --git a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala new file mode 100644 index 000000000000..8737c4c9bf49 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala @@ -0,0 +1,210 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core.DenotTransformers._ +import core.Symbols._ +import core.Contexts._ +import ast.TreeTypeMap +import core.Types._ +import core.Flags._ +import core.Decorators._ +import collection.mutable +import ast.Trees._ +import core.Names.TermName +import core.NameKinds.SuperArgName +import SymUtils._ + +/** This phase hoists complex arguments of supercalls and this-calls out of the enclosing class. + * Example: + * + * class B(y: Int) extends A({ def f(x: Int) = x * x; f(y)}) + * + * is translated to + * + * class B(y: Int) extends A(B#B$superArg$1(this.y)) { + * private def B$superArg$1(y: Int): Int = { + * def f(x: Int): Int = x.*(x); f(y) + * } + * } + * + * An argument is complex if it contains a method or template definition, a this or a new, + * or it contains an identifier which needs a `this` prefix to be accessed. This is the case + * if the identifer neither a global reference nor a reference to a parameter of the enclosing class. + * @see needsHoist for an implementation. + * + * A hoisted argument definition gets the parameters of the class it is hoisted from + * as method parameters. The definition is installed in the scope enclosing the class, + * or, if that is a package, it is made a static method of the class itself. + */ +class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + def phaseName = "hoistSuperArgs" + + override def runsAfter = Set(classOf[ByNameClosures]) + // By name closures need to be introduced first in order to be hoisted out here. + // There's an interaction with by name closures in that the marker + // application should not be hoisted, but be left at the point of call. + + /** Defines methods for hoisting complex supercall arguments out of + * parent super calls and constructor definitions. + * Hoisted superarg methods are collected in `superArgDefs` + */ + class Hoister(cls: Symbol)(implicit ctx: Context) { + val superArgDefs = new mutable.ListBuffer[DefDef] + + /** If argument is complex, hoist it out into its own method and refer to the + * method instead. + * @param arg The argument that might be hoisted + * @param cdef The definition of the constructor from which the call is made + * @return The argument after possible hoisting + */ + private def hoistSuperArg(arg: Tree, cdef: DefDef): Tree = { + val constr = cdef.symbol + lazy val origParams = // The parameters that can be accessed in the supercall + if (constr == cls.primaryConstructor) + cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor)) + else + (cdef.tparams ::: cdef.vparamss.flatten).map(_.symbol) + + /** The parameter references defined by the constructor info */ + def allParamRefs(tp: Type): List[ParamRef] = tp match { + case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType) + case _ => Nil + } + + /** Splice `restpe` in final result type position of `tp` */ + def replaceResult(tp: Type, restpe: Type): Type = tp match { + case tp: LambdaType => + tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe)) + case _ => restpe + } + + /** A method representing a hoisted supercall argument */ + def newSuperArgMethod(argType: Type) = { + val (staticFlag, methOwner) = + if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner) + val argTypeWrtConstr = argType.subst(origParams, allParamRefs(constr.info)) + // argType with references to paramRefs of the primary constructor instead of + // local parameter accessors + val meth = ctx.newSymbol( + owner = methOwner, + name = SuperArgName.fresh(cls.name.toTermName), + flags = Synthetic | Private | Method | staticFlag, + info = replaceResult(constr.info, argTypeWrtConstr), + coord = constr.coord) + if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth + } + + /** Type of a reference implies that it needs to be hoisted */ + def refNeedsHoist(tp: Type): Boolean = tp match { + case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls + case tp: TermRef => refNeedsHoist(tp.prefix) + case _ => false + } + + /** Super call argument is complex, needs to be hoisted */ + def needsHoist(tree: Tree) = tree match { + case _: DefDef => true + case _: Template => true + case _: New => !tree.tpe.typeSymbol.isStatic + case _: RefTree | _: This => refNeedsHoist(tree.tpe) + case _ => false + } + + // begin hoistSuperArg + arg match { + case Apply(fn, arg1 :: Nil) if fn.symbol == defn.cbnArg => + cpy.Apply(arg)(fn, hoistSuperArg(arg1, cdef) :: Nil) + case _ if (arg.existsSubTree(needsHoist)) => + val superMeth = newSuperArgMethod(arg.tpe) + val superArgDef = polyDefDef(superMeth, trefs => vrefss => { + val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol) + val tmap = new TreeTypeMap( + typeMap = new TypeMap { + lazy val origToParam = origParams.zip(paramSyms).toMap + def apply(tp: Type) = tp match { + case tp: NamedType + if (tp.symbol.owner == cls || tp.symbol.owner == constr) && + tp.symbol.is(ParamOrAccessor) => + val mappedSym = origToParam(tp.symbol) + if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef + case _ => + mapOver(tp) + } + }, + treeMap = { + case tree: RefTree if paramSyms.contains(tree.symbol) => + cpy.Ident(tree)(tree.name).withType(tree.tpe) + case tree => + tree + }) + tmap(arg).changeOwnerAfter(constr, superMeth, thisTransform) + }) + superArgDefs += superArgDef + def termParamRefs(tp: Type, params: List[Symbol]): List[List[Tree]] = tp match { + case tp: PolyType => + termParamRefs(tp.resultType, params) + case tp: MethodType => + val (thisParams, otherParams) = params.splitAt(tp.paramNames.length) + thisParams.map(ref) :: termParamRefs(tp.resultType, otherParams) + case _ => + Nil + } + val (typeParams, termParams) = origParams.span(_.isType) + val res = ref(superMeth) + .appliedToTypes(typeParams.map(_.typeRef)) + .appliedToArgss(termParamRefs(constr.info, termParams)) + ctx.log(i"hoist $arg, cls = $cls = $res") + res + case _ => arg + } + } + + /** Hoist complex arguments in super call out of the class. */ + def hoistSuperArgsFromCall(superCall: Tree, cdef: DefDef): Tree = superCall match { + case Apply(fn, args) => + cpy.Apply(superCall)(hoistSuperArgsFromCall(fn, cdef), args.mapconserve(hoistSuperArg(_, cdef))) + case _ => + superCall + } + + /** Hoist complex arguments in this-constructor call of secondary constructor out of the class. */ + def hoistSuperArgsFromConstr(stat: Tree): Tree = stat match { + case stat: DefDef if stat.symbol.isClassConstructor => + cpy.DefDef(stat)(rhs = + stat.rhs match { + case Block(superCall :: stats, expr) => + val superCall1 = hoistSuperArgsFromCall(superCall, stat) + if (superCall1 eq superCall) stat.rhs + else cpy.Block(stat.rhs)(superCall1 :: stats, expr) + case _ => + hoistSuperArgsFromCall(stat.rhs, stat) + }) + case _ => + stat + } + } + + override def transformTypeDef(tdef: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = + tdef.rhs match { + case impl @ Template(cdef, superCall :: others, _, _) => + val hoist = new Hoister(tdef.symbol) + val hoistedSuperCall = hoist.hoistSuperArgsFromCall(superCall, cdef) + val hoistedBody = impl.body.mapconserve(hoist.hoistSuperArgsFromConstr) + if (hoist.superArgDefs.isEmpty) tdef + else { + val (staticSuperArgDefs, enclSuperArgDefs) = + hoist.superArgDefs.toList.partition(_.symbol.is(JavaStatic)) + flatTree( + cpy.TypeDef(tdef)( + rhs = cpy.Template(impl)( + parents = hoistedSuperCall :: others, + body = hoistedBody ++ staticSuperArgDefs)) :: + enclSuperArgDefs) + } + case _ => + tdef + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index 5aabe1e0c661..ba764a91069c 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -66,7 +66,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform override def relaxedTyping = true - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors]) + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors], classOf[HoistSuperArgs]) // Constructors has to happen before LambdaLift because the lambda lift logic // becomes simpler if it can assume that parameter accessors have already been // converted to parameters in super calls. Without this it is very hard to get @@ -146,14 +146,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform if (sym.maybeOwner.isTerm && owner.isProperlyContainedIn(liftedOwner(sym)) && owner != sym) { - if (sym.is(InSuperCall) && owner.isProperlyContainedIn(sym.enclosingClass)) - narrowLiftedOwner(sym, sym.enclosingClass) - else { ctx.log(i"narrow lifted $sym to $owner") changedLiftedOwner = true liftedOwner(sym) = owner } - } /** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined * in `enclosure` or there is an intermediate class properly containing `enclosure` @@ -387,7 +383,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform local.copySymDenotation( owner = newOwner, name = newName(local), - initFlags = local.flags &~ (InSuperCall | Module) | Private | maybeStatic, + initFlags = local.flags &~ Module | Private | maybeStatic, // drop Module because class is no longer a singleton in the lifted context. info = liftedInfo(local)).installAfter(thisTransform) } diff --git a/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala b/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala index 5d60bb984102..b90c6ed37620 100644 --- a/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala +++ b/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala @@ -43,6 +43,8 @@ class SelectStatic extends MiniPhaseTransform with IdentityDenotTransformer { th Block(stats, Apply(qual, nm)) case TypeApply(Block(stats, qual), nm) => Block(stats, TypeApply(qual, nm)) + case Closure(env, Block(stats, qual), tpt) => + Block(stats, Closure(env, qual, tpt)) case _ => t } @@ -53,4 +55,8 @@ class SelectStatic extends MiniPhaseTransform with IdentityDenotTransformer { th override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { normalize(tree) } + + override def transformClosure(tree: tpd.Closure)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + normalize(tree) + } } diff --git a/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala new file mode 100644 index 000000000000..b9218163666b --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala @@ -0,0 +1,61 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core._ +import Symbols._ +import SymDenotations._ +import Contexts._ +import Types._ +import Flags._ +import Decorators._ +import DenotTransformers._ +import core.StdNames.nme +import ast.Trees._ + +/** Abstract base class of ByNameClosures and ElimByName, factoring out the + * common functionality to transform arguments of by-name parameters. + */ +abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer: DenotTransformer => + import ast.tpd._ + + /** The info of the tree's symbol before it is potentially transformed in this phase */ + private def originalDenotation(tree: Tree)(implicit ctx: Context) = + tree.symbol.denot(ctx.withPhase(thisTransformer)) + + /** If denotation had an ExprType before, it now gets a function type */ + protected def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = + (symd is Param) || (symd is (ParamAccessor, butNot = Method)) + + protected def isByNameRef(tree: Tree)(implicit ctx: Context) = { + val origDenot = originalDenotation(tree) + origDenot.info.isInstanceOf[ExprType] && exprBecomesFunction(origDenot) + } + + def mkByNameClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = unsupported(i"mkClosure($arg)") + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { + + def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { + case formalExpr: ExprType => + var argType = arg.tpe.widenIfUnstable + if (defn.isBottomType(argType)) argType = formal.widenExpr + def wrap(arg: Tree) = ref(defn.cbnArg).appliedToType(argType).appliedTo(arg) + arg match { + case Apply(Select(qual, nme.apply), Nil) + if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => + wrap(qual) + case _ => + if (isByNameRef(arg) || arg.symbol == defn.cbnArg) arg + else wrap(mkByNameClosure(arg, argType)) + } + case _ => + arg + } + + val mt @ MethodType(_) = tree.fun.tpe.widen + val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg) + cpy.Apply(tree)(tree.fun, args1) + } +} diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index da9f9f6ac218..23de2a089b93 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -278,14 +278,12 @@ class Namer { typer: Typer => else name } - val inSuperCall = if (ctx.mode is Mode.InSuperCall) InSuperCall else EmptyFlags - tree match { case tree: TypeDef if tree.isClassDef => val name = checkNoConflict(tree.name.encode).toTypeName val flags = checkFlags(tree.mods.flags &~ Implicit) val cls = recordSym(ctx.newClassSymbol( - ctx.owner, name, flags | inSuperCall, + ctx.owner, name, flags, cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree), privateWithinClass(tree.mods), tree.namePos, ctx.source.file), tree) cls.completer.asInstanceOf[ClassCompleter].init() @@ -296,8 +294,6 @@ class Namer { typer: Typer => val isDeferred = lacksDefinition(tree) val deferred = if (isDeferred) Deferred else EmptyFlags val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags - val inSuperCall1 = if (tree.mods is ParamOrAccessor) EmptyFlags else inSuperCall - // suppress inSuperCall for constructor parameters val higherKinded = tree match { case TypeDef(_, LambdaTypeTree(_, _)) if isDeferred => HigherKinded case _ => EmptyFlags @@ -320,7 +316,7 @@ class Namer { typer: Typer => } recordSym(ctx.newSymbol( - ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1, + ctx.owner, name, flags | deferred | method | higherKinded, adjustIfModule(completer, tree), privateWithinClass(tree.mods), tree.namePos), tree) case tree: Import => diff --git a/tests/neg/tailcall/t1672b.scala b/tests/neg/tailcall/t1672b.scala index 1ae3d8af86d9..24ba9fd91c25 100644 --- a/tests/neg/tailcall/t1672b.scala +++ b/tests/neg/tailcall/t1672b.scala @@ -46,7 +46,7 @@ object Test1772B { else 1 + (try { throw new RuntimeException } catch { - case _: Throwable => bar(i - 1) // old-error + case _: Throwable => bar(i - 1) // old error }) } } diff --git a/tests/run/supercalls-traits.scala b/tests/run/supercalls-traits.scala index 09e841f46226..241419314e68 100644 --- a/tests/run/supercalls-traits.scala +++ b/tests/run/supercalls-traits.scala @@ -10,6 +10,13 @@ class C extends A with B { override def foo = super[A].foo + super[B].foo } +class Base[A](exp: => Option[A]) + +object Empty extends Base[Nothing](None) + object Test { - def main(args: Array[String]) = assert(new C().foo == 3) + def main(args: Array[String]): Unit = { + assert(new C().foo == 3) + Empty + } }