Skip to content

Remove splice internal representation from public API #6073

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 3 commits into from
Mar 21, 2019
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
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -824,8 +824,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
* The result can be the contents of a term or type splice, which
* will return a term or type tree respectively.
*/
def unapply(tree: tpd.Select)(implicit ctx: Context): Option[tpd.Tree] =
if (tree.symbol.isSplice) Some(tree.qualifier) else None
def unapply(tree: tpd.Tree)(implicit ctx: Context): Option[tpd.Tree] = tree match {
case tree: tpd.Apply if tree.symbol.isSplice => Some(tree.args.head)
case tree: tpd.Select if tree.symbol.isSplice => Some(tree.qualifier)
case _ => None
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -704,12 +704,13 @@ class Definitions {

lazy val QuotedExprType: TypeRef = ctx.requiredClassRef("scala.quoted.Expr")
def QuotedExprClass(implicit ctx: Context): ClassSymbol = QuotedExprType.symbol.asClass
lazy val QuotedExpr_splice : TermSymbol = QuotedExprClass.requiredMethod(nme.splice)

lazy val InternalQuotedModule: TermRef = ctx.requiredModuleRef("scala.internal.Quoted")
def InternalQuotedModuleClass: Symbol = InternalQuotedModule.symbol
lazy val InternalQuoted_exprQuoteR: TermRef = InternalQuotedModuleClass.requiredMethodRef("exprQuote".toTermName)
def InternalQuoted_exprQuote(implicit ctx: Context): Symbol = InternalQuoted_exprQuoteR.symbol
lazy val InternalQuoted_exprSpliceR: TermRef = InternalQuotedModuleClass.requiredMethodRef("exprSplice".toTermName)
def InternalQuoted_exprSplice(implicit ctx: Context): Symbol = InternalQuoted_exprSpliceR.symbol
lazy val InternalQuoted_typeQuoteR: TermRef = InternalQuotedModuleClass.requiredMethodRef("typeQuote".toTermName)
def InternalQuoted_typeQuote(implicit ctx: Context): Symbol = InternalQuoted_typeQuoteR.symbol

Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if (name.isTypeName) typeText(txt)
else txt
case tree @ Select(qual, name) =>
if (tree.hasType && tree.symbol == defn.QuotedExpr_splice) keywordStr("${") ~ toTextLocal(qual) ~ keywordStr("}")
else if (tree.hasType && tree.symbol == defn.QuotedType_splice) typeText("${") ~ toTextLocal(qual) ~ typeText("}")
if (tree.hasType && tree.symbol == defn.QuotedType_splice) typeText("${") ~ toTextLocal(qual) ~ typeText("}")
else if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided (name != nme.CONSTRUCTOR || ctx.settings.YprintDebug.value))
case tree: This =>
Expand All @@ -343,6 +342,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}
else if (fun.hasType && fun.symbol == defn.InternalQuoted_exprQuote)
keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
else if (fun.hasType && fun.symbol == defn.InternalQuoted_exprSplice)
keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
else
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
case tree: TypeApply =>
Expand Down
28 changes: 12 additions & 16 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,23 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
* - If inside inlined code, expand the macro code.
* - If inside of a macro definition, check the validity of the macro.
*/
protected def transformSplice(splice: Select)(implicit ctx: Context): Tree = {
protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree = {
if (level >= 1) {
val body1 = transform(splice.qualifier)(spliceContext)
val splice1 = cpy.Select(splice)(body1, splice.name)
if (splice1.isType) splice1
else addSpliceCast(splice1)
val body1 = transform(body)(spliceContext)
splice match {
case Apply(fun: TypeApply, _) if splice.isTerm =>
// Type of the splice itsel must also be healed
// internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...)
val tp = checkType(splice.sourcePos).apply(splice.tpe.widenTermRefExpr)
cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil)
case splice: Select => cpy.Select(splice)(body1, splice.name)
}
}
else {
assert(!enclosingInlineds.nonEmpty, "unexpanded macro")
assert(ctx.owner.isInlineMethod)
if (Splicer.canBeSpliced(splice.qualifier)) { // level 0 inside an inline definition
transform(splice.qualifier)(spliceContext) // Just check PCP
if (Splicer.canBeSpliced(body)) { // level 0 inside an inline definition
transform(body)(spliceContext) // Just check PCP
splice
}
else { // level 0 inside an inline definition
Expand All @@ -72,15 +77,6 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
}
}


/** Add cast to force boundaries where T and $t (an alias of T) are used to ensure PCP.
* '{ ${...: T} } --> '{ ${...: T}.asInstanceOf[T] } --> '{ ${...: T}.asInstanceOf[$t] }
*/
protected def addSpliceCast(tree: Tree)(implicit ctx: Context): Tree = {
val tp = checkType(tree.sourcePos).apply(tree.tpe.widenTermRefExpr)
tree.cast(tp).withSpan(tree.span)
}

/** If `tree` refers to a locally defined symbol (either directly, or in a pickled type),
* check that its staging level matches the current level. References to types
* that are phase-incorrect can still be healed as follows:
Expand Down
34 changes: 18 additions & 16 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class ReifyQuotes extends MacroTransform {
*/
override protected def transformQuotation(body: Tree, quote: Tree)(implicit ctx: Context): Tree = {
val isType = quote.symbol eq defn.InternalQuoted_typeQuote
assert(!body.symbol.isSplice)
assert(!(body.symbol.isSplice && (body.isInstanceOf[GenericApply[_]] || body.isInstanceOf[Select])))
if (level > 0) {
val body1 = nested(isQuote = true).transform(body)(quoteContext)
super.transformQuotation(body1, quote)
Expand Down Expand Up @@ -222,21 +222,24 @@ class ReifyQuotes extends MacroTransform {
* and make a hole from these parts. Otherwise issue an error, unless we
* are in the body of an inline method.
*/
protected def transformSplice(splice: Select)(implicit ctx: Context): Tree = {
protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree = {
if (level > 1) {
val body1 = nested(isQuote = false).transform(splice.qualifier)(spliceContext)
body1.select(splice.name)
val body1 = nested(isQuote = false).transform(body)(spliceContext)
splice match {
case splice: Apply => cpy.Apply(splice)(splice.fun, body1 :: Nil)
case splice: Select => cpy.Select(splice)(body1, splice.name)
}
}
else {
assert(level == 1, "unexpected top splice outside quote")
val (body1, quotes) = nested(isQuote = false).splitSplice(splice.qualifier)(spliceContext)
val tpe = outer.embedded.getHoleType(splice)
val (body1, quotes) = nested(isQuote = false).splitSplice(body)(spliceContext)
val tpe = outer.embedded.getHoleType(body, splice)
val hole = makeHole(body1, quotes, tpe).withSpan(splice.span)
// We do not place add the inline marker for trees that where lifted as they come from the same file as their
// enclosing quote. Any intemediate splice will add it's own Inlined node and cancel it before splicig the lifted tree.
// Note that lifted trees are not necessarily expressions and that Inlined nodes are expected to be expressions.
// For example we can have a lifted tree containing the LHS of an assignment (see tests/run-with-compiler/quote-var.scala).
if (splice.isType || outer.embedded.isLiftedSymbol(splice.qualifier.symbol)) hole
if (splice.isType || outer.embedded.isLiftedSymbol(body.symbol)) hole
else Inlined(EmptyTree, Nil, hole).withSpan(splice.span)
}
}
Expand Down Expand Up @@ -346,14 +349,13 @@ class ReifyQuotes extends MacroTransform {
override def transform(tree: Tree)(implicit ctx: Context): Tree =
reporting.trace(i"Reifier.transform $tree at $level", show = true) {
tree match {
case TypeApply(Select(spliceTree @ Spliced(_), _), tp) if tree.symbol.isTypeCast =>
// Splice term which should be in the form `${x}.asInstanceOf[T]` where T is an artifact of
// typer to allow pickling/unpickling phase consistent types
transformSplice(spliceTree)

case tree: RefTree if isCaptured(tree.symbol, level) =>
val t = capturers(tree.symbol).apply(tree)
transformSplice(t.select(if (tree.isTerm) nme.splice else tpnme.splice))
val body = capturers(tree.symbol).apply(tree)
val splice: Tree =
if (tree.isType) body.select(tpnme.splice)
else ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body)

transformSplice(body, splice)

case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>
// Shrink size of the tree. The methods have already been inlined.
Expand Down Expand Up @@ -396,11 +398,11 @@ object ReifyQuotes {
}

/** Type used for the hole that will replace this splice */
def getHoleType(splice: tpd.Select)(implicit ctx: Context): Type = {
def getHoleType(body: tpd.Tree, splice: tpd.Tree)(implicit ctx: Context): Type = {
// For most expressions the splice.tpe but there are some types that are lost by lifting
// that can be recoverd from the original tree. Currently the cases are:
// * Method types: the splice represents a method reference
map.get(splice.qualifier.symbol).map(_.tpe.widen).getOrElse(splice.tpe)
map.get(body.symbol).map(_.tpe.widen).getOrElse(splice.tpe)
}

def isLiftedSymbol(sym: Symbol)(implicit ctx: Context): Boolean = map.contains(sym)
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Staging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ class Staging extends MacroTransform {
tree match {
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
val checker = new PCPCheckAndHeal(freshStagingContext) {
override protected def addSpliceCast(tree: Tree)(implicit ctx: Context): Tree = tree

override protected def tryHeal(sym: Symbol, tp: Type, pos: SourcePosition)(implicit ctx: Context): Option[tpd.Tree] = {
def symStr =
if (!tp.isInstanceOf[ThisType]) sym.show
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,5 @@ class SymUtils(val self: Symbol) extends AnyVal {

/** Is symbol a splice operation? */
def isSplice(implicit ctx: Context): Boolean =
self == defn.QuotedExpr_splice || self == defn.QuotedType_splice
self == defn.InternalQuoted_exprSplice || self == defn.QuotedType_splice
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
}
}

/** Transform the splice `splice`. */
protected def transformSplice(splice: Select)(implicit ctx: Context): Tree
/** Transform the splice `splice` which contains the spliced `body`. */
protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree

override def transform(tree: Tree)(implicit ctx: Context): Tree = {
reporting.trace(i"StagingTransformer.transform $tree at $level", show = true) {
Expand Down Expand Up @@ -93,7 +93,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
case tree @ Spliced(splicedTree) =>
dropEmptyBlocks(splicedTree) match {
case Quoted(t) => transform(t) // ${ 'x } --> x
case _ => transformSplice(tree)
case _ => transformSplice(splicedTree, tree)
}

case Block(stats, _) =>
Expand Down
30 changes: 17 additions & 13 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -968,21 +968,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
assert(tree.hasType, tree)
val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this))
val res =
if (tree.symbol == defn.QuotedExpr_splice && level == 0) expandMacro(qual1, tree.span)
else untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
val res = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.sourcePos)
res
}

private def expandMacro(body: Tree, span: Span)(implicit ctx: Context) = {
assert(level == 0)
val inlinedFrom = enclosingInlineds.last
val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx.withSource(inlinedFrom.source))
if (ctx.reporter.hasErrors) EmptyTree
else evaluatedSplice.withSpan(span)
}

override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree =
typed(tree.cond, defn.BooleanType) match {
case cond1 @ ConstantValue(b: Boolean) =>
Expand All @@ -1001,8 +991,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
}
}

override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree =
constToLiteral(betaReduce(super.typedApply(tree, pt)))
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice && level == 0 =>
expandMacro(res.args.head, tree.span)
case res => res
}
}

override def typedMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: Type)(implicit ctx: Context) =
if (!tree.isInline || ctx.owner.isInlineMethod) // don't reduce match of nested inline method yet
Expand Down Expand Up @@ -1159,4 +1154,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
inlineTermBindings(termBindings1.asInstanceOf[List[ValOrDefDef]], tree1)
}
}

private def expandMacro(body: Tree, span: Span)(implicit ctx: Context) = {
assert(level == 0)
val inlinedFrom = enclosingInlineds.last
val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx.withSource(inlinedFrom.source))
if (ctx.reporter.hasErrors) EmptyTree
else evaluatedSplice.withSpan(span)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ object PrepareInlineable {

var isMacro = false
new TreeMapWithStages(freshStagingContext) {
override protected def transformSplice(splice: tpd.Select)(implicit ctx: Context): tpd.Tree = {
override protected def transformSplice(body: tpd.Tree, splice: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
isMacro = true
splice
}
Expand Down
15 changes: 11 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1926,7 +1926,7 @@ class Typer extends Namer
}
}

/** Translate '{ t }` into `scala.quoted.Expr.apply(t)` and `'[T]` into `scala.quoted.Type.apply[T]`
/** Translate `'{ t }` into `scala.quoted.Expr.apply(t)` and `'[T]` into `scala.quoted.Type.apply[T]`
* while tracking the quotation level in the context.
*/
def typedQuote(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = track("typedQuote") {
Expand All @@ -1941,15 +1941,15 @@ class Typer extends Namer
}
}

/** Translate `${ t: Expr[T] }` into expresiion `t.splice` while tracking the quotation level in the context */
/** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */
def typedSplice(tree: untpd.Splice, pt: Type)(implicit ctx: Context): Tree = track("typedSplice") {
checkSpliceOutsideQuote(tree)
tree.expr match {
case untpd.Quote(innerExpr) =>
ctx.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.sourcePos)
typed(innerExpr, pt)
case expr =>
typedSelect(untpd.Select(expr, nme.splice), pt)(spliceContext).withSpan(tree.span)
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSpliceR), tree.expr), pt)(spliceContext).withSpan(tree.span)
}
}

