Skip to content

Fix #8619: Demand transparent for whitebox inlines #8630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 1 addition & 22 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,6 @@ object desugar {
* def f[T](x: Int)(y: String)(implicit evidence$0: B[T]) = ...
* def f$default$1[T] = 1
* def f$default$2[T](x: Int) = x + "m"
*
* 3. Convert <: T to : T in specializing inline methods. E.g.
*
* inline def f(x: Boolean) <: Any = if (x) 1 else ""
* ==>
* inline def f(x: Boolean): Any = if (x) 1 else ""
*
* 4. Upcast non-specializing inline methods. E.g.
*
* inline def f(x: Boolean): Any = if (x) 1 else ""
* ==>
* inline def f(x: Boolean): Any = (if (x) 1 else ""): Any
*/
private def defDef(meth0: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
val meth @ DefDef(_, tparams, vparamss, tpt, rhs) = transformQuotedPatternName(meth0)
Expand Down Expand Up @@ -247,18 +235,9 @@ object desugar {
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
}

var meth1 = addEvidenceParams(
val meth1 = addEvidenceParams(
cpy.DefDef(meth)(name = methName, tparams = tparams1), epbuf.toList)

if (meth1.mods.is(Inline))
meth1.tpt match {
case TypeBoundsTree(_, tpt1, _) =>
meth1 = cpy.DefDef(meth1)(tpt = tpt1)
case tpt if !tpt.isEmpty && !meth1.rhs.isEmpty =>
meth1 = cpy.DefDef(meth1)(rhs = Typed(meth1.rhs, tpt))
case _ =>
}

/** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
case vparams :: vparamss1 =>
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class Lazy()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Lazy)

case class Inline()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Inline)

case class Transparent()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.EmptyFlags)
}

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

/** Property key containing TypeTrees whose type is computed
/** Property key containing TypeTrees whose type is computed
* from the symbol in this type. These type trees have marker trees
* TypeRefOfSym or InfoOfSym as their originals.
*/
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ object StdNames {
val toString_ : N = "toString"
val toTypeConstructor: N = "toTypeConstructor"
val tpe : N = "tpe"
val transparent : N = "transparent"
val tree : N = "tree"
val true_ : N = "true"
val typedProductIterator: N = "typedProductIterator"
Expand Down
21 changes: 15 additions & 6 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ object Parsers {
import reporting.Message
import reporting.messages._

val AllowOldWhiteboxSyntax = true

case class OpInfo(operand: Tree, operator: Ident, offset: Offset)

class ParensCounters {
Expand Down Expand Up @@ -2692,6 +2694,7 @@ object Parsers {
case nme.inline => Mod.Inline()
case nme.opaque => Mod.Opaque()
case nme.open => Mod.Open()
case nme.transparent => Mod.Transparent()
}
}

Expand Down Expand Up @@ -2748,7 +2751,7 @@ object Parsers {
* | AccessModifier
* | override
* | opaque
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased | inline
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased | inline | transparent
*/
def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
@tailrec
Expand All @@ -2766,7 +2769,11 @@ object Parsers {
}
else
mods
normalize(loop(start))
val result = normalize(loop(start))
for case mod @ Mod.Transparent() <- result.mods do
if !result.is(Inline) then
syntaxError(em"`transparent` can only be used in conjunction with `inline`", mod.span)
result
}

val funTypeArgMods: BitSet = BitSet(ERASED)
Expand Down Expand Up @@ -3248,9 +3255,10 @@ object Parsers {
case rparamss =>
leadingVparamss ::: rparamss
var tpt = fromWithinReturnType {
if in.token == SUBTYPE && mods.is(Inline) then
if in.token == SUBTYPE && mods.is(Inline) && AllowOldWhiteboxSyntax then
in.nextToken()
TypeBoundsTree(EmptyTree, toplevelTyp())
mods1 = addMod(mods1, Mod.Transparent())
toplevelTyp()
else typedOpt()
}
if (in.isScala2CompatMode) newLineOptWhenFollowedBy(LBRACE)
Expand Down Expand Up @@ -3516,12 +3524,13 @@ object Parsers {
accept(EQUALS)
mods1 |= Final
DefDef(name, tparams, vparamss, tpt, subExpr())
if in.token == USCORE then
if in.token == USCORE && AllowOldWhiteboxSyntax then
if !mods.is(Inline) then
syntaxError("`_ <:` is only allowed for given with `inline` modifier")
in.nextToken()
accept(SUBTYPE)
givenAlias(TypeBoundsTree(EmptyTree, toplevelTyp()))
mods1 = addMod(mods1, Mod.Transparent())
givenAlias(toplevelTyp())
else
val parents = constrApps(commaOK = true, templateCanFollow = true)
if in.token == EQUALS && parents.length == 1 && parents.head.isType then
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -282,5 +282,5 @@ object Tokens extends TokensCommon {

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

final val softModifierNames = Set(nme.inline, nme.opaque, nme.open)
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open, nme.transparent)
}
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
val rawFlags = if (sym.exists) sym.flags else mods.flags
if (rawFlags.is(Param)) flagMask = flagMask &~ Given
val flags = rawFlags & flagMask
val flagsText = toTextFlags(sym, flags)
var flagsText = toTextFlags(sym, flags)
if mods.hasMod(classOf[untpd.Mod.Transparent]) then
flagsText = "transparent " ~ flagsText
val annotations =
if (sym.exists) sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree)
else mods.annotations.filterNot(tree => dropAnnotForModText(tree.symbol))
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -863,10 +863,10 @@ class Namer { typer: Typer =>

private def addInlineInfo(sym: Symbol) = original match {
case original: untpd.DefDef if sym.isInlineMethod =>
PrepareInlineable.registerInlineInfo(
sym,
implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
)(localContext(sym))
def rhsToInline(using Context): tpd.Tree =
val mdef = typedAheadExpr(original).asInstanceOf[tpd.DefDef]
PrepareInlineable.wrapRHS(original, mdef.tpt, mdef.rhs)
PrepareInlineable.registerInlineInfo(sym, rhsToInline)(localContext(sym))
case _ =>
}

Expand Down
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ object PrepareInlineable {
def isLocal(sym: Symbol, inlineMethod: Symbol)(implicit ctx: Context): Boolean =
isLocalOrParam(sym, inlineMethod) && !(sym.is(Param) && sym.owner == inlineMethod)

/** The type ascription `rhs: tpt`, unless `original` is `transparent`. */
def wrapRHS(original: untpd.DefDef, tpt: Tree, rhs: Tree)(using Context): Tree =
if original.mods.hasMod(classOf[untpd.Mod.Transparent]) then rhs
else Typed(rhs, tpt)

/** Register inline info for given inlineable method `sym`.
*
* @param sym The symbol denotation of the inlineable method for which info is registered
Expand All @@ -213,7 +218,7 @@ object PrepareInlineable {
* to have the inline method as owner.
*/
def registerInlineInfo(
inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit =
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 || ann.isEvaluating =>
Expand All @@ -223,7 +228,7 @@ object PrepareInlineable {
inlined.updateAnnotation(LazyBodyAnnotation {
given ctx as Context = inlineCtx
val initialErrorCount = ctx.reporter.errorCount
var inlinedBody = treeExpr(ctx)
var inlinedBody = treeExpr
if (ctx.reporter.errorCount == initialErrorCount) {
inlinedBody = ctx.compilationUnit.inlineAccessors.makeInlineable(inlinedBody)
checkInlineMethod(inlined, inlinedBody)
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1735,9 +1735,10 @@ class Typer extends Namer

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

if (sym.isInlineMethod)
PrepareInlineable.registerInlineInfo(sym, _ => rhs1)
PrepareInlineable.registerInlineInfo(sym, rhsToInline)

if (sym.isConstructor && !sym.isPrimaryConstructor) {
val ename = sym.erasedName
Expand Down
18 changes: 9 additions & 9 deletions compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947 = {
val source = """class Foo {
| inline def track[T](inline f: T) <: T = {
| transparent inline def track[T](inline f: T): T = {
| foo("tracking") // line 3
| f // line 4
| }
Expand Down Expand Up @@ -134,11 +134,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947b = {
val source = """class Foo {
| inline def track2[T](inline f: T) <: T = {
| transparent inline def track2[T](inline f: T): T = {
| foo("tracking2") // line 3
| f // line 4
| }
| inline def track[T](inline f: T) <: T = {
| transparent inline def track[T](inline f: T): T = {
| foo("tracking") // line 7
| track2 { // line 8
| f // line 9
Expand Down Expand Up @@ -194,11 +194,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947c = {
val source = """class Foo {
| inline def track2[T](inline f: T) <: T = {
| transparent inline def track2[T](inline f: T): T = {
| foo("tracking2") // line 3
| f // line 4
| }
| inline def track[T](inline f: T) <: T = {
| transparent inline def track[T](inline f: T): T = {
| track2 { // line 7
| foo("fgh") // line 8
| f // line 9
Expand Down Expand Up @@ -254,11 +254,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947d = {
val source = """class Foo {
| inline def track2[T](inline f: T) <: T = {
| transparent inline def track2[T](inline f: T): T = {
| foo("tracking2") // line 3
| f // line 4
| }
| inline def track[T](inline f: T) <: T = {
| transparent inline def track[T](inline f: T): T = {
| track2 { // line 7
| track2 { // line 8
| f // line 9
Expand Down Expand Up @@ -319,7 +319,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {
| def test: Int = {
| var a = 10
|
| inline def f() = {
| transparent inline def f() = {
| a += 1
| }
|
Expand Down Expand Up @@ -356,7 +356,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {
val source = """class Test:
| given Int = 0
| def f(): Int ?=> Boolean = true : (Int ?=> Boolean)
| inline def g(): Int ?=> Boolean = true
| transparent inline def g(): Int ?=> Boolean = true
| def test = g()
""".stripMargin

Expand Down
3 changes: 2 additions & 1 deletion docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ yield
### Soft keywords

```
as derives extension inline on opaque open using
as derives extension inline on opaque open transparent
using
* + -
```

Expand Down
13 changes: 6 additions & 7 deletions docs/docs/reference/contextual/givens.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,16 @@ given (using config: Config) as Factory = MemoizingFactory(config)
An alias given can have type parameters and context parameters just like any other given,
but it can only implement a single type.

## Given Whitebox Macro Instances
## Given Macros

An `inline` alias given can be marked as a whitebox macro by writing
`_ <:` in front of the implemented type. Example:
Given aliases can have the `inline` and `transparent` modifiers.
Example:
```scala
inline given mkAnnotations[A, T] as _ <: Annotations[A, T] = ${
transparent inline given mkAnnotations[A, T] as Annotations[A, T] = ${
// code producing a value of a subtype of Annotations
}
```
The type of an application of `mkAnnotations` is the type of its right hand side,
which can be a proper subtype of the declared result type `Annotations[A, T]`.
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]`.

## Given Instance Initialization

Expand All @@ -104,7 +103,7 @@ Here is the new syntax for given instances, seen as a delta from the [standard c
```
TmplDef ::= ...
| ‘given’ GivenDef
GivenDef ::= [GivenSig] [‘_’ ‘<:’] Type ‘=’ Expr
GivenDef ::= [GivenSig] Type ‘=’ Expr
| [GivenSig] ConstrApp {‘,’ ConstrApp } [TemplateBody]
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘as’
```
4 changes: 2 additions & 2 deletions docs/docs/reference/metaprogramming/erased-terms.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,11 @@ final class On extends State
final class Off extends State

class Machine[S <: State] {
inline def turnOn() <: Machine[On] = inline erasedValue[S] match {
transparent inline def turnOn(): Machine[On] = inline erasedValue[S] match {
case _: Off => new Machine[On]
case _: On => error("Turning on an already turned on machine")
}
inline def turnOff() <: Machine[Off] = inline erasedValue[S] match {
transparent inline def turnOff(): Machine[Off] = inline erasedValue[S] match {
case _: On => new Machine[Off]
case _: Off => error("Turning off an already turned off machine")
}
Expand Down
Loading