diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 52cf181a2240..f94558e438ee 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -1,10 +1,11 @@ package dotty.tools package dotc -import dotty.tools.dotc.core.Types.Type // Do not remove me #3383 +import core.Types.Type // Do not remove me #3383 import util.SourceFile import ast.{tpd, untpd} -import dotty.tools.dotc.ast.tpd.{ Tree, TreeTraverser } +import tpd.{ Tree, TreeTraverser } +import typer.Inliner.InlineAccessors import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.SymDenotations.ClassDenotation import dotty.tools.dotc.core.Symbols._ @@ -27,6 +28,9 @@ class CompilationUnit(val source: SourceFile) { * is used in phase ReifyQuotes in order to avoid traversing a quote-less tree. */ var containsQuotesOrSplices: Boolean = false + + /** A structure containing a temporary map for generating inline accessors */ + val inlineAccessors = new InlineAccessors } object CompilationUnit { diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index bf758ac77300..ffe221c02b63 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -275,7 +275,6 @@ object NameKinds { } /** Other unique names */ - val InlineAccessorName = new UniqueNameKind("$_inlineAccessor_$") val TempResultName = new UniqueNameKind("ev$") val EvidenceParamName = new UniqueNameKind("evidence$") val DepParamName = new UniqueNameKind("(param)") @@ -356,6 +355,9 @@ object NameKinds { val InitializerName = new PrefixNameKind(INITIALIZER, "initial$") val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$") val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility + val InlineGetterName = new PrefixNameKind(INLINEGETTER, "inline_get$") + val InlineSetterName = new PrefixNameKind(INLINESETTER, "inline_set$") + val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$") val DirectMethodName = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true } val FieldName = new SuffixNameKind(FIELD, "$$local") { diff --git a/compiler/src/dotty/tools/dotc/core/NameTags.scala b/compiler/src/dotty/tools/dotc/core/NameTags.scala index 11c890823100..fd65c5243fab 100644 --- a/compiler/src/dotty/tools/dotc/core/NameTags.scala +++ b/compiler/src/dotty/tools/dotc/core/NameTags.scala @@ -15,23 +15,23 @@ object NameTags extends TastyFormat.NameTags { // outer accessor that will be filled in by ExplicitOuter. // indicates the number of hops needed to select the outer field. - final val INITIALIZER = 24 // A mixin initializer method + final val INITIALIZER = 26 // A mixin initializer method - final val AVOIDCLASH = 25 // Adds a suffix to avoid a name clash; + final val AVOIDCLASH = 27 // Adds a suffix to avoid a name clash; // Used in FirstTransform for synthesized companion objects of classes // if they would clash with another value. - final val DIRECT = 26 // Used by ShortCutImplicits for the name of methods that + final val DIRECT = 28 // Used by ShortCutImplicits for the name of methods that // implement implicit function result types directly. - final val FIELD = 27 // Used by Memoize to tag the name of a class member field. + final val FIELD = 29 // Used by Memoize to tag the name of a class member field. - final val EXTMETH = 28 // Used by ExtensionMethods for the name of an extension method + final val EXTMETH = 30 // Used by ExtensionMethods for the name of an extension method // implementing a value class method. - final val ADAPTEDCLOSURE = 29 // Used in Erasure to adapt closures over primitive types. + final val ADAPTEDCLOSURE = 31 // Used in Erasure to adapt closures over primitive types. - final val IMPLMETH = 30 // Used to define methods in implementation classes + final val IMPLMETH = 32 // Used to define methods in implementation classes // (can probably be removed). def nameTagToString(tag: Int): String = tag match { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index c273b27bc0a6..962acf20b759 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -227,7 +227,7 @@ object TastyFormat { final val header = Array(0x5C, 0xA1, 0xAB, 0x1F) val MajorVersion = 7 - val MinorVersion = 0 + val MinorVersion = 1 /** Tags used to serialize names */ class NameTags { @@ -260,6 +260,10 @@ object TastyFormat { final val OBJECTCLASS = 23 // The name of an object class (or: module class) `$`. + final val INLINEGETTER = 24 // The name of an inline getter `inline_get$name` + + final val INLINESETTER = 25 // The name of an inline setter `inline_set$name` + final val SIGNED = 63 // A pair of a name and a signature, used to idenitfy // possibly overloaded methods. } diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala new file mode 100644 index 000000000000..46352f0143d2 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -0,0 +1,109 @@ +package dotty.tools.dotc +package transform + +import core._ +import Contexts.Context +import Symbols._ +import Flags._ +import Names._ +import Decorators._ +import TypeUtils._ +import Annotations.Annotation +import Types._ +import NameKinds.ClassifiedNameKind +import ast.Trees._ +import util.Property +import util.Positions.Position + +/** A utility class for generating access proxies. Currently used for + * inline accessors and protected accessors. + */ +abstract class AccessProxies { + import ast.tpd._ + + def getterName: ClassifiedNameKind + def setterName: ClassifiedNameKind + + /** accessor -> accessed */ + private val accessedBy = newMutableSymbolMap[Symbol] + + /** The accessor definitions that need to be added to class `cls` + * As a side-effect, this method removes entries from the `accessedBy` map. + * So a second call of the same method will yield the empty list. + */ + private def accessorDefs(cls: Symbol)(implicit ctx: Context): Iterator[DefDef] = + for (accessor <- cls.info.decls.iterator; accessed <- accessedBy.remove(accessor)) yield + polyDefDef(accessor.asTerm, tps => argss => { + val accessRef = ref(TermRef(cls.thisType, accessed)) + val rhs = + if (accessor.name.is(setterName) && + argss.nonEmpty && argss.head.nonEmpty) // defensive conditions + accessRef.becomes(argss.head.head) + else + accessRef.appliedToTypes(tps).appliedToArgss(argss) + rhs.withPos(accessed.pos) + }) + + /** Add all needed accessors to the `body` of class `cls` */ + def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] = { + val accDefs = accessorDefs(cls) + if (accDefs.isEmpty) body else body ++ accDefs + } + + trait Insert { + import ast.tpd._ + + def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean + + /** A fresh accessor symbol */ + def newAccessorSymbol(accessed: Symbol, name: TermName, info: Type)(implicit ctx: Context): TermSymbol = + ctx.newSymbol(accessed.owner.enclosingSubClass, name, Synthetic | Method, + info, coord = accessed.pos).entered + + /** Create an accessor unless one exists already, and replace the original + * access with a reference to the accessor. + * + * @param reference The original reference to the non-public symbol + * @param onLHS The reference is on the left-hand side of an assignment + */ + def useAccessor(reference: RefTree, onLHS: Boolean)(implicit ctx: Context): Tree = { + + def nameKind = if (onLHS) setterName else getterName + val accessed = reference.symbol.asTerm + + def refersToAccessed(sym: Symbol) = accessedBy.get(sym) == Some(accessed) + + val accessorInfo = + if (onLHS) MethodType(accessed.info :: Nil, defn.UnitType) + else accessed.info.ensureMethodic + val accessorName = nameKind(accessed.name) + val accessorSymbol = + accessed.owner.info.decl(accessorName).suchThat(refersToAccessed).symbol + .orElse { + val acc = newAccessorSymbol(accessed, accessorName, accessorInfo) + accessedBy(acc) = accessed + acc + } + + { reference match { + case Select(qual, _) => qual.select(accessorSymbol) + case Ident(name) => ref(accessorSymbol) + } + }.withPos(reference.pos) + } + + /** Replace tree with a reference to an accessor if needed */ + def accessorIfNeeded(tree: Tree)(implicit ctx: Context): Tree = tree match { + case tree: RefTree if needsAccessor(tree.symbol) => + if (tree.symbol.isConstructor) { + ctx.error("Implementation restriction: cannot use private constructors in inline methods", tree.pos) + tree // TODO: create a proper accessor for the private constructor + } + else useAccessor(tree, onLHS = false) + case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) => + cpy.Apply(tree)(useAccessor(lhs, onLHS = true), List(rhs)) + case _ => + tree + } + } +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index d0f7daa2cadc..48080e8718a8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -20,6 +20,7 @@ import typer.ProtoTypes._ import typer.ErrorReporting._ import core.TypeErasure._ import core.Decorators._ +import core.NameKinds._ import dotty.tools.dotc.ast.{Trees, tpd, untpd} import ast.Trees._ import scala.collection.mutable.ListBuffer @@ -30,7 +31,6 @@ import ExplicitOuter._ import core.Mode import reporting.trace - class Erasure extends Phase with DenotTransformer { override def phaseName: String = Erasure.name @@ -315,6 +315,10 @@ object Erasure { } } + /** The erasure typer. + * Also inserts protected accessors where needed. This logic is placed here + * since it is most naturally done in a macro transform. + */ class Typer extends typer.ReTyper with NoChecking { import Boxing._ @@ -323,6 +327,23 @@ object Erasure { if (tree.isTerm) erasedRef(tp) else valueErasure(tp) } + object ProtectedAccessors extends AccessProxies { + def getterName = ProtectedAccessorName + def setterName = ProtectedSetterName + + val insert = new Insert { + def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean = + false && + sym.isTerm && sym.is(Flags.Protected) && + ctx.owner.enclosingPackageClass != sym.enclosingPackageClass && + !ctx.owner.enclosingClass.derivesFrom(sym.owner) && + { println(i"need protected acc $sym accessed from ${ctx.owner}"); assert(false); false } + } + } + + override def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] = + ProtectedAccessors.addAccessorDefs(cls, body) + override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = { assert(tree.hasType) val erasedTp = erasedType(tree) @@ -357,6 +378,9 @@ object Erasure { else super.typedLiteral(tree) + override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = + ProtectedAccessors.insert.accessorIfNeeded(super.typedIdent(tree, pt)) + /** Type check select nodes, applying the following rewritings exhaustively * on selections `e.m`, where `OT` is the type of the owner of `m` and `ET` * is the erased type of the selection's original qualifier expression. @@ -437,9 +461,12 @@ object Erasure { } } - recur(typed(tree.qualifier, AnySelectionProto)) + ProtectedAccessors.insert.accessorIfNeeded(recur(typed(tree.qualifier, AnySelectionProto))) } + override def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree = + ProtectedAccessors.insert.accessorIfNeeded(super.typedAssign(tree, pt)) + override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree) else { diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 1cb9bf0936ae..eac354893f06 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -15,11 +15,11 @@ import StdNames.nme import Contexts.Context import Names.{Name, TermName, EmptyTermName} import NameOps._ -import NameKinds.InlineAccessorName +import NameKinds.{ClassifiedNameKind, InlineGetterName, InlineSetterName} import ProtoTypes.selectionProto import SymDenotations.SymDenotation import Annotations._ -import transform.ExplicitOuter +import transform.{ExplicitOuter, AccessProxies} import Inferencing.fullyDefinedType import config.Printers.inlining import ErrorReporting.errorTree @@ -31,169 +31,48 @@ import util.Positions.Position object Inliner { import tpd._ - /** Adds accessors for all non-public term members accessed - * from `tree`. Non-public type members are currently left as they are. - * This means that references to a private type will lead to typing failures - * on the code when it is inlined. Less than ideal, but hard to do better (see below). - * - * @return If there are accessors generated, a thicket consisting of the rewritten `tree` - * and all accessors, otherwise the original tree. - */ - private def makeInlineable(tree: Tree)(implicit ctx: Context) = { - - val inlineMethod = ctx.owner + class InlineAccessors extends AccessProxies { + def getterName = InlineGetterName + def setterName = InlineSetterName /** A tree map which inserts accessors for all non-public term members accessed - * from inlined code. Accessors are collected in the `accessors` buffer. - */ - object addAccessors extends TreeMap { - val accessors = new mutable.ListBuffer[MemberDef] + * from inlined code. Accessors are collected in the `accessors` buffer. + */ + class MakeInlineable(inlineSym: Symbol) extends TreeMap with Insert { /** A definition needs an accessor if it is private, protected, or qualified private - * and it is not part of the tree that gets inlined. The latter test is implemented - * by excluding all symbols properly contained in the inlined method. - */ + * and it is not part of the tree that gets inlined. The latter test is implemented + * by excluding all symbols properly contained in the inlined method. + */ def needsAccessor(sym: Symbol)(implicit ctx: Context) = sym.isTerm && (sym.is(AccessFlags) || sym.privateWithin.exists) && - !sym.owner.isContainedIn(inlineMethod) - - /** The name of the next accessor to be generated */ - def accessorName(implicit ctx: Context) = InlineAccessorName.fresh(inlineMethod.name.asTermName) - - /** A fresh accessor symbol. - * - * @param tree The tree representing the original access to the non-public member - * @param accessorInfo The type of the accessor - */ - def accessorSymbol(tree: Tree, accessorInfo: Type)(implicit ctx: Context): Symbol = - ctx.newSymbol( - owner = inlineMethod.owner, - name = if (tree.isTerm) accessorName.toTermName else accessorName.toTypeName, - flags = if (tree.isTerm) Synthetic | Method else Synthetic, - info = accessorInfo, - coord = tree.pos).entered - - /** Add an accessor to a non-public method and replace the original access with a - * call to the accessor. - * - * @param tree The original access to the non-public symbol - * @param refPart The part that refers to the method or field of the original access - * @param targs All type arguments passed in the access, if any - * @param argss All value arguments passed in the access, if any - * @param accessedType The type of the accessed method or field, as seen from the access site. - * @param rhs A function that builds the right-hand side of the accessor, - * given a reference to the accessed symbol and any type and - * value arguments the need to be integrated. - * @return The call to the accessor method that replaces the original access. - */ - def addAccessor(tree: Tree, refPart: Tree, targs: List[Tree], argss: List[List[Tree]], - accessedType: Type, rhs: (Tree, List[Type], List[List[Tree]]) => Tree)(implicit ctx: Context): Tree = { - val qual = qualifier(refPart) - def refIsLocal = qual match { - case qual: This => qual.symbol == refPart.symbol.owner - case _ => false - } - val (accessorDef, accessorRef) = - if (refPart.symbol.isStatic || refIsLocal) { - // Easy case: Reference to a static symbol or a symbol referenced via `this.` - val accessorType = accessedType.ensureMethodic - val accessor = accessorSymbol(tree, accessorType).asTerm - val accessorDef = polyDefDef(accessor, tps => argss => - rhs(refPart, tps, argss).withPos(tree.pos.focus)) - val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss).withPos(tree.pos) - (accessorDef, accessorRef) - } else { - // Hard case: Reference needs to go via a dynamic prefix - inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))") - - // Need to dealias in order to catch all possible references to abstracted over types in - // substitutions - val dealiasMap = new TypeMap { - def apply(t: Type) = mapOver(t.dealias) - } + !sym.isContainedIn(inlineSym) - val qualType = dealiasMap(qual.tpe.widen) - - // Add qualifier type as leading method argument to argument `tp` - def addQualType(tp: Type): Type = tp match { - case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType)) - case tp: ExprType => addQualType(tp.resultType) - case tp => MethodType(qualType :: Nil, tp) - } - - // The types that are local to the inlined method, and that therefore have - // to be abstracted out in the accessor, which is external to the inlined method - val localRefs = qualType.namedPartsWith(ref => - ref.isType && ref.symbol.isContainedIn(inlineMethod)).toList - - // Abstract accessed type over local refs - def abstractQualType(mtpe: Type): Type = - if (localRefs.isEmpty) mtpe - else PolyType.fromParams(localRefs.map(_.symbol.asType), mtpe) - .asInstanceOf[PolyType].flatten - - val accessorType = abstractQualType(addQualType(dealiasMap(accessedType))) - val accessor = accessorSymbol(tree, accessorType).asTerm - - val accessorDef = polyDefDef(accessor, tps => argss => - rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail) - .withPos(tree.pos.focus) - ) - - val accessorRef = ref(accessor) - .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs) - .appliedToArgss((qual :: Nil) :: argss) - .withPos(tree.pos) - (accessorDef, accessorRef) - } - accessors += accessorDef - inlining.println(i"added inline accessor: $accessorDef") - accessorRef - } - override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform { - tree match { - case _: Apply | _: TypeApply | _: RefTree if needsAccessor(tree.symbol) => - if (tree.isTerm) { - val (methPart, targs, argss) = decomposeCall(tree) - if (methPart.symbol.isConstructor && needsAccessor(methPart.symbol)) { - ctx.error("Cannot use private constructors in inline methods", tree.pos) - tree // TODO: create a proper accessor for the private constructor - } else { - addAccessor(tree, methPart, targs, argss, - accessedType = methPart.tpe.widen, - rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss)) - } - } else { - // TODO: Handle references to non-public types. - // This is quite tricky, as such types can appear anywhere, including as parts - // of types of other things. For the moment we do nothing and complain - // at the implicit expansion site if there's a reference to an inaccessible type. - // Draft code (incomplete): - // - // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType - // myAccessors += TypeDef(accessor).withPos(tree.pos.focus) - // ref(accessor).withPos(tree.pos) - // - tree - } - case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) => - addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil, - accessedType = MethodType(rhs.tpe.widen :: Nil, defn.UnitType), - rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head)) - case _ => tree - } - } + // TODO: Also handle references to non-public types. + // This is quite tricky, as such types can appear anywhere, including as parts + // of types of other things. For the moment we do nothing and complain + // at the implicit expansion site if there's a reference to an inaccessible type. + override def transform(tree: Tree)(implicit ctx: Context): Tree = + super.transform(accessorIfNeeded(tree)) } - if (inlineMethod.owner.isTerm) - // Inline methods in local scopes can only be called in the scope they are defined, - // so no accessors are needed for them. - tree - else { - val tree1 = addAccessors.transform(tree) - flatTree(tree1 :: addAccessors.accessors.toList) + /** Adds accessors for all non-public term members accessed + * from `tree`. Non-public type members are currently left as they are. + * This means that references to a private type will lead to typing failures + * on the code when it is inlined. Less than ideal, but hard to do better (see below). + * + * @return If there are accessors generated, a thicket consisting of the rewritten `tree` + * and all accessors, otherwise the original tree. + */ + def makeInlineable(tree: Tree)(implicit ctx: Context) = { + val inlineSym = ctx.owner + if (inlineSym.owner.isTerm) + // Inline methods in local scopes can only be called in the scope they are defined, + // so no accessors are needed for them. + tree + else new MakeInlineable(inlineSym).transform(tree) } } @@ -216,7 +95,7 @@ object Inliner { sym.updateAnnotation(LazyBodyAnnotation { _ => implicit val ctx = inlineCtx val body = treeExpr(ctx) - if (ctx.reporter.hasErrors) body else makeInlineable(body) + if (ctx.reporter.hasErrors) body else ctx.compilationUnit.inlineAccessors.makeInlineable(body) }) } } @@ -228,27 +107,11 @@ object Inliner { def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean = sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot) - private def bodyAndAccessors(sym: SymDenotation)(implicit ctx: Context): (Tree, List[MemberDef]) = - sym.unforcedAnnotation(defn.BodyAnnot).get.tree match { - case Thicket(body :: accessors) => (body, accessors.asInstanceOf[List[MemberDef]]) - case body => (body, Nil) - } - /** The body to inline for method `sym`. * @pre hasBodyToInline(sym) */ def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree = - bodyAndAccessors(sym)._1 - - /** The accessors to non-public members needed by the inlinable body of `sym`. - * These accessors are dropped as a side effect of calling this method. - * @pre hasBodyToInline(sym) - */ - def removeInlineAccessors(sym: SymDenotation)(implicit ctx: Context): List[MemberDef] = { - val (body, accessors) = bodyAndAccessors(sym) - if (accessors.nonEmpty) sym.updateAnnotation(ConcreteBodyAnnotation(body)) - accessors - } + sym.unforcedAnnotation(defn.BodyAnnot).get.tree /** Try to inline a call to a `@inline` method. Fail with error if the maximal * inline depth is exceeded. diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 689464d6cc0c..3a330209a352 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -122,7 +122,7 @@ class ReTyper extends Typer with ReChecking { override def inferView(from: Tree, to: Type)(implicit ctx: Context): Implicits.SearchResult = Implicits.NoMatchingImplicitsFailure override def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit = () - override def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] = mdef :: Nil - + override def inlineExpansion(mdef: DefDef)(implicit ctx: Context): Tree = mdef + override protected def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] = body override protected def checkEqualityEvidence(tree: tpd.Tree, pt: Type)(implicit ctx: Context): Unit = () } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 24ed19112fb6..ef714f516ccb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -25,7 +25,6 @@ import ErrorReporting._ import Checking._ import Inferencing._ import EtaExpansion.etaExpand -import dotty.tools.dotc.transform.Erasure.Boxing import util.Positions._ import util.common._ import util.{Property, SourcePosition} @@ -1523,7 +1522,8 @@ class Typer extends Namer cdef.withType(UnspecifiedErrorType) } else { val dummy = localDummy(cls, impl) - val body1 = typedStats(impl.body, dummy)(ctx.inClassContext(self1.symbol)) + val body1 = addAccessorDefs(cls, + typedStats(impl.body, dummy)(ctx.inClassContext(self1.symbol))) if (!ctx.isAfterTyper) cls.setNoInitsFlags((NoInitsInterface /: body1) ((fs, stat) => fs & defKind(stat))) @@ -1561,6 +1561,9 @@ class Typer extends Namer } } + protected def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] = + ctx.compilationUnit.inlineAccessors.addAccessorDefs(cls, body) + /** Ensure that the first type in a list of parent types Ps points to a non-trait class. * If that's not already the case, add one. The added class type CT is determined as follows. * First, let C be the unique class such that @@ -1882,7 +1885,7 @@ class Typer extends Namer case none => typed(mdef) match { case mdef1: DefDef if Inliner.hasBodyToInline(mdef1.symbol) => - buf ++= inlineExpansion(mdef1) + buf += inlineExpansion(mdef1) case mdef1 => import untpd.modsDeco mdef match { @@ -1913,12 +1916,11 @@ class Typer extends Namer } /** Given an inline method `mdef`, the method rewritten so that its body - * uses accessors to access non-public members, followed by the accessor definitions. + * uses accessors to access non-public members. * Overwritten in Retyper to return `mdef` unchanged. */ - protected def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] = - tpd.cpy.DefDef(mdef)(rhs = Inliner.bodyToInline(mdef.symbol)) :: - Inliner.removeInlineAccessors(mdef.symbol) + protected def inlineExpansion(mdef: DefDef)(implicit ctx: Context): Tree = + tpd.cpy.DefDef(mdef)(rhs = Inliner.bodyToInline(mdef.symbol)) def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = typed(tree, pt)(ctx retractMode Mode.PatternOrTypeBits) diff --git a/tests/pos/i4322.scala b/tests/pos/i4322.scala new file mode 100644 index 000000000000..70039b7a5e17 --- /dev/null +++ b/tests/pos/i4322.scala @@ -0,0 +1,14 @@ +object Foo { + private[this] var x: Int = 1 + + private def x(n: String): Int = n.toInt + + inline def foo: Int = x + x + x("22") + + inline def bar = { + x += 1 + x += 1 + } + + inline def baz = { x += x("11") } +} diff --git a/tests/run/inlineProtected.scala b/tests/run/inlineProtected.scala new file mode 100644 index 000000000000..1a725ec27346 --- /dev/null +++ b/tests/run/inlineProtected.scala @@ -0,0 +1,22 @@ +package P { + class C { + protected def f(): Int = 22 + } +} + +package Q { + class D extends P.C { + class Inner { + inline def g() = f() + } + } +} + +object Test extends App { + import Q._ + + val d = new D + val i = new d.Inner + val x = i.g() + assert(x == 22) +}