diff options
Diffstat (limited to 'src/dotty/tools')
53 files changed, 1937 insertions, 393 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index a4a8fbbc8..95f74e290 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -11,7 +11,6 @@ import reporting.ConsoleReporter import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.transform._ import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer} -import dotty.tools.dotc.transform.PostTyperTransformers.PostTyperTransformer import dotty.tools.dotc.core.DenotTransformers.DenotTransformer import dotty.tools.dotc.core.Denotations.SingleDenotation @@ -20,14 +19,17 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new LazyValsCreateCompanionObjects, - new TailRec), //force separataion between lazyVals and LVCreateCO + List(new Companions, new ElimRepeated /*, new ElimLocals*/), + List(new SuperAccessors), + List(new ExtensionMethods), + List(new TailRec), List(new PatternMatcher, new LazyValTranformContext().transformer, new Splitter), List(new Nullarify, new TypeTestsCasts, - new InterceptedMethods), + new InterceptedMethods, + new Literalize), List(new Erasure), List(new UncurryTreeTransform /* , new Constructors */) diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/ElimLocals.scala new file mode 100644 index 000000000..878783ffc --- /dev/null +++ b/src/dotty/tools/dotc/ElimLocals.scala @@ -0,0 +1,13 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import DenotTransformers._ + +/** Widens all private[this] and protected[this] qualifiers to just private/protected */ +abstract class ElimLocals extends TreeTransform with InfoTransformer { thisTransformer => + + // TODO complete + +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 734963ea3..1354f7375 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -282,41 +282,46 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => - /** Is tree a definition that has no side effects when - * evaluated as part of a block after the first time? + /** The purity level of this statement. + * @return pure if statement has no side effects + * idempotent if running the statement a second time has no side effects + * impure otherwise */ - def isIdempotentDef(tree: tpd.Tree)(implicit ctx: Context): Boolean = unsplice(tree) match { + private def statPurity(tree: tpd.Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { case EmptyTree | TypeDef(_, _, _) | Import(_, _) | DefDef(_, _, _, _, _, _) => - true + Pure case ValDef(mods, _, _, rhs) => - !(mods is Mutable) && isIdempotentExpr(rhs) + if (mods is Mutable) Impure else exprPurity(rhs) case _ => - false + Impure } - /** Is tree an expression which can be inlined without affecting program semantics? + /** The purity level of this expression. + * @return pure if expression has no side effects + * idempotent if running the expression a second time has no side effects + * impure otherwise * - * Note that this is not called "isExprPure" since purity (lack of side-effects) - * is not the litmus test. References to modules and lazy vals are side-effecting, - * both because side-effecting code may be executed and because the first reference - * takes a different code path than all to follow; but they are safe to inline - * because the expression result from evaluating them is always the same. + * Note that purity and idempotency are different. References to modules and lazy + * vals are impure (side-effecting) both because side-effecting code may be executed and because the first reference + * takes a different code path than all to follow; but they are idempotent + * because running the expression a second time gives the cached result. */ - def isIdempotentExpr(tree: tpd.Tree)(implicit ctx: Context): Boolean = unsplice(tree) match { + private def exprPurity(tree: tpd.Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { case EmptyTree | This(_) | Super(_, _) | Literal(_) => - true + Pure case Ident(_) => - isIdempotentRef(tree) + refPurity(tree) case Select(qual, _) => - isIdempotentRef(tree) && isIdempotentExpr(qual) + refPurity(tree).min( + if (tree.symbol.is(Inline)) Pure else exprPurity(qual)) case TypeApply(fn, _) => - isIdempotentExpr(fn) + exprPurity(fn) /* * Not sure we'll need that. Comment out until we find out case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => @@ -326,21 +331,36 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case Apply(fn, Nil) => // Note: After uncurry, field accesses are represented as Apply(getter, Nil), // so an Apply can also be pure. - // However, before typing, applications of nullary functional values are also - // Apply(function, Nil) trees. To prevent them from being treated as pure, - // we check that the callee is a method. - // The callee might also be a Block, which has a null symbol, so we guard against that (SI-7185) - fn.symbol != null && (fn.symbol is (Method, butNot = Lazy)) && isIdempotentExpr(fn) + if (fn.symbol is Stable) exprPurity(fn) else Impure case Typed(expr, _) => - isIdempotentExpr(expr) + exprPurity(expr) case Block(stats, expr) => - (stats forall isIdempotentDef) && isIdempotentExpr(expr) + (exprPurity(expr) /: stats.map(statPurity))(_ min _) case _ => - false + Impure } + def isPureExpr(tree: tpd.Tree)(implicit ctx: Context) = exprPurity(tree) == Pure + def isIdempotentExpr(tree: tpd.Tree)(implicit ctx: Context) = exprPurity(tree) >= Idempotent + + /** The purity level of this reference. + * @return + * pure if reference is (nonlazy and stable) or to a parameterized function + * idempotent if reference is lazy and stable + * impure otherwise + * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable + * flags set. + */ + private def refPurity(tree: tpd.Tree)(implicit ctx: Context): PurityLevel = + if (!tree.tpe.widen.isParameterless) Pure + else if (!tree.symbol.is(Stable)) Impure + else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start. + else Pure + + def isPureRef(tree: tpd.Tree)(implicit ctx: Context) = + refPurity(tree) == Pure def isIdempotentRef(tree: tpd.Tree)(implicit ctx: Context) = - tree.symbol.isStable || !tree.tpe.widen.isParameterless + refPurity(tree) >= Idempotent /** Is symbol potentially a getter of a mutable variable? */ @@ -456,6 +476,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case nil => Nil } + + private class PurityLevel(val x: Int) { + def >= (that: PurityLevel) = x >= that.x + def min(that: PurityLevel) = new PurityLevel(x min that.x) + } + + private val Pure = new PurityLevel(2) + private val Idempotent = new PurityLevel(1) + private val Impure = new PurityLevel(0) } /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index dfd69c03c..8a36fee3a 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -361,6 +361,7 @@ object Trees { type ThisTree[-T >: Untyped] <: DenotingTree[T] override def denot(implicit ctx: Context) = tpe match { case tpe: NamedType => tpe.denot + case ThisType(cls) => cls.denot case _ => NoDenotation } } @@ -1162,6 +1163,8 @@ object Trees { cpy.Alternative(tree, transform(trees)) case UnApply(fun, implicits, patterns) => cpy.UnApply(tree, transform(fun), transform(implicits), transform(patterns)) + case EmptyValDef => + tree case ValDef(mods, name, tpt, rhs) => cpy.ValDef(tree, mods, name, transform(tpt), transform(rhs)) case DefDef(mods, name, tparams, vparamss, tpt, rhs) => diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 3b240ad2c..fecfefd37 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -4,7 +4,7 @@ package ast import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ -import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ +import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._ import CheckTrees._, Denotations._, Decorators._ import config.Printers._ import typer.ErrorReporting._ @@ -25,17 +25,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Select(qualifier: Tree, name: Name)(implicit ctx: Context): Select = ta.assignType(untpd.Select(qualifier, name), qualifier) - def Select(qualifier: Tree, tp: NamedType)(implicit ctx: Context): Select = - untpd.Select(qualifier, tp.name).withType(tp) - - def Select(qualifier: Tree, sym: Symbol)(implicit ctx: Context): Select = - untpd.Select(qualifier, sym.name).withType( - TermRef.withSig(qualifier.tpe, sym.name.asTermName, sym.signature, sym.denot.asSeenFrom(qualifier.tpe))) - - def SelectWithSig(qualifier: Tree, name: Name, sig: Signature)(implicit ctx: Context) = - untpd.SelectWithSig(qualifier, name, sig) - .withType(TermRef.withSig(qualifier.tpe, name.asTermName, sig)) - def SelectFromTypeTree(qualifier: Tree, name: Name)(implicit ctx: Context): SelectFromTypeTree = ta.assignType(untpd.SelectFromTypeTree(qualifier, name), qualifier) @@ -182,7 +171,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = ta.assignType(DefDef(sym, Function.const(rhs) _), sym) - def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = { + def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = + polyDefDef(sym, Function.const(rhsFn)) + + def polyDefDef(sym: TermSymbol, rhsFn: List[Type] => List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = { val (tparams, mtp) = sym.info match { case tp: PolyType => val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateBounds) @@ -200,11 +192,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case tp => (Nil, tp) } val (vparamss, rtp) = valueParamss(mtp) - val argss = vparamss map (_ map (vparam => Ident(vparam.termRef))) + val targs = tparams map (_.typeRef) + val argss = vparamss.nestedMap(vparam => Ident(vparam.termRef)) ta.assignType( untpd.DefDef( Modifiers(sym), sym.name, tparams map TypeDef, - vparamss map (_ map (ValDef(_))), TypeTree(rtp), rhsFn(argss)), sym) + vparamss.nestedMap(ValDef(_)), TypeTree(rtp), rhsFn(targs)(argss)), sym) } def TypeDef(sym: TypeSymbol)(implicit ctx: Context): TypeDef = @@ -258,9 +251,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** A tree representing the same reference as the given type */ def ref(tp: NamedType)(implicit ctx: Context): NameTree = - if (tp.symbol.isStatic) Ident(tp) + if (tp.symbol.isStatic || tp.prefix == NoPrefix) Ident(tp) else tp.prefix match { - case pre: TermRef => Select(ref(pre), tp) + case pre: TermRef => ref(pre).select(tp) case pre => SelectFromTypeTree(TypeTree(pre), tp) } // no checks necessary @@ -283,12 +276,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** new C(args), calling given constructor `constr` of C */ def New(tp: Type, constr: TermSymbol, args: List[Tree])(implicit ctx: Context): Apply = { val targs = tp.argTypes - Apply( - Select( - New(tp withoutArgs targs), - TermRef.withSig(tp.normalizedPrefix, constr)) - .appliedToTypes(targs), - args) + New(tp withoutArgs targs) + .select(TermRef.withSig(tp.normalizedPrefix, constr)) + .appliedToTypes(targs) + .appliedToArgs(args) } /** An object def @@ -380,34 +371,95 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = new TreeTypeMap(ownerMap = (sym => if (sym == from) to else sym)).apply(tree) + def select(name: Name)(implicit ctx: Context): Select = + Select(tree, name) + + def select(tp: NamedType)(implicit ctx: Context): Select = + untpd.Select(tree, tp.name).withType(tp) + + def select(sym: Symbol)(implicit ctx: Context): Select = + untpd.Select(tree, sym.name).withType( + TermRef.withSig(tree.tpe, sym.name.asTermName, sym.signature, sym.denot.asSeenFrom(tree.tpe))) + + def selectWithSig(name: Name, sig: Signature)(implicit ctx: Context) = + untpd.SelectWithSig(tree, name, sig) + .withType(TermRef.withSig(tree.tpe, name.asTermName, sig)) + + def appliedTo(arg: Tree)(implicit ctx: Context): Tree = + appliedToArgs(arg :: Nil) + + def appliedTo(arg: Tree, args: Tree*)(implicit ctx: Context): Tree = + appliedToArgs(arg :: args.toList) + + def appliedToArgs(args: List[Tree])(implicit ctx: Context): Apply = + Apply(tree, args) + + def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree = + ((tree: Tree) /: argss)(Apply(_, _)) + + def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil) + + def appliedToType(targ: Type)(implicit ctx: Context): Tree = + appliedToTypes(targ :: Nil) + def appliedToTypes(targs: List[Type])(implicit ctx: Context): Tree = - if (targs.isEmpty) tree else TypeApply(tree, targs map (TypeTree(_))) + appliedToTypeTrees(targs map (TypeTree(_))) + + def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree = + if (targs.isEmpty) tree else TypeApply(tree, targs) } implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { def tpes: List[Type] = xs map (_.tpe) } - class TreeTypeMap(val typeMap: TypeMap = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity _)(implicit ctx: Context) extends TreeMap { - override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = super.transform { - tree.withType(typeMap(tree.tpe)) match { - case bind: tpd.Bind => - val sym = bind.symbol - val newOwner = ownerMap(sym.owner) - val newInfo = typeMap(sym.info) - if ((newOwner ne sym.owner) || (newInfo ne sym.info)) - bind.withType(sym.copy(owner = newOwner, info = newInfo).namedType) - else - bind + /** A map that applies three functions together to a tree and makes sure + * they are coordinated so that the result is well-typed. The functions are + * @param typeMap A function from Type to type that gets applied to the + * type of every tree node and to all locally defined symbols + * @param ownerMap A function that translates owners of top-level local symbols + * defined in the mapped tree. + * @param treeMap A transformer that translates all encountered subtrees in + * prefix traversal order. + */ + final class TreeTypeMap( + val typeMap: Type => Type = IdentityTypeMap, + val ownerMap: Symbol => Symbol = identity _, + val treeMap: Tree => Tree = identity _)(implicit ctx: Context) extends TreeMap { + + override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = { + val tree1 = treeMap(tree) + tree1.withType(typeMap(tree1.tpe)) match { + case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + val (tmap1, tparams1) = transformDefs(ddef.tparams) + val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss) + cpy.DefDef(ddef, mods, name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(rhs)) + case blk @ Block(stats, expr) => + val (tmap1, stats1) = transformDefs(stats) + cpy.Block(blk, stats1, tmap1.transform(expr)) + case cdef @ CaseDef(pat, guard, rhs) => + val tmap = withMappedSyms(patVars(pat)) + cpy.CaseDef(cdef, tmap.transform(pat), tmap.transform(guard), tmap.transform(rhs)) case tree1 => - tree1 + super.transform(tree1) } } - override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = { - val locals = ta.localSyms(trees) - val mapped = ctx.mapSymbols(locals, typeMap, ownerMap) - if (locals eq mapped) super.transform(trees) - else withSubstitution(locals, mapped).transform(trees) + + override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = + transformDefs(trees)._2 + + private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = { + val tmap = withMappedSyms(ta.localSyms(trees)) + (tmap, tmap.transformSub(trees)) + } + + private def transformVParamss(vparamss: List[List[ValDef]]): (TreeTypeMap, List[List[ValDef]]) = vparamss match { + case vparams :: rest => + val (tmap1, vparams1) = transformDefs(vparams) + val (tmap2, vparamss2) = tmap1.transformVParamss(rest) + (tmap2, vparams1 :: vparamss2) + case nil => + (this, vparamss) } def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree] @@ -418,10 +470,35 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } /** The current tree map composed with a substitution [from -> to] */ - def withSubstitution(from: List[Symbol], to: List[Symbol]) = - new TreeTypeMap( - typeMap andThen ((tp: Type) => tp.substSym(from, to)), - ownerMap andThen (from zip to).toMap) + def withSubstitution(from: List[Symbol], to: List[Symbol]): TreeTypeMap = + if (from eq to) this + else new TreeTypeMap( + typeMap andThen (_.substSym(from, to)), + ownerMap andThen { sym => + val idx = from.indexOf(sym) + if (idx >= 0) to(idx) else sym + }, + treeMap) + + /** Apply `typeMap` and `ownerMap` to given symbols `syms` + * and return a treemap that contains the substitution + * between original and mapped symbols. + */ + def withMappedSyms(syms: List[Symbol]): TreeTypeMap = { + val mapped = ctx.mapSymbols(syms, typeMap, ownerMap) + withSubstitution(syms, mapped) + } + } + + /** The variables defined by a pattern, in reverse order of their appearance. */ + def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = { + val acc = new TreeAccumulator[List[Symbol]] { + def apply(syms: List[Symbol], tree: Tree) = tree match { + case Bind(_, body) => apply(tree.symbol :: syms, body) + case _ => foldOver(syms, tree) + } + } + acc(Nil, tree) } // convert a numeric with a toXXX method @@ -429,7 +506,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val mname = ("to" + numericCls.name).toTermName val conversion = tree.tpe member mname if (conversion.symbol.exists) - ensureApplied(Select(tree, conversion.symbol.termRef)) + ensureApplied(tree.select(conversion.symbol.termRef)) else if (tree.tpe.widen isRef numericCls) tree else { @@ -449,7 +526,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = ??? def mkAnd(tree1: Tree, tree2: Tree)(implicit ctx: Context) = - Apply(Select(tree1, defn.Boolean_and), tree2 :: Nil) + tree1.select(defn.Boolean_and).appliedTo(tree2) + + def mkAsInstanceOf(tree: Tree, pt: Type)(implicit ctx: Context): Tree = + tree.select(defn.Any_asInstanceOf).appliedToType(pt) + + def ensureConforms(tree: Tree, pt: Type)(implicit ctx: Context): Tree = + if (tree.tpe <:< pt) tree else mkAsInstanceOf(tree, pt) // ensure that constructors are fully applied? // ensure that normal methods are fully applied? diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index 155ea87e0..cd7b46896 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -143,7 +143,7 @@ object Decorators { * 2) Lists can be formatted using the desired separator between two `%` signs, * eg `i"myList = (${myList}%, %)"` */ - implicit class InfoString(val sc: StringContext) extends AnyVal { + implicit class StringInterpolators(val sc: StringContext) extends AnyVal { def i(args: Any*)(implicit ctx: Context): String = { @@ -166,7 +166,39 @@ object Decorators { val (args1, suffixes1) = (args, suffixes).zipped.map(treatArg(_, _)).unzip new StringContext(prefix :: suffixes1.toList: _*).s(args1: _*) } - } + /** Lifted from scala.reflect.internal.util + * A safe combination of [[scala.collection.immutable.StringLike#stripMargin]] + * and [[scala.StringContext#raw]]. + * + * The margin of each line is defined by whitespace leading up to a '|' character. + * This margin is stripped '''before''' the arguments are interpolated into to string. + * + * String escape sequences are '''not''' processed; this interpolater is designed to + * be used with triple quoted Strings. + * + * {{{ + * scala> val foo = "f|o|o" + * foo: String = f|o|o + * scala> sm"""|${foo} + * |""" + * res0: String = + * "f|o|o + * " + * }}} + */ + final def sm(args: Any*): String = { + def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringLike#isLineBreak + def stripTrailingPart(s: String) = { + val (pre, post) = s.span(c => !isLineBreak(c)) + pre + post.stripMargin + } + val stripped: List[String] = sc.parts.toList match { + case head :: tail => head.stripMargin :: (tail map stripTrailingPart) + case Nil => Nil + } + new StringContext(stripped: _*).raw(args: _*) + } + } } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 5e335e240..594f0c013 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -37,7 +37,7 @@ class Definitions { scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - newTypeField(cls, name, flags | TypeParamCreationFlags, scope) + newTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) private def newSyntheticTypeParam(cls: ClassSymbol, scope: MutableScope, paramFlags: FlagSet, suffix: String = "T0") = newTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) @@ -409,20 +409,20 @@ class Definitions { lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule) - def isTupleType(tp: Type) = { + def isTupleType(tp: Type)(implicit ctx: Context) = { val arity = tp.dealias.argInfos.length arity <= MaxTupleArity && (tp isRef TupleClass(arity)) } - def isProductSubType(tp: Type) = + def isProductSubType(tp: Type)(implicit ctx: Context) = (tp derivesFrom ProductClass) && tp.baseClasses.exists(ProductClasses contains _) - def isFunctionType(tp: Type) = { + def isFunctionType(tp: Type)(implicit ctx: Context) = { val arity = functionArity(tp) 0 <= arity && arity <= MaxFunctionArity && (tp isRef FunctionClass(arity)) } - def functionArity(tp: Type) = tp.dealias.argInfos.length - 1 + def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 // ----- LambdaXYZ traits ------------------------------------------ @@ -544,7 +544,7 @@ class Definitions { val BooleanEnc = 17 val UnitEnc = 19 - def isValueSubClass(cls1: Symbol, cls2: Symbol) = + def isValueSubClass(cls1: Symbol, cls2: Symbol)(implicit ctx: Context) = valueClassEnc(cls2) % valueClassEnc(cls1) == 0 // ----- Initialization --------------------------------------------------- diff --git a/src/dotty/tools/dotc/core/DenotTransformers.scala b/src/dotty/tools/dotc/core/DenotTransformers.scala index 6daa028fc..e052a07ea 100644 --- a/src/dotty/tools/dotc/core/DenotTransformers.scala +++ b/src/dotty/tools/dotc/core/DenotTransformers.scala @@ -46,4 +46,12 @@ object DenotTransformers { } } } + + /** A `DenotTransformer` trait that has the identity as its `transform` method. + * You might want to inherit from this trait so that new denotations can be + * installed using `installAfter` and `enteredAfter` at the end of the phase. + */ + trait IdentityDenotTransformer extends DenotTransformer { + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref + } } diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 264f9aa46..43fff62ec 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -375,7 +375,7 @@ object Denotations { final def signature(implicit ctx: Context): Signature = { if (isType) Signature.NotAMethod // don't force info if this is a type SymDenotation else info match { - case info: SignedType => + case info: MethodicType => try info.signature catch { // !!! DEBUG case ex: Throwable => @@ -494,7 +494,8 @@ object Denotations { } while (d ne denot) initial.syncWithParents case _ => - staleSymbolError + if (coveredInterval.containsPhaseId(ctx.phaseId)) staleSymbolError + else NoDenotation } /** Produce a denotation that is valid for the given context. @@ -512,7 +513,13 @@ object Denotations { def current(implicit ctx: Context): SingleDenotation = { val currentPeriod = ctx.period val valid = myValidFor - assert(valid.code > 0) + if (valid.code <= 0) { + // can happen if we sit on a stale denotation which has been replaced + // wholesale by an installAfter; in this case, proceed to the next + // denotation and try again. + if (validFor == Nowhere && nextInRun.validFor != Nowhere) return nextInRun.current + assert(false) + } if (valid.runId != currentPeriod.runId) bringForward.current else { @@ -551,6 +558,7 @@ object Denotations { cur = next } cur.validFor = Period(currentPeriod.runId, startPid, transformer.lastPhaseId) + //printPeriods(cur) //println(s"new denot: $cur, valid for ${cur.validFor}") } cur.current // multiple transformations could be required @@ -563,23 +571,28 @@ object Denotations { //println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}") cur = cur.nextInRun cnt += 1 - assert(cnt <= MaxPossiblePhaseId, s"seems to be a loop in Denotations for $this, currentPeriod = $currentPeriod") + assert(cnt <= MaxPossiblePhaseId, demandOutsideDefinedMsg) } cur } - } } + private def demandOutsideDefinedMsg(implicit ctx: Context): String = + s"demanding denotation of $this at phase ${ctx.phase}(${ctx.phaseId}) outside defined interval: defined periods are${definedPeriodsString}" + /** Install this denotation to be the result of the given denotation transformer. * This is the implementation of the same-named method in SymDenotations. * It's placed here because it needs access to private fields of SingleDenotation. + * @pre Can only be called in `phase.next`. */ protected def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = { val targetId = phase.next.id assert(ctx.phaseId == targetId, s"denotation update for $this called in phase ${ctx.phase}, expected was ${phase.next}") val current = symbol.current + // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") + // printPeriods(current) this.nextInRun = current.nextInRun this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) if (current.validFor.firstPhaseId == targetId) { @@ -587,12 +600,14 @@ object Denotations { var prev = current while (prev.nextInRun ne current) prev = prev.nextInRun prev.nextInRun = this + current.validFor = Nowhere } else { // insert this denotation after current current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) current.nextInRun = this } + // printPeriods(this) } def staleSymbolError(implicit ctx: Context) = { @@ -604,6 +619,22 @@ object Denotations { throw new StaleSymbol(msg) } + /** The period (interval of phases) for which there exists + * a valid denotation in this flock. + */ + def coveredInterval(implicit ctx: Context): Period = { + var cur = this + var cnt = 0 + var interval = validFor + do { + cur = cur.nextInRun + cnt += 1 + assert(cnt <= MaxPossiblePhaseId, demandOutsideDefinedMsg) + interval |= cur.validFor + } while (cur ne this) + interval + } + /** For ClassDenotations only: * If caches influenced by parent classes are still valid, the denotation * itself, otherwise a freshly initialized copy. @@ -614,10 +645,24 @@ object Denotations { if (symbol == NoSymbol) symbol.toString else s"<SingleDenotation of type $infoOrCompleter>" + + def definedPeriodsString: String = { + var sb = new StringBuilder() + var cur = this + var cnt = 0 + do { + sb.append(" " + cur.validFor) + cur = cur.nextInRun + cnt += 1 + if (cnt > MaxPossiblePhaseId) { sb.append(" ..."); cur = this } + } while (cur ne this) + sb.toString + } + // ------ PreDenotation ops ---------------------------------------------- final def first = this - final def toDenot(pre: Type)(implicit ctx: Context) = this + final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym) final def containsSig(sig: Signature)(implicit ctx: Context) = exists && (signature matches sig) @@ -764,7 +809,7 @@ object Denotations { else DenotUnion(this, that) } - case class DenotUnion(denots1: PreDenotation, denots2: PreDenotation) extends PreDenotation { + final case class DenotUnion(denots1: PreDenotation, denots2: PreDenotation) extends PreDenotation { assert(denots1.exists && denots2.exists, s"Union of non-existing denotations ($denots1) and ($denots2)") def exists = true def first = denots1.first diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 40da7525d..c467a553f 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -102,8 +102,16 @@ object Flags { } /** The list of non-empty names of flags that are set in this FlagSet */ - def flagStrings: Seq[String] = - (2 to MaxFlag).flatMap(flagString) + def flagStrings: Seq[String] = { + val rawStrings = (2 to MaxFlag).flatMap(flagString) + if (this is Local) + rawStrings.filter(_ != "<local>").map { + case "private" => "private[this]" + case "protected" => "protected[this]" + case str => str + } + else rawStrings + } /** The string representation of this flag set */ override def toString = flagStrings.mkString(" ") @@ -316,8 +324,8 @@ object Flags { /** Symbol is initialized to the default value, e.g. var x: T = _ */ final val DefaultInit = termFlag(29, "<defaultinit>") - /** Symbol is a macro */ - final val Macro = commonFlag(30, "<macro>") + /** Symbol is inlined */ + final val Inline = commonFlag(30, "inline") /** Symbol is defined by a Java class */ final val JavaDefined = commonFlag(31, "<java>") @@ -387,14 +395,17 @@ object Flags { /** A definition that's initialized before the super call (Scala 2.x only) */ final val Scala2PreSuper = termFlag(58, "<presuper>") + /** A macro (Scala 2.x only) */ + final val Macro = commonFlag(59, "<macro>") + /** A method that is known to have inherited default parameters */ - final val InheritedDefaultParams = termFlag(59, "<inherited-default-param>") + final val InheritedDefaultParams = termFlag(60, "<inherited-default-param>") - /** A method that is known to no default parameters */ - final val NoDefaultParams = termFlag(60, "<no-default-param>") + /** A method that is known to have no default parameters */ + final val NoDefaultParams = termFlag(61, "<no-default-param>") /** A denotation that is valid in all run-ids */ - final val Permanent = commonFlag(61, "<permanent>") + final val Permanent = commonFlag(62, "<permanent>") // --------- Combined Flag Sets and Conjunctions ---------------------- @@ -405,7 +416,10 @@ object Flags { /** Flags representing modifiers that can appear in trees */ final val ModifierFlags = - SourceModifierFlags | Trait | Module | Param | Synthetic | Package + SourceModifierFlags | Module | Param | Synthetic | Package | Local + // | Trait is subsumed by commonFlags(Lazy) from SourceModifierFlags + + assert(ModifierFlags.isTermFlags && ModifierFlags.isTypeFlags) /** Flags representing access rights */ final val AccessFlags = Private | Protected | Local @@ -438,8 +452,8 @@ object Flags { /** The flags of the self symbol */ final val SelfSymFlags = Private | Local | Deferred - /** The flags of a type parameter */ - final val TypeParamCreationFlags = TypeParam | Deferred | Protected | Local + /** The flags of a class type parameter */ + final def ClassTypeParamCreationFlags = TypeParam | Deferred | Protected | Local /** Flags that can apply to both a module val and a module class, except those that * are added at creation anyway diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 5bdafcf8a..404a0844a 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -47,6 +47,13 @@ object NameOps { } } + object SuperAccessorName { + val pre = nme.SUPER_PREFIX + def apply(name: TermName): TermName = pre ++ name + def unapply(name: TermName): Option[TermName] = + if (name startsWith pre) Some(name.drop(pre.length).asTermName) else None + } + implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { import nme._ @@ -59,7 +66,6 @@ object NameOps { def isLocalDummyName = name startsWith LOCALDUMMY_PREFIX def isLoopHeaderLabel = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX) def isProtectedAccessorName = name startsWith PROTECTED_PREFIX - def isSuperAccessorName = name startsWith SUPER_PREFIX def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER def isSetterName = name endsWith SETTER_SUFFIX def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_SEPARATOR) @@ -126,6 +132,9 @@ object NameOps { if (flags is (ModuleClass, butNot = Package)) name.asTypeName.moduleClassName.asInstanceOf[N] else name + /** The superaccessor for method with given name */ + def superName: TermName = (nme.SUPER_PREFIX ++ name).toTermName + /** The expanded name of `name` relative to this class `base` with given `separator` */ def expandedName(base: Symbol, separator: Name = nme.EXPAND_SEPARATOR)(implicit ctx: Context): N = { @@ -247,17 +256,13 @@ object NameOps { else -1 } - /** The name of a super-accessor */ - def superAccessorName: TermName = - SUPER_PREFIX ++ name - /** The name of an accessor for protected symbols. */ def protectedAccessorName: TermName = - PROTECTED_PREFIX ++ name + PROTECTED_PREFIX ++ name.unexpandedName() /** The name of a setter for protected symbols. Used for inherited Java fields. */ - def protectedSetterName(name: Name): TermName = - PROTECTED_SET_PREFIX ++ name + def protectedSetterName: TermName = + PROTECTED_SET_PREFIX ++ name.unexpandedName() def moduleVarName: TermName = name ++ MODULE_VAR_SUFFIX diff --git a/src/dotty/tools/dotc/core/Periods.scala b/src/dotty/tools/dotc/core/Periods.scala index 4ab04fad0..e0d9e3b5d 100644 --- a/src/dotty/tools/dotc/core/Periods.scala +++ b/src/dotty/tools/dotc/core/Periods.scala @@ -67,6 +67,8 @@ object Periods { /** The first phase of this period */ def firstPhaseId = lastPhaseId - (code & PhaseMask) + def containsPhaseId(id: PhaseId) = firstPhaseId <= id && id <= lastPhaseId + /** Does this period contain given period? */ def contains(that: Period): Boolean = { // Let this = (r1, l1, d1), that = (r2, l2, d2) @@ -106,6 +108,11 @@ object Periods { else Nowhere + def | (that: Period): Period = + Period(this.runId, + this.firstPhaseId min that.firstPhaseId, + this.lastPhaseId max that.lastPhaseId) + override def toString = s"Period($firstPhaseId..$lastPhaseId, run = $runId)" } diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 7bc5f3052..aabde4cf9 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -9,7 +9,6 @@ import Denotations._ import config.Printers._ import scala.collection.mutable.{ListBuffer, ArrayBuffer} import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, TreeTransform} -import dotty.tools.dotc.transform.PostTyperTransformers.PostTyperTransformer import dotty.tools.dotc.transform.TreeTransforms import TreeTransforms.Separator import Periods._ @@ -72,12 +71,10 @@ object Phases { /** Squash TreeTransform's beloning to same sublist to a single TreeTransformer * Each TreeTransform gets own period, * whereas a combined TreeTransformer gets period equal to union of periods of it's TreeTransforms - * first TreeTransformer emitted is PostTyperTransformer that simplifies trees, see it's documentation */ private def squashPhases(phasess: List[List[Phase]]): Array[Phase] = { val squashedPhases = ListBuffer[Phase]() var prevPhases: Set[String] = Set.empty - var postTyperEmmited = false var i = 0 while (i < phasess.length) { if (phasess(i).length > 1) { @@ -95,17 +92,10 @@ object Phases { } } val transforms = phasess(i).asInstanceOf[List[TreeTransform]] - val block = - if (!postTyperEmmited) { - postTyperEmmited = true - new PostTyperTransformer { - override def name: String = transformations.map(_.name).mkString("TreeTransform:{", ", ", "}") - override def transformations: Array[TreeTransform] = transforms.toArray - } - } else new TreeTransformer { - override def name: String = transformations.map(_.name).mkString("TreeTransform:{", ", ", "}") - override def transformations: Array[TreeTransform] = transforms.toArray - } + val block = new TreeTransformer { + override def name: String = transformations.map(_.name).mkString("TreeTransform:{", ", ", "}") + override def transformations: Array[TreeTransform] = transforms.toArray + } squashedPhases += block prevPhases ++= phasess(i).map(_.name) block.init(this, phasess(i).head.id, phasess(i).last.id) diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 367713d11..919e35a7e 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -18,6 +18,7 @@ import SymDenotations._ import printing.Texts._ import printing.Printer import util.common._ +import util.DotClass import SymDenotations.NoDenotation import collection.mutable.ListBuffer @@ -55,7 +56,7 @@ object Scopes { * or to delete them. These methods are provided by subclass * MutableScope. */ - abstract class Scope extends printing.Showable with Iterable[Symbol] { + abstract class Scope extends DotClass with printing.Showable with Iterable[Symbol] { /** The last scope-entry from which all others are reachable via `prev` */ private[dotc] def lastEntry: ScopeEntry @@ -77,8 +78,8 @@ object Scopes { */ def iterator: Iterator[Symbol] = toList.iterator - /** Returns a new scope with the same content as this one. */ - def cloneScope(implicit ctx: Context): Scope + /** Returns a new mutable scope with the same content as this one. */ + def cloneScope(implicit ctx: Context): MutableScope /** Is the scope empty? */ override def isEmpty: Boolean = lastEntry eq null @@ -354,7 +355,7 @@ object Scopes { override def size = 0 override def nestingLevel = 0 override def toList = Nil - override def cloneScope(implicit ctx: Context): Scope = this + override def cloneScope(implicit ctx: Context): MutableScope = unsupported("cloneScope") override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = null override def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry = null } diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala index eb85fbb99..22d038d11 100644 --- a/src/dotty/tools/dotc/core/Signature.scala +++ b/src/dotty/tools/dotc/core/Signature.scala @@ -49,7 +49,7 @@ object Signature { * a type different from PolyType, MethodType, or ExprType. */ val NotAMethod = Signature(List(), EmptyTypeName) - + /** The signature of an overloaded denotation. */ val OverloadedSignature = Signature(List(tpnme.OVERLOADED), EmptyTypeName) diff --git a/src/dotty/tools/dotc/core/Substituters.scala b/src/dotty/tools/dotc/core/Substituters.scala index 1b96de47e..3d14317cb 100644 --- a/src/dotty/tools/dotc/core/Substituters.scala +++ b/src/dotty/tools/dotc/core/Substituters.scala @@ -205,7 +205,11 @@ trait Substituters { this: Context => final class SubstMap(from: List[Symbol], to: List[Type]) extends DeepTypeMap { def apply(tp: Type): Type = subst(tp, from, to, this) } - +/* not needed yet + final class SubstDealiasMap(from: List[Symbol], to: List[Type]) extends SubstMap(from, to) { + override def apply(tp: Type): Type = subst(tp.dealias, from, to, this) + } +*/ final class SubstSymMap(from: List[Symbol], to: List[Symbol]) extends DeepTypeMap { def apply(tp: Type): Type = substSym(tp, from, to, this) } diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 802762045..b0a09baf0 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -83,6 +83,9 @@ object SymDenotations { /** The owner of the symbol; overridden in NoDenotation */ def owner: Symbol = ownerIfExists + /** Same as owner, except returns NoSymbol for NoSymbol */ + def maybeOwner: Symbol = if (exists) owner else NoSymbol + /** The flag set */ final def flags(implicit ctx: Context): FlagSet = { ensureCompleted(); myFlags } @@ -212,6 +215,14 @@ object SymDenotations { final def addAnnotation(annot: Annotation): Unit = annotations = annot :: myAnnotations + /** Remove annotation with given class from this denotation */ + final def removeAnnotation(cls: Symbol)(implicit ctx: Context): Unit = + annotations = myAnnotations.filterNot(_ matches cls) + + /** Copy all annotations from given symbol by adding them to this symbol */ + final def addAnnotations(from: Symbol)(implicit ctx: Context): Unit = + from.annotations.foreach(addAnnotation) + @tailrec private def dropOtherAnnotations(anns: List[Annotation], cls: Symbol)(implicit ctx: Context): List[Annotation] = anns match { case ann :: rest => if (ann matches cls) anns else dropOtherAnnotations(rest, cls) @@ -321,6 +332,12 @@ object SymDenotations { final def isAnonymousClass(implicit ctx: Context): Boolean = initial.asSymDenotation.name startsWith tpnme.ANON_CLASS + /** Is symbol a primitive value class? */ + def isPrimitiveValueClass(implicit ctx: Context) = defn.ScalaValueClasses contains symbol + + /** Is symbol a phantom class for which no runtime representation exists? */ + def isPhantomClass(implicit ctx: Context) = defn.PhantomClasses contains symbol + /** Is this symbol a class representing a refinement? These classes * are used only temporarily in Typer and Unpickler as an intermediate * step for creating Refinement types. @@ -447,7 +464,7 @@ object SymDenotations { def accessWithin(boundary: Symbol) = ctx.owner.isContainedIn(boundary) && (!(this is JavaDefined) || // disregard package nesting for Java - ctx.owner.enclosingPackage == boundary.enclosingPackage) + ctx.owner.enclosingPackageClass == boundary.enclosingPackageClass) /** Are we within definition of linked class of `boundary`? */ def accessWithinLinked(boundary: Symbol) = { @@ -572,6 +589,12 @@ object SymDenotations { NoSymbol } + /** The field accessed by this getter or setter */ + def accessedField(implicit ctx: Context): Symbol = { + val fieldName = if (isSetter) name.asTermName.setterToGetter else name + owner.info.decl(fieldName).suchThat(d => !(d is Method)).symbol + } + /** The chain of owners of this denotation, starting with the denoting symbol itself */ final def ownersIterator(implicit ctx: Context) = new Iterator[Symbol] { private[this] var current = symbol @@ -624,8 +647,8 @@ object SymDenotations { } /** The package class containing this denotation */ - final def enclosingPackage(implicit ctx: Context): Symbol = - if (this is PackageClass) symbol else owner.enclosingPackage + final def enclosingPackageClass(implicit ctx: Context): Symbol = + if (this is PackageClass) symbol else owner.enclosingPackageClass /** The module object with the same (term-) name as this class or module class, * and which is also defined in the same scope and compilation unit. @@ -747,7 +770,6 @@ object SymDenotations { loop(base.info.baseClasses.dropWhile(owner != _).tail) } - /** A a member of class `base` is incomplete if * (1) it is declared deferred or * (2) it is abstract override and its super symbol in `base` is @@ -809,6 +831,11 @@ object SymDenotations { else if (this is Contravariant) -1 else 0 + /** The flags to be used for a type parameter owned by this symbol. + * Overridden by ClassDenotation. + */ + def typeParamCreationFlags: FlagSet = TypeParam + override def toString = { val kindString = if (myFlags is ModuleClass) "module class" @@ -848,6 +875,22 @@ object SymDenotations { /** Install this denotation as the result of the given denotation transformer. */ override def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = super.installAfter(phase) + + /** Remove private modifier from symbol's definition. If this symbol + * is not a constructor nor a static module, rename it by expanding its name to avoid name clashes + * @param base the fully qualified name of this class will be appended if name expansion is needed + */ + final def makeNotPrivateAfter(base: Symbol, phase: DenotTransformer)(implicit ctx: Context): Unit = { + if (this.is(Private)) { + val newName = + if (this.is(Module) && isStatic || isClassConstructor) name + else { + if (this.is(Module)) moduleClass.makeNotPrivateAfter(base, phase) + name.expandedName(base) + } + copySymDenotation(name = newName, initFlags = flags &~ Private).installAfter(phase) + } + } } /** The contents of a class definition during a period @@ -895,6 +938,15 @@ object SymDenotations { case _ => Nil } + /** The symbol of the superclass, NoSymbol if no superclass exists */ + def superClass(implicit ctx: Context): Symbol = classParents match { + case parent :: _ => + val cls = parent.classSymbol + if (cls is Trait) NoSymbol else cls + case _ => + NoSymbol + } + /** The denotation is fully completed: all attributes are fully defined. * ClassDenotations compiled from source are first completed, then fully completed. * @see Namer#ClassCompleter @@ -938,6 +990,7 @@ object SymDenotations { mySuperClassBits = null myMemberFingerPrint = FingerPrint.unknown myMemberCache = null + myMemberCachePeriod = Nowhere memberNamesCache = SimpleMap.Empty } @@ -1036,6 +1089,8 @@ object SymDenotations { (symbol eq defn.NothingClass) || (symbol eq defn.NullClass) && (base ne defn.NothingClass)) + final override def typeParamCreationFlags = ClassTypeParamCreationFlags + private[this] var myMemberFingerPrint: FingerPrint = FingerPrint.unknown private def computeMemberFingerPrint(implicit ctx: Context): FingerPrint = { @@ -1070,9 +1125,13 @@ object SymDenotations { } private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null + private[this] var myMemberCachePeriod: Period = Nowhere - private def memberCache: LRUCache[Name, PreDenotation] = { - if (myMemberCache == null) myMemberCache = new LRUCache + private def memberCache(implicit ctx: Context): LRUCache[Name, PreDenotation] = { + if (myMemberCachePeriod != ctx.period) { + myMemberCache = new LRUCache + myMemberCachePeriod = ctx.period + } myMemberCache } @@ -1289,9 +1348,20 @@ object SymDenotations { decls.denotsNamed(cname).first.symbol } - def underlyingOfValueClass: Type = ??? - - def valueClassUnbox: Symbol = ??? + /** If this class has the same `decls` scope reference in `phase` and + * `phase.next`, install a new denotation with a cloned scope in `phase.next`. + * @pre Can only be called in `phase.next`. + */ + def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = { + assert(ctx.phaseId == phase.next.id) + val prevCtx = ctx.withPhase(phase) + val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo + if (classInfo(prevCtx).decls eq decls) { + copySymDenotation( + info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo), + initFlags = this.flags &~ Frozen).installAfter(phase) + } + } } /** The denotation of a package class. diff --git a/src/dotty/tools/dotc/core/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala index bebad60cc..886c728b7 100644 --- a/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -12,7 +12,7 @@ import scala.compat.Platform.currentTime import dotty.tools.io.{ ClassPath, AbstractFile } import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, util.Positions._, Names._ import StdNames._, NameOps._ -import Decorators.StringDecorator +import Decorators.{StringDecorator, StringInterpolators} import pickling.ClassfileParser object SymbolLoaders { @@ -69,8 +69,8 @@ class SymbolLoaders { // require yjp.jar at runtime. See SI-2089. if (ctx.settings.termConflict.isDefault) throw new TypeError( - s"""$owner contains object and package with same name: $pname - |one of them needs to be removed from classpath""".stripMargin) + sm"""$owner contains object and package with same name: $pname + |one of them needs to be removed from classpath""") else if (ctx.settings.termConflict.value == "package") { ctx.warning( s"Resolving package/object name conflict in favor of package ${preExisting.fullName}. The object will be inaccessible.") diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 26553ddff..1767d7c0c 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -16,6 +16,7 @@ import printing.Printer import Types._ import Annotations._ import util.Positions._ +import DenotTransformers._ import StdNames._ import NameOps._ import ast.tpd.{TreeTypeMap, Tree} @@ -250,7 +251,7 @@ trait Symbols { this: Context => val tparams = tparamBuf.toList val bounds = boundsFn(trefBuf.toList) for ((name, tparam, bound) <- (names, tparams, bounds).zipped) - tparam.denot = SymDenotation(tparam, owner, name, flags | TypeParamCreationFlags, bound) + tparam.denot = SymDenotation(tparam, owner, name, flags | owner.typeParamCreationFlags, bound) tparams } @@ -260,16 +261,14 @@ trait Symbols { this: Context => newSymbol(owner, name, SyntheticArtifact, if (name.isTypeName) TypeAlias(ErrorType) else ErrorType) - type OwnerMap = Symbol => Symbol - /** Map given symbols, subjecting all types to given type map and owner map. * Cross symbol references are brought over from originals to copies. * Do not copy any symbols if all attributes of all symbols stay the same. */ def mapSymbols( originals: List[Symbol], - typeMap: TypeMap = IdentityTypeMap, - ownerMap: OwnerMap = identity) + typeMap: Type => Type = IdentityTypeMap, + ownerMap: Symbol => Symbol = identity) = if (originals forall (sym => (typeMap(sym.info) eq sym.info) && (ownerMap(sym.owner) eq sym.owner))) @@ -358,6 +357,10 @@ object Symbols { final def asType(implicit ctx: Context): TypeSymbol = { assert(isType, s"isType called on not-a-Type $this"); asInstanceOf[TypeSymbol] } final def asClass: ClassSymbol = asInstanceOf[ClassSymbol] + /** Special cased here, because it may be used on naked symbols in substituters */ + final def isStatic(implicit ctx: Context): Boolean = + lastDenot != null && denot.isStatic + /** A unique, densely packed integer tag for each class symbol, -1 * for all other symbols. To save memory, this method * should be called only if class is a super class of some other class. @@ -372,6 +375,17 @@ object Symbols { this } + /** Enter this symbol in its class owner after given `phase`. Create a fresh + * denotation for its owner class if the class has not yet already one + * that starts being valid after `phase`. + * @pre Symbol is a class member + */ + def enteredAfter(phase: DenotTransformer)(implicit ctx: Context): this.type = { + val nextCtx = ctx.withPhase(phase.next) + this.owner.asClass.ensureFreshScopeAfter(phase)(nextCtx) + entered(nextCtx) + } + /** This symbol, if it exists, otherwise the result of evaluating `that` */ def orElse(that: => Symbol)(implicit ctx: Context) = if (this.exists) this else that @@ -381,14 +395,8 @@ object Symbols { /** Is this symbol a user-defined value class? */ final def isDerivedValueClass(implicit ctx: Context): Boolean = - false && // value classes are not supported yet - isClass && denot.derivesFrom(defn.AnyValClass) && !isPrimitiveValueClass - - /** Is symbol a primitive value class? */ - def isPrimitiveValueClass(implicit ctx: Context) = defn.ScalaValueClasses contains this - - /** Is symbol a phantom class for which no runtime representation exists? */ - def isPhantomClass(implicit ctx: Context) = defn.PhantomClasses contains this + false // will migrate to ValueClasses.isDerivedValueClass; + // unsupported value class code will continue to use this stub while it exists /** The current name of this symbol */ final def name(implicit ctx: Context): ThisName = denot.name.asInstanceOf[ThisName] diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 4885b30d8..289515ae1 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -813,11 +813,15 @@ object Types { if (from1.isEmpty) ctx.subst1(this, from.head, to.head, null) else { val from2 = from1.tail - if (from2.isEmpty) ctx.subst2(this, from.head, to.head, from.tail.head, to.tail.head, null) + if (from2.isEmpty) ctx.subst2(this, from.head, to.head, from1.head, to.tail.head, null) else ctx.subst(this, from, to, null) } } +/* Not needed yet: + final def substDealias(from: List[Symbol], to: List[Type])(implicit ctx: Context): Type = + new ctx.SubstDealiasMap(from, to).apply(this) +*/ /** Substitute all types of the form `PolyParam(from, N)` by * `PolyParam(to, N)`. */ @@ -1602,17 +1606,22 @@ object Types { // and therefore two different poly types would never be equal. /** A trait that mixes in functionality for signature caching */ - trait SignedType extends Type { + trait MethodicType extends Type { private[this] var mySignature: Signature = _ private[this] var mySignatureRunId: Int = NoRunId protected def computeSignature(implicit ctx: Context): Signature - protected def resultSignature(implicit ctx: Context) = resultType match { - case rtp: SignedType => rtp.signature + protected def resultSignature(implicit ctx: Context) = try resultType match { + case rtp: MethodicType => rtp.signature case tp => Signature(tp, isJava = false) } + catch { + case ex: AssertionError => + println(i"failure while taking result signture of $resultType") + throw ex + } final override def signature(implicit ctx: Context): Signature = { if (ctx.runId != mySignatureRunId) { @@ -1623,7 +1632,7 @@ object Types { } } - trait MethodOrPoly extends SignedType + trait MethodOrPoly extends MethodicType abstract case class MethodType(paramNames: List[TermName], paramTypes: List[Type]) (resultTypeExp: MethodType => Type) @@ -1717,6 +1726,8 @@ object Types { def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = apply(paramNames, paramTypes)(_ => resultType) + def apply(paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp) def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { @@ -1748,7 +1759,7 @@ object Types { } abstract case class ExprType(override val resultType: Type) - extends CachedProxyType with TermType with SignedType { + extends CachedProxyType with TermType with MethodicType { override def underlying(implicit ctx: Context): Type = resultType protected def computeSignature(implicit ctx: Context): Signature = resultSignature def derivedExprType(resultType: Type)(implicit ctx: Context) = @@ -2019,19 +2030,31 @@ object Types { decls: Scope, selfInfo: DotClass /* should be: Type | Symbol */) extends CachedGroundType with TypeType { - def selfType(implicit ctx: Context): Type = selfInfo match { - case NoType => - if (selfTypeCache == null) selfTypeCache = computeSelfType(cls.typeRef, cls.typeParams) - selfTypeCache - case tp: Type => tp - case self: Symbol => self.info + /** The self type of a class is the conjunction of + * - the explicit self type if given (or the info of a given self symbol), and + * - the fully applied reference to the class itself. + */ + def selfType(implicit ctx: Context): Type = { + if (selfTypeCache == null) { + def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams) + selfTypeCache = selfInfo match { + case NoType => + fullRef + case tp: Type => + if (cls is Module) tp else AndType(tp, fullRef) + case self: Symbol => + assert(!(cls is Module)) + AndType(self.info, fullRef) + } + } + selfTypeCache } private var selfTypeCache: Type = null - private def computeSelfType(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = tparams match { + private def fullyAppliedRef(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = tparams match { case tparam :: tparams1 => - computeSelfType( + fullyAppliedRef( RefinedType(base, tparam.name, TypeRef(cls.thisType, tparam).toBounds(tparam)), tparams1) case nil => @@ -2077,8 +2100,8 @@ object Types { if (prefix eq this.prefix) this else ClassInfo(prefix, cls, classParents, decls, selfInfo) - def derivedClassInfo(prefix: Type = this.prefix, classParents: List[TypeRef] = classParents, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) = - if ((prefix eq this.prefix) && (classParents eq this.classParents) && (selfInfo eq this.selfInfo)) this + def derivedClassInfo(prefix: Type = this.prefix, classParents: List[TypeRef] = classParents, decls: Scope = this.decls, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) = + if ((prefix eq this.prefix) && (classParents eq this.classParents) && (decls eq this.decls) && (selfInfo eq this.selfInfo)) this else ClassInfo(prefix, cls, classParents, decls, selfInfo) override def computeHash = doHash(cls, prefix) @@ -2431,7 +2454,7 @@ object Types { case self: Type => this(self) case _ => tp.self } - tp.derivedClassInfo(prefix1, parents1, self1) + tp.derivedClassInfo(prefix1, parents1, tp.decls, self1) } } diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 0ed301732..59658c9c1 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -48,8 +48,8 @@ class ClassfileParser( case e: RuntimeException => if (ctx.debug) e.printStackTrace() throw new IOException( - s"""class file $classfile is broken, reading aborted with $e.getClass - |${Option(e.getMessage).getOrElse("")}""".stripMargin) + sm"""class file $classfile is broken, reading aborted with $e.getClass + |${Option(e.getMessage).getOrElse("")}""") } private def parseHeader(): Unit = { @@ -353,7 +353,7 @@ class ClassfileParser( val tpname = subName(':'.==).toTypeName val expname = if (owner.isClass) tpname.expandedName(owner) else tpname val s = ctx.newSymbol( - owner, expname, Flags.TypeParamCreationFlags, + owner, expname, owner.typeParamCreationFlags, typeParamCompleter(index), coord = indexCoord(index)) if (owner.isClass) owner.asClass.enter(s, owner.decls) tparams = tparams + (tpname -> s) @@ -702,12 +702,12 @@ class ClassfileParser( getMember(owner, innerName.toTypeName) } assert(result ne NoSymbol, - s"""failure to resolve inner class: - |externalName = $externalName, - |outerName = $outerName, - |innerName = $innerName - |owner.fullName = owner.showFullName - |while parsing ${classfile}""".stripMargin) + sm"""failure to resolve inner class: + |externalName = $externalName, + |outerName = $outerName, + |innerName = $innerName + |owner.fullName = owner.showFullName + |while parsing ${classfile}""") result case None => @@ -752,7 +752,7 @@ class ClassfileParser( private def setPrivateWithin(denot: SymDenotation, jflags: Int)(implicit ctx: Context): Unit = { if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC)) == 0) - denot.privateWithin = denot.enclosingPackage + denot.privateWithin = denot.enclosingPackageClass } private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0 diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index dd26b20df..36b2c99bf 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -53,10 +53,10 @@ object UnPickler { val result = restpe.parameterizeWith(tparams) for ((msg, pos) <- err) ctx.warning( - s"""$msg - |originally parsed type : ${tp.show} - |will be approximated by: ${result.show}. - |Proceed at own risk.""".stripMargin) + sm"""$msg + |originally parsed type : ${tp.show} + |will be approximated by: ${result.show}. + |Proceed at own risk.""") result } else @@ -161,8 +161,8 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: protected def errorBadSignature(msg: String, original: Option[RuntimeException] = None)(implicit ctx: Context) = { val ex = new BadSignature( - s"""error reading Scala signature of $classRoot from $source: - |error occured at position $readIndex: $msg""".stripMargin) + sm"""error reading Scala signature of $classRoot from $source: + |error occured at position $readIndex: $msg""") /*if (debug)*/ original.getOrElse(ex).printStackTrace() // !!! DEBUG throw ex } @@ -453,7 +453,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: var flags1 = flags if (flags is TypeParam) { name1 = name1.expandedName(owner) - flags1 |= TypeParamCreationFlags | ExpandedName + flags1 |= owner.typeParamCreationFlags | ExpandedName } ctx.newSymbol(owner, name1, flags1, localMemberUnpickler, coord = start) case CLASSsym => @@ -1080,7 +1080,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: setSym() val qualifier = readTreeRef() val selector = readNameRef() - Select(qualifier, symbol.namedType) + qualifier.select(symbol.namedType) case IDENTtree => setSymName() Ident(symbol.namedType) diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index da14f72d1..e35cdd128 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -146,7 +146,7 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) - tp.derivedClassInfo(this(pre), parents, this(tp.selfType)) + tp.derivedClassInfo(this(pre), parents, decls, this(tp.selfType)) case NoType | NoPrefix | ErrorType => tp case tp: WildcardType if wildcardOK => diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 3a322648a..ec4a4db88 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -319,6 +319,8 @@ class PlainPrinter(_ctx: Context) extends Printer { case _ => "" } + def annotsText(sym: Symbol): Text = Text(sym.annotations.map(toText)) + def dclText(sym: Symbol): Text = (toTextFlags(sym) ~~ keyString(sym) ~~ (varianceString(sym) ~ nameString(sym)) ~ toTextRHS(sym.unforcedInfo)).close diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 385b407b5..acba22afe 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -15,7 +15,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override protected def recursionLimitExceeded() = {} - protected val PrintableFlags = (SourceModifierFlags | Label | Module).toCommonFlags + protected val PrintableFlags = (SourceModifierFlags | Label | Module | Local).toCommonFlags /** The closest enclosing DefDef, TypeDef, or ClassDef node */ private var currentOwner: untpd.Tree = untpd.EmptyTree @@ -194,6 +194,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case _ => toTextGlobal(arg) } + def dclTextOr(treeText: => Text) = + if (ctx.isAfterTyper(ctx.phase) && tree.symbol != null && tree.symbol.exists) + annotsText(tree.symbol) ~~ dclText(tree.symbol) + else treeText + import untpd._ var txt: Text = tree match { @@ -283,19 +288,24 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "(" ~ toTextGlobal(patterns, ", ") ~ ")" ~ ("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty) case ValDef(mods, name, tpt, rhs) => - modText(mods, if (mods is Mutable) "var" else "val") ~~ toText(name) ~ - optAscription(tpt) ~ optText(rhs)(" = " ~ _) + dclTextOr { + modText(mods, if (mods is Mutable) "var" else "val") ~~ toText(name) ~ + optAscription(tpt) + } ~ optText(rhs)(" = " ~ _) case DefDef(mods, name, tparams, vparamss, tpt, rhs) => atOwner(tree) { - val first = modText(mods, "def") ~~ toText(name) ~ tparamsText(tparams) - addVparamssText(first, vparamss) ~ optAscription(tpt) ~ optText(rhs)(" = " ~ _) + dclTextOr { + val first = modText(mods, "def") ~~ toText(name) ~ tparamsText(tparams) + addVparamssText(first, vparamss) ~ optAscription(tpt) + } ~ optText(rhs)(" = " ~ _) } case tree @ TypeDef(mods, name, rhs) => atOwner(tree) { - def typeDefText(rhsText: Text) = { - val rhsText1 = if (tree.hasType) toText(tree.symbol.info) else rhsText - modText(mods, "type") ~~ toText(name) ~ tparamsText(tree.tparams) ~ rhsText1 - } + def typeDefText(rhsText: Text) = + dclTextOr { + val rhsText1 = if (tree.hasType) toText(tree.symbol.info) else rhsText + modText(mods, "type") ~~ toText(name) ~ tparamsText(tree.tparams) ~ rhsText1 + } rhs match { case impl: Template => modText(mods, if (mods is Trait) "trait" else "class") ~~ toText(name) ~ toText(impl) ~ diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index 82b0a1158..7f0c88bc5 100644 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/src/dotty/tools/dotc/reporting/Reporter.scala @@ -93,6 +93,9 @@ trait Reporting { this: Context => reporter.report(new Error(msg, pos)) } + def restrictionError(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = + error(s"Implementation restriction: $msg", pos) + def incompleteInputError(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit = reporter.incomplete(new Error(msg, pos))(ctx) diff --git a/src/dotty/tools/dotc/transform/Companions.scala b/src/dotty/tools/dotc/transform/Companions.scala new file mode 100644 index 000000000..0e31b511d --- /dev/null +++ b/src/dotty/tools/dotc/transform/Companions.scala @@ -0,0 +1,67 @@ +package dotty.tools.dotc +package transform + +import core._ +import Names._ +import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import ast.Trees.flatten +import Flags._ +import Contexts.Context +import Symbols._ +import scala.collection.mutable +import DenotTransformers._ +import Names.Name +import NameOps._ + + +/** A transformer that provides a convenient way to create companion objects + */ +class Companions extends TreeTransform with IdentityDenotTransformer { thisTransformer => + import ast.tpd._ + + override def name = "companions" + + /** Reorder statements so that module classes always come after their companion classes, add missing companion classes */ + private def reorderAndComplete(stats: List[Tree])(implicit ctx: Context): List[Tree] = { + val moduleClassDefs, singleClassDefs = mutable.Map[Name, Tree]() + + def reorder(stats: List[Tree]): List[Tree] = stats match { + case (stat: TypeDef) :: stats1 if stat.symbol.isClass => + if (stat.symbol is Flags.Module) { + moduleClassDefs += (stat.name -> stat) + singleClassDefs -= stat.name.stripModuleClassSuffix + val stats1r = reorder(stats1) + if (moduleClassDefs contains stat.name) stat :: stats1r else stats1r + } else { + def stats1r = reorder(stats1) + val normalized = moduleClassDefs remove stat.name.moduleClassName match { + case Some(mcdef) => + mcdef :: stats1r + case None => + singleClassDefs += (stat.name -> stat) + stats1r + } + stat :: normalized + } + case stat :: stats1 => stat :: reorder(stats1) + case Nil => Nil + } + + def newCompanion(name: TermName): Thicket = { + val modul = ctx.newCompleteModuleSymbol(ctx.owner, name, Synthetic, Synthetic, + defn.ObjectClass.typeRef :: Nil, Scopes.newScope) + if (ctx.owner.isClass) modul.enteredAfter(thisTransformer) + ModuleDef(modul, Nil) + } + + def addMissingCompanions(stats: List[Tree]): List[Tree] = stats map { + case stat: TypeDef if singleClassDefs contains stat.name => + Thicket(stat :: newCompanion(stat.name.toTermName).trees) + case stat => stat + } + addMissingCompanions(reorder(stats)) + } + + override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = + ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next))) +} diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index bc9d6ea61..4bef41d8f 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -17,7 +17,8 @@ class Constructors extends TreeTransform { val claz = tree.symbol.enclosingClass.asClass val zuper = claz.info.parents.head.typeSymbol cpy.DefDef(tree, tree.mods, tree.name, tree.tparams, tree.vparamss, tree.tpt, rhs = { - val parentCall = Apply(Select(Super(This(claz), tpnme.EMPTY, true), zuper.primaryConstructor), Nil) + val parentCall = + Super(This(claz), tpnme.EMPTY, true).select(zuper.primaryConstructor).appliedToNone if(tree.rhs.isEmpty) parentCall else Block(List(parentCall), tree.rhs) diff --git a/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala b/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala deleted file mode 100644 index b1cc8ea52..000000000 --- a/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala +++ /dev/null @@ -1,53 +0,0 @@ -package dotty.tools.dotc.transform - -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import scala.collection.mutable.ListBuffer -import dotty.tools.dotc.core.{Scopes, Flags} -import dotty.tools.dotc.core.Symbols.NoSymbol -import scala.annotation.tailrec -import dotty.tools.dotc.core._ -import Symbols._ -import scala.Some -import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer} -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import scala.collection.mutable -import dotty.tools.dotc.core.Names.Name -import NameOps._ - -/** A transformer that provides a convenient way to create companion objects - */ -abstract class CreateCompanionObjects extends TreeTransform { - - import tpd._ - - /** Given class definition should return true if companion object creation should be enforced - */ - def predicate(cls: TypeDef)(implicit ctx: Context): Boolean - - override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = { - @tailrec - def transformStats0(trees: List[Tree], acc: ListBuffer[Tree]): List[Tree] = { - trees match { - case Nil => acc.toList - case (claz: TypeDef) :: stats if claz.symbol.isClass && !(claz.symbol is Flags.Module) => { - val moduleExists = !(claz.symbol.companionModule eq NoSymbol) - if (moduleExists || !predicate(claz)) transformStats0(stats, acc += claz) - else { - val moduleSymbol = ctx.newCompleteModuleSymbol(claz.symbol.owner, claz.name.toTermName, Flags.Synthetic, Flags.Synthetic, List(defn.ObjectClass.typeRef), Scopes.newScope) - if (moduleSymbol.owner.isClass) moduleSymbol.entered - val companion = tpd.ModuleDef(moduleSymbol, List(EmptyTree)).withPos(claz.pos) - acc += claz - acc += companion - transformStats0(stats, acc) - } - } - case stat :: stats => transformStats0(stats, acc += stat) - } - } - - transformStats0(trees, ListBuffer()) - } -} diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala new file mode 100644 index 000000000..a362aee07 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -0,0 +1,61 @@ +package dotty.tools.dotc +package transform + +import core._ +import Names._ +import Types._ +import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import ast.Trees.flatten +import Flags._ +import Contexts.Context +import Symbols._ +import Denotations._, SymDenotations._ +import Decorators.StringInterpolators +import scala.collection.mutable +import DenotTransformers._ +import Names.Name +import NameOps._ +import TypeUtils._ + +/** A transformer that removes repeated parameters (T*) from all types, replacing + * them with Seq types. + */ +class ElimRepeated extends TreeTransform with InfoTransformer { thisTransformer => + import ast.tpd._ + + override def name = "elimrepeated" + + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = + elimRepeated(tp) + + private def elimRepeated(tp: Type)(implicit ctx: Context): Type = tp.stripTypeVar match { + case tp @ MethodType(paramNames, paramTypes) => + val resultType1 = elimRepeated(tp.resultType) + val paramTypes1 = + if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) { + paramTypes.init :+ + paramTypes.last.translateParameterized(defn.RepeatedParamClass, defn.SeqClass) + } + else paramTypes + tp.derivedMethodType(paramNames, paramTypes1, resultType1) + case tp: PolyType => + tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimRepeated(tp.resultType)) + case tp => + tp + } + + def transformTypeOfTree(tree: Tree)(implicit ctx: Context): Tree = + tree.withType(elimRepeated(tree.tpe)) + + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = + transformTypeOfTree(tree) + + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = + transformTypeOfTree(tree) + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + transformTypeOfTree(tree) + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = + transformTypeOfTree(tree) +} diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index e56132057..35742ac8c 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -22,6 +22,7 @@ import dotty.tools.dotc.ast.{Trees, tpd, untpd} import ast.Trees._ import scala.collection.mutable.ListBuffer import dotty.tools.dotc.core.Flags +import ValueClasses._ class Erasure extends Phase with DenotTransformer { @@ -85,12 +86,12 @@ object Erasure { def isPrimitiveValueType(tpe: Type)(implicit ctx: Context): Boolean = tpe.classSymbol.isPrimitiveValueClass def constant(tree: Tree, const: Tree)(implicit ctx: Context) = - if (isIdempotentExpr(tree)) Block(tree :: Nil, const) else const + if (isPureExpr(tree)) Block(tree :: Nil, const) else const final def box(tree: Tree, target: => String = "")(implicit ctx: Context): Tree = ctx.traceIndented(i"boxing ${tree.showSummary}: ${tree.tpe} into $target") { tree.tpe.widen match { case ErasedValueType(clazz, _) => - New(clazz.typeRef, cast(tree, clazz.underlyingOfValueClass) :: Nil) // todo: use adaptToType? + New(clazz.typeRef, cast(tree, underlyingOfValueClass(clazz)) :: Nil) // todo: use adaptToType? case tp => val cls = tp.classSymbol if (cls eq defn.UnitClass) constant(tree, ref(defn.BoxedUnit_UNIT)) @@ -98,7 +99,7 @@ object Erasure { else { assert(cls ne defn.ArrayClass) val arg = safelyRemovableUnboxArg(tree) - if (arg.isEmpty) Apply(ref(boxMethod(cls.asClass)), tree :: Nil) + if (arg.isEmpty) ref(boxMethod(cls.asClass)).appliedTo(tree) else { ctx.log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}") arg @@ -116,14 +117,16 @@ object Erasure { // via the unboxed type would yield a NPE (see SI-5866) unbox(tree, underlying) else - Apply(Select(adaptToType(tree, clazz.typeRef), clazz.valueClassUnbox), Nil) + adaptToType(tree, clazz.typeRef) + .select(valueClassUnbox(clazz)) + .appliedToNone cast(tree1, pt) case _ => val cls = pt.classSymbol if (cls eq defn.UnitClass) constant(tree, Literal(Constant(()))) else { assert(cls ne defn.ArrayClass) - Apply(ref(unboxMethod(cls.asClass)), tree :: Nil) + ref(unboxMethod(cls.asClass)).appliedTo(tree) } } } @@ -139,7 +142,7 @@ object Erasure { cast(runtimeCall(nme.toObjectArray, tree :: Nil), pt) case _ => ctx.log(s"casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}") - TypeApply(Select(tree, defn.Any_asInstanceOf), TypeTree(pt) :: Nil) + mkAsInstanceOf(tree, pt) } /** Adaptation of an expression `e` to an expected type `PT`, applying the following @@ -267,7 +270,7 @@ object Erasure { override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = EmptyTree - override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[tpd.Tree] = { + override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { val statsFlatten = Trees.flatten(stats) val stats1 = super.typedStats(statsFlatten, exprOwner) @@ -343,7 +346,7 @@ object Erasure { bridge.entered // this should be safe, as we're executing in context of next phase ctx.debuglog(s"generating bridge from ${newDef.symbol} to $bridge") - val sel: Tree = tpd.Select(This(newDef.symbol.owner.asClass), newDef.symbol.termRef) + val sel: Tree = This(newDef.symbol.owner.asClass).select(newDef.symbol.termRef) val resultType = bridge.info.widen.resultType tpd.DefDef(bridge, { paramss: List[List[tpd.Tree]] => @@ -363,4 +366,4 @@ object Erasure { if (tree.isEmpty) tree else adaptToType(tree, pt) } } -}
\ No newline at end of file +} diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala new file mode 100644 index 000000000..ba5b9fab6 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -0,0 +1,188 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ +package dotty.tools.dotc +package transform + +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import ValueClasses._ +import dotty.tools.dotc.ast.{Trees, tpd} +import scala.collection.{ mutable, immutable } +import mutable.ListBuffer +import core._ +import Phases.Phase +import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ +import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ +import TypeUtils._ +import util.Positions._ +import Decorators._ + +/** + * Perform Step 1 in the inline classes SIP: Creates extension methods for all + * methods in a value class, except parameter or super accessors, or constructors. + */ +class ExtensionMethods extends MacroTransform with DenotTransformer with FullParameterization { thisTransformer => + + import tpd._ + + /** the following two members override abstract members in Transform */ + val name: String = "extmethods" + + override def runsAfter: Set[String] = Set("elimrepeated") // TODO: add tailrec + + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { + case ref: ClassDenotation if ref is ModuleClass => + ref.linkedClass match { + case origClass: ClassSymbol if isDerivedValueClass(origClass) => + val cinfo = ref.classInfo + val decls1 = cinfo.decls.cloneScope + ctx.atPhase(thisTransformer.next) { implicit ctx => + for (decl <- origClass.classInfo.decls) { + if (isMethodWithExtension(decl)) + decls1.enter(createExtensionMethod(decl, ref.symbol)) + } + } + if (decls1.isEmpty) ref + else ref.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1)) + case _ => + ref + } + case ref: SymDenotation + if isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnotationClass) => + val ref1 = ref.copySymDenotation() + ref1.removeAnnotation(defn.TailrecAnnotationClass) + ref1 + case _ => + ref + } + + def newTransformer(implicit ctx: Context): Transformer = new Extender + + override def transformPhase(implicit ctx: Context): Phase = thisTransformer.next + + protected def rewiredTarget(target: Symbol, derived: Symbol)(implicit ctx: Context): Symbol = + if (isMethodWithExtension(target) && + target.owner.linkedClass == derived.owner) extensionMethod(target) + else NoSymbol + + /** Generate stream of possible names for the extension version of given instance method `imeth`. + * If the method is not overloaded, this stream consists of just "imeth$extension". + * If the method is overloaded, the stream has as first element "imeth$extenionX", where X is the + * index of imeth in the sequence of overloaded alternatives with the same name. This choice will + * always be picked as the name of the generated extension method. + * After this first choice, all other possible indices in the range of 0 until the number + * of overloaded alternatives are returned. The secondary choices are used to find a matching method + * in `extensionMethod` if the first name has the wrong type. We thereby gain a level of insensitivity + * of how overloaded types are ordered between phases and picklings. + */ + private def extensionNames(imeth: Symbol)(implicit ctx: Context): Stream[Name] = { + val decl = imeth.owner.info.decl(imeth.name) + + /** No longer needed for Dotty, as we are more disciplined with scopes now. + // Bridge generation is done at phase `erasure`, but new scopes are only generated + // for the phase after that. So bridges are visible in earlier phases. + // + // `info.member(imeth.name)` filters these out, but we need to use `decl` + // to restrict ourselves to members defined in the current class, so we + // must do the filtering here. + val declTypeNoBridge = decl.filter(sym => !sym.isBridge).tpe + */ + decl match { + case decl: MultiDenotation => + val alts = decl.alternatives + val index = alts indexOf imeth.denot + assert(index >= 0, alts+" does not contain "+imeth) + def altName(index: Int) = (imeth.name+"$extension"+index).toTermName + altName(index) #:: ((0 until alts.length).toStream filter (index != _) map altName) + case decl => + assert(decl.exists, imeth.name+" not found in "+imeth.owner+"'s decls: "+imeth.owner.info.decls) + Stream((imeth.name+"$extension").toTermName) + } + } + + /** Return the extension method that corresponds to given instance method `meth`. */ + def extensionMethod(imeth: Symbol)(implicit ctx: Context): TermSymbol = + ctx.atPhase(thisTransformer.next) { implicit ctx => + // FIXME use toStatic instead? + val companionInfo = imeth.owner.companionModule.info + val candidates = extensionNames(imeth) map (companionInfo.decl(_).symbol) filter (_.exists) + val matching = candidates filter (c => memberSignature(c.info) == imeth.signature) + assert(matching.nonEmpty, + sm"""|no extension method found for: + | + | $imeth:${imeth.info.show} with signature ${imeth.signature} + | + | Candidates: + | + | ${candidates.map(c => c.name + ":" + c.info.show).mkString("\n")} + | + | Candidates (signatures normalized): + | + | ${candidates.map(c => c.name + ":" + c.info.signature + ":" + memberSignature(c.info)).mkString("\n")} + | + | Eligible Names: ${extensionNames(imeth).mkString(",")}""") + matching.head.asTerm + } + + private def createExtensionMethod(imeth: Symbol, staticClass: Symbol)(implicit ctx: Context): TermSymbol = { + assert(ctx.phase == thisTransformer.next) + val extensionName = extensionNames(imeth).head.toTermName + val extensionMeth = ctx.newSymbol(staticClass, extensionName, + imeth.flags | Final &~ (Override | Protected | AbsOverride), + fullyParameterizedType(imeth.info, imeth.owner.asClass), + privateWithin = imeth.privateWithin, coord = imeth.coord) + extensionMeth.addAnnotations(from = imeth)(ctx.withPhase(thisTransformer)) + // need to change phase to add tailrec annotation which gets removed from original method in the same phase. + extensionMeth + } + + class Extender extends Transformer { + private val extensionDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() + + def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Unit = + if (seen contains clazz) + ctx.error("value class may not unbox to itself", pos) + else { + val unboxed = underlyingOfValueClass(clazz).typeSymbol + if (isDerivedValueClass(unboxed)) checkNonCyclic(pos, seen + clazz, unboxed.asClass) + } + + override def transform(tree: Tree)(implicit ctx: Context): Tree = { + tree match { + case tree: Template => + if (isDerivedValueClass(ctx.owner)) { + /* This is currently redundant since value classes may not + wrap over other value classes anyway. + checkNonCyclic(ctx.owner.pos, Set(), ctx.owner) */ + extensionDefs(ctx.owner.linkedClass) = new mutable.ListBuffer[Tree] + ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) + // SI-7859 make param accessors accessible so the erasure can generate unbox operations. + val paramAccessors = ctx.owner.info.decls.filter(_.is(TermParamAccessor)) + paramAccessors.foreach(_.makeNotPrivateAfter(ctx.owner, thisTransformer)) + super.transform(tree) + } else if (ctx.owner.isStaticOwner) { + val tree1 @ Template(constr, parents, selfType, body) = super.transform(tree) + extensionDefs remove tree1.symbol.owner match { + case Some(defns) if defns.nonEmpty => + cpy.Template(tree1, constr, parents, selfType, body ++ defns) + case _ => + tree1 + } + } else tree + case ddef: DefDef if isMethodWithExtension(tree.symbol) => + val origMeth = tree.symbol + val origClass = ctx.owner.asClass + val staticClass = origClass.linkedClass + assert(staticClass.exists, s"$origClass lacks companion, ${origClass.owner.definedPeriodsString} ${origClass.owner.info.decls} ${origClass.owner.info.decls}") + val extensionMeth = extensionMethod(origMeth) + ctx.log(s"Value class $origClass spawns extension method.\n Old: ${origMeth.showDcl}\n New: ${extensionMeth.showDcl}") + extensionDefs(staticClass) += fullyParameterizedDef(extensionMeth, ddef) + cpy.DefDef(tree, ddef.mods, ddef.name, ddef.tparams, ddef.vparamss, ddef.tpt, + forwarder(extensionMeth, ddef)) + case _ => + super.transform(tree) + } + } + } +} diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala new file mode 100644 index 000000000..fea0482a0 --- /dev/null +++ b/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -0,0 +1,225 @@ +package dotty.tools.dotc +package transform + +import core._ +import Types._ +import Contexts._ +import Symbols._ +import Decorators._ +import StdNames.nme +import NameOps._ +import ast._ +import ast.Trees._ + +/** Provides methods to produce fully parameterized versions of instance methods, + * where the `this` of the enclosing class is abstracted out in an extra leading + * `$this` parameter and type parameters of the class become additional type + * parameters of the fully parameterized method. + * + * Example usage scenarios are: + * + * - extension methods of value classes + * - implementations of trait methods + * - static protected accessors + * - local methods produced by tailrec transform + * + * Note that the methods lift out type parameters of the class containing + * the instance method, but not type parameters of enclosing classes. The + * fully instantiated method therefore needs to be put in a scope "close" + * to the original method, i.e. they need to share the same outer pointer. + * Examples of legal positions are: in the companion object, or as a local + * method inside the original method. + * + * Note: The scheme does not handle yet methods where type parameter bounds + * depend on value parameters of the enclosing class, as in: + * + * class C(val a: String) extends AnyVal { + * def foo[U <: a.type]: Unit = ... + * } + * + * The expansion of method `foo` would lead to + * + * def foo$extension[U <: $this.a.type]($this: C): Unit = ... + * + * which is not typable. Not clear yet what to do. Maybe allow PolyTypes + * to follow method parameters and translate to the following: + * + * def foo$extension($this: C)[U <: $this.a.type]: Unit = ... + * + * @see class-dependent-extension-method.scala in pending/pos. + */ +trait FullParameterization { + + import tpd._ + + /** If references to original symbol `referenced` from within fully parameterized method + * `derived` should be rewired to some fully parameterized method, the rewiring target symbol, + * otherwise NoSymbol. + */ + protected def rewiredTarget(referenced: Symbol, derived: Symbol)(implicit ctx: Context): Symbol + + /** If references to some original symbol from given tree node within fully parameterized method + * `derived` should be rewired to some fully parameterized method, the rewiring target symbol, + * otherwise NoSymbol. By default implemented as + * + * rewiredTarget(tree.symbol, derived) + * + * but can be overridden. + */ + protected def rewiredTarget(tree: Tree, derived: Symbol)(implicit ctx: Context): Symbol = + rewiredTarget(tree.symbol, derived) + + /** Converts the type `info` of a member of class `clazz` to a method type that + * takes the `this` of the class and any type parameters of the class + * as additional parameters. Example: + * + * class Foo[+A <: AnyRef](val xs: List[A]) extends AnyVal { + * def baz[B >: A](x: B): List[B] = ... + * } + * + * leads to: + * + * object Foo { + * def extension$baz[B >: A <: Any, A >: Nothing <: AnyRef]($this: Foo[A])(x: B): List[B] + * } + * + * If a self type is present, $this has this self type as its type. + */ + def fullyParameterizedType(info: Type, clazz: ClassSymbol)(implicit ctx: Context): Type = { + val (mtparamCount, origResult) = info match { + case info @ PolyType(mtnames) => (mtnames.length, info.resultType) + case info: ExprType => (0, info.resultType) + case _ => (0, info) + } + val ctparams = clazz.typeParams + val ctnames = ctparams.map(_.name.unexpandedName()) + + /** The method result type */ + def resultType(mapClassParams: Type => Type) = { + val thisParamType = mapClassParams(clazz.classInfo.selfType) + MethodType(nme.SELF :: Nil, thisParamType :: Nil)(mt => + mapClassParams(origResult).substThis(clazz, MethodParam(mt, 0))) + } + + /** Replace class type parameters by the added type parameters of the polytype `pt` */ + def mapClassParams(tp: Type, pt: PolyType): Type = { + val classParamsRange = (mtparamCount until mtparamCount + ctparams.length).toList + tp.subst(clazz.typeParams, classParamsRange map (PolyParam(pt, _))) + } + + /** The bounds for the added type paraneters of the polytype `pt` */ + def mappedClassBounds(pt: PolyType): List[TypeBounds] = + ctparams.map(tparam => mapClassParams(tparam.info, pt).bounds) + + info match { + case info @ PolyType(mtnames) => + PolyType(mtnames ++ ctnames)( + pt => (info.paramBounds ++ mappedClassBounds(pt)) + .mapConserve(_.subst(info, pt).bounds), + pt => resultType(mapClassParams(_, pt)).subst(info, pt)) + case _ => + if (ctparams.isEmpty) resultType(identity) + else PolyType(ctnames)(mappedClassBounds, pt => resultType(mapClassParams(_, pt))) + } + } + + /** Assuming `info` is a result of a `fullyParameterizedType` call, the signature of the + * original method type `X` such that `info = fullyParameterizedType(X, ...)`. + */ + def memberSignature(info: Type)(implicit ctx: Context): Signature = info match { + case info: PolyType => memberSignature(info.resultType) + case info @ MethodType(nme.SELF :: Nil, _) => + val normalizedResultType = info.resultType match { + case rtp: MethodType => rtp + case rtp => ExprType(rtp) + } + normalizedResultType.signature + case _ => + Signature.NotAMethod + } + + /** The type parameters (skolems) of the method definition `originalDef`, + * followed by the class parameters of its enclosing class. + */ + private def allInstanceTypeParams(originalDef: DefDef)(implicit ctx: Context): List[Symbol] = + originalDef.tparams.map(_.symbol) ::: originalDef.symbol.owner.typeParams + + /** Given an instance method definition `originalDef`, return a + * fully parameterized method definition derived from `originalDef`, which + * has `derived` as symbol and `fullyParameterizedType(originalDef.symbol.info)` + * as info. + */ + def fullyParameterizedDef(derived: TermSymbol, originalDef: DefDef)(implicit ctx: Context): Tree = + polyDefDef(derived, trefs => vrefss => { + val origMeth = originalDef.symbol + val origClass = origMeth.owner.asClass + val origTParams = allInstanceTypeParams(originalDef) + val origVParams = originalDef.vparamss.flatten map (_.symbol) + val thisRef :: argRefs = vrefss.flatten + + /** If tree should be rewired, the rewired tree, otherwise EmptyTree. + * @param targs Any type arguments passed to the rewired tree. + */ + def rewireTree(tree: Tree, targs: List[Tree])(implicit ctx: Context): Tree = { + def rewireCall(thisArg: Tree): Tree = { + val rewired = rewiredTarget(tree, derived) + if (rewired.exists) { + val base = thisArg.tpe.baseTypeWithArgs(origClass) + assert(base.exists) + ref(rewired.termRef) + .appliedToTypeTrees(targs ++ base.argInfos.map(TypeTree(_))) + .appliedTo(thisArg) + } else EmptyTree + } + tree match { + case Ident(_) => rewireCall(thisRef) + case Select(qual, _) => rewireCall(qual) + case tree @ TypeApply(fn, targs1) => + assert(targs.isEmpty) + rewireTree(fn, targs1) + case _ => EmptyTree + } + } + + /** Type rewiring is needed because a previous reference to an instance + * method might still persist in the types of enclosing nodes. Example: + * + * if (true) this.imeth else this.imeth + * + * is rewritten to + * + * if (true) xmeth($this) else xmeth($this) + * + * but the type `this.imeth` still persists as the result type of the `if`, + * because it is kept by the `cpy` operation of the tree transformer. + * It needs to be rewritten to the common result type of `imeth` and `xmeth`. + */ + def rewireType(tpe: Type) = tpe match { + case tpe: TermRef if rewiredTarget(tpe.symbol, derived).exists => tpe.widen + case _ => tpe + } + + new TreeTypeMap( + typeMap = rewireType(_) + .subst(origTParams, trefs) + .subst(origVParams, argRefs.map(_.tpe)) + .substThis(origClass, thisRef.tpe), + ownerMap = (sym => if (sym eq origMeth) derived else sym), + treeMap = { + case tree: This if tree.symbol == origClass => thisRef + case tree => rewireTree(tree, Nil) orElse tree + }).transform(originalDef.rhs) + }) + + /** A forwarder expression which calls `derived`, passing along + * - the type parameters and enclosing class parameters of `originalDef`, + * - the `this` of the enclosing class, + * - the value parameters of the original method `originalDef`. + */ + def forwarder(derived: TermSymbol, originalDef: DefDef)(implicit ctx: Context): Tree = + ref(derived.termRef) + .appliedToTypes(allInstanceTypeParams(originalDef).map(_.typeRef)) + .appliedTo(This(originalDef.symbol.owner.asClass)) + .appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol))) + .withPos(originalDef.rhs.pos) +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala index f5fed6fda..6dd66ec75 100644 --- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala @@ -91,9 +91,8 @@ class InterceptedMethods extends TreeTransform { def alt2 = defn.ScalaRuntimeModule.info.member(nme.hash_) .suchThat(_.info.firstParamTypes.head.typeSymbol == defn.AnyClass) - if (defn.ScalaNumericValueClasses contains s) { - tpd.Apply(Ident(alt1.termRef), List(tree)) - } else tpd.Apply(Ident(alt2.termRef), List(tree)) + Ident((if (defn.ScalaNumericValueClasses contains s) alt1 else alt2).termRef) + .appliedTo(tree) } } @@ -111,9 +110,9 @@ class InterceptedMethods extends TreeTransform { PoundPoundValue(qual) } else if (Any_comparisons contains tree.fun.symbol.asTerm) { if (tree.fun.symbol eq defn.Any_==) { - Apply(Select(qual, defn.Any_equals), tree.args) + qual.select(defn.Any_equals).appliedToArgs(tree.args) } else if (tree.fun.symbol eq defn.Any_!=) { - Select(Apply(Select(qual, defn.Any_equals), tree.args), defn.Boolean_!) + qual.select(defn.Any_equals).appliedToArgs(tree.args).select(defn.Boolean_!) } else unknown } /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { // todo: this is needed to support value classes @@ -130,7 +129,7 @@ class InterceptedMethods extends TreeTransform { // we get a primitive form of _getClass trying to target a boxed value // so we need replace that method name with Object_getClass to get correct behavior. // See SI-5568. - Apply(Select(qual, defn.Any_getClass), Nil) + qual.select(defn.Any_getClass).appliedToNone } else { unknown } diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index fe6c3e2e4..75dc10ce4 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -7,6 +7,7 @@ import Contexts._ import Symbols._ import Decorators._ import NameOps._ +import StdNames.nme import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, TreeTransform} import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.ast.{untpd, tpd} @@ -19,25 +20,6 @@ import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.DenotTransformers.DenotTransformer - -class LazyValsCreateCompanionObjects extends CreateCompanionObjects { - import tpd._ - - - override def name: String = "lazyValsModules" - - /** Companion classes are required to hold offsets for volatile lazy vals */ - override def predicate(forClass: tpd.TypeDef)(implicit ctx: Context): Boolean = { - (!(forClass.symbol is Flags.Module)) && forClass.rhs.isInstanceOf[Template] && { - val body = forClass.rhs.asInstanceOf[Template].body - body.exists { - case x: ValDef => - (x.mods is Flags.Lazy) && x.symbol.hasAnnotation(defn.VolatileAnnot) - case _ => false - } - } - } -} class LazyValTranformContext { import tpd._ @@ -67,7 +49,7 @@ class LazyValTranformContext { /** List of names of phases that should have finished their processing of all compilation units * before this phase starts */ - override def runsAfterGroupsOf: Set[String] = Set("lazyValsModules") + /** List of names of phases that should have finished their processing of all compilation units * before this phase starts */ @@ -144,9 +126,11 @@ class LazyValTranformContext { val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.symbol.coord) val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List(valueInitter))) - val methodBody = - if(holderType != "LazyRef") Select(Ident(holderSymbol.termRef), "value".toTermName) - else TypeApply(Select(Select(Ident(holderSymbol.termRef), "value".toTermName), defn.Any_asInstanceOf), List(TypeTree(tpe))) + val methodBody = { + val prefix = ref(holderSymbol).select("value".toTermName) + if (holderType != "LazyRef") prefix + else prefix.select(defn.Any_asInstanceOf).appliedToType(tpe) + } val methodTree = DefDef(x.symbol.asTerm, methodBody) ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}") Thicket(holderTree, methodTree) @@ -160,7 +144,7 @@ class LazyValTranformContext { * flag = true * target * } - * } + * }` */ def mkNonThreadSafeDef(target: Symbol, flag: Symbol, rhs: Tree)(implicit ctx: Context) = { @@ -181,7 +165,7 @@ class LazyValTranformContext { * } */ def mkDefNonThreadSafeNonNullable(target: Symbol, rhs: Tree)(implicit ctx: Context) = { - val cond = Apply(Select(Ident(target.termRef), "eq".toTermName), List(Literal(Constant(null)))) + val cond = Ident(target.termRef).select(nme.eq).appliedTo(Literal(Constant(null))) val exp = Ident(target.termRef) val setTarget = Assign(exp, rhs) val init = Block(List(setTarget), exp) @@ -255,6 +239,7 @@ class LazyValTranformContext { * } * result * } + * FIXME: Don't use strings with toTermName, use predefined names instead. */ def mkThreadSafeDef(methodSymbol: TermSymbol, claz: ClassSymbol, ord: Int, target: Symbol, rhs: Tree, tp: Types.Type, offset: Tree, getFlag: Tree, stateMask: Tree, casFlag: Tree, setFlagState: Tree, waitOnLock: Tree)(implicit ctx: Context) = { val initState = Literal(Constants.Constant(0)) @@ -281,16 +266,16 @@ class LazyValTranformContext { val handler = Closure(handlerSymbol, { args => val exception = args.head.head - val complete = Apply(setFlagState, List(thiz, offset, initState, Literal(Constant(ord)))) + val complete = setFlagState.appliedTo(thiz, offset, initState, Literal(Constant(ord))) Block(List(complete), Throw(exception)) }) val compute = Assign(Ident(resultSymbol.termRef), rhs) val tr = Try(compute, handler, EmptyTree) val assign = Assign(Ident(target.termRef), Ident(resultSymbol.termRef)) - val complete = Apply(setFlagState, List(thiz, offset, computedState, Literal(Constant(ord)))) + val complete = setFlagState.appliedTo(thiz, offset, computedState, Literal(Constant(ord))) val noRetry = Assign(Ident(retrySymbol.termRef), Literal(Constants.Constant(false))) - val body = If(Apply(casFlag, List(thiz, offset, Ident(flagSymbol.termRef), computeState, Literal(Constant(ord)))), + val body = If(casFlag.appliedTo(thiz, offset, Ident(flagSymbol.termRef), computeState, Literal(Constant(ord))), Block(tr :: assign :: complete :: noRetry :: Nil, Literal(Constant(()))), Literal(Constant(()))) @@ -298,12 +283,12 @@ class LazyValTranformContext { } val waitFirst = { - val wait = Apply(waitOnLock, List(thiz, offset, Ident(flagSymbol.termRef), Literal(Constant(ord)))) + val wait = waitOnLock.appliedTo(thiz, offset, Ident(flagSymbol.termRef), Literal(Constant(ord))) CaseDef(computeState, EmptyTree, wait) } val waitSecond = { - val wait = Apply(waitOnLock, List(thiz, offset, Ident(flagSymbol.termRef), Literal(Constant(ord)))) + val wait = waitOnLock.appliedTo(thiz, offset, Ident(flagSymbol.termRef), Literal(Constant(ord))) CaseDef(notifyState, EmptyTree, wait) } @@ -314,10 +299,10 @@ class LazyValTranformContext { CaseDef(computedState, EmptyTree, body) } - val cases = Match(Apply(stateMask, List(Ident(flagSymbol.termRef), Literal(Constant(ord)))), + val cases = Match(stateMask.appliedTo(Ident(flagSymbol.termRef), Literal(Constant(ord))), List(compute, waitFirst, waitSecond, computed)) //todo: annotate with @switch - val whileBody = Block(List(Assign(Ident(flagSymbol.termRef), Apply(getFlag, List(thiz, offset)))), cases) + val whileBody = Block(List(Assign(Ident(flagSymbol.termRef), getFlag.appliedTo(thiz, offset))), cases) val cycle = untpd.WhileDo(whileCond, whileBody).withTypeUnchecked(defn.UnitType) DefDef(methodSymbol, Block(resultDef :: retryDef :: flagDef :: cycle :: Nil, Ident(resultSymbol.termRef))) } @@ -353,7 +338,7 @@ class LazyValTranformContext { val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType) addSym(claz, flagSymbol) flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) - val offsetTree = ValDef(offsetSymbol, Apply(getOffset, List(thiz, Literal(Constant(flagName.toString))))) + val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thiz, Literal(Constant(flagName.toString)))) info.defs = offsetTree :: info.defs } @@ -363,7 +348,7 @@ class LazyValTranformContext { val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType) addSym(claz, flagSymbol) flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) - val offsetTree = ValDef(offsetSymbol, Apply(getOffset, List(thiz, Literal(Constant(flagName.toString))))) + val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thiz, Literal(Constant(flagName.toString)))) appendOffsetDefs += (companion.name.moduleClassName -> new OffsetInfo(List(offsetTree), ord)) } diff --git a/src/dotty/tools/dotc/transform/Literalize.scala b/src/dotty/tools/dotc/transform/Literalize.scala new file mode 100644 index 000000000..14ce8fd05 --- /dev/null +++ b/src/dotty/tools/dotc/transform/Literalize.scala @@ -0,0 +1,65 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core.DenotTransformers._ +import core.Symbols._ +import core.Contexts._ +import core.Types._ +import core.Flags._ +import core.Decorators._ +import core.StdNames.nme +import ast.Trees._ + +/** This phase rewrites idempotent expressions with constant types to Literals. + * The constant types are eliminated by erasure, so we need to keep + * the info about constantness in the trees. + */ +class Literalize extends TreeTransform { + import ast.tpd._ + + override def name: String = "literalize" + + /** Note: Demanding idempotency instead of purity is strictly speaking too loose. + * Example + * + * object O { final val x = 42; println("43") } + * O.x + * + * Strictly speaking we can't replace `O.x` with `42`. But this would make + * most expressions non-constant. Maybe we can change the spec to accept this + * kind of eliding behavior. Or else enforce true purity in the compiler. + * The choice will be affected by what we will do with `inline` and with + * Singleton type bounds (see SIP 23). Presumably + * + * object O1 { val x: Singleton = 42; println("43") } + * object O2 { inline val x = 42; println("43") } + * + * should behave differently. + * + * O1.x should have the same effect as { println("43"; 42 } + * + * whereas + * + * O2.x = 42 + * + * Revisit this issue once we have implemented `inline`. Then we can demand + * purity of the prefix unless the selection goes to an inline val. + */ + def literalize(tree: Tree)(implicit ctx: Context): Tree = tree.tpe match { + case ConstantType(value) if isIdempotentExpr(tree) => Literal(value) + case _ => tree + } + + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = + literalize(tree) + + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = + literalize(tree) + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + literalize(tree) + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = + literalize(tree) +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala new file mode 100644 index 000000000..0ee92bccd --- /dev/null +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -0,0 +1,76 @@ +package dotty.tools.dotc +package transform + +import core._ +import typer._ +import Phases._ +import ast.Trees._ +import Contexts._ +import Symbols._ +import Flags.PackageVal +import Decorators._ + +/** A base class for transforms. + * A transform contains a compiler phase which applies a tree transformer. + */ +abstract class MacroTransform extends Phase { + + import ast.tpd._ + + override def run(implicit ctx: Context): Unit = { + val unit = ctx.compilationUnit + unit.tpdTree = newTransformer.transform(unit.tpdTree)(ctx.withPhase(transformPhase)) + } + + protected def newTransformer(implicit ctx: Context): Transformer + + /** The phase in which the transformation should be run. + * By default this is the phase given by the this macro transformer, + * but it could be overridden to be the phase following that one. + */ + protected def transformPhase(implicit ctx: Context): Phase = this + + class Transformer extends TreeMap { + + protected def localCtx(tree: Tree)(implicit ctx: Context) = { + val sym = tree.symbol + val owner = if (sym is PackageVal) sym.moduleClass else sym + ctx.fresh.setTree(tree).setOwner(owner) + } + + /** The current enclosing class + * @pre We must be inside a class + */ + def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass + + def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { + val exprCtx = ctx.withOwner(exprOwner) + def transformStat(stat: Tree): Tree = stat match { + case _: Import | _: DefTree => transform(stat) + case Thicket(stats) => cpy.Thicket(stat, stats mapConserve transformStat) + case _ => transform(stat)(exprCtx) + } + flatten(trees.mapconserve(transformStat(_))) + } + + override def transform(tree: Tree)(implicit ctx: Context): Tree = { + tree match { + case EmptyValDef => + tree + case _: PackageDef | _: MemberDef => + super.transform(tree)(localCtx(tree)) + case Template(constr, parents, self, body) => + cpy.Template(tree, + transformSub(constr), + transform(parents), + transformSelf(self), + transformStats(body, tree.symbol)) + case _ => + super.transform(tree) + } + } + + def transformSelf(vd: ValDef)(implicit ctx: Context) = + cpy.ValDef(vd, vd.mods, vd.name, transform(vd.tpt), vd.rhs) + } +} diff --git a/src/dotty/tools/dotc/transform/Nullarify.scala b/src/dotty/tools/dotc/transform/Nullarify.scala index 664b4bee1..8d967cc1a 100644 --- a/src/dotty/tools/dotc/transform/Nullarify.scala +++ b/src/dotty/tools/dotc/transform/Nullarify.scala @@ -85,11 +85,11 @@ class Nullarify extends TreeTransform with InfoTransformer { def result(implicit ctx: Context) = { tp1.widen match { case MethodType(Nil, _) if origType.widenExpr.isInstanceOf[ValueType] => - Apply(tree1, Nil) + tree1.appliedToNone case _ => origType match { case _: ExprType => // it's a by-name parameter - Apply(Select(tree1, defn.Function0_apply), Nil) + tree1.select(defn.Function0_apply).appliedToNone case _ => tree1 } diff --git a/src/dotty/tools/dotc/transform/PostTyperTransformers.scala b/src/dotty/tools/dotc/transform/PostTyperTransformers.scala deleted file mode 100644 index 25f122cf5..000000000 --- a/src/dotty/tools/dotc/transform/PostTyperTransformers.scala +++ /dev/null @@ -1,62 +0,0 @@ -package dotty.tools.dotc.transform - -import dotty.tools.dotc.core._ -import Symbols._ -import scala.Some -import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer} -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import scala.collection.mutable -import dotty.tools.dotc.core.Names.Name -import NameOps._ - -object PostTyperTransformers { - - import tpd._ - - - /** A trait that's assumed by the transformers that run right after typer. - * Ensures that trees are normalized when seen by other transforms. This means: - * (1) All module class definitions appear after their companion class definitions - * (2) There are no import clauses or named arguments - * (3) All trees designating types are instances of TypeTree - */ - abstract class PostTyperTransformer extends TreeTransformer { - - /** Reorder statements so that module classes always come after their companion classes, add missing companion classes */ - def reorder(stats: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = { - val moduleClassDefs = mutable.Map[Name, Tree]() - def reorder0(stats: List[Tree]): List[Tree] = { - stats match { - case (stat: TypeDef) :: stats1 if stat.symbol.isClass => - if (stat.symbol is Flags.Module) { - moduleClassDefs += (stat.name -> stat) - val stats1r = reorder0(stats1) - if (moduleClassDefs contains stat.name) stat :: stats1r else stats1r - } - else { - val mclsName = stat.name.moduleClassName - moduleClassDefs remove mclsName match { - case Some(mcdef) => stat :: mcdef :: reorder0(stats1) - case None => stat :: reorder0(stats1) - } - } - case stat :: stats1 => stat :: reorder0(stats1) - case Nil => Nil - } - } - reorder0(stats) - } - - override def transformStats(trees: List[tpd.Tree], exprOwner: Symbol, info: TransformerInfo, current: Int)(implicit ctx: Context): List[tpd.Tree] = - super.transformStats(reorder(trees)(ctx, info), exprOwner, info, current) - - override def transform(tree: tpd.Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): tpd.Tree = tree match { - case tree: Import => EmptyTree - case tree: NamedArg => super.transform(tree.arg, info, cur) - case tree: TypeTree => super.transform(tree, info, cur) - case tree => super.transform(if (tree.isType) TypeTree(tree.tpe) else tree, info, cur) - } - } - -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Splitter.scala b/src/dotty/tools/dotc/transform/Splitter.scala index 978a9cce4..745919f3a 100644 --- a/src/dotty/tools/dotc/transform/Splitter.scala +++ b/src/dotty/tools/dotc/transform/Splitter.scala @@ -80,12 +80,12 @@ class Splitter extends TreeTransform { else { def choose(qual: Tree, syms: List[Symbol]): Tree = { def testOrCast(which: Symbol, mbr: Symbol) = - TypeApply(Select(qual, which), TypeTree(mbr.owner.typeRef) :: Nil) + qual.select(which).appliedToType(mbr.owner.typeRef) def select(sym: Symbol) = { val qual1 = if (qual.tpe derivesFrom sym.owner) qual else testOrCast(defn.Any_asInstanceOf, sym) - Select(qual1, sym) withPos tree.pos + qual1.select(sym).withPos(tree.pos) } syms match { case Nil => diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala new file mode 100644 index 000000000..52306956e --- /dev/null +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -0,0 +1,557 @@ +package dotty.tools.dotc +package transform + +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import dotty.tools.dotc.ast.{Trees, tpd} +import scala.collection.{ mutable, immutable } +import ValueClasses._ +import mutable.ListBuffer +import scala.annotation.tailrec +import core._ +import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ +import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ +import util.Positions._ +import Decorators._ +import Symbols._ + +/** This phase performs the following functions, each of which could be split out in a + * mini-phase: + * + * (1) Adds super accessors for all super calls that either + * appear in a trait or have as a target a member of some outer class. + * + * (2) Converts parameter fields that have the same name as a corresponding + * public parameter field in a superclass to a forwarder to the superclass + * field (corresponding = super class field is initialized with subclass field) + * + * (3) Adds protected accessors if the access to the protected member happens + * in a class which is not a subclass of the member's owner. + * + * (4) Finally, the phase used to mangle the names of class-members which are + * private up to an enclosing non-package class, in order to avoid overriding conflicts. + * This is currently disabled, and class-qualified private is deprecated. + * + * It also checks that: + * + * (1) Symbols accessed from super are not abstract, or are overridden by + * an abstract override. + * + * (2) If a symbol accessed accessed from super is defined in a real class (not a trait), + * there are no abstract members which override this member in Java's rules + * (see SI-4989; such an access would lead to illegal bytecode) + * + * (3) Super calls do not go to some synthetic members of Any (see isDisallowed) + * + * (4) Super calls do not go to synthetic field accessors + * + * (5) A class and its companion object do not both define a class or module with the + * same name. + * + * TODO: Rename phase to "Accessors" because it handles more than just super accessors + */ +class SuperAccessors extends MacroTransform with IdentityDenotTransformer { thisTransformer => + + import tpd._ + + /** the following two members override abstract members in Transform */ + val name: String = "superaccessors" + + protected def newTransformer(implicit ctx: Context): Transformer = + new SuperAccTransformer + + class SuperAccTransformer extends Transformer { + + /** validCurrentOwner arrives undocumented, but I reverse engineer it to be + * a flag for needsProtectedAccessor which is false while transforming either + * a by-name argument block or a closure. This excludes them from being + * considered able to access protected members via subclassing (why?) which in turn + * increases the frequency with which needsProtectedAccessor will be true. + */ + private var validCurrentOwner = true + + private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]() + + private def storeAccessorDefinition(clazz: Symbol, tree: Tree) = { + val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for "+clazz)) + buf += tree + } + + private def ensureMethodic(tpe: Type)(implicit ctx: Context) = tpe match { + case tpe: MethodicType => tpe + case _ => ExprType(tpe) + } + + private def ensureAccessor(sel: Select)(implicit ctx: Context) = { + val Select(qual, name) = sel + val sym = sel.symbol + val clazz = qual.symbol.asClass + val supername = name.superName + + val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse { + ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz") + val acc = ctx.newSymbol( + clazz, supername, SuperAccessor | Private | Artifact, + ensureMethodic(sel.tpe.widenSingleton), coord = sym.coord).enteredAfter(thisTransformer) + // Diagnostic for SI-7091 + if (!accDefs.contains(clazz)) + ctx.error(s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz is Package}. Accessor required for ${sel} (${sel.show})", sel.pos) + else storeAccessorDefinition(clazz, DefDef(acc, EmptyTree)) + acc + } + + This(clazz).select(superAcc).withPos(sel.pos) + } + + private def transformArgs(formals: List[Type], args: List[Tree])(implicit ctx: Context) = + args.zipWithConserve(formals) {(arg, formal) => + formal match { + case _: ExprType => withInvalidOwner(transform(arg)) + case _ => transform(arg) + } + } + + /** Check that a class and its companion object to not both define + * a class or module with same name + */ + private def checkCompanionNameClashes(cls: ClassSymbol)(implicit ctx: Context): Unit = + if (!(cls.owner is ModuleClass)) { + val other = cls.owner.linkedClass.info.decl(cls.name) + if (other.symbol.isClass) + ctx.error(s"name clash: ${cls.owner} defines $cls" + "\n" + + s"and its companion ${cls.owner.companionModule} also defines $other", + cls.pos) + } + + /** Expand all declarations in this class which are private within a class. + * Note: It's not sure whether this is the right way. Persumably, we expand + * qualified privates to prvent them from overriding or be overridden by + * symbols that are defined in classes where the qualified private is not + * visible. But it seems a bit dubiuous to do this between type checking + * and refchecks. + */ + def expandQualifiedPrivates(cls: ClassSymbol)(implicit ctx: Context) = { + val decls = cls.info.decls + val decls1: MutableScope = newScope + def needsExpansion(sym: Symbol) = + sym.privateWithin.isClass && + !(sym is Protected) && + !(sym.privateWithin is ModuleClass) && + !(sym is ExpandedName) && + !sym.isConstructor + val nextCtx = ctx.withPhase(thisTransformer.next) + for (s <- decls) { + // !!! hacky to do this by mutation; would be better to do with an infotransformer + // !!! also, why is this done before pickling? + if (needsExpansion(s)) { + ctx.deprecationWarning(s"private qualified with a class has been deprecated, use package enclosing ${s.privateWithin} instead", s.pos) + /* disabled for now + decls.asInstanceOf[MutableScope].unlink(s) + s.copySymDenotation(name = s.name.expandedName(s.privateWithin)) + .installAfter(thisTransformer) + decls1.enter(s)(nextCtx) + ctx.log(i"Expanded ${s.name}, ${s.name(nextCtx)}, sym") + */ + } + } + /* Disabled for now: + if (decls1.nonEmpty) { + for (s <- decls) + if (!needsExpansion(s)) decls1.enter(s)(nextCtx) + val ClassInfo(pre, _, ps, _, selfInfo) = cls.classInfo + cls.copySymDenotation(info = ClassInfo(pre, cls, ps, decls1, selfInfo)) + .installAfter(thisTransformer) + } + */ + } + + private def transformSuperSelect(sel: Select)(implicit ctx: Context): Tree = { + val Select(sup @ Super(_, mix), name) = sel + val sym = sel.symbol + assert(sup.symbol.exists, s"missing symbol in $sel: ${sup.tpe}") + val clazz = sup.symbol.asClass + + if (sym is Deferred) { + val member = sym.overridingSymbol(clazz) + if (mix != tpnme.EMPTY || + !member.exists || + !(member is AbsOverride) && member.isIncompleteIn(clazz)) + ctx.error( + i"${sym.showLocated} is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override'", + sel.pos) + } + else if (mix == tpnme.EMPTY && !(sym.owner is Trait)) + // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract. + for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) { + val overriding = sym.overridingSymbol(intermediateClass) + if ((overriding is (Deferred, butNot = AbsOverride)) && !(overriding.owner is Trait)) + ctx.error( + s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract", + sel.pos) + + } + if (name.isTermName && mix == tpnme.EMPTY && + ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentOwner)) + ensureAccessor(sel)(ctx.withPhase(thisTransformer.next)) + else sel + } + + // Disallow some super.XX calls targeting Any methods which would + // otherwise lead to either a compiler crash or runtime failure. + private def isDisallowed(sym: Symbol)(implicit ctx: Context) = { + val d = defn + import d._ + (sym eq Any_isInstanceOf) || + (sym eq Any_asInstanceOf) || + (sym eq Any_==) || + (sym eq Any_!=) || + (sym eq Any_##) + } + + override def transform(tree: Tree)(implicit ctx: Context): Tree = { + val sym = tree.symbol + + def mayNeedProtectedAccessor(sel: Select, targs: List[Tree], goToSuper: Boolean) = + if (sym.exists && needsProtectedAccessor(sym, tree.pos)) { + ctx.debuglog("Adding protected accessor for " + tree) + transform(makeAccessor(sel, targs)) + } + else if (goToSuper) super.transform(tree) + else tree + + try tree match { + // Don't transform patterns or strange trees will reach the matcher (ticket #4062) + // TODO Drop once this runs after pattern matcher + case CaseDef(pat, guard, body) => + cpy.CaseDef(tree, pat, transform(guard), transform(body)) + + case TypeDef(_, _, impl: Template) => + val cls = sym.asClass + checkCompanionNameClashes(cls) + expandQualifiedPrivates(cls) + super.transform(tree) + + case impl: Template => + + /** For all parameter accessors + * + * val x: T = ... + * + * if + * (1) x is forwarded in the supercall to a parameter that's also named `x` + * (2) the superclass parameter accessor for `x` is accessible from the current class to + * change the accessor to + * + * def x: T = super.x.asInstanceOf[T] + * + * Do the same also if there are intermediate inaccessible parameter accessor forwarders. + * The aim of this transformation is to avoid redundant parameter accessor fields. + */ + def forwardParamAccessors(stats: List[Tree]): List[Tree] = { + val (superArgs, superParamNames) = impl.parents match { + case superCall @ Apply(fn, args) :: _ => + fn.tpe.widen match { + case MethodType(paramNames, _) => (args, paramNames) + case _ => (Nil, Nil) + } + case _ => (Nil, Nil) + } + def inheritedAccessor(sym: Symbol): Symbol = { + val candidate = sym.owner.asClass.superClass + .info.decl(sym.name).suchThat(_ is (ParamAccessor, butNot = Mutable)).symbol + if (candidate.isAccessibleFrom(currentClass.thisType, superAccess = true)) candidate + else if (candidate is Method) inheritedAccessor(candidate) + else NoSymbol + } + def forwardParamAccessor(stat: Tree): Tree = { + stat match { + case stat: ValDef => + val sym = stat.symbol.asTerm + if (sym is (PrivateLocalParamAccessor, butNot = Mutable)) { + val idx = superArgs.indexWhere(_.symbol == sym) + if (idx >= 0 && superParamNames(idx) == stat.name) { // supercall to like-named parameter + val alias = inheritedAccessor(sym) + if (alias.exists) { + def forwarder(implicit ctx: Context) = { + sym.copySymDenotation(initFlags = sym.flags | Method, info = ensureMethodic(sym.info)) + .installAfter(thisTransformer) + val superAcc = + Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) + DefDef(sym, ensureConforms(superAcc, sym.info.widen)) + } + return forwarder(ctx.withPhase(thisTransformer.next)) + } + } + } + case _ => + } + stat + } + stats map forwardParamAccessor + } + + def transformTemplate = { + val ownStats = new ListBuffer[Tree] + accDefs(currentClass) = ownStats + val body1 = forwardParamAccessors(transformStats(impl.body, tree.symbol)) + accDefs -= currentClass + ownStats ++= body1 + cpy.Template(tree, impl.constr, impl.parents, impl.self, body1) + } + transformTemplate + + case TypeApply(sel @ Select(This(_), name), args) => + mayNeedProtectedAccessor(sel, args, goToSuper = false) + + case sel @ Select(qual, name) => + def transformSelect = { + + qual match { + case This(_) => + // warn if they are selecting a private[this] member which + // also exists in a superclass, because they may be surprised + // to find out that a constructor parameter will shadow a + // field. See SI-4762. + /* to be added + if (settings.lint) { + if (sym.isPrivateLocal && sym.paramss.isEmpty) { + qual.symbol.ancestors foreach { parent => + parent.info.decls filterNot (x => x.isPrivate || x.isLocalToThis) foreach { m2 => + if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) { + unit.warning(sel.pos, + sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name + + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within " + + sym.owner + " - you may want to give them distinct names.") + } + } + } + } + } + */ + + /* + * A trait which extends a class and accesses a protected member + * of that class cannot implement the necessary accessor method + * because its implementation is in an implementation class (e.g. + * Foo$class) which inherits nothing, and jvm access restrictions + * require the call site to be in an actual subclass. So non-trait + * classes inspect their ancestors for any such situations and + * generate the accessors. See SI-2296. + */ + // FIXME - this should be unified with needsProtectedAccessor, but some + // subtlety which presently eludes me is foiling my attempts. + val shouldEnsureAccessor = ( + (currentClass is Trait) + && (sym is Protected) + && sym.enclosingClass != currentClass + && !(sym.owner is PackageClass) // SI-7091 no accessor needed package owned (ie, top level) symbols + && !(sym.owner is Trait) + && sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass + && qual.symbol.info.member(sym.name).exists + && !needsProtectedAccessor(sym, tree.pos)) + if (shouldEnsureAccessor) { + ctx.log("Ensuring accessor for call to protected " + sym.showLocated + " from " + currentClass) + ensureAccessor(sel) + } else + mayNeedProtectedAccessor(sel, Nil, goToSuper = false) + + case Super(_, mix) => + if ((sym.isTerm) && !(sym is Method) || (sym is Accessor)) { + ctx.error(s"super may be not be used on ${sym.accessedField orElse sym}", tree.pos) + } else if (isDisallowed(sym)) { + ctx.error(s"super not allowed here: use this.${name.decode} instead", tree.pos) + } + transformSuperSelect(sel) + + case _ => + mayNeedProtectedAccessor(sel, Nil, goToSuper = true) + } + } + transformSelect + + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + val rhs1 = if (isMethodWithExtension(sym)) withInvalidOwner(transform(rhs)) else transform(rhs) + cpy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1) + + case TypeApply(sel @ Select(qual, name), args) => + mayNeedProtectedAccessor(sel, args, goToSuper = true) + + case Assign(lhs @ Select(qual, name), rhs) => + def transformAssign = { + if ((lhs.symbol is Mutable) && + (lhs.symbol is JavaDefined) && + needsProtectedAccessor(lhs.symbol, tree.pos)) { + ctx.debuglog("Adding protected setter for " + tree) + val setter = makeSetter(lhs) + ctx.debuglog("Replaced " + tree + " with " + setter) + transform(Apply(setter, qual :: rhs :: Nil)) + } else + super.transform(tree) + } + transformAssign + + case Apply(fn, args) => + val MethodType(_, formals) = fn.tpe.widen + cpy.Apply(tree, transform(fn), transformArgs(formals, args)) + + case _ => + super.transform(tree) + } + catch { + case ex : AssertionError => + if (sym != null && sym != NoSymbol) + Console.println("TRANSFORM: " + tree.symbol.sourceFile) + + Console.println("TREE: " + tree) + throw ex + } + } + + private def withInvalidOwner[A](trans: => A): A = { + val saved = validCurrentOwner + validCurrentOwner = false + try trans + finally validCurrentOwner = saved + } + + /** Add a protected accessor, if needed, and return a tree that calls + * the accessor and returns the same member. The result is already + * typed. + * TODO why is targs needed? It looks like we can do without. + */ + private def makeAccessor(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { + val Select(qual, _) = tree + val sym = tree.symbol.asTerm + val clazz = hostForAccessorOf(sym, currentClass) + assert(clazz.exists, sym) + ctx.debuglog("Decided for host class: " + clazz) + + val accName = sym.name.protectedAccessorName + + // if the result type depends on the this type of an enclosing class, the accessor + // has to take an object of exactly this type, otherwise it's more general + val receiverType = if (isThisType(sym.info.finalResultType)) clazz.thisType else clazz.classInfo.selfType + val accType = { + def accTypeOf(tpe: Type): Type = tpe match { + case tpe: PolyType => + tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) + case _ => + MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) + } + accTypeOf(sym.info) + } + val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { + val newAcc = ctx.newSymbol( + clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisTransformer) + val code = polyDefDef(newAcc, trefs => vrefss => { + val (receiver :: _) :: tail = vrefss + val base = receiver.select(sym).appliedToTypes(trefs) + (base /: vrefss)(Apply(_, _)) + }) + ctx.debuglog("created protected accessor: " + code) + storeAccessorDefinition(clazz, code) + newAcc + } + val res = This(clazz) + .select(protectedAccessor) + .appliedToTypeTrees(targs) + .appliedTo(qual) + .withPos(tree.pos) + ctx.debuglog(s"Replaced $tree with $res") + res + } + + /** Add an accessor for field, if needed, and return a selection tree for it . + * The result is not typed. + */ + private def makeSetter(tree: Select)(implicit ctx: Context): Tree = { + val field = tree.symbol.asTerm + val clazz = hostForAccessorOf(field, currentClass) + assert(clazz.exists, field) + ctx.debuglog("Decided for host class: " + clazz) + + val accName = field.name.protectedSetterName + val accType = MethodType(clazz.classInfo.selfType :: field.info :: Nil, defn.UnitType) + val protectedAccessor = clazz.info.decl(accName).symbol orElse { + val newAcc = ctx.newSymbol( + clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisTransformer) + val code = DefDef(newAcc, vrefss => { + val (receiver :: value :: Nil) :: Nil = vrefss + Assign(receiver.select(field), value).withPos(tree.pos) + }) + ctx.debuglog("created protected setter: " + code) + storeAccessorDefinition(clazz, code) + newAcc + } + This(clazz).select(protectedAccessor).withPos(tree.pos) + } + + /** Does `sym` need an accessor when accessed from `currentClass`? + * A special case arises for classes with explicit self-types. If the + * self type is a Java class, and a protected accessor is needed, we issue + * an error. If the self type is a Scala class, we don't add an accessor. + * An accessor is not needed if the access boundary is larger than the + * enclosing package, since that translates to 'public' on the host sys. + * (as Java has no real package nesting). + * + * If the access happens inside a 'trait', access is more problematic since + * the implementation code is moved to an '$class' class which does not + * inherit anything. Since we can't (yet) add accessors for 'required' + * classes, this has to be signaled as error. + * FIXME Need to better understand this logic + */ + private def needsProtectedAccessor(sym: Symbol, pos: Position)(implicit ctx: Context): Boolean = { + val clazz = currentClass + val host = hostForAccessorOf(sym, clazz) + val selfType = host.classInfo.selfType + def accessibleThroughSubclassing = + validCurrentOwner && (selfType <:< sym.owner.typeRef) && !clazz.is(Trait) + + val isCandidate = ( + sym.is(Protected) + && sym.is(JavaDefined) + && !sym.effectiveOwner.is(Package) + && !accessibleThroughSubclassing + && (sym.enclosingPackageClass != currentClass.enclosingPackageClass) + && (sym.enclosingPackageClass == sym.accessBoundary(sym.enclosingPackageClass)) + ) + def isSelfType = !(host.typeRef <:< selfType) && { + if (selfType.typeSymbol.is(JavaDefined)) + ctx.restrictionError(s"cannot accesses protected $sym from within $clazz with self type $selfType", pos) + true + } + def isJavaProtected = host.is(Trait) && sym.is(JavaDefined) && { + ctx.restrictionError( + s"""$clazz accesses protected $sym inside a concrete trait method. + |Add an accessor in a class extending ${sym.enclosingClass} as a workaround.""".stripMargin, + pos + ) + true + } + isCandidate && !host.is(Package) && !isSelfType && !isJavaProtected + } + + /** Return the innermost enclosing class C of referencingClass for which either + * of the following holds: + * - C is a subclass of sym.owner or + * - C is declared in the same package as sym's owner + */ + private def hostForAccessorOf(sym: Symbol, referencingClass: ClassSymbol)(implicit ctx: Context): ClassSymbol = + if (referencingClass.derivesFrom(sym.owner) + || referencingClass.classInfo.selfType <:< sym.owner.typeRef + || referencingClass.enclosingPackageClass == sym.owner.enclosingPackageClass) { + assert(referencingClass.isClass, referencingClass) + referencingClass + } + else if(referencingClass.owner.enclosingClass.exists) + hostForAccessorOf(sym, referencingClass.owner.enclosingClass.asClass) + else + referencingClass + + /** Is 'tpe' the type of a member of an enclosing class? */ + private def isThisType(tpe: Type)(implicit ctx: Context): Boolean = tpe match { + case ThisType(cls) => !cls.is(PackageClass) + case tpe: TypeProxy => isThisType(tpe.underlying) + case _ => false + } + } +} diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index cd0643a6e..a2278e72f 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -329,7 +329,7 @@ class TailRec extends TreeTransform with DenotTransformer { val newDenot = d.copySymDenotation(initFlags = sym.flags &~ Flags.Local) newDenot.installAfter(TailRec.this) } - Select(thiz, sym) + thiz.select(sym) case _ => tree } } diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 347762678..8e7c4f6d0 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -5,6 +5,7 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Flags.PackageVal import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.core.Decorators._ import scala.annotation.tailrec @@ -883,7 +884,12 @@ object TreeTransforms { } } else tree - def localContext(owner: Symbol)(implicit ctx: Context) = ctx.fresh.setOwner(owner) + // TODO merge with localCtx in MacroTransform + // Generally: If we will keep MacroTransform, merge common behavior with TreeTransform + def localContext(sym: Symbol)(implicit ctx: Context) = { + val owner = if (sym is PackageVal) sym.moduleClass else sym + ctx.fresh.setOwner(owner) + } final private[TreeTransforms] def transformNamed(tree: NameTree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = tree match { @@ -1124,8 +1130,12 @@ object TreeTransforms { val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx) goPackageDef(cpy.PackageDef(tree, pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) } + case tree: Import => EmptyTree + case tree: NamedArg => transform(tree.arg, info, cur) case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur)) - case tree => tree + case tree => + if (tree.isType) transform(TypeTree(tree.tpe).withPos(tree.pos), info, cur) + else tree } def transform(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show} at ${ctx.phase}", transforms, show = true) { diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 5f65ee414..b209f7647 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -34,7 +34,7 @@ class TypeTestsCasts extends TreeTransform { def isPrimitive(tp: Type) = tp.classSymbol.isPrimitiveValueClass def derivedTree(qual1: Tree, sym: Symbol, tp: Type) = - cpy.TypeApply(tree, Select(qual1, sym) withPos qual.pos, List(TypeTree(tp))) + cpy.TypeApply(tree, qual1.select(sym).withPos(qual.pos), List(TypeTree(tp))) def qualCls = qual.tpe.classSymbol @@ -49,7 +49,7 @@ class TypeTestsCasts extends TreeTransform { else argType.dealias match { case _: SingletonType => val cmpOp = if (argType derivesFrom defn.AnyValClass) defn.Any_equals else defn.Object_eq - Apply(Select(expr, cmpOp), singleton(argType) :: Nil) + expr.select(cmpOp).appliedTo(singleton(argType)) case AndType(tp1, tp2) => evalOnce(expr) { fun => val erased1 = transformIsInstanceOf(fun, tp1) diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala new file mode 100644 index 000000000..f11bb980a --- /dev/null +++ b/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -0,0 +1,23 @@ +package dotty.tools.dotc +package transform + +import core._ +import Types._ +import Contexts._ +import Symbols._ +import Decorators._ +import StdNames.nme +import NameOps._ +import language.implicitConversions + +object TypeUtils { + implicit def decorateTypeUtils(tpe: Type): TypeUtils = new TypeUtils(tpe) +} + +/** A decorator that provides methods for type transformations + * that are needed in the transofmer pipeline (not needed right now) + */ +class TypeUtils(val self: Type) extends AnyVal { + import TypeUtils._ + +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/ValueClasses.scala b/src/dotty/tools/dotc/transform/ValueClasses.scala new file mode 100644 index 000000000..c5cf44552 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ValueClasses.scala @@ -0,0 +1,36 @@ +package dotty.tools.dotc +package transform + +import core._ +import Types._ +import Symbols._ +import SymDenotations._ +import Contexts._ +import Flags._ + +/** Methods that apply to user-defined value classes */ +object ValueClasses { + + def isDerivedValueClass(d: SymDenotation)(implicit ctx: Context) = + d.isClass && d.derivesFrom(defn.AnyValClass) && !d.isPrimitiveValueClass + + def isMethodWithExtension(d: SymDenotation)(implicit ctx: Context) = + d.isSourceMethod && + isDerivedValueClass(d.owner) && + !d.isConstructor && + !d.is(SuperAccessor) && + !d.is(Macro) + + /** The member that of a derived value class that unboxes it. */ + def valueClassUnbox(d: ClassDenotation)(implicit ctx: Context): Symbol = + // (info.decl(nme.unbox)).orElse(...) uncomment once we accept unbox methods + d.classInfo.decls + .find(d => d.isTerm && d.symbol.is(ParamAccessor)) + .map(_.symbol) + .getOrElse(NoSymbol) + + /** The unboxed type that underlies a derived value class */ + def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type = + valueClassUnbox(d).info.resultType + +} diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 91f4ce9a5..242985b57 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -735,9 +735,9 @@ trait Applications extends Compatibility { self: Typer => onMethod(tp, isApplicable(_, args, resultType)) private def onMethod(tp: Type, p: TermRef => Boolean)(implicit ctx: Context): Boolean = tp match { - case methRef: TermRef if methRef.widenSingleton.isInstanceOf[SignedType] => + case methRef: TermRef if methRef.widenSingleton.isInstanceOf[MethodicType] => p(methRef) - case mt: SignedType => + case mt: MethodicType => p(mt.narrow) case _ => tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d))) @@ -767,8 +767,7 @@ trait Applications extends Compatibility { self: Typer => */ def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { tp1 match { case tp1: PolyType => - def bounds(tparamRefs: List[TypeRef]) = tp1.paramBounds map (_.substParams(tp1, tparamRefs)) - val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, bounds) + val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds) isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2) case tp1: MethodType => def repeatedToSingle(tp: Type) = if (tp.isRepeatedParam) tp.argTypesHi.head else tp diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 0b016769b..89e73013f 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -127,7 +127,7 @@ object ErrorReporting { case _ => true } - val s = new InfoString(sc).i(args : _*) + val s = new StringInterpolators(sc).i(args : _*) if (args.forall(isSensical(_))) s else nonSensicalStartTag + s + nonSensicalEndTag } } diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 110dc6152..69b512416 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -21,7 +21,7 @@ object EtaExpansion { import tpd._ private def lift(defs: mutable.ListBuffer[Tree], expr: Tree, prefix: String = "")(implicit ctx: Context): Tree = - if (isIdempotentExpr(expr)) expr + if (isPureExpr(expr)) expr else { val name = ctx.freshName(prefix).toTermName val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, expr.tpe.widen, coord = positionCoord(expr.pos)) @@ -83,7 +83,7 @@ object EtaExpansion { cpy.Apply(tree, liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) case TypeApply(fn, targs) => cpy.TypeApply(tree, liftApp(defs, fn), targs) - case Select(pre, name) if tpd.isIdempotentRef(tree) => + case Select(pre, name) if isPureRef(tree) => cpy.Select(tree, liftApp(defs, pre), name) case Block(stats, expr) => liftApp(defs ++= stats, expr) diff --git a/src/dotty/tools/dotc/typer/ImportInfo.scala b/src/dotty/tools/dotc/typer/ImportInfo.scala index 9fbd07102..18e5db209 100644 --- a/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -7,7 +7,7 @@ import ast.Trees._ import core._ import util.SimpleMap import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._ -import Decorators.InfoString +import Decorators.StringInterpolators object ImportInfo { /** The import info for a root import from given symbol `sym` */ diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 76817fd16..6adfa3052 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -1,10 +1,11 @@ package dotty.tools.dotc package typer -import core.Contexts._ -import core.Types._ -import core.Symbols._ -import core.Decorators._ +import core._ +import Contexts._ +import Types._ +import Symbols._ +import Decorators._ import typer.ProtoTypes._ import ast.{tpd, untpd} import ast.Trees._ @@ -59,4 +60,10 @@ class ReTyper extends Typer { override def localTyper(sym: Symbol) = this override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx + + override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree = + fallBack(tree, ctx.typerState) + + override def addTypedModifiersAnnotations(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = + typedModifiers(mods, sym) }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 48c263085..ae56df82f 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -279,7 +279,7 @@ trait TypeAssigner { def assignType(tree: untpd.SeqLiteral, elems: List[Tree])(implicit ctx: Context) = { val ownType = if (ctx.erasedTypes) defn.SeqType - else defn.SeqType.appliedTo(ctx.typeComparer.lub(elems.tpes)) + else defn.SeqType.appliedTo(ctx.typeComparer.lub(elems.tpes).widen) tree.withType(ownType) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index c01cf714f..6dc9a49b1 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -744,9 +744,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit assignType(cpy.Alternative(tree, trees1), trees1) } + def addTypedModifiersAnnotations(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = { + val mods1 = typedModifiers(mods, sym) + for (tree <- mods1.annotations) sym.addAnnotation(Annotation(tree)) + mods1 + } + def typedModifiers(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = track("typedModifiers") { val annotations1 = mods.annotations mapconserve typedAnnotation - for (tree <- annotations1) sym.addAnnotation(Annotation(tree)) if (annotations1 eq mods.annotations) mods.asInstanceOf[Modifiers] else Modifiers(mods.flags, mods.privateWithin, annotations1) } @@ -757,7 +762,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = track("typedValDef") { val ValDef(mods, name, tpt, rhs) = vdef - val mods1 = typedModifiers(mods, sym) + val mods1 = addTypedModifiersAnnotations(mods, sym) val tpt1 = typedType(tpt) val rhs1 = rhs match { case Ident(nme.WILDCARD) => rhs withType tpt1.tpe @@ -768,7 +773,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") { val DefDef(mods, name, tparams, vparamss, tpt, rhs) = ddef - val mods1 = typedModifiers(mods, sym) + val mods1 = addTypedModifiersAnnotations(mods, sym) val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) @@ -780,7 +785,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedTypeDef") { val TypeDef(mods, name, rhs) = tdef - val mods1 = typedModifiers(mods, sym) + val mods1 = addTypedModifiersAnnotations(mods, sym) val _ = typedType(rhs) // unused, typecheck only to remove from typedTree assignType(cpy.TypeDef(tdef, mods1, name, TypeTree(sym.info)), sym) } @@ -807,7 +812,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val TypeDef(mods, name, impl @ Template(constr, parents, self, body)) = cdef - val mods1 = typedModifiers(mods, cls) + val mods1 = addTypedModifiersAnnotations(mods, cls) val constr1 = typed(constr).asInstanceOf[DefDef] val parents1 = ensureConstrCall(ensureFirstIsClass( parents mapconserve typedParent, cdef.pos.toSynthetic)) |