Skip to content

Commit 9a69ea8

Browse files
authored
Merge pull request #8630 from dotty-staging/fix-#8619
Fix #8619: Demand `transparent` for whitebox inlines
2 parents 9b640f6 + bb01e6f commit 9a69ea8

File tree

25 files changed

+134
-96
lines changed

25 files changed

+134
-96
lines changed

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

+1-22
Original file line numberDiff line numberDiff line change
@@ -202,18 +202,6 @@ object desugar {
202202
* def f[T](x: Int)(y: String)(implicit evidence$0: B[T]) = ...
203203
* def f$default$1[T] = 1
204204
* def f$default$2[T](x: Int) = x + "m"
205-
*
206-
* 3. Convert <: T to : T in specializing inline methods. E.g.
207-
*
208-
* inline def f(x: Boolean) <: Any = if (x) 1 else ""
209-
* ==>
210-
* inline def f(x: Boolean): Any = if (x) 1 else ""
211-
*
212-
* 4. Upcast non-specializing inline methods. E.g.
213-
*
214-
* inline def f(x: Boolean): Any = if (x) 1 else ""
215-
* ==>
216-
* inline def f(x: Boolean): Any = (if (x) 1 else ""): Any
217205
*/
218206
private def defDef(meth0: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
219207
val meth @ DefDef(_, tparams, vparamss, tpt, rhs) = transformQuotedPatternName(meth0)
@@ -247,18 +235,9 @@ object desugar {
247235
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
248236
}
249237

250-
var meth1 = addEvidenceParams(
238+
val meth1 = addEvidenceParams(
251239
cpy.DefDef(meth)(name = methName, tparams = tparams1), epbuf.toList)
252240

253-
if (meth1.mods.is(Inline))
254-
meth1.tpt match {
255-
case TypeBoundsTree(_, tpt1, _) =>
256-
meth1 = cpy.DefDef(meth1)(tpt = tpt1)
257-
case tpt if !tpt.isEmpty && !meth1.rhs.isEmpty =>
258-
meth1 = cpy.DefDef(meth1)(rhs = Typed(meth1.rhs, tpt))
259-
case _ =>
260-
}
261-
262241
/** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
263242
def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
264243
case vparams :: vparamss1 =>

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
195195
case class Lazy()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Lazy)
196196

197197
case class Inline()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Inline)
198+
199+
case class Transparent()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.EmptyFlags)
198200
}
199201

200202
/** Modifiers and annotations for definitions
@@ -326,7 +328,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
326328
def derivedTree(originalSym: Symbol)(implicit ctx: Context): tpd.Tree
327329
}
328330

329-
/** Property key containing TypeTrees whose type is computed
331+
/** Property key containing TypeTrees whose type is computed
330332
* from the symbol in this type. These type trees have marker trees
331333
* TypeRefOfSym or InfoOfSym as their originals.
332334
*/

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

+1
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ object StdNames {
594594
val toString_ : N = "toString"
595595
val toTypeConstructor: N = "toTypeConstructor"
596596
val tpe : N = "tpe"
597+
val transparent : N = "transparent"
597598
val tree : N = "tree"
598599
val true_ : N = "true"
599600
val typedProductIterator: N = "typedProductIterator"

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+15-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ object Parsers {
3333
import reporting.Message
3434
import reporting.messages._
3535

36+
val AllowOldWhiteboxSyntax = true
37+
3638
case class OpInfo(operand: Tree, operator: Ident, offset: Offset)
3739

3840
class ParensCounters {
@@ -2692,6 +2694,7 @@ object Parsers {
26922694
case nme.inline => Mod.Inline()
26932695
case nme.opaque => Mod.Opaque()
26942696
case nme.open => Mod.Open()
2697+
case nme.transparent => Mod.Transparent()
26952698
}
26962699
}
26972700

@@ -2748,7 +2751,7 @@ object Parsers {
27482751
* | AccessModifier
27492752
* | override
27502753
* | opaque
2751-
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased | inline
2754+
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased | inline | transparent
27522755
*/
27532756
def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
27542757
@tailrec
@@ -2766,7 +2769,11 @@ object Parsers {
27662769
}
27672770
else
27682771
mods
2769-
normalize(loop(start))
2772+
val result = normalize(loop(start))
2773+
for case mod @ Mod.Transparent() <- result.mods do
2774+
if !result.is(Inline) then
2775+
syntaxError(em"`transparent` can only be used in conjunction with `inline`", mod.span)
2776+
result
27702777
}
27712778

27722779
val funTypeArgMods: BitSet = BitSet(ERASED)
@@ -3248,9 +3255,10 @@ object Parsers {
32483255
case rparamss =>
32493256
leadingVparamss ::: rparamss
32503257
var tpt = fromWithinReturnType {
3251-
if in.token == SUBTYPE && mods.is(Inline) then
3258+
if in.token == SUBTYPE && mods.is(Inline) && AllowOldWhiteboxSyntax then
32523259
in.nextToken()
3253-
TypeBoundsTree(EmptyTree, toplevelTyp())
3260+
mods1 = addMod(mods1, Mod.Transparent())
3261+
toplevelTyp()
32543262
else typedOpt()
32553263
}
32563264
if (in.isScala2CompatMode) newLineOptWhenFollowedBy(LBRACE)
@@ -3516,12 +3524,13 @@ object Parsers {
35163524
accept(EQUALS)
35173525
mods1 |= Final
35183526
DefDef(name, tparams, vparamss, tpt, subExpr())
3519-
if in.token == USCORE then
3527+
if in.token == USCORE && AllowOldWhiteboxSyntax then
35203528
if !mods.is(Inline) then
35213529
syntaxError("`_ <:` is only allowed for given with `inline` modifier")
35223530
in.nextToken()
35233531
accept(SUBTYPE)
3524-
givenAlias(TypeBoundsTree(EmptyTree, toplevelTyp()))
3532+
mods1 = addMod(mods1, Mod.Transparent())
3533+
givenAlias(toplevelTyp())
35253534
else
35263535
val parents = constrApps(commaOK = true, templateCanFollow = true)
35273536
if in.token == EQUALS && parents.length == 1 && parents.head.isType then

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,5 +282,5 @@ object Tokens extends TokensCommon {
282282

283283
final val scala3keywords = BitSet(ENUM, ERASED, GIVEN)
284284

285-
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open)
285+
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open, nme.transparent)
286286
}

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
864864
val rawFlags = if (sym.exists) sym.flags else mods.flags
865865
if (rawFlags.is(Param)) flagMask = flagMask &~ Given
866866
val flags = rawFlags & flagMask
867-
val flagsText = toTextFlags(sym, flags)
867+
var flagsText = toTextFlags(sym, flags)
868+
if mods.hasMod(classOf[untpd.Mod.Transparent]) then
869+
flagsText = "transparent " ~ flagsText
868870
val annotations =
869871
if (sym.exists) sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree)
870872
else mods.annotations.filterNot(tree => dropAnnotForModText(tree.symbol))

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -863,10 +863,10 @@ class Namer { typer: Typer =>
863863

864864
private def addInlineInfo(sym: Symbol) = original match {
865865
case original: untpd.DefDef if sym.isInlineMethod =>
866-
PrepareInlineable.registerInlineInfo(
867-
sym,
868-
implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
869-
)(localContext(sym))
866+
def rhsToInline(using Context): tpd.Tree =
867+
val mdef = typedAheadExpr(original).asInstanceOf[tpd.DefDef]
868+
PrepareInlineable.wrapRHS(original, mdef.tpt, mdef.rhs)
869+
PrepareInlineable.registerInlineInfo(sym, rhsToInline)(localContext(sym))
870870
case _ =>
871871
}
872872

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ object PrepareInlineable {
204204
def isLocal(sym: Symbol, inlineMethod: Symbol)(implicit ctx: Context): Boolean =
205205
isLocalOrParam(sym, inlineMethod) && !(sym.is(Param) && sym.owner == inlineMethod)
206206

207+
/** The type ascription `rhs: tpt`, unless `original` is `transparent`. */
208+
def wrapRHS(original: untpd.DefDef, tpt: Tree, rhs: Tree)(using Context): Tree =
209+
if original.mods.hasMod(classOf[untpd.Mod.Transparent]) then rhs
210+
else Typed(rhs, tpt)
211+
207212
/** Register inline info for given inlineable method `sym`.
208213
*
209214
* @param sym The symbol denotation of the inlineable method for which info is registered
@@ -213,7 +218,7 @@ object PrepareInlineable {
213218
* to have the inline method as owner.
214219
*/
215220
def registerInlineInfo(
216-
inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit =
221+
inlined: Symbol, treeExpr: Context ?=> Tree)(implicit ctx: Context): Unit =
217222
inlined.unforcedAnnotation(defn.BodyAnnot) match {
218223
case Some(ann: ConcreteBodyAnnotation) =>
219224
case Some(ann: LazyBodyAnnotation) if ann.isEvaluated || ann.isEvaluating =>
@@ -223,7 +228,7 @@ object PrepareInlineable {
223228
inlined.updateAnnotation(LazyBodyAnnotation {
224229
given ctx as Context = inlineCtx
225230
val initialErrorCount = ctx.reporter.errorCount
226-
var inlinedBody = treeExpr(ctx)
231+
var inlinedBody = treeExpr
227232
if (ctx.reporter.errorCount == initialErrorCount) {
228233
inlinedBody = ctx.compilationUnit.inlineAccessors.makeInlineable(inlinedBody)
229234
checkInlineMethod(inlined, inlinedBody)

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1735,9 +1735,10 @@ class Typer extends Namer
17351735

17361736
if (sym.isInlineMethod) rhsCtx.addMode(Mode.InlineableBody)
17371737
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)
1738+
val rhsToInline = PrepareInlineable.wrapRHS(ddef, tpt1, rhs1)
17381739

17391740
if (sym.isInlineMethod)
1740-
PrepareInlineable.registerInlineInfo(sym, _ => rhs1)
1741+
PrepareInlineable.registerInlineInfo(sym, rhsToInline)
17411742

17421743
if (sym.isConstructor && !sym.isPrimaryConstructor) {
17431744
val ename = sym.erasedName

compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala

+9-9
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {
7575

7676
@Test def i4947 = {
7777
val source = """class Foo {
78-
| inline def track[T](inline f: T) <: T = {
78+
| transparent inline def track[T](inline f: T): T = {
7979
| foo("tracking") // line 3
8080
| f // line 4
8181
| }
@@ -134,11 +134,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {
134134

135135
@Test def i4947b = {
136136
val source = """class Foo {
137-
| inline def track2[T](inline f: T) <: T = {
137+
| transparent inline def track2[T](inline f: T): T = {
138138
| foo("tracking2") // line 3
139139
| f // line 4
140140
| }
141-
| inline def track[T](inline f: T) <: T = {
141+
| transparent inline def track[T](inline f: T): T = {
142142
| foo("tracking") // line 7
143143
| track2 { // line 8
144144
| f // line 9
@@ -194,11 +194,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {
194194

195195
@Test def i4947c = {
196196
val source = """class Foo {
197-
| inline def track2[T](inline f: T) <: T = {
197+
| transparent inline def track2[T](inline f: T): T = {
198198
| foo("tracking2") // line 3
199199
| f // line 4
200200
| }
201-
| inline def track[T](inline f: T) <: T = {
201+
| transparent inline def track[T](inline f: T): T = {
202202
| track2 { // line 7
203203
| foo("fgh") // line 8
204204
| f // line 9
@@ -254,11 +254,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {
254254

255255
@Test def i4947d = {
256256
val source = """class Foo {
257-
| inline def track2[T](inline f: T) <: T = {
257+
| transparent inline def track2[T](inline f: T): T = {
258258
| foo("tracking2") // line 3
259259
| f // line 4
260260
| }
261-
| inline def track[T](inline f: T) <: T = {
261+
| transparent inline def track[T](inline f: T): T = {
262262
| track2 { // line 7
263263
| track2 { // line 8
264264
| f // line 9
@@ -319,7 +319,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {
319319
| def test: Int = {
320320
| var a = 10
321321
|
322-
| inline def f() = {
322+
| transparent inline def f() = {
323323
| a += 1
324324
| }
325325
|
@@ -356,7 +356,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {
356356
val source = """class Test:
357357
| given Int = 0
358358
| def f(): Int ?=> Boolean = true : (Int ?=> Boolean)
359-
| inline def g(): Int ?=> Boolean = true
359+
| transparent inline def g(): Int ?=> Boolean = true
360360
| def test = g()
361361
""".stripMargin
362362

docs/docs/internals/syntax.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ yield
104104
### Soft keywords
105105

106106
```
107-
as derives extension inline on opaque open using
107+
as derives extension inline on opaque open transparent
108+
using
108109
* + -
109110
```
110111

docs/docs/reference/contextual/givens.md

+6-7
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,16 @@ given (using config: Config) as Factory = MemoizingFactory(config)
7979
An alias given can have type parameters and context parameters just like any other given,
8080
but it can only implement a single type.
8181

82-
## Given Whitebox Macro Instances
82+
## Given Macros
8383

84-
An `inline` alias given can be marked as a whitebox macro by writing
85-
`_ <:` in front of the implemented type. Example:
84+
Given aliases can have the `inline` and `transparent` modifiers.
85+
Example:
8686
```scala
87-
inline given mkAnnotations[A, T] as _ <: Annotations[A, T] = ${
87+
transparent inline given mkAnnotations[A, T] as Annotations[A, T] = ${
8888
// code producing a value of a subtype of Annotations
8989
}
9090
```
91-
The type of an application of `mkAnnotations` is the type of its right hand side,
92-
which can be a proper subtype of the declared result type `Annotations[A, T]`.
91+
Since `mkAnnotations` is `transparent`, the type of an application is the type of its right hand side, which can be a proper subtype of the declared result type `Annotations[A, T]`.
9392

9493
## Given Instance Initialization
9594

@@ -104,7 +103,7 @@ Here is the new syntax for given instances, seen as a delta from the [standard c
104103
```
105104
TmplDef ::= ...
106105
| ‘given’ GivenDef
107-
GivenDef ::= [GivenSig] [‘_’ ‘<:’] Type ‘=’ Expr
106+
GivenDef ::= [GivenSig] Type ‘=’ Expr
108107
| [GivenSig] ConstrApp {‘,’ ConstrApp } [TemplateBody]
109108
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘as’
110109
```

docs/docs/reference/metaprogramming/erased-terms.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,11 @@ final class On extends State
171171
final class Off extends State
172172

173173
class Machine[S <: State] {
174-
inline def turnOn() <: Machine[On] = inline erasedValue[S] match {
174+
transparent inline def turnOn(): Machine[On] = inline erasedValue[S] match {
175175
case _: Off => new Machine[On]
176176
case _: On => error("Turning on an already turned on machine")
177177
}
178-
inline def turnOff() <: Machine[Off] = inline erasedValue[S] match {
178+
transparent inline def turnOff(): Machine[Off] = inline erasedValue[S] match {
179179
case _: On => new Machine[Off]
180180
case _: Off => error("Turning off an already turned off machine")
181181
}

0 commit comments

Comments
 (0)