diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 634cd15568dc..7baf74ec90c9 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -131,32 +131,20 @@ class ReifyQuotes extends MacroTransform { val body1 = nested(isQuote = true).transform(body)(quoteContext) super.transformQuotation(body1, quote) } - else body match { - case body: RefTree if isCaptured(body.symbol, level + 1) => - // Optimization: avoid the full conversion when capturing `x` - // in '{ x } to '{ ${x$1} } and go directly to `x$1` - capturers(body.symbol)(body) - case _ => - val (body1, splices) = nested(isQuote = true).splitQuote(body)(quoteContext) - if (level == 0) { - val body2 = - if (body1.isType) body1 - else Inlined(Inliner.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body1) - pickledQuote(body2, splices, body.tpe, isType).withSpan(quote.span) - } - else - body + else { + val (body1, splices) = nested(isQuote = true).splitQuote(body)(quoteContext) + if (level == 0) { + val body2 = + if (body1.isType) body1 + else Inlined(Inliner.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body1) + pickledQuote(body2, splices, body.tpe, isType).withSpan(quote.span) + } + else + body } } private def pickledQuote(body: Tree, splices: List[Tree], originalTp: Type, isType: Boolean)(implicit ctx: Context) = { - def qctx: Tree = { - val qctx = ctx.typer.inferImplicitArg(defn.QuoteContextClass.typeRef, body.span) - if (qctx.tpe.isInstanceOf[SearchFailureType]) - ctx.error(ctx.typer.missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(body.span)) - qctx - } - def pickleAsLiteral(lit: Literal) = { def liftedValue(lifter: Symbol) = ref(lifter).appliedToType(originalTp).select(nme.toExpr).appliedTo(lit) @@ -185,9 +173,9 @@ class ReifyQuotes extends MacroTransform { } if (isType) { - def tag(tagName: String) = ref(defn.QuotedTypeModule).select(tagName.toTermName).appliedTo(qctx) + def tag(tagName: String) = ref(defn.QuotedTypeModule).select(tagName.toTermName) if (splices.isEmpty && body.symbol.isPrimitiveValueClass) tag(s"${body.symbol.name}Tag") - else pickleAsTasty().select(nme.apply).appliedTo(qctx) + else pickleAsTasty() } else getLiteral(body) match { case Some(lit) => pickleAsLiteral(lit) @@ -362,6 +350,10 @@ class ReifyQuotes extends MacroTransform { transform(tree)(ctx.withSource(tree.source)) else reporting.trace(i"Reifier.transform $tree at $level", show = true) { tree match { + case Apply(Select(TypeApply(fn, (body: RefTree) :: Nil), _), _) if fn.symbol == defn.InternalQuoted_typeQuote && isCaptured(body.symbol, level + 1) => + // Optimization: avoid the full conversion when capturing `x` + // in '{ x } to '{ ${x$1} } and go directly to `x$1` + capturers(body.symbol)(body) case tree: RefTree if isCaptured(tree.symbol, level) => val body = capturers(tree.symbol).apply(tree) val splice: Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 2e535d58d0e3..451ec7c660df 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -228,7 +228,7 @@ object Splicer { } interpretQuote(quoted1) - case TypeApply(fn, quoted :: Nil) if fn.symbol == defn.InternalQuoted_typeQuote => + case Apply(Select(TypeApply(fn, quoted :: Nil), _), _) if fn.symbol == defn.InternalQuoted_typeQuote => interpretTypeQuote(quoted) case Literal(Constant(value)) => diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala index e36b97cf9641..699140ff2da4 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala @@ -92,6 +92,13 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap } tree match { + case Apply(Select(Quoted(quotedTree), _), _) if quotedTree.isType => + dropEmptyBlocks(quotedTree) match + case Spliced(t) => + // '[ x.$splice ] --> x + transform(t) + case _ => + super.transform(tree) case Quoted(quotedTree) => val old = inQuoteOrSplice diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 7bbabf459e02..1d1c2db683b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -57,7 +57,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.Pattern) then typedQuotePattern(tree, pt, qctx) else if (tree.quoted.isType) - typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), tree.quoted :: Nil), pt)(using quoteContext) + typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), tree.quoted :: Nil), pt)(using quoteContext).select(nme.apply).appliedTo(qctx) else typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), tree.quoted), pt)(using pushQuoteContext(qctx)).select(nme.apply).appliedTo(qctx) tree1.withSpan(tree.span) @@ -401,7 +401,7 @@ trait QuotesAndSplices { val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx) - else ref(defn.InternalQuoted_typeQuote.termRef).appliedToTypeTree(shape) + else ref(defn.InternalQuoted_typeQuote.termRef).appliedToTypeTree(shape).select(nme.apply).appliedTo(qctx) UnApply( fun = ref(unapplySym.termRef).appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil), implicits = quotedPattern :: Literal(Constant(typeBindings.nonEmpty)) :: qctx :: Nil, diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 6d0bfa588bc6..8eaa2a0e4b86 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -55,7 +55,11 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): def quotedType(t: Type) = if StagingContext.level == 0 then ctx.compilationUnit.needsStaging = true // We will need to run ReifyQuotes - ref(defn.InternalQuoted_typeQuote).appliedToType(t) + val qctx = ctx.typer.inferImplicitArg(defn.QuoteContextClass.typeRef, span) + qctx.tpe match + case tpe: Implicits.SearchFailureType => ctx.error(tpe.msg, ctx.source.atSpan(span)) + case _ => + ref(defn.InternalQuoted_typeQuote).appliedToType(t).select(nme.apply).appliedTo(qctx) formal.argInfos match case arg :: Nil => val deepDealias = new TypeMap: diff --git a/library/src-bootstrapped/scala/internal/quoted/CompileTime.scala b/library/src-bootstrapped/scala/internal/quoted/CompileTime.scala index fe66368905b3..2f6e8402afb2 100644 --- a/library/src-bootstrapped/scala/internal/quoted/CompileTime.scala +++ b/library/src-bootstrapped/scala/internal/quoted/CompileTime.scala @@ -22,7 +22,7 @@ object CompileTime { /** A type quote is desugared by the compiler into a call to this method */ @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.typeQuote`") - def typeQuote[T <: AnyKind]: Type[T] = ??? + def typeQuote[T <: AnyKind]: QuoteContext ?=> Type[T] = ??? /** A splice in a quoted pattern is desugared by the compiler into a call to this method */ @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternHole`") diff --git a/library/src-bootstrapped/scala/quoted/Type.scala b/library/src-bootstrapped/scala/quoted/Type.scala index e982a7d8c15e..5adb2401c83a 100644 --- a/library/src-bootstrapped/scala/quoted/Type.scala +++ b/library/src-bootstrapped/scala/quoted/Type.scala @@ -23,31 +23,31 @@ class Type[T <: AnyKind] private[scala] { /** Some basic type tags, currently incomplete */ object Type { - given UnitTag(using qctx: QuoteContext) as Type[Unit] = + def UnitTag: QuoteContext ?=> Type[Unit] = qctx.tasty.defn.UnitType.seal.asInstanceOf[quoted.Type[Unit]] - given BooleanTag(using qctx: QuoteContext) as Type[Boolean] = + def BooleanTag: QuoteContext ?=> Type[Boolean] = qctx.tasty.defn.BooleanType.seal.asInstanceOf[quoted.Type[Boolean]] - given ByteTag(using qctx: QuoteContext) as Type[Byte] = + def ByteTag: QuoteContext ?=> Type[Byte] = qctx.tasty.defn.ByteType.seal.asInstanceOf[quoted.Type[Byte]] - given CharTag(using qctx: QuoteContext) as Type[Char] = + def CharTag: QuoteContext ?=> Type[Char] = qctx.tasty.defn.CharType.seal.asInstanceOf[quoted.Type[Char]] - given ShortTag(using qctx: QuoteContext) as Type[Short] = + def ShortTag: QuoteContext ?=> Type[Short] = qctx.tasty.defn.ShortType.seal.asInstanceOf[quoted.Type[Short]] - given IntTag(using qctx: QuoteContext) as Type[Int] = + def IntTag: QuoteContext ?=> Type[Int] = qctx.tasty.defn.IntType.seal.asInstanceOf[quoted.Type[Int]] - given LongTag(using qctx: QuoteContext) as Type[Long] = + def LongTag: QuoteContext ?=> Type[Long] = qctx.tasty.defn.LongType.seal.asInstanceOf[quoted.Type[Long]] - given FloatTag(using qctx: QuoteContext) as Type[Float] = + def FloatTag: QuoteContext ?=> Type[Float] = qctx.tasty.defn.FloatType.seal.asInstanceOf[quoted.Type[Float]] - given DoubleTag(using qctx: QuoteContext) as Type[Double] = + def DoubleTag: QuoteContext ?=> Type[Double] = qctx.tasty.defn.DoubleType.seal.asInstanceOf[quoted.Type[Double]] } diff --git a/tests/neg/i6530.scala b/tests/neg-macros/i6530.scala similarity index 100% rename from tests/neg/i6530.scala rename to tests/neg-macros/i6530.scala diff --git a/tests/pos-macros/i4023/Test_2.scala b/tests/pos-macros/i4023/Test_2.scala index 73250b37c2c1..135f83d5afd2 100644 --- a/tests/pos-macros/i4023/Test_2.scala +++ b/tests/pos-macros/i4023/Test_2.scala @@ -1,3 +1,3 @@ -object Test { +def test(using quoted.QuoteContext) = { Macro.ff(3) } diff --git a/tests/pos-macros/i4023b/Test_2.scala b/tests/pos-macros/i4023b/Test_2.scala index 4bd71c799fbf..c6c976437049 100644 --- a/tests/pos-macros/i4023b/Test_2.scala +++ b/tests/pos-macros/i4023b/Test_2.scala @@ -1,3 +1,4 @@ object Test { + given quoted.QuoteContext = ??? Macro.ff[Int] } diff --git a/tests/pos/i6588.scala b/tests/pos-macros/i6588.scala similarity index 89% rename from tests/pos/i6588.scala rename to tests/pos-macros/i6588.scala index c9ad60584be2..2216d3a4bf1d 100644 --- a/tests/pos/i6588.scala +++ b/tests/pos-macros/i6588.scala @@ -2,7 +2,7 @@ import scala.quoted._ inline def foo[T:Type]: Int = 10 -def main = { +def main(using QuoteContext) = { type S = Int foo[S] foo[Int] diff --git a/tests/pos/i7405.scala b/tests/pos-macros/i7405.scala similarity index 100% rename from tests/pos/i7405.scala rename to tests/pos-macros/i7405.scala diff --git a/tests/pos/i7405b.scala b/tests/pos-macros/i7405b.scala similarity index 100% rename from tests/pos/i7405b.scala rename to tests/pos-macros/i7405b.scala diff --git a/tests/pos/i8302.scala b/tests/pos-macros/i8302.scala similarity index 79% rename from tests/pos/i8302.scala rename to tests/pos-macros/i8302.scala index 83ea6a285071..46fd22acc7e0 100644 --- a/tests/pos/i8302.scala +++ b/tests/pos-macros/i8302.scala @@ -1,6 +1,6 @@ import scala.quoted._ def foo[T](using qctx: QuoteContext, tpe: Type[T]): Expr[Any] = - '{ + '{ (using qctx: QuoteContext) => type TT = T val t = '[TT] ??? diff --git a/tests/run-custom-args/run-macros-erased/reflect-isFunctionType/macro_1.scala b/tests/run-custom-args/run-macros-erased/reflect-isFunctionType/macro_1.scala index cb1d7e413bf6..ef3840d8e67e 100644 --- a/tests/run-custom-args/run-macros-erased/reflect-isFunctionType/macro_1.scala +++ b/tests/run-custom-args/run-macros-erased/reflect-isFunctionType/macro_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ -inline def isFunctionType[T:Type]: Boolean = ${ isFunctionTypeImpl('[T]) } +inline def isFunctionType[T]: Boolean = ${ isFunctionTypeImpl('[T]) } def isFunctionTypeImpl[T](tp: Type[T])(using qctx: QuoteContext) : Expr[Boolean] = { import qctx.tasty._ @@ -9,7 +9,7 @@ def isFunctionTypeImpl[T](tp: Type[T])(using qctx: QuoteContext) : Expr[Boolean] } -inline def isContextFunctionType[T:Type]: Boolean = ${ isContextFunctionTypeImpl('[T]) } +inline def isContextFunctionType[T]: Boolean = ${ isContextFunctionTypeImpl('[T]) } def isContextFunctionTypeImpl[T](tp: Type[T])(using qctx: QuoteContext) : Expr[Boolean] = { import qctx.tasty._ @@ -17,14 +17,14 @@ def isContextFunctionTypeImpl[T](tp: Type[T])(using qctx: QuoteContext) : Expr[B } -inline def isErasedFunctionType[T:Type]: Boolean = ${ isErasedFunctionTypeImpl('[T]) } +inline def isErasedFunctionType[T]: Boolean = ${ isErasedFunctionTypeImpl('[T]) } def isErasedFunctionTypeImpl[T](tp: Type[T])(using qctx: QuoteContext) : Expr[Boolean] = { import qctx.tasty._ Expr(tp.unseal.tpe.isErasedFunctionType) } -inline def isDependentFunctionType[T:Type]: Boolean = ${ isDependentFunctionTypeImpl('[T]) } +inline def isDependentFunctionType[T]: Boolean = ${ isDependentFunctionTypeImpl('[T]) } def isDependentFunctionTypeImpl[T](tp: Type[T])(using qctx: QuoteContext) : Expr[Boolean] = { import qctx.tasty._ diff --git a/tests/run-staging/quote-nested-4.check b/tests/run-staging/quote-nested-4.check index 51fbb8dc9e00..ebfd27eb5652 100644 --- a/tests/run-staging/quote-nested-4.check +++ b/tests/run-staging/quote-nested-4.check @@ -1,5 +1,5 @@ -{ - val t: scala.quoted.Type[scala.Predef.String] = scala.internal.quoted.CompileTime.typeQuote[scala.Predef.String] +((qctx: scala.quoted.QuoteContext) ?=> { + val t: scala.quoted.Type[scala.Predef.String] = scala.internal.quoted.CompileTime.typeQuote[scala.Predef.String].apply(using qctx) (t: scala.quoted.Type[scala.Predef.String]) -} +}) diff --git a/tests/run-staging/quote-nested-4.scala b/tests/run-staging/quote-nested-4.scala index f854fd20e7dc..efc12f2ec978 100644 --- a/tests/run-staging/quote-nested-4.scala +++ b/tests/run-staging/quote-nested-4.scala @@ -5,7 +5,7 @@ object Test { given Toolbox = Toolbox.make(getClass.getClassLoader) def main(args: Array[String]): Unit = withQuoteContext { - val q = '{ + val q = '{ (using qctx: QuoteContext) => val t = '[String] t } diff --git a/tests/run-staging/staged-tuples/Test.scala b/tests/run-staging/staged-tuples/Test.scala index e846c765c83e..1a699b1f34a7 100644 --- a/tests/run-staging/staged-tuples/Test.scala +++ b/tests/run-staging/staged-tuples/Test.scala @@ -6,7 +6,7 @@ object Test { def main(args: Array[String]): Unit = { implicit val toolbox: scala.quoted.staging.Toolbox = scala.quoted.staging.Toolbox.make(getClass.getClassLoader) - assert(run(fromArrayStaged('{ Array.empty[Object] }, Some(0))).==(())) + assert(run(fromArrayStaged[Unit]('{ Array.empty[Object] }, Some(0))).==(())) assert(run(fromArrayStaged[Tuple1[String]]('{ Array[Object]("a") }, Some(1))) == Tuple1("a")) assert(run(fromArrayStaged[(String, String)]('{ Array[Object]("a", "b") }, Some(2))) == ("a", "b")) assert(run(fromArrayStaged[(String, String, String)]('{ Array[Object]("a", "b", "c") }, Some(3))) == ("a", "b", "c"))