diff options
author | Dmitry Petrashko <dark@d-d.me> | 2015-04-28 11:35:34 +0200 |
---|---|---|
committer | Dmitry Petrashko <dark@d-d.me> | 2015-04-28 11:35:34 +0200 |
commit | a18b3faeda0a33843bb60cb1475c974aff3a1621 (patch) | |
tree | 5f1f515abc423b1168a0ab75d2e52675267e4f46 /src | |
parent | 6d1138e2ce2d212c051faa20e1f4bf419ede3fdf (diff) | |
parent | 2b3591cec6a1d58f3346b6c8933ca0742f13c1cf (diff) | |
download | dotty-a18b3faeda0a33843bb60cb1475c974aff3a1621.tar.gz dotty-a18b3faeda0a33843bb60cb1475c974aff3a1621.tar.bz2 dotty-a18b3faeda0a33843bb60cb1475c974aff3a1621.zip |
Merge pull request #495 from dotty-staging/refactor/SuperAccessors
Refactor/super accessors
Diffstat (limited to 'src')
20 files changed, 543 insertions, 531 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index cb3d0e21e..1657adbbb 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -6,7 +6,7 @@ import Contexts._ import Periods._ import Symbols._ import Scopes._ -import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks, InstChecks} +import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks} import reporting.ConsoleReporter import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.transform._ @@ -38,11 +38,9 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new InstChecks), - List(new FirstTransform, - new SyntheticMethods), - List(new SuperAccessors), - List(new Pickler), // Pickler needs to come last in a group since it should not pickle trees generated later + List(new PostTyper), + List(new Pickler), + List(new FirstTransform), List(new RefChecks, new ElimRepeated, new NormalizeFlags, diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index b856e3190..0a1611b61 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -244,7 +244,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = - ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(expr)) + ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(ctx.owner, expr)) def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef = ta.assignType(untpd.PackageDef(pid, stats), pid) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 3566595f2..83499ca7b 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -211,6 +211,10 @@ object SymDenotations { final def hasAnnotation(cls: Symbol)(implicit ctx: Context) = dropOtherAnnotations(annotations, cls).nonEmpty + /** Apply transform `f` to all annotations of this denotation */ + final def transformAnnotations(f: Annotation => Annotation)(implicit ctx: Context): Unit = + annotations = annotations.mapConserve(f) + /** Optionally, the annotation matching the given class symbol */ final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] = dropOtherAnnotations(annotations, cls) match { diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 52040bcfd..9f18e723c 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -228,8 +228,8 @@ trait Symbols { this: Context => newSymbol(cls, nme.localDummyName(cls), EmptyFlags, NoType) /** Create an import symbol pointing back to given qualifier `expr`. */ - def newImportSymbol(expr: Tree, coord: Coord = NoCoord) = - newSymbol(NoSymbol, nme.IMPORT, EmptyFlags, ImportType(expr), coord = coord) + def newImportSymbol(owner: Symbol, expr: Tree, coord: Coord = NoCoord) = + newSymbol(owner, nme.IMPORT, EmptyFlags, ImportType(expr), coord = coord) /** Create a class constructor symbol for given class `cls`. */ def newConstructor(cls: ClassSymbol, flags: FlagSet, paramNames: List[TermName], paramTypes: List[Type], privateWithin: Symbol = NoSymbol, coord: Coord = NoCoord) = @@ -558,13 +558,17 @@ object Symbols { ctx.newSymbol(owner, name, flags, info, privateWithin, coord) } - implicit def defn(implicit ctx: Context): Definitions = ctx.definitions - /** Makes all denotation operations available on symbols */ implicit def toDenot(sym: Symbol)(implicit ctx: Context): SymDenotation = sym.denot /** Makes all class denotations available on class symbols */ implicit def toClassDenot(cls: ClassSymbol)(implicit ctx: Context): ClassDenotation = cls.classDenot + /** The Definitions object */ + def defn(implicit ctx: Context): Definitions = ctx.definitions + + /** The current class */ + def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass + var stubs: List[Symbol] = Nil // diagnostic } diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index de42b3e5f..5325189e1 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -279,7 +279,7 @@ class TypeApplications(val self: Type) extends AnyVal { default } } - + /** Translate a type of the form From[T] to To[T], keep other types as they are. * `from` and `to` must be static classes, both with one type parameter, and the same variance. */ diff --git a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala index a58fc9071..ba3023ed1 100644 --- a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala @@ -657,10 +657,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { } def readIndexedStat(exprOwner: Symbol)(implicit ctx: Context): Tree = nextByte match { - case TYPEDEF | VALDEF | DEFDEF | IMPORT => + case TYPEDEF | VALDEF | DEFDEF => readIndexedDef() case IMPORT => - ??? + readImport() case PACKAGE => val start = currentAddr processPackage { (pid, end) => implicit ctx => @@ -670,6 +670,24 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { readTerm()(ctx.withOwner(exprOwner)) } + def readImport()(implicit ctx: Context): Tree = { + readByte() + readEnd() + val expr = readTerm() + def readSelectors(): List[untpd.Tree] = nextByte match { + case RENAMED => + readByte() + readEnd() + untpd.Pair(untpd.Ident(readName()), untpd.Ident(readName())) :: readSelectors() + case IMPORTED => + readByte() + untpd.Ident(readName()) :: readSelectors() + case _ => + Nil + } + Import(expr, readSelectors()) + } + def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = until(end)(readIndexedStat(exprOwner)) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index f3ffd6f6f..099ca93cf 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -114,7 +114,7 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp) match { case tp: TypeType => toTextRHS(tp) - case tp: TermRef if !tp.denotationIsCurrent || tp.symbol.is(Module) => + case tp: TermRef if !tp.denotationIsCurrent || tp.symbol.is(Module) || tp.symbol.name.isImportName => toTextRef(tp) ~ ".type" case tp: TermRef if tp.denot.isOverloaded => "<overloaded " ~ toTextRef(tp) ~ ">" diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 26f26fc2f..ae22adc39 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -31,6 +31,8 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimRepeated]) + override def runsAfterGroupsOf = Set(classOf[FirstTransform]) // need companion objects to exist + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: ClassDenotation if ref is ModuleClass => ref.linkedClass match { diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index 02d0bb2ba..aecc1b86f 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -24,21 +24,13 @@ import StdNames._ /** The first tree transform * - ensures there are companion objects for all classes except module classes - * - eliminates some kinds of trees: Imports, NamedArgs, all TypTrees other than TypeTree - * - converts Select/Ident/SelectFromTypeTree nodes that refer to types to TypeTrees. - * - inserts `.package` for selections of package object members - * - checks the bounds of AppliedTypeTrees + * - eliminates some kinds of trees: Imports, NamedArgs * - stubs out native methods - * - removes java-defined ASTs */ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer with AnnotationTransformer { thisTransformer => import ast.tpd._ override def phaseName = "firstTransform" - - override def runsAfter = Set(classOf[typer.InstChecks]) - // This phase makes annotations disappear in types, so InstChecks should - // run before so that it can get at all annotations. def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp @@ -101,10 +93,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi case stat => stat } - def skipJava(stats: List[Tree]): List[Tree] = // packages get a JavaDefined flag. Dont skip them - stats.filter(t => !(t.symbol is(Flags.JavaDefined, Flags.Package))) - - addMissingCompanions(reorder(skipJava(stats))) + addMissingCompanions(reorder(stats)) } override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = { @@ -119,47 +108,10 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next))) - private def normalizeType(tree: Tree)(implicit ctx: Context) = - if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = tree.tpe match { - case tpe: ThisType => - /* - A this reference hide in a self ident, and be subsequently missed - when deciding on whether outer accessors are needed and computing outer paths. - We do this normalization directly after Typer, because during typer the - ident should rest available for hyperlinking.*/ - This(tpe.cls).withPos(tree.pos) - case _ => normalizeType(tree) - } - - - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = - normalizeType { - val qual = tree.qualifier - qual.symbol.moduleClass.denot match { - case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) => - cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name) - case _ => - tree - } - } - - override def transformSelectFromTypeTree(tree: SelectFromTypeTree)(implicit ctx: Context, info: TransformerInfo) = - normalizeType(tree) - override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { case tree: Import => EmptyTree case tree: NamedArg => transform(tree.arg) - case AppliedTypeTree(tycon, args) => - val tparams = tycon.tpe.typeSymbol.typeParams - val bounds = tparams.map(tparam => - tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) - Checking.checkBounds(args, bounds, _.substDealias(tparams, _)) - normalizeType(tree) - case tree => - normalizeType(tree) + case tree => tree } // invariants: all modules have companion objects diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala index acfeda48e..f46942fb3 100644 --- a/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -6,6 +6,7 @@ import Types._ import Contexts._ import Symbols._ import Decorators._ +import TypeUtils._ import StdNames.nme import NameOps._ import ast._ @@ -128,14 +129,8 @@ trait FullParameterization { */ 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 + case info @ MethodType(nme.SELF :: Nil, _) => info.resultType.ensureMethodic.signature + case _ => Signature.NotAMethod } /** The type parameters (skolems) of the method definition `originalDef`, diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 9b35d1d99..1a23d887c 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -107,18 +107,18 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform * in `enclosure` or there is an intermediate class properly containing `enclosure` * in which `sym` is also free. Also, update `liftedOwner` of `enclosure` so * that `enclosure` can access `sym`, or its proxy in an intermediate class. - * This means: - * + * This means: + * * 1. If there is an intermediate class in which `sym` is free, `enclosure` - * must be contained in that class (in order to access the `sym proxy stored + * must be contained in that class (in order to access the `sym proxy stored * in the class). - * + * * 2. If there is no intermediate class, `enclosure` must be contained * in the class enclosing `sym`. - * + * * Return the closest enclosing intermediate class between `enclosure` and * the owner of sym, or NoSymbol if none exists. - * + * * pre: sym.owner.isTerm, (enclosure.isMethod || enclosure.isClass) * * The idea of `markFree` is illustrated with an example: @@ -150,10 +150,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform else { ctx.log(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure") ctx.debuglog(i"$enclosure != ${sym.enclosure}") - val intermediate = + val intermediate = if (enclosure.is(PackageClass)) enclosure - else markFree(sym, enclosure.skipConstructor.enclosure) - // `enclosure` might be a constructor, in which case we want the enclosure + else markFree(sym, enclosure.skipConstructor.enclosure) + // `enclosure` might be a constructor, in which case we want the enclosure // of the enclosing class, so skipConstructor is needed here. if (intermediate.exists) { narrowLiftedOwner(enclosure, intermediate) @@ -394,12 +394,12 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform val sym = tree.symbol tree.tpe match { case tpe @ TermRef(prefix, _) => - if (prefix eq NoPrefix) + if (prefix eq NoPrefix) if (sym.enclosure != currentEnclosure && !sym.isStatic) (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos) else if (sym.owner.isClass) // sym was lifted out ref(sym).withPos(tree.pos) - else + else tree else if (!prefixIsElidable(tpe)) ref(tpe) else tree diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 0f57c3ff5..9634decaa 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -38,11 +38,6 @@ abstract class MacroTransform extends Phase { 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] = { def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat) diff --git a/src/dotty/tools/dotc/transform/ParamForwarding.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala new file mode 100644 index 000000000..87ecaba07 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -0,0 +1,71 @@ +package dotty.tools.dotc +package transform + +import core._ +import ast.Trees._ +import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._, StdNames._ + +/** 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. + */ +class ParamForwarding(thisTransformer: DenotTransformer) { + import ast.tpd._ + + def forwardParamAccessors(impl: Template)(implicit ctx: Context): Template = { + def fwd(stats: List[Tree])(implicit ctx: Context): 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 = sym.info.ensureMethodic) + .installAfter(thisTransformer) + val superAcc = + Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) + DefDef(sym, superAcc.ensureConforms(sym.info.widen)) + } + return forwarder(ctx.withPhase(thisTransformer.next)) + } + } + } + case _ => + } + stat + } + stats map forwardParamAccessor + } + + cpy.Template(impl)(body = fwd(impl.body)(ctx.withPhase(thisTransformer))) + } +} diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala new file mode 100644 index 000000000..55270f233 --- /dev/null +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -0,0 +1,169 @@ +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 scala.annotation.tailrec +import core._ +import typer.ErrorReporting._ +import typer.Checking +import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ +import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ +import util.Positions._ +import Decorators._ +import Symbols._, TypeUtils._ + +/** A macro transform that runs immediately after typer and that performs the following functions: + * + * (1) Add super accessors and protected accessors (@see SuperAccessors) + * + * (2) Convert 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) + * (@see ForwardParamAccessors) + * + * (3) Add synthetic methods (@see SyntheticMethods) + * + * (4) Check that `New` nodes can be instantiated, and that annotations are valid + * + * (5) Convert all trees representing types to TypeTrees. + * + * (6) Check the bounds of AppliedTypeTrees + * + * (7) Insert `.package` for selections of package object members + * + * (8) Replaces self references by name with `this` + * + * The reason for making this a macro transform is that some functions (in particular + * super and protected accessors and instantiation checks) are naturally top-down and + * don't lend themselves to the bottom-up approach of a mini phase. The other two functions + * (forwarding param accessors and synthetic methods) only apply to templates and fit + * mini-phase or subfunction of a macro phase equally well. But taken by themselves + * they do not warrant their own group of miniphases before pickling. + */ +class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTransformer => + + import tpd._ + + /** the following two members override abstract members in Transform */ + override def phaseName: String = "posttyper" + + override def transformPhase(implicit ctx: Context) = thisTransformer.next + + protected def newTransformer(implicit ctx: Context): Transformer = + new PostTyperTransformer + + val superAcc = new SuperAccessors(thisTransformer) + val paramFwd = new ParamForwarding(thisTransformer) + val synthMth = new SyntheticMethods(thisTransformer) + + private def newPart(tree: Tree): Option[New] = methPart(tree) match { + case Select(nu: New, _) => Some(nu) + case _ => None + } + + private def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = { + // TODO fill in + } + + private def normalizeTypeTree(tree: Tree)(implicit ctx: Context) = { + def norm(tree: Tree) = if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree + tree match { + case tree: TypeTree => + tree + case AppliedTypeTree(tycon, args) => + val tparams = tycon.tpe.typeSymbol.typeParams + val bounds = tparams.map(tparam => + tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) + Checking.checkBounds(args, bounds, _.substDealias(tparams, _)) + norm(tree) + case _ => + norm(tree) + } + } + + class PostTyperTransformer extends Transformer { + + private var inJavaAnnot: Boolean = false + + private var parentNews: Set[New] = Set() + + private def transformAnnot(annot: Tree)(implicit ctx: Context): Tree = { + val saved = inJavaAnnot + inJavaAnnot = annot.symbol is JavaDefined + if (inJavaAnnot) checkValidJavaAnnotation(annot) + try transform(annot) + finally inJavaAnnot = saved + } + + private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation = + annot.derivedAnnotation(transformAnnot(annot.tree)) + + private def transformAnnots(tree: MemberDef)(implicit ctx: Context): Unit = + tree.symbol.transformAnnotations(transformAnnot) + + private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context) = { + val qual = tree.qualifier + qual.symbol.moduleClass.denot match { + case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) => + assert(targs.isEmpty) + cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name) + case _ => + superAcc.transformSelect(super.transform(tree), targs) + } + } + + override def transform(tree: Tree)(implicit ctx: Context): Tree = + try normalizeTypeTree(tree) match { + case tree: Ident => + tree.tpe match { + case tpe: ThisType => This(tpe.cls).withPos(tree.pos) + case _ => tree + } + case tree: Select => + transformSelect(tree, Nil) + case tree @ TypeApply(sel: Select, args) => + val args1 = transform(args) + val sel1 = transformSelect(sel, args1) + if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1) + case tree @ Assign(sel: Select, _) => + superAcc.transformAssign(super.transform(tree)) + case tree: Template => + val saved = parentNews + parentNews ++= tree.parents.flatMap(newPart) + try + synthMth.addSyntheticMethods( + paramFwd.forwardParamAccessors( + superAcc.wrapTemplate(tree)( + super.transform(_).asInstanceOf[Template]))) + finally parentNews = saved + case tree: DefDef => + transformAnnots(tree) + superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef]) + case tree: MemberDef => + transformAnnots(tree) + super.transform(tree) + case tree: New if !inJavaAnnot && !parentNews.contains(tree) => + Checking.checkInstantiable(tree.tpe, tree.pos) + super.transform(tree) + case tree @ Annotated(annot, annotated) => + cpy.Annotated(tree)(transformAnnot(annot), transform(annotated)) + case tree: TypeTree => + tree.withType( + tree.tpe match { + case AnnotatedType(annot, tpe) => AnnotatedType(transformAnnot(annot), tpe) + case tpe => tpe + } + ) + case tree => + super.transform(tree) + } + catch { + case ex : AssertionError => + println(i"error while transforming $tree") + throw ex + } + } +} diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 8857b6921..b111fdb92 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -5,174 +5,104 @@ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform 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._ +import Symbols._, TypeUtils._ -/** This phase performs the following functions, each of which could be split out in a - * mini-phase: +/** This class performs the following functions: * * (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 + * (2) 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), + * (2) If a symbol 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 => +class SuperAccessors(thisTransformer: DenotTransformer) { import tpd._ - /** the following two members override abstract members in Transform */ - override def phaseName: 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. + /** Some parts of trees will get a new owner in subsequent phases. + * These are value class methods, which will become extension methods. + * (By-name arguments used to be included also, but these + * don't get a new class anymore, they are just wrapped in a new method). + * + * These regions will have to be treated specially for the purpose + * of adding accessors. For instance, super calls from these regions + * always have to go through an accessor. + * + * The `invalidOwner` field, if different from NoSymbol, + * contains the symbol that is not a valid owner. */ - private var validCurrentOwner = true - - private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]() + private var invalidEnclClass: Symbol = NoSymbol - private def storeAccessorDefinition(clazz: Symbol, tree: Tree) = { - val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for " + clazz)) - buf += tree + private def withInvalidCurrentClass[A](trans: => A)(implicit ctx: Context): A = { + val saved = invalidEnclClass + invalidEnclClass = ctx.owner + try trans + finally invalidEnclClass = saved } - /** Turn types which are not methodic into ExprTypes. */ - private def ensureMethodic(tpe: Type)(implicit ctx: Context) = tpe match { - case tpe: MethodicType => tpe - case _ => ExprType(tpe) - } + private def validCurrentClass(implicit ctx: Context): Boolean = + ctx.owner.enclosingClass != invalidEnclClass - private def ensureAccessor(sel: Select)(implicit ctx: Context) = { + /** List buffers for new accessor definitions, indexed by class */ + private val accDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() + + /** A super accessor call corresponding to `sel` */ + private def superAccessorCall(sel: Select)(implicit ctx: Context) = { val Select(qual, name) = sel - val sym = sel.symbol - val clazz = qual.symbol.asClass - val supername = name.superName + 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 maybeDeferred = if (clazz is Trait) Deferred else EmptyFlags val acc = ctx.newSymbol( clazz, supername, SuperAccessor | Private | Artifact | Method | maybeDeferred, - ensureMethodic(sel.tpe.widenSingleton), coord = sym.coord).enteredAfter(thisTransformer) + sel.tpe.widenSingleton.ensureMethodic, 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)) + else accDefs(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 + /** Check selection `super.f` for conforming to rules. If necessary, + * replace by a super accessor call. */ - 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.openForMutations.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) { + if ((sym.isTerm) && !(sym is Method) || (sym is Accessor)) + ctx.error(s"super may be not be used on ${sym.underlyingSymbol}", sel.pos) + else if (isDisallowed(sym)) + ctx.error(s"super not allowed here: use this.${sel.name.decode} instead", sel.pos) + else if (sym is Deferred) { val member = sym.overridingSymbol(clazz) if (mix != tpnme.EMPTY || !member.exists || @@ -192,13 +122,14 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this } if (name.isTermName && mix == tpnme.EMPTY && - ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentOwner)) - ensureAccessor(sel)(ctx.withPhase(thisTransformer.next)) + ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass)) + superAccessorCall(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. + /** 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._ @@ -209,228 +140,81 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this (sym eq Any_##) } - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - val sym = tree.symbol + /** Replace `sel` (or `sel[targs]` if `targs` is nonempty) with a protected accessor + * call, if necessary. + */ + private def ensureProtectedAccessOK(sel: Select, targs: List[Tree])(implicit ctx: Context) = { + val sym = sel.symbol + if (sym.exists && needsProtectedAccessor(sym, sel.pos)) { + ctx.debuglog("Adding protected accessor for " + sel) + protectedAccessorCall(sel, targs) + } else sel + } - 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)(ctx.withPhase(thisTransformer.next)) - else tree - - try tree match { - // Don't transform patterns or strange trees will reach the matcher (ticket #4062) - // TODO Query `ctx.mode is Pattern` instead. - 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, superAcc.ensureConforms(sym.info.widen)) - } - return forwarder(ctx.withPhase(thisTransformer.next)) - } - } - } - case _ => - } - stat - } - stats map forwardParamAccessor - } - - def transformTemplate = { - val ownStats = new ListBuffer[Tree] - accDefs(currentClass) = ownStats - // write super accessors after parameters and type aliases (so - // that order is stable under pickling/unpickling) - val (params, rest) = impl.body span { - case td: TypeDef => !td.isClassDef - case vd: ValOrDefDef => vd.symbol.flags is ParamAccessor - case _ => false - } - ownStats ++= params - val rest1 = forwardParamAccessors(transformStats(rest, tree.symbol)) - accDefs -= currentClass - ownStats ++= rest1 - cpy.Template(impl)(body = ownStats.toList) - } - 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.underlyingSymbol}", 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 tree: DefDef => - cpy.DefDef(tree)( - rhs = if (isMethodWithExtension(sym)) withInvalidOwner(transform(tree.rhs)) else transform(tree.rhs)) - - 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 - ctx.atPhase(thisTransformer.next) { implicit ctx => - cpy.Apply(tree)(transform(fn), transformArgs(formals, args)) - } + /** Add a protected accessor, if needed, and return a tree that calls + * the accessor and returns the same member. The result is already + * typed. + */ + private def protectedAccessorCall(sel: Select, targs: List[Tree])(implicit ctx: Context): Tree = { + val Select(qual, _) = sel + val sym = sel.symbol.asTerm + val clazz = hostForAccessorOf(sym, currentClass) + assert(clazz.exists, sym) + ctx.debuglog("Decided for host class: " + clazz) - case _ => - super.transform(tree) + val accName = sym.name.protectedAccessorName + + def isThisType(tpe: Type): Boolean = tpe match { + case tpe: ThisType => !tpe.cls.is(PackageClass) + case tpe: TypeProxy => isThisType(tpe.underlying) + case _ => false } - catch { - case ex : AssertionError => - if (sym != null && sym != NoSymbol) - Console.println("TRANSFORM: " + tree.symbol.sourceFile) - Console.println("TREE: " + tree) - throw ex + // 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 = sel.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) + accDefs(clazz) += code + newAcc } + val res = This(clazz) + .select(protectedAccessor) + .appliedToTypeTrees(targs) + .appliedTo(qual) + .withPos(sel.pos) + ctx.debuglog(s"Replaced $sel with $res") + res } - private def withInvalidOwner[A](trans: => A): A = { - val saved = validCurrentOwner - validCurrentOwner = false - try trans - finally validCurrentOwner = saved + def isProtectedAccessor(tree: Tree)(implicit ctx: Context): Boolean = tree match { + case Apply(TypeApply(Select(_, name), _), qual :: Nil) => name.isProtectedAccessorName + case _ => false } /** 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 = { + private def protectedAccessor(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { val Select(qual, _) = tree val sym = tree.symbol.asTerm val clazz = hostForAccessorOf(sym, currentClass) @@ -441,16 +225,16 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this // 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 receiverType = + if (isThisType(sym.info.finalResultType)) clazz.thisType + else clazz.classInfo.selfType + 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))) } + val accType = 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) @@ -460,7 +244,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this (base /: vrefss)(Apply(_, _)) }) ctx.debuglog("created protected accessor: " + code) - storeAccessorDefinition(clazz, code) + accDefs(clazz) += code newAcc } val res = This(clazz) @@ -475,7 +259,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this /** 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 = { + private def protectedSetter(tree: Select)(implicit ctx: Context): Tree = { val field = tree.symbol.asTerm val clazz = hostForAccessorOf(field, currentClass) assert(clazz.exists, field) @@ -491,7 +275,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this Assign(receiver.select(field), value).withPos(tree.pos) }) ctx.debuglog("created protected setter: " + code) - storeAccessorDefinition(clazz, code) + accDefs(clazz) += code newAcc } This(clazz).select(protectedAccessor).withPos(tree.pos) @@ -516,7 +300,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val host = hostForAccessorOf(sym, clazz) val selfType = host.classInfo.selfType def accessibleThroughSubclassing = - validCurrentOwner && (selfType <:< sym.owner.typeRef) && !clazz.is(Trait) + validCurrentClass && (selfType <:< sym.owner.typeRef) && !clazz.is(Trait) val isCandidate = ( sym.is(Protected) @@ -559,11 +343,85 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this else referencingClass - /** Is 'tpe' the type of a member of an enclosing class? */ + /** Is 'tpe' a ThisType, or a type proxy with a ThisType as transitively underlying type? */ private def isThisType(tpe: Type)(implicit ctx: Context): Boolean = tpe match { case tpe: ThisType => !tpe.cls.is(PackageClass) case tpe: TypeProxy => isThisType(tpe.underlying) case _ => false } - } + + /** Transform select node, adding super and protected accessors as needed */ + def transformSelect(tree: Tree, targs: List[Tree])(implicit ctx: Context) = { + val sel @ Select(qual, name) = tree + val sym = sel.symbol + qual match { + case _: This => + /* + * 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 (from scalac's SuperAccessors) + // - 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, sel.pos)) + if (shouldEnsureAccessor) { + ctx.log("Ensuring accessor for call to protected " + sym.showLocated + " from " + currentClass) + superAccessorCall(sel) + } else + ensureProtectedAccessOK(sel, targs) + + case Super(_, mix) => + transformSuperSelect(sel) + + case _ => + ensureProtectedAccessOK(sel, targs) + } + } + + /** Transform assignment, adding a protected setter if needed */ + def transformAssign(tree: Tree)(implicit ctx: Context) = { + val Assign(lhs @ Select(qual, name), rhs) = tree + if ((lhs.symbol is Mutable) && + (lhs.symbol is JavaDefined) && + needsProtectedAccessor(lhs.symbol, tree.pos)) { + ctx.debuglog("Adding protected setter for " + tree) + val setter = protectedSetter(lhs) + ctx.debuglog("Replaced " + tree + " with " + setter) + setter.appliedTo(qual, rhs) + } + else tree + } + + /** Wrap template to template transform `op` with needed initialization and finalization */ + def wrapTemplate(tree: Template)(op: Template => Template)(implicit ctx: Context) = { + accDefs(currentClass) = new mutable.ListBuffer[Tree] + val impl = op(tree) + val accessors = accDefs.remove(currentClass).get + if (accessors.isEmpty) impl + else { + val (params, rest) = impl.body span { + case td: TypeDef => !td.isClassDef + case vd: ValOrDefDef => vd.symbol.flags is ParamAccessor + case _ => false + } + cpy.Template(impl)(body = params ++ accessors ++ rest) + } + } + + /** Wrap `DefDef` producing operation `op`, potentially setting `invalidClass` info */ + def wrapDefDef(ddef: DefDef)(op: => DefDef)(implicit ctx: Context) = + if (isMethodWithExtension(ddef.symbol)) withInvalidCurrentClass(op) else op } diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 4726105c6..9d0aebe45 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -31,19 +31,20 @@ import scala.language.postfixOps * def equals(other: Any): Boolean * def hashCode(): Int */ -class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer { thisTransformer => +class SyntheticMethods(thisTransformer: DenotTransformer) { import ast.tpd._ - override def phaseName = "synthetics" + private var myValueSymbols: List[Symbol] = Nil + private var myCaseSymbols: List[Symbol] = Nil - private var valueSymbols: List[Symbol] = _ - private var caseSymbols: List[Symbol] = _ + private def initSymbols(implicit ctx: Context) = + if (myValueSymbols.isEmpty) { + myValueSymbols = List(defn.Any_hashCode, defn.Any_equals) + myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, defn.Product_productArity) + } - override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - valueSymbols = List(defn.Any_hashCode, defn.Any_equals) - caseSymbols = valueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, defn.Product_productArity) - this - } + def valueSymbols(implicit ctx: Context) = { initSymbols; myValueSymbols } + def caseSymbols(implicit ctx: Context) = { initSymbols; myCaseSymbols } /** The synthetic methods of the case or value class `clazz`. */ @@ -185,10 +186,9 @@ class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer symbolsToSynthesize flatMap syntheticDefIfMissing } - override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = + def addSyntheticMethods(impl: Template)(implicit ctx: Context) = if (ctx.owner.is(Case) || isDerivedValueClass(ctx.owner)) - cpy.Template(impl)( - body = impl.body ++ syntheticMethods(ctx.owner.asClass)) + cpy.Template(impl)(body = impl.body ++ syntheticMethods(ctx.owner.asClass)) else impl } diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala index e510fcc88..c01b6478c 100644 --- a/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -26,4 +26,9 @@ class TypeUtils(val self: Type) extends AnyVal { def isPrimitiveValueType(implicit ctx: Context): Boolean = self.classSymbol.isPrimitiveValueClass + + def ensureMethodic(implicit ctx: Context): Type = self match { + case self: MethodicType => self + case _ => ExprType(self) + } } diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index b28afa6f2..148e31885 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -39,6 +39,23 @@ object Checking { d"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}", arg.pos) + /** Check that `tp` refers to a nonAbstract class + * and that the instance conforms to the self type of the created class. + */ + def checkInstantiable(tp: Type, pos: Position)(implicit ctx: Context): Unit = + tp.underlyingClassRef(refinementOK = false) match { + case tref: TypeRef => + val cls = tref.symbol + if (cls.is(AbstractOrTrait)) + ctx.error(d"$cls is abstract; cannot be instantiated", pos) + if (!cls.is(Module)) { + val selfType = tp.givenSelfType.asSeenFrom(tref.prefix, cls.owner) + if (selfType.exists && !(tp <:< selfType)) + ctx.error(d"$tp does not conform to its self type $selfType; cannot be instantiated") + } + case _ => + } + /** A type map which checks that the only cycles in a type are F-bounds * and that protects all F-bounded references by LazyRefs. */ diff --git a/src/dotty/tools/dotc/typer/InstChecks.scala b/src/dotty/tools/dotc/typer/InstChecks.scala deleted file mode 100644 index 7148a6e68..000000000 --- a/src/dotty/tools/dotc/typer/InstChecks.scala +++ /dev/null @@ -1,90 +0,0 @@ -package dotty.tools.dotc -package typer - -import core._ -import Contexts.Context -import Decorators._ -import Phases._ -import Types._, Symbols._, Flags._, StdNames._ -import util.Positions._ -import ast.Trees._ -import typer.ErrorReporting._ -import DenotTransformers._ - -/** This checks `New` nodes, verifying that they can be instantiated. */ -class InstChecks extends Phase with IdentityDenotTransformer { - import ast.tpd._ - - override def phaseName: String = "instchecks" - - override def run(implicit ctx: Context): Unit = - instCheck.traverse(ctx.compilationUnit.tpdTree) - - /** Check that `tp` refers to a nonAbstract class - * and that the instance conforms to the self type of the created class. - */ - def checkInstantiatable(tp: Type, pos: Position)(implicit ctx: Context): Unit = - tp.underlyingClassRef(refinementOK = false) match { - case tref: TypeRef => - val cls = tref.symbol - if (cls.is(AbstractOrTrait)) - ctx.error(d"$cls is abstract; cannot be instantiated", pos) - if (!cls.is(Module)) { - val selfType = tp.givenSelfType.asSeenFrom(tref.prefix, cls.owner) - if (selfType.exists && !(tp <:< selfType)) - ctx.error(d"$tp does not conform to its self type $selfType; cannot be instantiated") - } - case _ => - } - - def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = { - // TODO fill in - } - - val instCheck = new TreeTraverser { - - def checkAnnot(annot: Tree)(implicit ctx: Context): Unit = - if (annot.symbol.is(JavaDefined)) checkValidJavaAnnotation(annot) - else traverse(annot) - - def traverseNoCheck(tree: Tree)(implicit ctx: Context): Unit = tree match { - case Apply(fn, args) => - traverseNoCheck(fn) - args.foreach(traverse) - case TypeApply(fn, args) => - traverseNoCheck(fn) - args.foreach(traverse) - case Select(qual, nme.CONSTRUCTOR) => - traverseNoCheck(qual) - case New(tpt) => - traverse(tpt) - case _ => - traverse(tree) - } - - def traverse(tree: Tree)(implicit ctx: Context): Unit = tree match { - case tree: New => - checkInstantiatable(tree.tpe, tree.pos) - traverseChildren(tree) - case impl @ Template(constr, parents, self, _) => - traverse(constr) - parents.foreach(traverseNoCheck) - traverse(self) - impl.body.foreach(traverse) - case Annotated(annot, tree) => - checkAnnot(annot) - traverse(tree) - case TypeTree(original) => - tree.tpe match { - case AnnotatedType(annot, tpe) => checkAnnot(annot.tree) - case _ => - } - traverse(original) - case tree: MemberDef => - tree.symbol.annotations.foreach(annot => checkAnnot(annot.tree)) - traverseChildren(tree) - case _ => - traverseChildren(tree) - } - } -} diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 6a1f3652b..93cd412f2 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -82,6 +82,18 @@ object RefChecks { case _ => } + /** Check that a class and its companion object to not both define + * a class or module with same name + */ + private def checkCompanionNameClashes(cls: Symbol)(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) + } + // Override checking ------------------------------------------------------------ /** 1. Check all members of class `clazz` for overriding conditions. @@ -690,6 +702,7 @@ import RefChecks._ * - any value classes conform to rules laid down by `checkAnyValSubClass`. * - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals). * - no forward reference in a local block jumps over a non-lazy val definition. + * - a class and its companion object do not both define a class or module with the same name. * * 2. It warns about references to symbols labeled deprecated or migration. @@ -782,6 +795,7 @@ class RefChecks extends MiniPhase with SymTransformer { thisTransformer => val cls = ctx.owner checkOverloadedRestrictions(cls) checkSelfType(cls) + checkCompanionNameClashes(cls) checkAllOverrides(cls) checkAnyValSubclass(cls) tree |