From 47a15866e733e311c6649780f27a1b58cf871721 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 11 May 2020 17:25:38 +0200 Subject: [PATCH 1/3] Keep QuoteContext found in typer for quoted.Type We already do this for quoted terms. We missed this last one. After this change we won't need to do implicit search after pickling anymore. --- .../tools/dotc/transform/ReifyQuotes.scala | 40 ++++++++----------- .../dotty/tools/dotc/transform/Splicer.scala | 2 +- .../dotc/transform/TreeMapWithStages.scala | 7 ++++ .../tools/dotc/typer/QuotesAndSplices.scala | 4 +- .../dotty/tools/dotc/typer/Synthesizer.scala | 6 ++- .../scala/internal/quoted/CompileTime.scala | 2 +- .../src-bootstrapped/scala/quoted/Type.scala | 18 ++++----- tests/{neg => neg-macros}/i6530.scala | 0 tests/pos-macros/i4023/Test_2.scala | 2 +- tests/pos-macros/i4023b/Test_2.scala | 1 + tests/{pos => pos-macros}/i6588.scala | 2 +- tests/{pos => pos-macros}/i7405.scala | 0 tests/{pos => pos-macros}/i7405b.scala | 0 tests/{pos => pos-macros}/i8302.scala | 2 +- .../reflect-isFunctionType/macro_1.scala | 8 ++-- tests/run-staging/quote-nested-4.check | 6 +-- tests/run-staging/quote-nested-4.scala | 2 +- tests/run-staging/staged-tuples/Test.scala | 2 +- 18 files changed, 54 insertions(+), 50 deletions(-) rename tests/{neg => neg-macros}/i6530.scala (100%) rename tests/{pos => pos-macros}/i6588.scala (89%) rename tests/{pos => pos-macros}/i7405.scala (100%) rename tests/{pos => pos-macros}/i7405b.scala (100%) rename tests/{pos => pos-macros}/i8302.scala (79%) 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..6782132f213c 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] +((qtx: scala.quoted.QuoteContext) ?=> { + val t: scala.quoted.Type[scala.Predef.String] = scala.internal.quoted.CompileTime.typeQuote[scala.Predef.String].apply(using qtx) (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..dea01f02f193 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 = '{ (qtx: 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")) From 9118e59e9c01b2c921bbdac1281a6ca49e738e68 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 13 May 2020 12:01:08 +0200 Subject: [PATCH 2/3] Update tests/run-staging/quote-nested-4.scala Co-authored-by: Fengyun Liu --- tests/run-staging/quote-nested-4.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-staging/quote-nested-4.scala b/tests/run-staging/quote-nested-4.scala index dea01f02f193..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 = '{ (qtx: QuoteContext) ?=> + val q = '{ (using qctx: QuoteContext) => val t = '[String] t } From 243ac3b81aa6b6374a9cd29a6f5ea84297bbb3f5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 13 May 2020 13:29:55 +0200 Subject: [PATCH 3/3] Adapt test output --- tests/run-staging/quote-nested-4.check | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-staging/quote-nested-4.check b/tests/run-staging/quote-nested-4.check index 6782132f213c..ebfd27eb5652 100644 --- a/tests/run-staging/quote-nested-4.check +++ b/tests/run-staging/quote-nested-4.check @@ -1,5 +1,5 @@ -((qtx: scala.quoted.QuoteContext) ?=> { - val t: scala.quoted.Type[scala.Predef.String] = scala.internal.quoted.CompileTime.typeQuote[scala.Predef.String].apply(using qtx) +((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]) })