Skip to content

Commit 68e2023

Browse files
Merge pull request #8543 from dotty-staging/change-inline-override
Implement inline override
2 parents 6cd3a9d + 453319b commit 68e2023

37 files changed

+315
-138
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,9 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
191191
case arg => arg.typeOpt.widen.isRepeatedParam
192192
}
193193

194-
/** If this tree has type parameters, those. Otherwise Nil.
195-
def typeParameters(tree: Tree): List[TypeDef] = tree match {
196-
case DefDef(_, _, tparams, _, _, _) => tparams
197-
case ClassDef(_, _, tparams, _) => tparams
198-
case TypeDef(_, _, tparams, _) => tparams
199-
case _ => Nil
200-
}*/
194+
/** All type and value parameter symbols of this DefDef */
195+
def allParamSyms(ddef: DefDef)(using Context): List[Symbol] =
196+
(ddef.tparams ::: ddef.vparamss.flatten).map(_.symbol)
201197

202198
/** Does this argument list end with an argument of the form <expr> : _* ? */
203199
def isWildcardStarArgList(trees: List[Tree])(implicit ctx: Context): Boolean =

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ object Flags {
519519
val AbstractSealed: FlagSet = Abstract | Sealed
520520
val AbstractOrTrait: FlagSet = Abstract | Trait
521521
val EffectivelyOpenFlags = Abstract | JavaDefined | Open | Scala2x | Trait
522+
val AccessorOrDeferred: FlagSet = Accessor | Deferred
522523
val PrivateAccessor: FlagSet = Accessor | Private
523524
val AccessorOrSynthetic: FlagSet = Accessor | Synthetic
524525
val EnumCase: FlagSet = Case | Enum

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ object NameKinds {
353353
val ProtectedAccessorName: PrefixNameKind = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
354354
val InlineAccessorName: PrefixNameKind = new PrefixNameKind(INLINEACCESSOR, "inline$")
355355

356-
val AvoidClashName: SuffixNameKind = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
356+
val BodyRetainerName: SuffixNameKind = new SuffixNameKind(BODYRETAINER, "$retainedBody")
357357
val FieldName: SuffixNameKind = new SuffixNameKind(FIELD, "$$local") {
358358
override def mkString(underlying: TermName, info: ThisInfo) = underlying.toString
359359
}

compiler/src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ object NameOps {
128128
/** If flags is a ModuleClass but not a Package, add module class suffix */
129129
def adjustIfModuleClass(flags: FlagSet): N = likeSpacedN {
130130
if (flags.is(ModuleClass, butNot = Package)) name.asTypeName.moduleClassName
131-
else name.toTermName.exclude(AvoidClashName)
131+
else name.toTermName
132132
}
133133

134134
/** The expanded name.

compiler/src/dotty/tools/dotc/core/NameTags.scala

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,6 @@ object NameTags extends TastyFormat.NameTags {
1717

1818
final val INITIALIZER = 26 // A mixin initializer method
1919

20-
final val AVOIDCLASH = 27 // Adds a suffix to avoid a name clash;
21-
// Used in FirstTransform for synthesized companion objects of classes
22-
// if they would clash with another value.
23-
24-
final val DIRECT = 28 // Used by ShortCutImplicits for the name of methods that
25-
// implement implicit function result types directly.
26-
2720
final val FIELD = 29 // Used by Memoize to tag the name of a class member field.
2821

2922
final val EXTMETH = 30 // Used by ExtensionMethods for the name of an extension method
@@ -51,8 +44,6 @@ object NameTags extends TastyFormat.NameTags {
5144
case INLINEACCESSOR => "INLINEACCESSOR"
5245
case PROTECTEDACCESSOR => "PROTECTEDACCESSOR"
5346
case INITIALIZER => "INITIALIZER"
54-
case AVOIDCLASH => "AVOIDCLASH"
55-
case DIRECT => "DIRECT"
5647
case FIELD => "FIELD"
5748
case EXTMETH => "EXTMETH"
5849
case IMPLMETH => "IMPLMETH"

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ object SymDenotations {
282282
*/
283283
def effectiveName(implicit ctx: Context): Name =
284284
if (this.is(ModuleClass)) name.stripModuleClassSuffix
285-
else name.exclude(AvoidClashName)
285+
else name
286286

287287
/** The privateWithin boundary, NoSymbol if no boundary is given.
288288
*/
@@ -940,13 +940,17 @@ object SymDenotations {
940940
def isInlineMethod(implicit ctx: Context): Boolean =
941941
isAllOf(InlineMethod, butNot = Accessor)
942942

943+
def isRetainedInlineMethod(using Context): Boolean =
944+
isAllOf(InlineMethod, butNot = AccessorOrDeferred)
945+
&& allOverriddenSymbols.exists(!_.is(Inline))
946+
943947
/** Is this a Scala 2 macro */
944948
final def isScala2Macro(implicit ctx: Context): Boolean = is(Macro) && symbol.owner.is(Scala2x)
945949

946950
/** An erased value or an inline method.
947951
*/
948952
def isEffectivelyErased(implicit ctx: Context): Boolean =
949-
is(Erased) || isInlineMethod
953+
is(Erased) || isInlineMethod && !isRetainedInlineMethod
950954

951955
/** ()T and => T types should be treated as equivalent for this symbol.
952956
* Note: For the moment, we treat Scala-2 compiled symbols as loose matching,

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ class TreeUnpickler(reader: TastyReader,
774774
def readRhs(implicit ctx: Context): LazyTree =
775775
if (nothingButMods(end))
776776
EmptyTree
777-
else if (sym.isInlineMethod)
777+
else if sym.isInlineMethod && !sym.is(Deferred) then
778778
// The body of an inline method is stored in an annotation, so no need to unpickle it again
779779
new Trees.Lazy[Tree] {
780780
def complete(implicit ctx: Context) = typer.Inliner.bodyToInline(sym)

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
17011701
}
17021702
def Symbol_annots(self: Symbol)(using ctx: Context): List[Term] =
17031703
self.annotations.flatMap {
1704-
case _: core.Annotations.LazyBodyAnnotation => Nil
1704+
case _: core.Annotations.BodyAnnotation => Nil
17051705
case annot => annot.tree :: Nil
17061706
}
17071707

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@ import core.Types._
1212
import core.Names._
1313
import core.StdNames._
1414
import core.NameOps._
15-
import core.NameKinds.AdaptedClosureName
15+
import core.NameKinds.{AdaptedClosureName, BodyRetainerName}
1616
import core.Decorators._
1717
import core.Constants._
1818
import core.Definitions._
19+
import core.Annotations.BodyAnnotation
1920
import typer.{NoChecking, LiftErased}
2021
import typer.Inliner
2122
import typer.ProtoTypes._
2223
import core.TypeErasure._
2324
import core.Decorators._
2425
import dotty.tools.dotc.ast.{tpd, untpd}
2526
import ast.Trees._
27+
import ast.TreeTypeMap
2628
import dotty.tools.dotc.core.{Constants, Flags}
2729
import ValueClasses._
2830
import TypeUtils._
@@ -78,17 +80,32 @@ class Erasure extends Phase with DenotTransformer {
7880
val oldInfo = ref.info
7981
val newInfo = transformInfo(oldSymbol, oldInfo)
8082
val oldFlags = ref.flags
81-
val newFlags =
83+
var newFlags =
8284
if (oldSymbol.is(Flags.TermParam) && isCompacted(oldSymbol.owner)) oldFlags &~ Flags.Param
8385
else oldFlags &~ Flags.HasDefaultParamsFlags // HasDefaultParamsFlags needs to be dropped because overriding might become overloading
84-
86+
val oldAnnotations = ref.annotations
87+
var newAnnotations = oldAnnotations
88+
if oldSymbol.isRetainedInlineMethod then
89+
newFlags = newFlags &~ Flags.Inline
90+
newAnnotations = newAnnotations.filterConserve(!_.isInstanceOf[BodyAnnotation])
8591
// TODO: define derivedSymDenotation?
86-
if ((oldSymbol eq newSymbol) && (oldOwner eq newOwner) && (oldName eq newName) && (oldInfo eq newInfo) && (oldFlags == newFlags))
92+
if (oldSymbol eq newSymbol)
93+
&& (oldOwner eq newOwner)
94+
&& (oldName eq newName)
95+
&& (oldInfo eq newInfo)
96+
&& (oldFlags == newFlags)
97+
&& (oldAnnotations eq newAnnotations)
98+
then
8799
ref
88-
else {
100+
else
89101
assert(!ref.is(Flags.PackageClass), s"trans $ref @ ${ctx.phase} oldOwner = $oldOwner, newOwner = $newOwner, oldInfo = $oldInfo, newInfo = $newInfo ${oldOwner eq newOwner} ${oldInfo eq newInfo}")
90-
ref.copySymDenotation(symbol = newSymbol, owner = newOwner, name = newName, initFlags = newFlags, info = newInfo)
91-
}
102+
ref.copySymDenotation(
103+
symbol = newSymbol,
104+
owner = newOwner,
105+
name = newName,
106+
initFlags = newFlags,
107+
info = newInfo,
108+
annotations = newAnnotations)
92109
}
93110
case ref: JointRefDenotation =>
94111
new UniqueRefDenotation(
@@ -813,7 +830,8 @@ object Erasure {
813830
* parameter of type `[]Object`.
814831
*/
815832
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context): Tree =
816-
if (sym.isEffectivelyErased) erasedDef(sym)
833+
if sym.isEffectivelyErased || sym.name.is(BodyRetainerName) then
834+
erasedDef(sym)
817835
else
818836
val restpe = if sym.isConstructor then defn.UnitType else sym.info.resultType
819837
var vparams = outerParamDefs(sym)
@@ -874,6 +892,50 @@ object Erasure {
874892
outerParamDefs(constr)
875893
else Nil
876894

895+
/** For all statements in stats: given a retained inline method and
896+
* its retainedBody method such as
897+
*
898+
* inline override def f(x: T) = body1
899+
* private def f$retainedBody(x: T) = body2
900+
*
901+
* return the runtime version of `f` as
902+
*
903+
* override def f(x: T) = body2
904+
*
905+
* Here, the owner of body2 is changed to f and all references
906+
* to parameters of f$retainedBody are changed to references of
907+
* corresponding parameters in f.
908+
*
909+
* `f$retainedBody` is subseqently mapped to the empty tree in `typedDefDef`
910+
* which is then dropped in `typedStats`.
911+
*/
912+
private def addRetainedInlineBodies(stats: List[untpd.Tree])(using ctx: Context): List[untpd.Tree] =
913+
lazy val retainerDef: Map[Symbol, DefDef] = stats.collect {
914+
case stat: DefDef if stat.symbol.name.is(BodyRetainerName) =>
915+
val retainer = stat.symbol
916+
val origName = retainer.name.asTermName.exclude(BodyRetainerName)
917+
val inlineMeth = ctx.atPhase(ctx.typerPhase) {
918+
retainer.owner.info.decl(origName)
919+
.matchingDenotation(retainer.owner.thisType, stat.symbol.info)
920+
.symbol
921+
}
922+
(inlineMeth, stat)
923+
}.toMap
924+
stats.mapConserve {
925+
case stat: DefDef if stat.symbol.isRetainedInlineMethod =>
926+
val rdef = retainerDef(stat.symbol)
927+
val fromParams = untpd.allParamSyms(rdef)
928+
val toParams = untpd.allParamSyms(stat)
929+
assert(fromParams.hasSameLengthAs(toParams))
930+
val mapBody = TreeTypeMap(
931+
oldOwners = rdef.symbol :: Nil,
932+
newOwners = stat.symbol :: Nil,
933+
substFrom = fromParams,
934+
substTo = toParams)
935+
cpy.DefDef(stat)(rhs = mapBody.transform(rdef.rhs))
936+
case stat => stat
937+
}
938+
877939
override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context): Tree = {
878940
val xxl = defn.isXXLFunctionClass(tree.typeOpt.typeSymbol)
879941
var implClosure = super.typedClosure(tree, pt).asInstanceOf[Closure]
@@ -888,9 +950,10 @@ object Erasure {
888950
typed(tree.arg, pt)
889951

890952
override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): (List[Tree], Context) = {
953+
val stats0 = addRetainedInlineBodies(stats)(using preErasureCtx)
891954
val stats1 =
892-
if (takesBridges(ctx.owner)) new Bridges(ctx.owner.asClass, erasurePhase).add(stats)
893-
else stats
955+
if (takesBridges(ctx.owner)) new Bridges(ctx.owner.asClass, erasurePhase).add(stats0)
956+
else stats0
894957
val (stats2, finalCtx) = super.typedStats(stats1, exprOwner)
895958
(stats2.filter(!_.isEmpty), finalCtx)
896959
}

compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
6969
if (constr == cls.primaryConstructor)
7070
cls.info.decls.filter(d => d.is(TypeParam) || d.is(ParamAccessor))
7171
else
72-
(cdef.tparams ::: cdef.vparamss.flatten).map(_.symbol)
72+
allParamSyms(cdef)
7373

7474
/** The parameter references defined by the constructor info */
7575
def allParamRefs(tp: Type): List[ParamRef] = tp match {

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ class TreeChecker extends Phase with SymTransformer {
8080
val badDeferredAndPrivate =
8181
sym.is(Method) && sym.is(Deferred) && sym.is(Private)
8282
&& !sym.hasAnnotation(defn.NativeAnnot)
83-
&& !sym.is(Erased)
83+
&& !sym.isEffectivelyErased
84+
8485
assert(!badDeferredAndPrivate, i"$sym is both Deferred and Private")
8586

8687
checkCompanion(symd)

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,6 @@ object Checking {
472472
fail(OnlyClassesCanHaveDeclaredButUndefinedMembers(sym))
473473
checkWithDeferred(Private)
474474
checkWithDeferred(Final)
475-
checkWithDeferred(Inline)
476475
}
477476
if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
478477
fail(CannotExtendAnyVal(sym))
@@ -883,7 +882,7 @@ trait Checking {
883882
typr.println(i"check no double declarations $cls")
884883

885884
def checkDecl(decl: Symbol): Unit = {
886-
for (other <- seen(decl.name) if (!decl.isAbsent() && !other.isAbsent())) {
885+
for (other <- seen(decl.name) if !decl.isAbsent() && !other.isAbsent()) {
887886
typr.println(i"conflict? $decl $other")
888887
def javaFieldMethodPair =
889888
decl.is(JavaDefined) && other.is(JavaDefined) &&

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import StdNames._
1515
import transform.SymUtils._
1616
import Contexts.Context
1717
import Names.{Name, TermName}
18-
import NameKinds.{InlineAccessorName, InlineBinderName, InlineScrutineeName}
18+
import NameKinds.{InlineAccessorName, InlineBinderName, InlineScrutineeName, BodyRetainerName}
1919
import ProtoTypes.selectionProto
2020
import SymDenotations.SymDenotation
2121
import Inferencing.isFullyDefined
@@ -124,6 +124,32 @@ object Inliner {
124124
)
125125
}
126126

127+
/** For a retained inline method, another method that keeps track of
128+
* the body that is kept at runtime. For instance, an inline method
129+
*
130+
* inline override def f(x: T) = b
131+
*
132+
* is complemented by the body retainer method
133+
*
134+
* private def f$retainedBody(x: T) = f(x)
135+
*
136+
* where the call `f(x)` is inline-expanded. This body is then transferred
137+
* back to `f` at erasure, using method addRetainedInlineBodies.
138+
*/
139+
def bodyRetainer(mdef: DefDef)(using ctx: Context): DefDef =
140+
val meth = mdef.symbol.asTerm
141+
142+
val retainer = meth.copy(
143+
name = BodyRetainerName(meth.name),
144+
flags = meth.flags &~ (Inline | Override) | Private,
145+
coord = mdef.rhs.span.startPos).asTerm
146+
polyDefDef(retainer, targs => prefss =>
147+
inlineCall(
148+
ref(meth).appliedToTypes(targs).appliedToArgss(prefss)
149+
.withSpan(mdef.rhs.span.startPos))(
150+
using ctx.withOwner(retainer)))
151+
.reporting(i"retainer for $meth: $result", inlining)
152+
127153
/** Replace `Inlined` node by a block that contains its bindings and expansion */
128154
def dropInlined(inlined: Inlined)(implicit ctx: Context): Tree =
129155
if (enclosingInlineds.nonEmpty) inlined // Remove in the outer most inlined call

compiler/src/dotty/tools/dotc/typer/ReTyper.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class ReTyper extends Typer with ReChecking {
129129
throw ex
130130
}
131131

132-
override def inlineExpansion(mdef: DefDef)(implicit ctx: Context): Tree = mdef
132+
override def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] = mdef :: Nil
133133

134134
override def inferView(from: Tree, to: Type)(implicit ctx: Context): Implicits.SearchResult =
135135
Implicits.NoMatchingImplicitsFailure

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,7 @@ object RefChecks {
157157
* 1.8.3 M is of type ()S, O is of type []T and S <: T, or
158158
* 1.9.1 If M is erased, O is erased. If O is erased, M is erased or inline.
159159
* 1.9.2 If M or O are extension methods, they must both be extension methods.
160-
* 1.10 If M is an inline or Scala-2 macro method, O cannot be deferred unless
161-
* there's also a concrete method that M overrides.
160+
* 1.10 If O is inline, M must be inline
162161
* 1.11. If O is a Scala-2 macro, M must be a Scala-2 macro.
163162
* 2. Check that only abstract classes have deferred members
164163
* 3. Check that concrete classes do not have deferred definitions
@@ -398,9 +397,8 @@ object RefChecks {
398397
overrideError("is an extension method, cannot override a normal method")
399398
else if (other.isAllOf(ExtensionMethod) && !member.isAllOf(ExtensionMethod)) // (1.9.2)
400399
overrideError("is a normal method, cannot override an extension method")
401-
else if ((member.isInlineMethod || member.isScala2Macro) && other.is(Deferred) &&
402-
member.extendedOverriddenSymbols.forall(_.is(Deferred))) // (1.10)
403-
overrideError("is an inline method, must override at least one concrete method")
400+
else if other.isInlineMethod && !member.isInlineMethod then // (1.10)
401+
overrideError("is not inline, cannot override an inline method")
404402
else if (other.isScala2Macro && !member.isScala2Macro) // (1.11)
405403
overrideError("cannot be used here - only Scala-2 macros can override Scala-2 macros")
406404
else if (!compatibleTypes(memberTp(self), otherTp(self)) &&

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,8 +2332,9 @@ class Typer extends Namer
23322332
val newCtx = if (ctx.owner.isTerm && adaptCreationContext(mdef)) ctx
23332333
else ctx.withNotNullInfos(initialNotNullInfos)
23342334
typed(mdef)(using newCtx) match {
2335-
case mdef1: DefDef if !Inliner.bodyToInline(mdef1.symbol).isEmpty =>
2336-
buf += inlineExpansion(mdef1)
2335+
case mdef1: DefDef
2336+
if mdef1.symbol.is(Inline, butNot = Deferred) && !Inliner.bodyToInline(mdef1.symbol).isEmpty =>
2337+
buf ++= inlineExpansion(mdef1)
23372338
// replace body with expansion, because it will be used as inlined body
23382339
// from separately compiled files - the original BodyAnnotation is not kept.
23392340
case mdef1: TypeDef if mdef1.symbol.is(Enum, butNot = Case) =>
@@ -2410,11 +2411,13 @@ class Typer extends Namer
24102411
}
24112412

24122413
/** Given an inline method `mdef`, the method rewritten so that its body
2413-
* uses accessors to access non-public members.
2414+
* uses accessors to access non-public members. Also, if the inline method
2415+
* is retained, add a method to record the retained version of the body.
24142416
* Overwritten in Retyper to return `mdef` unchanged.
24152417
*/
2416-
protected def inlineExpansion(mdef: DefDef)(implicit ctx: Context): Tree =
2418+
protected def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] =
24172419
tpd.cpy.DefDef(mdef)(rhs = Inliner.bodyToInline(mdef.symbol))
2420+
:: (if mdef.symbol.isRetainedInlineMethod then Inliner.bodyRetainer(mdef) :: Nil else Nil)
24182421

24192422
def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
24202423
typed(tree, pt)(ctx retractMode Mode.PatternOrTypeBits)
@@ -2932,9 +2935,13 @@ class Typer extends Namer
29322935
!suppressInline) {
29332936
tree.tpe <:< wildApprox(pt)
29342937
val errorCount = ctx.reporter.errorCount
2935-
val inlined = Inliner.inlineCall(tree)
2936-
if ((inlined ne tree) && errorCount == ctx.reporter.errorCount) readaptSimplified(inlined)
2937-
else inlined
2938+
val meth = methPart(tree).symbol
2939+
if meth.is(Deferred) then
2940+
errorTree(tree, i"Deferred inline ${meth.showLocated} cannot be invoked")
2941+
else
2942+
val inlined = Inliner.inlineCall(tree)
2943+
if ((inlined ne tree) && errorCount == ctx.reporter.errorCount) readaptSimplified(inlined)
2944+
else inlined
29382945
}
29392946
else if (tree.symbol.isScala2Macro &&
29402947
// raw and s are eliminated by the StringInterpolatorOpt phase

0 commit comments

Comments
 (0)