Expand All @@ -1961,7 +1961,14 @@ class Typer extends Namer

private def checkSpliceOutsideQuote(tree: untpd.Tree)(implicit ctx: Context): Unit = {
if (level == 0 && !ctx.owner.isInlineMethod)
ctx.error("splice outside quotes or inline method", tree.sourcePos)
ctx.error("Splice ${...} outside quotes '{...} or inline method", tree.sourcePos)
else if (level < 0)
ctx.error(
"""Splice ${...} at level -1.
|
|Inline method may contain a splice at level 0 but the contents of this splice cannot have a splice.
|""".stripMargin, tree.sourcePos
)
}

/** Retrieve symbol attached to given tree */
Expand Down
3 changes: 2 additions & 1 deletion library/src-bootstrapped/scala/StagedTuple.scala
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ object StagedTuple {
if (!specialize) '{dynamic_++[Self, That]($self, $that)}
else {
def genericConcat(xs: Expr[Tuple], ys: Expr[Tuple]): Expr[Tuple] =
fromArrayStaged[Tuple]('{${ toArrayStaged(xs, None) } ++ ${ toArrayStaged(ys, None) }}, None)
// TODO remove ascriptions when #6126 is fixed
fromArrayStaged[Tuple]('{${ toArrayStaged(xs, None) } ++ (${ toArrayStaged(ys, None) }: Array[Object])}, None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the ascription needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the desugaring of ${...} to def exprSplice[T](x: Expr[T]): T we get now need to infer T as well as the types of ++.

The acctual error is

[error] 206 |        fromArrayStaged[Tuple]('{${ toArrayStaged(xs, None) } ++ ${ toArrayStaged(ys, None) }}, None)
[error]     |                                                                    ^^^^^^^^^^^^^^^^^^^^^^^
[error]     |    Found:    quoted.Expr[Array[Object]]
[error]     |    Required: quoted.Expr[scala.collection.GenTraversableOnce[Object]]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's problematic. We should avoid type ascriptions that need to be added for obscure reasons. Should we change the desugaring instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is an instance of #6126 which is quite specific to ++ with Arrays. If fixed this would not be an issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a look at #6126.


val res = selfSize match {
case Some(0) =>
Expand Down
4 changes: 4 additions & 0 deletions library/src-bootstrapped/scala/internal/Quoted.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ object Quoted {
def exprQuote[T](x: T): Expr[T] =
throw new Error("Internal error: this method call should have been replaced by the compiler")

/** A term splice is desugared by the compiler into a call to this method */
def exprSplice[T](x: Expr[T]): T =
throw new Error("Internal error: this method call should have been replaced by the compiler")

/** A type quote is desugared by the compiler into a call to this method */
def typeQuote[T <: AnyKind]: Type[T] =
throw new Error("Internal error: this method call should have been replaced by the compiler")
Expand Down
4 changes: 4 additions & 0 deletions library/src-non-bootstrapped/scala/internal/Quoted.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ object Quoted {
def exprQuote[T](x: T): Expr[T] =
throw new Error("Internal error: this method call should have been replaced by the compiler")

/** A term splice is desugared by the compiler into a call to this method */
def exprSplice[T](x: Expr[T]): T =
throw new Error("Internal error: this method call should have been replaced by the compiler")

/** A type quote is desugared by the compiler into a call to this method */
def typeQuote[T/* <: AnyKind */]: Type[T] =
throw new Error("Internal error: this method call should have been replaced by the compiler")
Expand Down
2 changes: 0 additions & 2 deletions library/src/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import scala.runtime.quoted.Unpickler.Pickled

sealed abstract class Expr[+T] {

final def `$splice`: T = throw new Error("splice should have been compiled away")

/** Evaluate the contents of this expression and return the result.
*
* May throw a FreeVariableError on expressions that came from a macro.
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/i4774f.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ object Test {
'{ def y: T = $x; ${ loop('y) } }

def loop2[T](x: Expr[T])(implicit t: Type[T]): Expr[T] =
'{ def y(): T = $x; ${ loop('{y()}) } }
'{ def y(): T = $x; ${ loop2('{y()}) } }
}
22 changes: 22 additions & 0 deletions tests/pos/quote-nested.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import scala.annotation.tailrec
import scala.quoted._

object Macro {

inline def foo: Unit = ${ nested() }

private def nested(): Expr[Unit] = '{
var i = 0
${
val x: Expr[Double] = '{
val y: Boolean = ${
val z: Expr[Int] = '{i + 3}
'{true}
}
4.2
}
'{}
}
()
}
}
10 changes: 10 additions & 0 deletions tests/pos/quote-whitebox-2/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import scala.quoted._

object Macro {

inline def charOrString(inline str: String) <: Any = ${ impl(str) }

def impl(str: String) = if (str.length == 1) str.charAt(0).toExpr else str.toExpr

}
Loading