diff --git a/community-build/community-projects/scalatest b/community-build/community-projects/scalatest index 541e71561a1e..903de7647602 160000 --- a/community-build/community-projects/scalatest +++ b/community-build/community-projects/scalatest @@ -1 +1 @@ -Subproject commit 541e71561a1ec377be7936a00ad954365c290114 +Subproject commit 903de7647602592fc7c206c62ce7dc3d087dc4a0 diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 65d71b62e4e3..ad6d080273dd 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -14,6 +14,7 @@ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.tasty.TreePickler.Hole import dotty.tools.dotc.core.tasty.{PositionPickler, TastyPickler, TastyPrinter, TastyString} import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode +import dotty.tools.dotc.tastyreflect.ReflectionImpl import scala.internal.quoted._ import scala.reflect.ClassTag @@ -47,7 +48,7 @@ object PickledQuotes { forceAndCleanArtefacts.transform(unpickled) case expr: TastyTreeExpr[Tree] @unchecked => healOwner(expr.tree) case expr: FunctionAppliedTo[_] => - functionAppliedTo(quotedExprToTree(expr.f), expr.args.map(arg => quotedExprToTree(arg)).toList) + functionAppliedTo(quotedExprToTree(expr.f), expr.args.map(arg => quotedExprToTree(arg(new scala.quoted.QuoteContext(ReflectionImpl(ctx))))).toList) } /** Transform the expression into its fully spliced TypeTree */ diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 92331ad80bf1..f53894969d03 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1273,14 +1273,15 @@ class TreeUnpickler(reader: TastyReader, val args = until(end)(readTerm()) val splice = splices(idx) def wrap(arg: Tree) = - if (arg.isTerm) new TastyTreeExpr(arg) + if (arg.isTerm) given (qctx: scala.quoted.QuoteContext) => new TastyTreeExpr(arg) else new TreeType(arg) val reifiedArgs = args.map(wrap) val filled = if (isType) { val quotedType = splice.asInstanceOf[Seq[Any] => quoted.Type[_]](reifiedArgs) PickledQuotes.quotedTypeToTree(quotedType) } else { - val quotedExpr = splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](reifiedArgs) + val splice1 = splice.asInstanceOf[Seq[Any] => given scala.quoted.QuoteContext => quoted.Expr[_]] + val quotedExpr = splice1(reifiedArgs) given new scala.quoted.QuoteContext(tastyreflect.ReflectionImpl(ctx)) PickledQuotes.quotedExprToTree(quotedExpr) } // We need to make sure a hole is created with the source file of the surrounding context, even if diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index bbb49d505b1a..1a46d787dc72 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -14,16 +14,19 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.tastyreflect.FromSymbol.{definitionFromSym, packageDefFromSym} import dotty.tools.dotc.parsing.Parsers.Parser import dotty.tools.dotc.typer.Implicits.{AmbiguousImplicits, DivergingImplicit, NoMatchingImplicits, SearchFailure, SearchFailureType} -import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.util.{SourceFile, SourcePosition, Spans} import scala.tasty.reflect.Kernel -class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.SourcePosition) extends Kernel { +class KernelImpl(val rootContext: core.Contexts.Context) extends Kernel { private implicit def ctx: core.Contexts.Context = rootContext def settings: Settings = rootContext.settings + def rootPosition: util.SourcePosition = + tastyreflect.MacroExpansion.position.getOrElse(SourcePosition(rootContext.source, Spans.NoSpan)) + // // CONTEXT // diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/MacroExpansion.scala b/compiler/src/dotty/tools/dotc/tastyreflect/MacroExpansion.scala new file mode 100644 index 000000000000..4c2297273b8c --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tastyreflect/MacroExpansion.scala @@ -0,0 +1,18 @@ +package dotty.tools.dotc.tastyreflect + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.util.{Property, SourcePosition, Spans} + +object MacroExpansion { + + private val MacroExpansionPosition = new Property.Key[SourcePosition] + + def position(implicit ctx: Context): Option[SourcePosition] = + ctx.property(MacroExpansionPosition) + + def context(inlinedFrom: tpd.Tree)(implicit ctx: Context): Context = + ctx.fresh.setProperty(MacroExpansionPosition, SourcePosition(inlinedFrom.source, inlinedFrom.span)).withSource(inlinedFrom.source) + +} diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala index 146f74db64cd..874906f7422f 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala @@ -2,20 +2,17 @@ package dotty.tools.dotc.tastyreflect import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core._ -import dotty.tools.dotc.util.{SourcePosition, Spans} +import dotty.tools.dotc.core.Contexts._ import scala.quoted.show.SyntaxHighlight object ReflectionImpl { def apply(rootContext: Contexts.Context): scala.tasty.Reflection = - apply(rootContext, SourcePosition(rootContext.source, Spans.NoSpan)) - - def apply(rootContext: Contexts.Context, rootPosition: SourcePosition): scala.tasty.Reflection = - new scala.tasty.Reflection(new KernelImpl(rootContext, rootPosition)) + new scala.tasty.Reflection(new KernelImpl(rootContext)) def showTree(tree: tpd.Tree)(implicit ctx: Contexts.Context): String = { - val refl = new scala.tasty.Reflection(new KernelImpl(ctx, tree.sourcePos)) + val refl = new scala.tasty.Reflection(new KernelImpl(MacroExpansion.context(tree))) val reflCtx = ctx.asInstanceOf[refl.Context] val reflTree = tree.asInstanceOf[refl.Tree] val syntaxHighlight = diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 7ab344a263e2..654da8141848 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -177,7 +177,6 @@ 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 && (body.isInstanceOf[GenericApply[_]] || body.isInstanceOf[Select]))) if (level > 0) { val body1 = nested(isQuote = true).transform(body)(quoteContext) super.transformQuotation(body1, quote) @@ -209,35 +208,37 @@ class ReifyQuotes extends MacroTransform { qctx } - def liftedValue[T](value: T, name: TermName, qctx: Tree) = - ref(defn.LiftableModule).select(name).select("toExpr".toTermName).appliedTo(Literal(Constant(value))).select(nme.apply).appliedTo(qctx) - - def pickleAsValue[T](value: T) = value match { - case null => ref(defn.QuotedExprModule).select("nullExpr".toTermName).appliedTo(qctx) - case _: Unit => ref(defn.QuotedExprModule).select("unitExpr".toTermName).appliedTo(qctx) - case _: Boolean => liftedValue(value, "Liftable_Boolean_delegate".toTermName, qctx) - case _: Byte => liftedValue(value, "Liftable_Byte_delegate".toTermName, qctx) - case _: Short => liftedValue(value, "Liftable_Short_delegate".toTermName, qctx) - case _: Int => liftedValue(value, "Liftable_Int_delegate".toTermName, qctx) - case _: Long => liftedValue(value, "Liftable_Long_delegate".toTermName, qctx) - case _: Float => liftedValue(value, "Liftable_Float_delegate".toTermName, qctx) - case _: Double => liftedValue(value, "Liftable_Double_delegate".toTermName, qctx) - case _: Char => liftedValue(value, "Liftable_Char_delegate".toTermName, qctx) - case _: String => liftedValue(value, "Liftable_String_delegate".toTermName, qctx) + def liftedValue[T](value: T, name: TermName) = + ref(defn.LiftableModule).select(name).select("toExpr".toTermName).appliedTo(Literal(Constant(value))) + + def pickleAsValue[T](value: T) = { + value match { + case null => ref(defn.QuotedExprModule).select("nullExpr".toTermName) + case _: Unit => ref(defn.QuotedExprModule).select("unitExpr".toTermName) + case _: Boolean => liftedValue(value, "Liftable_Boolean_delegate".toTermName) + case _: Byte => liftedValue(value, "Liftable_Byte_delegate".toTermName) + case _: Short => liftedValue(value, "Liftable_Short_delegate".toTermName) + case _: Int => liftedValue(value, "Liftable_Int_delegate".toTermName) + case _: Long => liftedValue(value, "Liftable_Long_delegate".toTermName) + case _: Float => liftedValue(value, "Liftable_Float_delegate".toTermName) + case _: Double => liftedValue(value, "Liftable_Double_delegate".toTermName) + case _: Char => liftedValue(value, "Liftable_Char_delegate".toTermName) + case _: String => liftedValue(value, "Liftable_String_delegate".toTermName) + } } def pickleAsTasty() = { val meth = if (isType) ref(defn.Unpickler_unpickleType).appliedToType(originalTp) else ref(defn.Unpickler_unpickleExpr).appliedToType(originalTp.widen) - def wildcardQuotedType = defn.QuotedTypeClass.typeRef.appliedTo(WildcardType) val spliceResType = - if (isType) wildcardQuotedType - else defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType) | wildcardQuotedType - meth.appliedTo( - liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType), - liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), spliceResType))) + if (isType) defn.QuotedTypeClass.typeRef.appliedTo(WildcardType) + else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)) | defn.QuotedTypeClass.typeRef.appliedTo(WildcardType) + val pickledQuoteStrings = liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType) + val splicesList = liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), spliceResType)) + meth.appliedTo(pickledQuoteStrings, splicesList) } + if (splices.nonEmpty) pickleAsTasty() else if (isType) { def tag(tagName: String) = ref(defn.QuotedTypeModule).select(tagName.toTermName).appliedTo(qctx) @@ -316,7 +317,7 @@ class ReifyQuotes extends MacroTransform { assert(tpw.isInstanceOf[ValueType]) val argTpe = if (tree.isType) defn.QuotedTypeClass.typeRef.appliedTo(tpw) - else defn.QuotedExprClass.typeRef.appliedTo(tpw) + else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw)) val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).cast(argTpe) val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg) i += 1 @@ -357,7 +358,9 @@ class ReifyQuotes extends MacroTransform { val tree2 = transform(tree) capturers --= outer.localSymbols - seq(captured.result().valuesIterator.toList, tree2) + val captures = captured.result().valuesIterator.toList + if (captures.isEmpty) tree2 + else Block(captures, tree2) } /** Returns true if this tree will be captured by `makeLambda`. Checks phase consistency and presence of capturer. */ diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 1a1db7b33673..0bf8902dd44d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -41,15 +41,18 @@ object Splicer { val interpreter = new Interpreter(pos, classLoader) try { // Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree - val interpretedExpr = interpreter.interpret[scala.quoted.Expr[Any]](tree) - interpretedExpr.fold(tree)(x => PickledQuotes.quotedExprToTree(x)) + val interpretedExpr = interpreter.interpret[scala.quoted.QuoteContext => scala.quoted.Expr[Any]](tree) + interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(new scala.quoted.QuoteContext(ReflectionImpl(ctx))))) } catch { + case ex: StopInterpretation => + ctx.error(ex.msg, ex.pos) + EmptyTree case NonFatal(ex) => val msg = s"""Failed to evaluate macro. | Caused by ${ex.getClass}: ${if (ex.getMessage == null) "" else ex.getMessage} - | ${ex.getStackTrace.takeWhile(_.getClassName != "dotty.tools.dotc.transform.Splicer$").init.mkString("\n ")} + | ${ex.getStackTrace.takeWhile(_.getClassName != "dotty.tools.dotc.transform.Splicer$").drop(1).mkString("\n ")} """.stripMargin ctx.error(msg, pos) EmptyTree @@ -65,19 +68,24 @@ object Splicer { def checkValidMacroBody(tree: Tree)(implicit ctx: Context): Unit = tree match { case Quoted(_) => // ok case _ => - def checkValidStat(tree: Tree): Unit = tree match { + type Env = Set[Symbol] + + def checkValidStat(tree: Tree) given Env: Env = tree match { case tree: ValDef if tree.symbol.is(Synthetic) => // Check val from `foo(j = x, i = y)` which it is expanded to // `val j$1 = x; val i$1 = y; foo(i = i$1, j = j$1)` checkIfValidArgument(tree.rhs) + the[Env] + tree.symbol case _ => ctx.error("Macro should not have statements", tree.sourcePos) + the[Env] } - def checkIfValidArgument(tree: Tree): Unit = tree match { + + def checkIfValidArgument(tree: Tree) given Env: Unit = tree match { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(TypeApply(fn, _), quoted :: Nil) if fn.symbol == defn.InternalQuoted_exprQuote => + case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.InternalQuoted_exprQuote => // OK case TypeApply(fn, quoted :: Nil) if fn.symbol == defn.InternalQuoted_typeQuote => @@ -101,7 +109,7 @@ object Splicer { case SeqLiteral(elems, _) => elems.foreach(checkIfValidArgument) - case tree: Ident if tree.symbol.is(Inline) || tree.symbol.is(Synthetic) => + case tree: Ident if tree.symbol.is(Inline) || the[Env].contains(tree.symbol) => // OK case _ => @@ -114,10 +122,14 @@ object Splicer { | * Literal values of primitive types |""".stripMargin, tree.sourcePos) } - def checkIfValidStaticCall(tree: Tree): Unit = tree match { + + def checkIfValidStaticCall(tree: Tree) given Env: Unit = tree match { + case closureDef(ddef @ DefDef(_, Nil, (ev :: Nil) :: Nil, _, _)) if ddef.symbol.info.isContextualMethod => + checkIfValidStaticCall(ddef.rhs) given (the[Env] + ev.symbol) + case Block(stats, expr) => - stats.foreach(checkValidStat) - checkIfValidStaticCall(expr) + val newEnv = stats.foldLeft(the[Env])((env, stat) => checkValidStat(stat) given env) + checkIfValidStaticCall(expr) given newEnv case Typed(expr, _) => checkIfValidStaticCall(expr) @@ -126,6 +138,8 @@ object Splicer { if (fn.symbol.isConstructor && fn.symbol.owner.owner.is(Package)) || fn.symbol.is(Module) || fn.symbol.isStatic || (fn.qualifier.symbol.is(Module) && fn.qualifier.symbol.isStatic) => + if (fn.symbol.flags.is(Inline)) + ctx.error("Macro cannot be implemented with an `inline` method", fn.sourcePos) args.flatten.foreach(checkIfValidArgument) case _ => @@ -136,35 +150,29 @@ object Splicer { |""".stripMargin, tree.sourcePos) } - checkIfValidStaticCall(tree) + checkIfValidStaticCall(tree) given Set.empty } /** Tree interpreter that evaluates the tree */ private class Interpreter(pos: SourcePosition, classLoader: ClassLoader)(implicit ctx: Context) { - type Env = Map[Name, Object] + type Env = Map[Symbol, Object] /** Returns the interpreted result of interpreting the code a call to the symbol with default arguments. * Return Some of the result or None if some error happen during the interpretation. */ def interpret[T](tree: Tree)(implicit ct: ClassTag[T]): Option[T] = { - try { - interpretTree(tree)(Map.empty) match { - case obj: T => Some(obj) - case obj => - // TODO upgrade to a full type tag check or something similar - ctx.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", pos) - None - } - } catch { - case ex: StopInterpretation => - ctx.error(ex.msg, ex.pos) + interpretTree(tree)(Map.empty) match { + case obj: T => Some(obj) + case obj => + // TODO upgrade to a full type tag check or something similar + ctx.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", pos) None } } def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { - case Apply(TypeApply(fn, _), quoted :: Nil) if fn.symbol == defn.InternalQuoted_exprQuote => + case Apply(Select(Apply(TypeApply(fn, _), quoted :: Nil), nme.apply), _) if fn.symbol == defn.InternalQuoted_exprQuote => val quoted1 = quoted match { case quoted: Ident if quoted.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter @@ -199,14 +207,17 @@ object Splicer { val staticMethodCall = interpretedStaticMethodCall(fn.qualifier.symbol.moduleClass, fn.symbol) staticMethodCall(args.flatten.map(interpretTree)) } - } else if (env.contains(fn.name)) { - env(fn.name) + } else if (env.contains(fn.symbol)) { + env(fn.symbol) } else if (tree.symbol.is(InlineProxy)) { interpretTree(tree.symbol.defTree.asInstanceOf[ValOrDefDef].rhs) } else { unexpectedTree(tree) } + case closureDef((ddef @ DefDef(_, _, (arg :: Nil) :: Nil, _, _))) => + (obj: AnyRef) => interpretTree(ddef.rhs) given env.updated(arg.symbol, obj) + // Interpret `foo(j = x, i = y)` which it is expanded to // `val j$1 = x; val i$1 = y; foo(i = i$1, j = j$1)` case Block(stats, expr) => interpretBlock(stats, expr) @@ -228,7 +239,7 @@ object Splicer { var unexpected: Option[Object] = None val newEnv = stats.foldLeft(env)((accEnv, stat) => stat match { case stat: ValDef => - accEnv.updated(stat.name, interpretTree(stat.rhs)(accEnv)) + accEnv.updated(stat.symbol, interpretTree(stat.rhs)(accEnv)) case stat => if (unexpected.isEmpty) unexpected = Some(unexpectedTree(stat)) @@ -250,7 +261,7 @@ object Splicer { args.toSeq private def interpretQuoteContext()(implicit env: Env): Object = - new scala.quoted.QuoteContext(ReflectionImpl(ctx, pos)) + new scala.quoted.QuoteContext(ReflectionImpl(ctx)) private def interpretedStaticMethodCall(moduleClass: Symbol, fn: Symbol)(implicit env: Env): List[Object] => Object = { val (inst, clazz) = @@ -339,10 +350,13 @@ object Splicer { throw new StopInterpretation(sw.toString, pos) case ex: InvocationTargetException => val sw = new StringWriter() - sw.write("An exception occurred while executing macro expansion\n") + sw.write("An exception occurred while executing macro expansion: ") sw.write(ex.getTargetException.getMessage) sw.write("\n") - ex.getTargetException.printStackTrace(new PrintWriter(sw)) + for (stack <- ex.getTargetException.getStackTrace.iterator.takeWhile(_.getClassName != this.getClass.getName).drop(1)) { + sw.write(stack.toString) + sw.write("\n") + } sw.write("\n") throw new StopInterpretation(sw.toString, pos) } @@ -404,11 +418,12 @@ object Splicer { allParams.map(paramClass) } - /** Exception that stops interpretation if some issue is found */ - private class StopInterpretation(val msg: String, val pos: SourcePosition) extends Exception } + /** Exception that stops interpretation if some issue is found */ + private class StopInterpretation(val msg: String, val pos: SourcePosition) extends Exception + object Call { /** Matches an expression that is either a field access or an application * It retruns a TermRef containing field accessed or a method reference and the arguments passed to it. diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index bf282476389a..32fa6971c1bb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -21,6 +21,7 @@ import SymDenotations.SymDenotation import Inferencing.fullyDefinedType import config.Printers.inlining import ErrorReporting.errorTree +import dotty.tools.dotc.tastyreflect.ReflectionImpl import dotty.tools.dotc.util.{SimpleIdentityMap, SimpleIdentitySet, SourceFile, SourcePosition} import collection.mutable @@ -1196,7 +1197,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { 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)) + val ctx1 = tastyreflect.MacroExpansion.context(inlinedFrom) + val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx1) val inlinedNormailizer = new TreeMap { override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { diff --git a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala index 713f78f75fde..a809b781198d 100644 --- a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala @@ -248,8 +248,6 @@ object PrepareInlineable { if (sym.is(Macro) && !ctx.isAfterTyper) { def isValidMacro(tree: Tree)(implicit ctx: Context): Unit = tree match { case Spliced(code) => - if (code.symbol.flags.is(Inline)) - ctx.error("Macro cannot be implemented with an `inline` method", code.sourcePos) Splicer.checkValidMacroBody(code) new PCPCheckAndHeal(freshStagingContext).transform(rhs) // Ignore output, only check PCP diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index e10303726109..3906f0efc229 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -36,60 +36,53 @@ trait QuotesAndSplices { self: Typer => */ def typedQuote(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = { record("typedQuote") - val qctx = inferImplicitArg(defn.QuoteContextClass.typeRef, tree.span) - if (level == 0 && qctx.tpe.isInstanceOf[SearchFailureType]) - ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span)) - + ctx.compilationUnit.needsStaging = true tree.quoted match { case untpd.Splice(innerExpr) if tree.isTerm => ctx.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.sourcePos) - typed(innerExpr, pt) case untpd.TypSplice(innerType) if tree.isType => ctx.warning("Canceled splice directly inside a quote. '[ ${ XYZ } ] is equivalent to XYZ.", tree.sourcePos) - typed(innerType, pt) - case quoted => - ctx.compilationUnit.needsStaging = true - val tree1 = - if (quoted.isType) typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), quoted :: Nil), pt)(quoteContext) - else if (ctx.mode.is(Mode.Pattern) && level == 0) typedQuotePattern(quoted, pt, qctx) - else typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), quoted), pt)(quoteContext) - tree1.withSpan(tree.span) + case _ => } + val tree1 = + if (tree.quoted.isType) typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), tree.quoted :: Nil), pt)(quoteContext) + else if (ctx.mode.is(Mode.Pattern) && level == 0) typedQuotePattern(tree, pt) + else typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), tree.quoted), pt)(quoteContext) + tree1.withSpan(tree.span) } /** 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 = { record("typedSplice") + ctx.compilationUnit.needsStaging = true checkSpliceOutsideQuote(tree) tree.expr match { case untpd.Quote(innerExpr) if innerExpr.isTerm => ctx.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.sourcePos) - typed(innerExpr, pt) - case expr => - ctx.compilationUnit.needsStaging = true - if (ctx.mode.is(Mode.QuotedPattern) && level == 1) { - if (isFullyDefined(pt, ForceDegree.all)) { - def spliceOwner(ctx: Context): Symbol = - if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner - val pat = typedPattern(expr, defn.QuotedExprClass.typeRef.appliedTo(pt))( - spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) - Splice(pat) - } else { - ctx.error(i"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", expr.sourcePos) - tree.withType(UnspecifiedErrorType) - } - } - else { - if (StagingContext.level == 0) { - // Mark the first inline method from the context as a macro - def markAsMacro(c: Context): Unit = - if (c.owner eq c.outer.owner) markAsMacro(c.outer) - else if (c.owner.isInlineMethod) c.owner.setFlag(Macro) - else if (!c.outer.owner.is(Package)) markAsMacro(c.outer) - markAsMacro(ctx) - } - typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSplice.termRef), tree.expr), pt)(spliceContext).withSpan(tree.span) - } + case _ => + } + if (ctx.mode.is(Mode.QuotedPattern) && level == 1) { + if (isFullyDefined(pt, ForceDegree.all)) { + def spliceOwner(ctx: Context): Symbol = + if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner + val pat = typedPattern(tree.expr, defn.QuotedExprClass.typeRef.appliedTo(pt))( + spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) + Splice(pat) + } else { + ctx.error(i"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.sourcePos) + tree.withType(UnspecifiedErrorType) + } + } + else { + if (StagingContext.level == 0) { + // Mark the first inline method from the context as a macro + def markAsMacro(c: Context): Unit = + if (c.owner eq c.outer.owner) markAsMacro(c.outer) + else if (c.owner.isInlineMethod) c.owner.setFlag(Macro) + else if (!c.outer.owner.is(Package)) markAsMacro(c.outer) + markAsMacro(ctx) + } + typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSplice.termRef), tree.expr), pt)(spliceContext).withSpan(tree.span) } } @@ -101,30 +94,29 @@ trait QuotesAndSplices { self: Typer => tree.expr match { case untpd.Quote(innerType) if innerType.isType => ctx.warning("Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ.", tree.sourcePos) - typed(innerType, pt) - case expr => - if (ctx.mode.is(Mode.QuotedPattern) && level == 1) { - if (isFullyDefined(pt, ForceDegree.all)) { - ctx.error(i"Spliced type pattern must not be fully defined. Consider using $pt directly", tree.expr.sourcePos) - tree.withType(UnspecifiedErrorType) - } else { - def spliceOwner(ctx: Context): Symbol = - if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner - val name = expr match { - case Ident(name) => ("$" + name).toTypeName - case Typed(Ident(name), _) => ("$" + name).toTypeName - case Bind(name, _) => ("$" + name).toTypeName - case _ => NameKinds.UniqueName.fresh("$".toTypeName) - } - val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, TypeBounds.empty, NoSymbol, expr.span) - typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(expr.span))) - val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( - spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) - pat.select(tpnme.splice) - } - } else { - typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span) + case _ => + } + if (ctx.mode.is(Mode.QuotedPattern) && level == 1) { + if (isFullyDefined(pt, ForceDegree.all)) { + ctx.error(i"Spliced type pattern must not be fully defined. Consider using $pt directly", tree.expr.sourcePos) + tree.withType(UnspecifiedErrorType) + } else { + def spliceOwner(ctx: Context): Symbol = + if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner + val name = tree.expr match { + case Ident(name) => ("$" + name).toTypeName + case Typed(Ident(name), _) => ("$" + name).toTypeName + case Bind(name, _) => ("$" + name).toTypeName + case _ => NameKinds.UniqueName.fresh("$".toTypeName) } + val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, TypeBounds.empty, NoSymbol, tree.expr.span) + typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span))) + val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( + spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) + pat.select(tpnme.splice) + } + } else { + typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span) } } @@ -256,7 +248,7 @@ trait QuotesAndSplices { self: Typer => (typeBindings.toMap, shape2, patterns) } - /** Type a quote pattern `case '{ } =>` qiven the a current prototype. Typing the pattern + /** Type a quote pattern `case '{ } =>` qiven the a current prototype. Typing the pattern * will also transform it into a call to `scala.internal.quoted.Matcher.unapply`. * * Code directly inside the quote is typed as an expression using Mode.QuotedPattern. Splices @@ -301,7 +293,12 @@ trait QuotesAndSplices { self: Typer => * ) => ... * ``` */ - private def typedQuotePattern(quoted: untpd.Tree, pt: Type, qctx: tpd.Tree)(implicit ctx: Context): Tree = { + private def typedQuotePattern(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = { + val qctx = inferImplicitArg(defn.QuoteContextClass.typeRef, tree.span) + if (level == 0 && qctx.tpe.isInstanceOf[SearchFailureType]) + ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span)) + + val quoted = tree.quoted val exprPt = pt.baseType(defn.QuotedExprClass) val quotedPt = exprPt.argInfos.headOption match { case Some(argPt: ValueType) => argPt // excludes TypeBounds @@ -352,7 +349,7 @@ trait QuotesAndSplices { self: Typer => UnApply( fun = ref(defn.InternalQuotedMatcher_unapply.termRef).appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil), implicits = - ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(shape.tpe).appliedTo(shape) :: + ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx) :: Literal(Constant(typeBindings.nonEmpty)) :: qctx :: Nil, patterns = splicePat :: Nil, diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index ecf3d238f412..45ed3694fe7c 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -110,7 +110,10 @@ class BootstrappedOnlyCompilationTests extends ParallelTesting { @Test def runMacros: Unit = { implicit val testGroup: TestGroup = TestGroup("runMacros") - compileFilesInDir("tests/run-macros", defaultOptions) + aggregateTests( + compileFilesInDir("tests/run-macros", defaultOptions), + compileFilesInDir("tests/run-custom-args/Yretain-trees", defaultOptions and "-Yretain-trees"), + ) }.checkRuns() @Test def runWithCompiler: Unit = { diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 8356f6da14ad..03184a19a06b 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -196,7 +196,6 @@ class CompilationTests extends ParallelTesting { @Test def runAll: Unit = { implicit val testGroup: TestGroup = TestGroup("runAll") aggregateTests( - compileFilesInDir("tests/run-custom-args/Yretain-trees", defaultOptions and "-Yretain-trees"), compileFile("tests/run-custom-args/tuple-cons.scala", allowDeepSubtypes), compileFile("tests/run-custom-args/i5256.scala", allowDeepSubtypes), compileFile("tests/run-custom-args/fors.scala", defaultOptions and "-strict"), diff --git a/library/src-bootstrapped/dotty/internal/StringContextMacro.scala b/library/src-bootstrapped/dotty/internal/StringContextMacro.scala index 333988e703d7..c1c0a62286ed 100644 --- a/library/src-bootstrapped/dotty/internal/StringContextMacro.scala +++ b/library/src-bootstrapped/dotty/internal/StringContextMacro.scala @@ -753,4 +753,4 @@ object StringContextMacro { // macro expansion '{(${parts.mkString.toExpr}).format(${argsExpr}: _*)} } -} \ No newline at end of file +} diff --git a/library/src/scala/quoted/Liftable.scala b/library/src-bootstrapped/scala/quoted/Liftable.scala similarity index 100% rename from library/src/scala/quoted/Liftable.scala rename to library/src-bootstrapped/scala/quoted/Liftable.scala diff --git a/library/src/scala/tasty/reflect/TreeUtils.scala b/library/src-bootstrapped/scala/tasty/reflect/TreeUtils.scala similarity index 100% rename from library/src/scala/tasty/reflect/TreeUtils.scala rename to library/src-bootstrapped/scala/tasty/reflect/TreeUtils.scala diff --git a/library/src-non-bootstrapped/scala/tasty/reflect/TreeUtils.scala b/library/src-non-bootstrapped/scala/tasty/reflect/TreeUtils.scala new file mode 100644 index 000000000000..6731add13c44 --- /dev/null +++ b/library/src-non-bootstrapped/scala/tasty/reflect/TreeUtils.scala @@ -0,0 +1,11 @@ +package scala.tasty +package reflect + +/** Tasty reflect case definition */ +trait TreeUtils + extends Core + with PatternOps + with SymbolOps + with TreeOps { self: Reflection => + +} diff --git a/library/src/scala/internal/Quoted.scala b/library/src/scala/internal/Quoted.scala index c9a5a2727320..bcab397baa73 100644 --- a/library/src/scala/internal/Quoted.scala +++ b/library/src/scala/internal/Quoted.scala @@ -7,11 +7,11 @@ object Quoted { /** A term quote is desugared by the compiler into a call to this method */ @compileTimeOnly("Illegal reference to `scala.internal.Quoted.exprQuote`") - def exprQuote[T](x: T): Expr[T] = ??? + def exprQuote[T](x: T): given QuoteContext => Expr[T] = ??? /** A term splice is desugared by the compiler into a call to this method */ @compileTimeOnly("Illegal reference to `scala.internal.Quoted.exprSplice`") - def exprSplice[T](x: Expr[T]): T = ??? + def exprSplice[T](x: given QuoteContext => Expr[T]): T = ??? /** A type quote is desugared by the compiler into a call to this method */ @compileTimeOnly("Illegal reference to `scala.internal.Quoted.typeQuote`") diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index bf4794357231..86fcfa0e317d 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -33,28 +33,28 @@ package quoted { import scala.internal.quoted._ /** Converts a tuple `(T1, ..., Tn)` to `(Expr[T1], ..., Expr[Tn])` */ - type TupleOfExpr[Tup <: Tuple] = Tuple.Map[Tup, Expr] + type TupleOfExpr[Tup <: Tuple] = Tuple.Map[Tup, [X] =>> given QuoteContext => Expr[X]] implicit class AsFunction[F, Args <: Tuple, R](f: Expr[F]) given (tf: TupledFunction[F, Args => R]) { /** Beta-reduces the function appication. Generates the an expression only containing the body of the function */ def apply[G] given (tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = - tg.untupled(args => new FunctionAppliedTo[R](f, args.toArray.map(_.asInstanceOf[Expr[_]]))) + tg.untupled(args => new FunctionAppliedTo[R](f, args.toArray.map(_.asInstanceOf[QuoteContext => Expr[_]]))) } implicit class AsContextualFunction[F, Args <: Tuple, R](f: Expr[F]) given (tf: TupledFunction[F, given Args => R]) { /** Beta-reduces the function appication. Generates the an expression only containing the body of the function */ def apply[G] given (tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = - tg.untupled(args => new FunctionAppliedTo[R](f, args.toArray.map(_.asInstanceOf[Expr[_]]))) + tg.untupled(args => new FunctionAppliedTo[R](f, args.toArray.map(_.asInstanceOf[QuoteContext => Expr[_]]))) } /** Returns a null expresssion equivalent to `'{null}` */ - def nullExpr given (qctx: QuoteContext): Expr[Null] = { + def nullExpr: given QuoteContext => Expr[Null] = given qctx => { import qctx.tasty._ Literal(Constant(null)).seal.asInstanceOf[Expr[Null]] } /** Returns a unit expresssion equivalent to `'{}` or `'{()}` */ - def unitExpr given (qctx: QuoteContext): Expr[Unit] = { + def unitExpr: given QuoteContext => Expr[Unit] = given qctx => { import qctx.tasty._ Literal(Constant(())).seal.asInstanceOf[Expr[Unit]] } @@ -97,7 +97,7 @@ package internal { // FIXME: Having the List in the code above trigers an assertion error while testing dotty.tools.dotc.reporting.ErrorMessagesTests.i3187 // This test does redefine `scala.collection`. Further investigation is needed. /** An Expr representing `'{($f).apply($x1, ..., $xn)}` but it is beta-reduced when the closure is known */ - final class FunctionAppliedTo[+R](val f: Expr[_], val args: Array[Expr[_]]) extends Expr[R] { + final class FunctionAppliedTo[+R](val f: Expr[_], val args: Array[QuoteContext => Expr[_]]) extends Expr[R] { override def toString: String = s"Expr($f ${args.toList})" } diff --git a/library/src/scala/runtime/quoted/Unpickler.scala b/library/src/scala/runtime/quoted/Unpickler.scala index ab538a7b2d7d..00726b27e03e 100644 --- a/library/src/scala/runtime/quoted/Unpickler.scala +++ b/library/src/scala/runtime/quoted/Unpickler.scala @@ -1,7 +1,7 @@ package scala.runtime.quoted import scala.internal.quoted.{TastyExpr, TastyType} -import scala.quoted.{Expr, Type} +import scala.quoted.{Expr, QuoteContext, Type} /** Provides methods to unpickle `Expr` and `Type` trees. */ object Unpickler { @@ -14,7 +14,7 @@ object Unpickler { /** Unpickle `repr` which represents a pickled `Expr` tree, * replacing splice nodes with `args` */ - def unpickleExpr[T](repr: Pickled, args: Seq[Seq[Any] => (Expr[Any] | Type[_])]): Expr[T] = new TastyExpr[T](repr, args) + def unpickleExpr[T](repr: Pickled, args: Seq[Seq[Any] => ((given QuoteContext => Expr[Any]) | Type[_])]): given QuoteContext => Expr[T] = new TastyExpr[T](repr, args) /** Unpickle `repr` which represents a pickled `Type` tree, * replacing splice nodes with `args` diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 539e98966ac2..9a94151fc47f 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -826,9 +826,16 @@ trait Printers printTypeTree(args.head) this += "]" + case Apply(fn, arg :: Nil) if fn.symbol.fullName == "scala.internal.Quoted$.exprSplice" => + this += "${" + printTree(arg) + this += "}" + case Apply(fn, args) => fn match { case Select(This(_), "") => this += "this" // call to constructor inside a constructor + case Select(qual, "apply") if qual.tpe.isImplicitFunctionType => + printTree(qual) += " given " case _ => printQualTree(fn) } val args1 = args match { diff --git a/tests/neg-macros/quote-this.scala b/tests/neg-macros/quote-this.scala index 993015de8c2f..faf7d8b8333f 100644 --- a/tests/neg-macros/quote-this.scala +++ b/tests/neg-macros/quote-this.scala @@ -12,10 +12,12 @@ class Foo { } inline def i(): Unit = ${ Foo.impl[Any]('{ + given as QuoteContext = ??? 'this // error }) } inline def j(that: Foo): Unit = ${ Foo.impl[Any]('{ + given as QuoteContext = ??? 'that // error }) } diff --git a/tests/neg/i4044a.scala b/tests/neg/i4044a.scala index fe157c1a39b1..27a42828d2eb 100644 --- a/tests/neg/i4044a.scala +++ b/tests/neg/i4044a.scala @@ -4,10 +4,11 @@ def test given QuoteContext = { val a = '{1} '{ + given as QuoteContext = ??? a // error $a '{$a} // error - '{ '{$a} } // error + '{ given as QuoteContext = ???; '{$a} } // error } } diff --git a/tests/neg/i4044b.scala b/tests/neg/i4044b.scala index 833c2abd2956..62a9e98e53f2 100644 --- a/tests/neg/i4044b.scala +++ b/tests/neg/i4044b.scala @@ -3,14 +3,17 @@ import scala.quoted._ def test given QuoteContext = { '{ + given as QuoteContext = ??? val b = '{3} '{ + given as QuoteContext = ??? + b // error ${b} ${ '{b} } // error - '{ '{$b} } // error + '{ given as QuoteContext = ???; '{$b} } // error } } diff --git a/tests/neg/i4431.scala b/tests/neg/i4431.scala new file mode 100644 index 000000000000..2523a90d6153 --- /dev/null +++ b/tests/neg/i4431.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +object Macros { + inline def h(f: => Int => String): String = ${'{f(42)}} // error +} diff --git a/tests/neg/i6530.check b/tests/neg/i6530.check index 2934daac1448..02f77c1a97b7 100644 --- a/tests/neg/i6530.check +++ b/tests/neg/i6530.check @@ -3,3 +3,11 @@ | ^^^^^^^^ | Found: quoted.Type[Int] | Required: quoted.Expr[Int] +-- Error: tests/neg/i6530.scala:3:20 ----------------------------------------------------------------------------------- +3 | val x : Int = 1 + q // error + | ^ + |Failed to evaluate macro. + | Caused by class java.lang.ClassCastException: scala.internal.quoted.TreeType cannot be cast to scala.quoted.Expr + | + | + | This location is in code that was inlined at i6530.scala:3 diff --git a/tests/neg/i6530.scala b/tests/neg/i6530.scala index 09aac71901f7..4fd703b7a4de 100644 --- a/tests/neg/i6530.scala +++ b/tests/neg/i6530.scala @@ -1,4 +1,4 @@ object Macros { inline def q : Int = ${ '[ Int ] } // error - val x : Int = 1 + q + val x : Int = 1 + q // error } diff --git a/tests/neg/quote-0.scala b/tests/neg/quote-0.scala index 8daee12b382c..b7e2a8aa9d83 100644 --- a/tests/neg/quote-0.scala +++ b/tests/neg/quote-0.scala @@ -4,7 +4,10 @@ def test given QuoteContext = { val x: Int = 0 - '{ '{x + 1} // error: wrong staging level + '{ + given as QuoteContext = ??? + + '{x + 1} // error: wrong staging level '{(y: Expr[Int]) => $y } // error: wrong staging level diff --git a/tests/neg/quotedPatterns-2.scala b/tests/neg/quotedPatterns-2.scala index 6bfc3cf9932a..7da7f34057c7 100644 --- a/tests/neg/quotedPatterns-2.scala +++ b/tests/neg/quotedPatterns-2.scala @@ -1,6 +1,6 @@ object Test { def test(x: quoted.Expr[Int]) given scala.quoted.QuoteContext = x match { - case '{ val a = 4; '{ a }; $y } => y // error: access to value a from wrong staging level + case '{ val a = 4; '{ a }; $y } => y // error // error: access to value a from wrong staging level case _ => } } diff --git a/tests/pos/i4846.scala b/tests/pos/i4846.scala index a8007c29b7dd..b147d03f54af 100644 --- a/tests/pos/i4846.scala +++ b/tests/pos/i4846.scala @@ -2,5 +2,5 @@ import scala.quoted._ object Test { inline def foo(inline x: Int): Int = ${fooImpl(x, 'x, '{ 'x }, '{ '{ 'x } })} - def fooImpl(a: Int, b: Expr[Int], c: Expr[Expr[Int]], d: Expr[Expr[Expr[Int]]]): Expr[Int] = ??? + def fooImpl(a: Int, b: Expr[Int], c: Expr[given QuoteContext => Expr[Int]], d: Expr[given QuoteContext => Expr[given QuoteContext => Expr[Int]]]): Expr[Int] = ??? } diff --git a/tests/pos/quote-this.scala b/tests/pos/quote-this.scala index 90a164442ef2..2c6690f7d873 100644 --- a/tests/pos/quote-this.scala +++ b/tests/pos/quote-this.scala @@ -1,25 +1,22 @@ import scala.quoted._ class Foo { - def a given QuoteContext: Expr[Int] = '{1} - def b given QuoteContext: Expr[Int] = '{ - ${ this.a } - } + def a given QuoteContext: Expr[Int] = '{1} + def b given QuoteContext: Expr[Int] = '{ + ${ this.a } + } - def d given QuoteContext: Expr[Expr[Int]] = '{ '{1} } - def e given QuoteContext: Expr[Expr[Int]] = '{ - '{${${this.d}}} - } + def d given QuoteContext: Expr[given QuoteContext => Expr[Int]] = '{ '{1} } - def foo[T](x: T): T = x + def foo[T](x: T): T = x - def f given QuoteContext = '{ - ${ foo[this.type](this).a } - } + def f given QuoteContext = '{ + ${ foo[this.type](this).a } + } - inline def g(): Unit = ${ Foo.impl[this.type](1) } - inline def h(): Unit = ${ Foo.impl[Any]('this) } - inline def i(that: Foo): Unit = ${ Foo.impl[that.type](1) } + inline def g(): Unit = ${ Foo.impl[this.type](1) } + inline def h(): Unit = ${ Foo.impl[Any]('this) } + inline def i(that: Foo): Unit = ${ Foo.impl[that.type](1) } } diff --git a/tests/run-macros/i4431/quoted_1.scala b/tests/run-macros/i4431/quoted_1.scala deleted file mode 100644 index 245c60680cf7..000000000000 --- a/tests/run-macros/i4431/quoted_1.scala +++ /dev/null @@ -1,5 +0,0 @@ -import scala.quoted._ - -object Macros { - inline def h(f: => Int => String): String = ${'{f(42)}} -} diff --git a/tests/run-macros/i4431/quoted_2.scala b/tests/run-macros/i4431/quoted_2.scala deleted file mode 100644 index 00cdb38258d1..000000000000 --- a/tests/run-macros/i4431/quoted_2.scala +++ /dev/null @@ -1,8 +0,0 @@ -import scala.quoted._ -import Macros._ - -object Test { - def main(args: Array[String]): Unit = { - println(h(x => "abc" + x)) - } -} diff --git a/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala b/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala index 3cc44df56786..3add72815945 100644 --- a/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala +++ b/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala @@ -11,15 +11,12 @@ object Macros { def lift(e: Expr[DSL]): Expr[T] = e match { case '{ LitDSL(${ Const(c) }) } => - // case scala.internal.quoted.Matcher.unapply[Tuple1[Expr[Int]]](Tuple1(Literal(c)))(/*implicits*/ '{ LitDSL(patternHole[Int]) }, reflect) => '{ $sym.value(${c.toExpr}) } case '{ ($x: DSL) + ($y: DSL) } => - // case scala.internal.quoted.Matcher.unapply[Tuple2[Expr[DSL], Expr[DSL]]](Tuple2(x, y))(/*implicits*/ '{ patternHole[DSL] + patternHole[DSL] }, reflect) => '{ $sym.plus(${lift(x)}, ${lift(y)}) } case '{ ($x: DSL) * ($y: DSL) } => - // case scala.internal.quoted.Matcher.unapply[Tuple2[Expr[DSL], Expr[DSL]]](Tuple2(x, y))(/*implicits*/ '{ patternHole[DSL] * patternHole[DSL] }, reflect) => '{ $sym.times(${lift(x)}, ${lift(y)}) } case _ => diff --git a/tests/run-with-compiler/tasty-unsafe-let.check b/tests/run-macros/tasty-unsafe-let.check similarity index 100% rename from tests/run-with-compiler/tasty-unsafe-let.check rename to tests/run-macros/tasty-unsafe-let.check diff --git a/tests/run-with-compiler/tasty-unsafe-let/quoted_1.scala b/tests/run-macros/tasty-unsafe-let/quoted_1.scala similarity index 100% rename from tests/run-with-compiler/tasty-unsafe-let/quoted_1.scala rename to tests/run-macros/tasty-unsafe-let/quoted_1.scala diff --git a/tests/run-with-compiler/tasty-unsafe-let/quoted_2.scala b/tests/run-macros/tasty-unsafe-let/quoted_2.scala similarity index 100% rename from tests/run-with-compiler/tasty-unsafe-let/quoted_2.scala rename to tests/run-macros/tasty-unsafe-let/quoted_2.scala diff --git a/tests/run-with-compiler/quote-nested-1.check b/tests/run-with-compiler/quote-nested-1.check index f1fa83d430c4..6cdeea03b35b 100644 --- a/tests/run-with-compiler/quote-nested-1.check +++ b/tests/run-with-compiler/quote-nested-1.check @@ -1 +1 @@ -'{3} +((qctx: scala.quoted.QuoteContext) => '{3} given (qctx)) diff --git a/tests/run-with-compiler/quote-nested-1.scala b/tests/run-with-compiler/quote-nested-1.scala index d7cb9edddaea..fc718f4995ce 100644 --- a/tests/run-with-compiler/quote-nested-1.scala +++ b/tests/run-with-compiler/quote-nested-1.scala @@ -3,7 +3,7 @@ import quoted._ object Test { implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader) def main(args: Array[String]): Unit = withQuoteContext { - val q = '{ '{3} } + val q = '{ given (qctx: QuoteContext) => '{3} } println(q.show) } } diff --git a/tests/run-with-compiler/quote-nested-2.check b/tests/run-with-compiler/quote-nested-2.check index 87a43aeeee24..af38faf8bf56 100644 --- a/tests/run-with-compiler/quote-nested-2.check +++ b/tests/run-with-compiler/quote-nested-2.check @@ -1,5 +1,4 @@ -{ - val a: scala.quoted.Expr[scala.Int] = '{4} - - (a: scala.quoted.Expr[scala.Int]) -} +((qctx: scala.quoted.QuoteContext) => { + val a: scala.quoted.Expr[scala.Int] = '{4} given (qctx) + ((evidence$2: scala.quoted.QuoteContext) => a) given (qctx) +}) diff --git a/tests/run-with-compiler/quote-nested-2.scala b/tests/run-with-compiler/quote-nested-2.scala index 786c4170c1a5..c76635652c25 100644 --- a/tests/run-with-compiler/quote-nested-2.scala +++ b/tests/run-with-compiler/quote-nested-2.scala @@ -4,7 +4,7 @@ object Test { implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader) def main(args: Array[String]): Unit = withQuoteContext { - val q = '{ + val q = '{ given (qctx: QuoteContext) => val a = '{4} '{${a}} } diff --git a/tests/run-with-compiler/quote-nested-5.check b/tests/run-with-compiler/quote-nested-5.check index 87a43aeeee24..e8b0441e22e3 100644 --- a/tests/run-with-compiler/quote-nested-5.check +++ b/tests/run-with-compiler/quote-nested-5.check @@ -1,5 +1,4 @@ -{ - val a: scala.quoted.Expr[scala.Int] = '{4} - - (a: scala.quoted.Expr[scala.Int]) -} +((qctx: scala.quoted.QuoteContext) => { + val a: scala.quoted.Expr[scala.Int] = '{4} given (qctx) + ((qctx2: scala.quoted.QuoteContext) => ((evidence$3: scala.quoted.QuoteContext) => a) given (qctx2)) given (qctx) +}) diff --git a/tests/run-with-compiler/quote-nested-5.scala b/tests/run-with-compiler/quote-nested-5.scala index 642f903fb813..40d3183c5b0c 100644 --- a/tests/run-with-compiler/quote-nested-5.scala +++ b/tests/run-with-compiler/quote-nested-5.scala @@ -4,9 +4,9 @@ object Test { implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader) def main(args: Array[String]): Unit = withQuoteContext { - val q = '{ + val q = '{ given (qctx: QuoteContext) => val a = '{4} - ${'{ + ${'{ given (qctx2: QuoteContext) => '{${a}} }}