diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index b817131c0ce6..69c415ff7e66 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -551,6 +551,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => /** An extractor for def of a closure contained the block of the closure. */ object closureDef { def unapply(tree: Tree): Option[DefDef] = tree match { + case Block(Nil, expr) => unapply(expr) case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) => Some(meth) case _ => None diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 5d68de1bb284..80e4365a943c 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -132,6 +132,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Inline() extends Mod(Flags.Inline) + case class Transparent() extends Mod(Flags.Transparent) + case class Enum() extends Mod(Flags.Enum) } diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index ad052f84c7fb..2fc48608dfe6 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -363,7 +363,10 @@ object Flags { /** Symbol is a Java default method */ final val DefaultMethod = termFlag(38, "") - /** Symbol is a Java enum */ + /** Labelled with `transparent` modifier */ + final val Transparent = termFlag(39, "transparent") + + /** Symbol is an enum class or enum case (if used with case) */ final val Enum = commonFlag(40, "") /** Labeled with `erased` modifier (erased value) */ @@ -436,7 +439,7 @@ object Flags { /** Flags representing source modifiers */ final val SourceModifierFlags = - commonFlags(Private, Protected, Abstract, Final, Inline, + commonFlags(Private, Protected, Abstract, Final, Inline, Transparent, Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic, Erased) /** Flags representing modifiers that can appear in trees */ @@ -457,7 +460,7 @@ object Flags { Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags | NonMember | ImplicitCommon | Permanent | Synthetic | - SuperAccessorOrScala2x | Inline + SuperAccessorOrScala2x | Inline | Transparent.toCommonFlags /** Flags that are not (re)set when completing the denotation, or, if symbol is * a top-level class or object, when completing the denotation once the class @@ -513,7 +516,7 @@ object Flags { /** Flags that can apply to a module val */ final val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags | Override | Final | Method | Implicit | Lazy | - Accessor | AbsOverride | Stable | Captured | Synchronized | Inline | Erased + Accessor | AbsOverride | Stable | Captured | Synchronized | Erased /** Flags that can apply to a module class */ final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | ImplClass | Enum @@ -548,8 +551,8 @@ object Flags { /** Either method or lazy or deferred */ final val MethodOrLazyOrDeferred = Method | Lazy | Deferred - /** Labeled `private`, `final`, or `inline` */ - final val PrivateOrFinalOrInline = Private | Final | Inline + /** Labeled `private`, `final`, `inline`, or `transparent` */ + final val EffectivelyFinal = Private | Final | Inline | Transparent.toCommonFlags /** A private method */ final val PrivateMethod = allOf(Private, Method) @@ -560,6 +563,9 @@ object Flags { /** An inline method */ final val InlineMethod = allOf(Inline, Method) + /** An transparent method */ + final val TransparentMethod = allOf(Transparent, Method) + /** An inline parameter */ final val InlineParam = allOf(Inline, Param) @@ -575,6 +581,9 @@ object Flags { /** An accessor or label */ final val AccessorOrLabel = Accessor | Label + /** An accessor or a synthetic symbol */ + final val AccessorOrSynthetic = Accessor | Synthetic + /** A synthetic or private definition */ final val SyntheticOrPrivate = Synthetic | Private @@ -584,8 +593,8 @@ object Flags { /** A deferred type member or parameter (these don't have right hand sides) */ final val DeferredOrTypeParam = Deferred | TypeParam - /** value that's final or inline */ - final val FinalOrInline = Final | Inline + /** value that's final, inline, or transparent */ + final val FinalOrInlineOrTransparent = Final | Inline | Transparent.toCommonFlags /** A covariant type parameter instance */ final val LocalCovariant = allOf(Local, Covariant) diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 6689a5371244..c97e20a88d49 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -93,5 +93,4 @@ object Mode { /** We are in the IDE */ val Interactive = newMode(20, "Interactive") - } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 7d2ecdf21436..a8a53da9ffa5 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -779,7 +779,13 @@ object SymDenotations { def isSkolem: Boolean = name == nme.SKOLEM - def isInlineMethod(implicit ctx: Context): Boolean = is(InlineMethod, butNot = Accessor) + def isInlinedMethod(implicit ctx: Context): Boolean = + is(InlineMethod, butNot = Accessor) + + def isTransparentMethod(implicit ctx: Context): Boolean = + is(TransparentMethod, butNot = Accessor) + + def isInlineableMethod(implicit ctx: Context) = isInlinedMethod || isTransparentMethod /** ()T and => T types should be treated as equivalent for this symbol. * Note: For the moment, we treat Scala-2 compiled symbols as loose matching, @@ -905,7 +911,7 @@ object SymDenotations { /** A symbol is effectively final if it cannot be overridden in a subclass */ final def isEffectivelyFinal(implicit ctx: Context): Boolean = - is(PrivateOrFinalOrInline) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass + is(EffectivelyFinal) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass /** The class containing this denotation which has the given effective name. */ final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 751304b31c4c..1d659828abfe 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -179,7 +179,8 @@ Standard-Section: "ASTs" TopLevelStat* ERASED LAZY OVERRIDE - INLINE // inline method + INLINE + TRANSPARENT MACRO // inline method containing toplevel splices STATIC // mapped to static Java member OBJECT // an object or its class @@ -280,25 +281,26 @@ object TastyFormat { final val LAZY = 14 final val OVERRIDE = 15 final val INLINE = 16 - final val STATIC = 17 - final val OBJECT = 18 - final val TRAIT = 19 - final val ENUM = 20 - final val LOCAL = 21 - final val SYNTHETIC = 22 - final val ARTIFACT = 23 - final val MUTABLE = 24 - final val LABEL = 25 - final val FIELDaccessor = 26 - final val CASEaccessor = 27 - final val COVARIANT = 28 - final val CONTRAVARIANT = 29 - final val SCALA2X = 30 - final val DEFAULTparameterized = 31 - final val STABLE = 32 - final val MACRO = 33 - final val ERASED = 34 - final val PARAMsetter = 35 + final val TRANSPARENT = 17 + final val STATIC = 18 + final val OBJECT = 19 + final val TRAIT = 20 + final val ENUM = 21 + final val LOCAL = 22 + final val SYNTHETIC = 23 + final val ARTIFACT = 24 + final val MUTABLE = 25 + final val LABEL = 26 + final val FIELDaccessor = 27 + final val CASEaccessor = 28 + final val COVARIANT = 29 + final val CONTRAVARIANT = 30 + final val SCALA2X = 31 + final val DEFAULTparameterized = 32 + final val STABLE = 33 + final val MACRO = 34 + final val ERASED = 35 + final val PARAMsetter = 36 // Cat. 2: tag Nat @@ -446,6 +448,7 @@ object TastyFormat { | LAZY | OVERRIDE | INLINE + | TRANSPARENT | MACRO | STATIC | OBJECT @@ -502,6 +505,7 @@ object TastyFormat { case LAZY => "LAZY" case OVERRIDE => "OVERRIDE" case INLINE => "INLINE" + case TRANSPARENT => "TRANSPARENT" case MACRO => "MACRO" case STATIC => "STATIC" case OBJECT => "OBJECT" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 32816fcbc98f..df0ebfd89b01 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -589,6 +589,7 @@ class TreePickler(pickler: TastyPickler) { if (flags is Case) writeByte(CASE) if (flags is Override) writeByte(OVERRIDE) if (flags is Inline) writeByte(INLINE) + if (flags is Transparent) writeByte(TRANSPARENT) if (flags is Macro) writeByte(MACRO) if (flags is JavaStatic) writeByte(STATIC) if (flags is Module) writeByte(OBJECT) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index c77006510a06..ce955cee96c3 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -513,7 +513,7 @@ class TreeUnpickler(reader: TastyReader, val rhsStart = currentAddr val rhsIsEmpty = nothingButMods(end) if (!rhsIsEmpty) skipTree() - val (givenFlags, annots, privateWithin) = readModifiers(end) + val (givenFlags, annotFns, privateWithin) = readModifiers(end) pickling.println(i"creating symbol $name at $start with flags $givenFlags") val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty) def adjustIfModule(completer: LazyType) = @@ -532,13 +532,12 @@ class TreeUnpickler(reader: TastyReader, rootd.symbol case _ => val completer = adjustIfModule(new Completer(ctx.owner, subReader(start, end))) - if (isClass) ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, privateWithin, coord) else ctx.newSymbol(ctx.owner, name, flags, completer, privateWithin, coord) } - sym.annotations = annots + sym.annotations = annotFns.map(_(sym)) ctx.owner match { case cls: ClassSymbol => cls.enter(sym) case _ => @@ -548,7 +547,7 @@ class TreeUnpickler(reader: TastyReader, sym.completer.withDecls(newScope) forkAt(templateStart).indexTemplateParams()(localContext(sym)) } - else if (sym.isInlineMethod) + else if (sym.isInlineableMethod) sym.addAnnotation(LazyBodyAnnotation { ctx0 => implicit val ctx: Context = localContext(sym)(ctx0).addMode(Mode.ReadPositions) // avoids space leaks by not capturing the current context @@ -561,9 +560,9 @@ class TreeUnpickler(reader: TastyReader, /** Read modifier list into triplet of flags, annotations and a privateWithin * boundary symbol. */ - def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Annotation], Symbol) = { + def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Symbol => Annotation], Symbol) = { var flags: FlagSet = EmptyFlags - var annots: List[Annotation] = Nil + var annotFns: List[Symbol => Annotation] = Nil var privateWithin: Symbol = NoSymbol while (currentAddr.index != end.index) { def addFlag(flag: FlagSet) = { @@ -588,6 +587,7 @@ class TreeUnpickler(reader: TastyReader, case LAZY => addFlag(Lazy) case OVERRIDE => addFlag(Override) case INLINE => addFlag(Inline) + case TRANSPARENT => addFlag(Transparent) case MACRO => addFlag(Macro) case STATIC => addFlag(JavaStatic) case OBJECT => addFlag(Module) @@ -614,23 +614,25 @@ class TreeUnpickler(reader: TastyReader, addFlag(Protected) privateWithin = readType().typeSymbol case ANNOTATION => - annots = readAnnot(ctx) :: annots + annotFns = readAnnot(ctx) :: annotFns case tag => assert(false, s"illegal modifier tag $tag at $currentAddr, end = $end") } } - (flags, annots.reverse, privateWithin) + (flags, annotFns.reverse, privateWithin) } - private val readAnnot: Context => Annotation = { + private val readAnnot: Context => Symbol => Annotation = { implicit ctx => readByte() val end = readEnd() val tp = readType() - val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx)) - Annotation.deferredSymAndTree( - implicit ctx => tp.typeSymbol, - implicit ctx => lazyAnnotTree.complete) + val lazyAnnotTree = readLaterWithOwner(end, rdr => ctx => rdr.readTerm()(ctx)) + + owner => + Annotation.deferredSymAndTree( + implicit ctx => tp.typeSymbol, + implicit ctx => lazyAnnotTree(owner).complete) } /** Create symbols for the definitions in the statement sequence between @@ -1153,10 +1155,13 @@ class TreeUnpickler(reader: TastyReader, setPos(start, CaseDef(pat, guard, rhs)) } - def readLater[T <: AnyRef](end: Addr, op: TreeReader => Context => T)(implicit ctx: Context): Trees.Lazy[T] = { + def readLater[T <: AnyRef](end: Addr, op: TreeReader => Context => T)(implicit ctx: Context): Trees.Lazy[T] = + readLaterWithOwner(end, op)(ctx)(ctx.owner) + + def readLaterWithOwner[T <: AnyRef](end: Addr, op: TreeReader => Context => T)(implicit ctx: Context): Symbol => Trees.Lazy[T] = { val localReader = fork goto(end) - new LazyReader(localReader, ctx.owner, ctx.mode, op) + owner => new LazyReader(localReader, owner, ctx.mode, op) } def readHole(end: Addr, isType: Boolean)(implicit ctx: Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 0024a285a025..9ffa8cf9c52e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1675,16 +1675,17 @@ object Parsers { /* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */ private def modOfToken(tok: Int): Mod = tok match { - case ABSTRACT => Mod.Abstract() - case FINAL => Mod.Final() - case IMPLICIT => Mod.Implicit() - case ERASED => Mod.Erased() - case INLINE => Mod.Inline() - case LAZY => Mod.Lazy() - case OVERRIDE => Mod.Override() - case PRIVATE => Mod.Private() - case PROTECTED => Mod.Protected() - case SEALED => Mod.Sealed() + case ABSTRACT => Mod.Abstract() + case FINAL => Mod.Final() + case IMPLICIT => Mod.Implicit() + case ERASED => Mod.Erased() + case INLINE => Mod.Inline() + case TRANSPARENT => Mod.Transparent() + case LAZY => Mod.Lazy() + case OVERRIDE => Mod.Override() + case PRIVATE => Mod.Private() + case PROTECTED => Mod.Protected() + case SEALED => Mod.Sealed() } /** Drop `private' modifier when followed by a qualifier. diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 0cc0cc16fcea..17b3e055f418 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -176,8 +176,9 @@ object Tokens extends TokensCommon { final val THEN = 60; enter(THEN, "then") final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate final val INLINE = 62; enter(INLINE, "inline") - final val ENUM = 63; enter(ENUM, "enum") - final val ERASED = 64; enter(ERASED, "erased") + final val TRANSPARENT = 63; enter(TRANSPARENT, "transparent") + final val ENUM = 64; enter(ENUM, "enum") + final val ERASED = 65; enter(ERASED, "erased") /** special symbols */ final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") @@ -226,7 +227,7 @@ object Tokens extends TokensCommon { final val defIntroTokens = templateIntroTokens | dclIntroTokens final val localModifierTokens = BitSet( - ABSTRACT, FINAL, SEALED, IMPLICIT, INLINE, LAZY, ERASED) + ABSTRACT, FINAL, SEALED, IMPLICIT, INLINE, TRANSPARENT, LAZY, ERASED) final val accessModifierTokens = BitSet( PRIVATE, PROTECTED) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index d9c5aa83f582..f1cf87ad9c1f 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -612,7 +612,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } private def toTextOwner(tree: Tree[_]) = - "[owner = " ~ tree.symbol.owner.show ~ "]" provided ctx.settings.YprintDebugOwners.value + "[owner = " ~ tree.symbol.maybeOwner.show ~ "]" provided ctx.settings.YprintDebugOwners.value protected def dclTextOr[T >: Untyped](tree: Tree[T])(treeText: => Text) = toTextOwner(tree) ~ { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 5fde8885f0d0..b8123e6d039b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1575,7 +1575,7 @@ object messages { private val varNote = if (sym.is(Mutable)) "Note that variables need to be initialized to be defined." else "" - val msg = hl"""only classes can have declared but undefined members""" + val msg = hl"""declaration of $sym not allowed here: only classes can have declared but undefined members""" val kind = "Syntax" val explanation = s"$varNote" } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index e7266b48a7aa..918d85afeca6 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -165,7 +165,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase def markAsMacro(c: Context): Unit = if (c.owner eq c.outer.owner) markAsMacro(c.outer) - else if (c.owner.isInlineMethod) c.owner.setFlag(Macro) + else if (c.owner.isInlineableMethod) c.owner.setFlag(Macro) else if (!c.outer.owner.is(Package)) markAsMacro(c.outer) if (sym.isSplice || sym.isQuote) { @@ -192,10 +192,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } else transformSelect(tree, Nil) - case tree: Super => - if (ctx.owner.enclosingMethod.isInlineMethod) - ctx.error(SuperCallsNotAllowedInline(ctx.owner), tree.pos) - super.transform(tree) case tree: Apply => methPart(tree) match { case Select(nu: New, nme.CONSTRUCTOR) if isCheckable(nu) => diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index fdfa66031f50..e909906a8b7a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -404,6 +404,8 @@ object Checking { checkCombination(Private, Protected) checkCombination(Abstract, Override) checkCombination(Lazy, Inline) + checkCombination(Module, Inline) + checkCombination(Transparent, Inline) checkNoConflict(Lazy, ParamAccessor, s"parameter may not be `lazy`") if (sym.is(Inline)) checkApplicable(Inline, sym.isTerm && !sym.is(Mutable | Module)) if (sym.is(Lazy)) checkApplicable(Lazy, !sym.is(Method | Mutable)) @@ -682,7 +684,8 @@ trait Checking { case tp => tp.widenTermRefExpr match { case tp: ConstantType if exprPurity(tree) >= purityLevel => // ok case _ => - if (!ctx.erasedTypes) ctx.error(em"$what must be a constant expression", tree.pos) + val allow = ctx.erasedTypes || ctx.owner.ownersIterator.exists(_.isInlineableMethod) + if (!allow) ctx.error(em"$what must be a constant expression", tree.pos) } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 3db32b4a08f3..59b8c6a32f6e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -53,23 +53,13 @@ object Inliner { // 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 = tree match { - case tree: Assign => - transform(tree.lhs) match { - case lhs1: RefTree => - lhs1.name match { - case InlineAccessorName(name) => - cpy.Apply(tree)(useSetter(lhs1), transform(tree.rhs) :: Nil) - case _ => - super.transform(tree) - } - case _ => - super.transform(tree) - } - case _ => - super.transform(accessorIfNeeded(tree)) - } - + override def transform(tree: Tree)(implicit ctx: Context): Tree = + super.transform(accessorIfNeeded(tree)) match { + case tree1 @ Assign(lhs: RefTree, rhs) if lhs.symbol.name.is(InlineAccessorName) => + cpy.Apply(tree1)(useSetter(lhs), rhs :: Nil) + case tree1 => + tree1 + } } /** Adds accessors for all non-public term members accessed @@ -90,6 +80,11 @@ object Inliner { } } + def isLocal(sym: Symbol, inlineMethod: Symbol)(implicit ctx: Context) = + sym.isContainedIn(inlineMethod) && + sym != inlineMethod && + (!sym.is(Param) || sym.owner != inlineMethod) + /** Register inline info for given inline method `sym`. * * @param sym The symbol denotatioon of the inline method for which info is registered @@ -99,14 +94,14 @@ object Inliner { * to have the inlined method as owner. */ def registerInlineInfo( - sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { - sym.unforcedAnnotation(defn.BodyAnnot) match { + inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { + inlined.unforcedAnnotation(defn.BodyAnnot) match { case Some(ann: ConcreteBodyAnnotation) => case Some(ann: LazyBodyAnnotation) if ann.isEvaluated => case _ => if (!ctx.isAfterTyper) { val inlineCtx = ctx - sym.updateAnnotation(LazyBodyAnnotation { _ => + inlined.updateAnnotation(LazyBodyAnnotation { _ => implicit val ctx = inlineCtx val body = treeExpr(ctx) if (ctx.reporter.hasErrors) body else ctx.compilationUnit.inlineAccessors.makeInlineable(body) @@ -119,7 +114,7 @@ object Inliner { * from Scala2x class files might be `@inline`, but still lack that body. */ def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean = - sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot) + sym.isInlinedMethod && sym.hasAnnotation(defn.BodyAnnot) // TODO: Open this up for transparent methods as well /** The body to inline for method `sym`. * @pre hasBodyToInline(sym) @@ -168,10 +163,10 @@ object Inliner { /** Produces an inlined version of `call` via its `inlined` method. * - * @param call The original call to a `@inline` method - * @param rhs The body of the inline method that replaces the call. + * @param call the original call to a `@inline` method + * @param rhsToInline the body of the inline method that replaces the call. */ -class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { +class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { import tpd._ import Inliner._ @@ -195,7 +190,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { */ private val paramProxy = new mutable.HashMap[Type, Type] - /** A map from the classes of (direct and outer) this references in `rhs` + /** A map from the classes of (direct and outer) this references in `rhsToInline` * to references of their proxies. * Note that we can't index by the ThisType itself since there are several * possible forms to express what is logicaly the same ThisType. E.g. @@ -310,7 +305,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { if (!isIdempotentExpr(prefix)) registerType(meth.owner.thisType) // Register types of all leaves of inlined body so that the `paramProxy` and `thisProxy` maps are defined. - rhs.foreachSubTree(registerLeaf) + rhsToInline.foreachSubTree(registerLeaf) // The class that the this-proxy `selfSym` represents def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol @@ -382,7 +377,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { // the owner from the inlined method to the current owner. val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil)(inlineCtx) - val expansion = inliner(rhs.withPos(call.pos)) + val expansion = inliner(rhsToInline.withPos(call.pos)) trace(i"inlining $call\n, BINDINGS =\n${bindingsBuf.toList}%\n%\nEXPANSION =\n$expansion", inlining, show = true) { // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details. @@ -421,7 +416,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { /** A typer for inlined code. Its purpose is: * 1. Implement constant folding over inlined code * 2. Selectively expand ifs with constant conditions - * 3. Inline arguments that are inlineable closures + * 3. Inline arguments that are by-name closures * 4. Make sure inlined code is type-correct. * 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed) */ diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 592006e2b57f..6e6c44f7a02d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -821,12 +821,12 @@ class Namer { typer: Typer => else completeInCreationContext(denot) } - private def addInlineInfo(denot: SymDenotation) = original match { - case original: untpd.DefDef if denot.isInlineMethod => + private def addInlineInfo(sym: Symbol) = original match { + case original: untpd.DefDef if sym.isInlineableMethod => Inliner.registerInlineInfo( - denot, + sym, implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs - )(localContext(denot.symbol)) + )(localContext(sym)) case _ => } @@ -839,7 +839,7 @@ class Namer { typer: Typer => case original: MemberDef => addAnnotations(sym, original) case _ => } - addInlineInfo(denot) + addInlineInfo(sym) denot.info = typeSig(sym) Checking.checkWellFormed(sym) denot.info = avoidPrivateLeaks(sym, sym.pos) @@ -1113,7 +1113,7 @@ class Namer { typer: Typer => // println(s"final inherited for $sym: ${inherited.toString}") !!! // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") - def isInline = sym.is(FinalOrInline, butNot = Method | Mutable) + def isInline = sym.is(FinalOrInlineOrTransparent, butNot = Method | Mutable) // Widen rhs type and eliminate `|' but keep ConstantTypes if // definition is inline (i.e. final in Scala2) and keep module singleton types diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index f05e7c61818c..733ac8be3a41 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -476,6 +476,9 @@ class Typer extends Namer case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => true case _ => false } + val enclosingInlineable = ctx.owner.ownersIterator.findSymbol(_.isInlineableMethod) + if (enclosingInlineable.exists && !Inliner.isLocal(qual1.symbol, enclosingInlineable)) + ctx.error(SuperCallsNotAllowedInline(enclosingInlineable), tree.pos) pt match { case pt: SelectionProto if pt.name.isTypeName => qual1 // don't do super references for types; they are meaningless anyway @@ -1068,7 +1071,7 @@ class Typer extends Namer (EmptyTree, WildcardType) } else if (owner != cx.outer.owner && owner.isRealMethod) { - if (owner.isInlineMethod) + if (owner.isInlineableMethod) (EmptyTree, errorType(NoReturnFromInline(owner), tree.pos)) else if (!owner.isCompleted) (EmptyTree, errorType(MissingReturnTypeWithReturnStatement(owner), tree.pos)) @@ -1428,7 +1431,7 @@ class Typer extends Namer val rhs1 = normalizeErasedRhs(typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx), sym) // Overwrite inline body to make sure it is not evaluated twice - if (sym.isInlineMethod) Inliner.registerInlineInfo(sym, _ => rhs1) + if (sym.isInlineableMethod) Inliner.registerInlineInfo(sym, _ => rhs1) if (sym.isConstructor && !sym.isPrimaryConstructor) for (param <- tparams1 ::: vparamss1.flatten) @@ -2343,7 +2346,7 @@ class Typer extends Namer if (pt.hasAnnotation(defn.InlineParamAnnot)) checkInlineConformant(tree, isFinal = false, "argument to inline parameter") if (Inliner.hasBodyToInline(tree.symbol) && - !ctx.owner.ownersIterator.exists(_.isInlineMethod) && + !ctx.owner.ownersIterator.exists(_.isInlineableMethod) && !ctx.settings.YnoInline.value && !ctx.isAfterTyper && !ctx.reporter.hasErrors) diff --git a/tests/neg/i2421.scala b/tests/neg/i2421.scala index c09da6a8a3f2..759145119e7b 100644 --- a/tests/neg/i2421.scala +++ b/tests/neg/i2421.scala @@ -1,4 +1,4 @@ -inline object Foo // error: modifier(s) `inline' incompatible with type definition +inline object Foo // OK (error would be detected later, in PostTyper) inline class Bar // error: modifier(s) `inline' incompatible with type definition inline abstract class Baz // error: modifier(s) `inline' incompatible with type definition inline trait Qux // error: modifier(s) `inline' incompatible with type definition diff --git a/tests/run/inline-implicits.check b/tests/run/inline-implicits.check new file mode 100644 index 000000000000..17a1d370d730 --- /dev/null +++ b/tests/run/inline-implicits.check @@ -0,0 +1 @@ +(X(),Y()) diff --git a/tests/run/inline-implicits.scala b/tests/run/inline-implicits.scala new file mode 100644 index 000000000000..6d46a61d28e4 --- /dev/null +++ b/tests/run/inline-implicits.scala @@ -0,0 +1,26 @@ +case class X() +case class Y() + +object impl { + implicit val y: Y = new Y() +} + +object inlines { + import impl._ + + class C { + implicit val x: X = new X() + + inline + def f(): (X, Y) = + (implicitly[X], implicitly[Y]) + } +} + +object Test { + def main(args: Array[String]) = { + val c = new inlines.C + val xy = c.f() + println(xy) + } +} diff --git a/tests/run/typelevel.check b/tests/run/typelevel.check new file mode 100644 index 000000000000..e85e836bb30b --- /dev/null +++ b/tests/run/typelevel.check @@ -0,0 +1,3 @@ +ToNat(Z) +ToNat(S(Z)) +ToNat(S(S(Z))) diff --git a/tests/run/typelevel.scala b/tests/run/typelevel.scala new file mode 100644 index 000000000000..9a836565ef51 --- /dev/null +++ b/tests/run/typelevel.scala @@ -0,0 +1,31 @@ +object Test extends App { + + trait Nat + + case object Z extends Nat + type Z = Z.type + case class S[N <: Nat](n: Nat) extends Nat + + abstract class HasResult[T] { type Result = T } + case class ToNat[+T](val value: T) extends HasResult[T] + + transparent def ToNat(inline n: Int): ToNat[Nat] = + if n == 0 then new ToNat(Z) + else { + val n1 = ToNat(n - 1) + new ToNat[S[n1.Result]](S(n1.value)) + } + + val x0 = ToNat(0) + //val y0: Z = x0.value + //val z0: x0.Result = y0 + val x1 = ToNat(1) + //val y1: S[Z] = x1.value + //val z1: x1.Result = y1 + val x2 = ToNat(2) + //val y2: S[S[Z]] = x2.value + //val z2: x2.Result = y2 + println(x0) + println(x1) + println(x2) +} \ No newline at end of file