diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e6ca5adc1e5f..e3bd3147cc35 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -42,7 +42,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Super(qual, if (mixName.isEmpty) untpd.EmptyTypeIdent else untpd.Ident(mixName), mixinClass) def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = { - assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply[_]] || fn.isInstanceOf[Inlined]) + assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply[_]] || fn.isInstanceOf[Inlined] || fn.isInstanceOf[tasty.TreePickler.Hole]) ta.assignType(untpd.Apply(fn, args), fn, args) } diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index c2deb60098ca..4b4a7d18667a 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -13,6 +13,7 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.tasty.TreePickler.Hole import dotty.tools.dotc.core.tasty.{ PositionPickler, TastyPickler, TastyPrinter } +import dotty.tools.dotc.core.tasty.DottyUnpickler import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode import dotty.tools.dotc.quoted.QuoteContext import dotty.tools.dotc.tastyreflect.{ReflectionImpl, TastyTreeExpr, TreeType} @@ -49,56 +50,118 @@ object PickledQuotes { healOwner(tpe1.typeTree) } - private def dealiasTypeTags(tp: Type)(implicit ctx: Context): Type = new TypeMap() { - override def apply(tp: Type): Type = { - val tp1 = tp match { - case tp: TypeRef if tp.typeSymbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) => - tp.symbol.info.hiBound - case _ => tp - } - mapOver(tp1) - } - }.apply(tp) - /** Unpickle the tree contained in the TastyExpr */ - def unpickleExpr(tasty: PickledQuote, args: PickledArgs)(implicit ctx: Context): Tree = { + def unpickleExpr(tasty: PickledQuote, splices: PickledArgs)(implicit ctx: Context): Tree = { val tastyBytes = TastyString.unpickle(tasty) - val unpickled = unpickle(tastyBytes, args, isType = false)(ctx.addMode(Mode.ReadPositions)) - /** Force unpickling of the tree, removes the spliced type `@quotedTypeTag type` definitions and dealiases references to `@quotedTypeTag type` */ - val forceAndCleanArtefacts = new TreeMap { - override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { - case Block(stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) => - assert(rest.forall { case tdef: TypeDef => tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) }) - transform(expr1) - case tree => super.transform(tree).withType(dealiasTypeTags(tree.tpe)) - } - } - forceAndCleanArtefacts.transform(unpickled) + val unpickled = unpickle(tastyBytes, splices, isType = false)(ctx.addMode(Mode.ReadPositions)) + val Inlined(call, Nil, expnasion) = unpickled + val inlineCtx = inlineContext(call) + val expansion1 = spliceTypes(expnasion, splices)(using inlineCtx) + val expansion2 = spliceTerms(expansion1, splices)(using inlineCtx) + cpy.Inlined(unpickled)(call, Nil, expansion2) } /** Unpickle the tree contained in the TastyType */ def unpickleType(tasty: PickledQuote, args: PickledArgs)(implicit ctx: Context): Tree = { val tastyBytes = TastyString.unpickle(tasty) val unpickled = unpickle(tastyBytes, args, isType = true)(ctx.addMode(Mode.ReadPositions)) - val tpt = unpickled match { - case Block(aliases, tpt) => - // `@quoteTypeTag type` aliases are not required after unpickling. - // Type definitions are placeholders for type holes in the pickled quote, at this point - // those holes have been filled. As we already dealias al references to them in `dealiasTypeTags` - // there is no need to keep their definitions in the tree. As artifacts of quote reification - // they also do not have a meaningful position in the source. - val aliases1 = aliases.filter(!_.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot)) - seq(aliases1, tpt) - case tpt => tpt + spliceTypes(unpickled, args) + } + + /** Replace all term holes with the spliced terms */ + private def spliceTerms(tree: Tree, splices: PickledArgs)(using Context): Tree = { + val evaluateHoles = new TreeMap { + override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + case Hole(isTerm, idx, args) => + val reifiedArgs = args.map { arg => + if (arg.isTerm) (using qctx: scala.quoted.QuoteContext) => new TastyTreeExpr(arg, QuoteContext.scopeId) + else new TreeType(arg, QuoteContext.scopeId) + } + if isTerm then + val splice1 = splices(idx).asInstanceOf[Seq[Any] => scala.quoted.QuoteContext ?=> quoted.Expr[?]] + val quotedExpr = splice1(reifiedArgs)(using dotty.tools.dotc.quoted.QuoteContext()) + val filled = PickledQuotes.quotedExprToTree(quotedExpr) + + // We need to make sure a hole is created with the source file of the surrounding context, even if + // it filled with contents a different source file. + if filled.source == ctx.source then filled + else filled.cloneIn(ctx.source).withSpan(tree.span) + else + // Replaces type holes generated by ReifyQuotes (non-spliced types). + // These are types defined in a quote and used at the same level in a nested quote. + val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](reifiedArgs) + PickledQuotes.quotedTypeToTree(quotedType) + case tree: Select => + // Retain selected members + val qual = transform(tree.qualifier) + qual.select(tree.symbol).withSpan(tree.span) + + case tree => + if tree.isDef then + tree.symbol.annotations = tree.symbol.annotations.map { + annot => annot.derivedAnnotation(transform(annot.tree)) + } + end if + + val tree1 = super.transform(tree) + tree1.withType(mapAnnots(tree1.tpe)) + } + + // Evaluate holes in type annotations + private val mapAnnots = new TypeMap { + override def apply(tp: Type): Type = { + tp match + case tp @ AnnotatedType(underlying, annot) => + val underlying1 = this(underlying) + derivedAnnotatedType(tp, underlying1, annot.derivedAnnotation(transform(annot.tree))) + case _ => mapOver(tp) + } + } } - tpt.withType(dealiasTypeTags(tpt.tpe)) + val tree1 = evaluateHoles.transform(tree) + quotePickling.println(i"**** evaluated quote\n$tree1") + tree1 + } + + /** Replace all type holes generated with the spliced types */ + private def spliceTypes(tree: Tree, splices: PickledArgs)(using Context): Tree = { + tree match + case Block(stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) => + val typeSpliceMap = (stat :: rest).iterator.map { + case tdef: TypeDef => + assert(tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot)) + val tree = tdef.rhs match + case TypeBoundsTree(_, Hole(_, idx, args), _) => + val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](args) + PickledQuotes.quotedTypeToTree(quotedType) + case TypeBoundsTree(_, tpt, _) => + tpt + (tdef.symbol, tree.tpe) + }.toMap + class ReplaceSplicedTyped extends TypeMap() { + override def apply(tp: Type): Type = { + val tp1 = tp match { + case tp: TypeRef => + typeSpliceMap.get(tp.symbol) match + case Some(t) if tp.typeSymbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) => t + case None => tp + case _ => tp + } + mapOver(tp1) + } + } + val expansion2 = new TreeTypeMap(new ReplaceSplicedTyped).transform(expr1) + quotePickling.println(i"**** typed quote\n${expansion2.show}") + expansion2 + case _ => + tree } // TASTY picklingtests/pos/quoteTest.scala /** Pickle tree into it's TASTY bytes s*/ private def pickle(tree: Tree)(implicit ctx: Context): Array[Byte] = { - quotePickling.println(i"**** pickling quote of \n${tree.show}") + quotePickling.println(i"**** pickling quote of\n$tree") val pickler = new TastyPickler(defn.RootClass) val treePkl = pickler.treePkl treePkl.pickle(tree :: Nil) @@ -118,11 +181,17 @@ object PickledQuotes { quotePickling.println(s"**** unpickling quote from TASTY\n${new TastyPrinter(bytes).printContents()}") val mode = if (isType) UnpickleMode.TypeTree else UnpickleMode.Term - val unpickler = new QuoteUnpickler(bytes, splices, mode) + val unpickler = new DottyUnpickler(bytes, mode) unpickler.enter(Set.empty) val tree = unpickler.tree - quotePickling.println(i"**** unpickle quote ${tree.show}") + + // Make sure trees and positions are fully loaded + new TreeTraverser { + def traverse(tree: Tree)(implicit ctx: Context): Unit = traverseChildren(tree) + }.traverse(tree) + + quotePickling.println(i"**** unpickled quote\n$tree") tree } diff --git a/compiler/src/dotty/tools/dotc/core/quoted/QuoteUnpickler.scala b/compiler/src/dotty/tools/dotc/core/quoted/QuoteUnpickler.scala deleted file mode 100644 index b0927b99c3ed..000000000000 --- a/compiler/src/dotty/tools/dotc/core/quoted/QuoteUnpickler.scala +++ /dev/null @@ -1,27 +0,0 @@ -package dotty.tools.dotc.core.quoted - -import dotty.tools.dotc.core.tasty._ -import dotty.tools.dotc.core.tasty.TastyUnpickler.NameTable -import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode - -import dotty.tools.tasty.TastyReader - -object QuoteUnpickler { - class QuotedTreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], splices: Seq[Any]) - extends DottyUnpickler.TreeSectionUnpickler(posUnpickler, None) { - override def unpickle(reader: TastyReader, nameAtRef: NameTable): TreeUnpickler = - new TreeUnpickler(reader, nameAtRef, posUnpickler, None, splices) - } -} - -/** A class for unpickling quoted Tasty trees and symbols. Comments are never unpickled. - * @param bytes the bytearray containing the Tasty file from which we unpickle - * @param splices splices that will fill the holes in the quote - */ -class QuoteUnpickler(bytes: Array[Byte], splices: Seq[Any], mode: UnpickleMode) extends DottyUnpickler(bytes, mode) { - import DottyUnpickler._ - import QuoteUnpickler._ - - protected override def treeSectionUnpickler(posUnpicklerOpt: Option[PositionUnpickler], commentUnpicklerOpt: Option[CommentUnpickler]): TreeSectionUnpickler = - new QuotedTreeSectionUnpickler(posUnpicklerOpt, splices) -} diff --git a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala index 40356da7a40c..5c80e4437e3e 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala @@ -20,7 +20,7 @@ object DottyUnpickler { class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], commentUnpickler: Option[CommentUnpickler]) extends SectionUnpickler[TreeUnpickler](TreePickler.sectionName) { def unpickle(reader: TastyReader, nameAtRef: NameTable): TreeUnpickler = - new TreeUnpickler(reader, nameAtRef, posUnpickler, commentUnpickler, Seq.empty) + new TreeUnpickler(reader, nameAtRef, posUnpickler, commentUnpickler) } class PositionsSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 42281f023e8e..85f465c8168e 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -26,7 +26,8 @@ object TreePickler { override def isTerm: Boolean = isTermHole override def isType: Boolean = !isTermHole override def fallbackToText(printer: Printer): Text = - s"[[$idx|" ~~ printer.toTextGlobal(args, ", ") ~~ "]]" + if isTermHole then s"{{{ $idx |" ~~ printer.toTextGlobal(tpe) ~~ "|" ~~ printer.toTextGlobal(args, ", ") ~~ "}}}" + else s"[[[ $idx |" ~~ printer.toTextGlobal(tpe) ~~ "|" ~~ printer.toTextGlobal(args, ", ") ~~ "]]]" } } @@ -603,6 +604,7 @@ class TreePickler(pickler: TastyPickler) { writeByte(HOLE) withLength { writeNat(idx) + pickleType(tree.tpe, richTypes = true) args.foreach(pickleTree) } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 1f3d68f0672a..515bae0f1504 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -47,13 +47,11 @@ import scala.annotation.internal.sharable * @param reader the reader from which to unpickle * @param posUnpicklerOpt the unpickler for positions, if it exists * @param commentUnpicklerOpt the unpickler for comments, if it exists - * @param splices */ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpicklerOpt: Option[PositionUnpickler], - commentUnpicklerOpt: Option[CommentUnpickler], - splices: Seq[Any]) { + commentUnpicklerOpt: Option[CommentUnpickler]) { import TreeUnpickler._ import tpd._ @@ -1224,7 +1222,10 @@ class TreeUnpickler(reader: TastyReader, val alias = if currentAddr == end then EmptyTree else readTpt() TypeBoundsTree(lo, hi, alias) case HOLE => - readHole(end, isType = false) + val idx = readNat() + val tpe = readType() + val args = until(end)(readTerm()) + TreePickler.Hole(true, idx, args).withType(tpe) case _ => readPathTerm() } @@ -1257,7 +1258,10 @@ class TreeUnpickler(reader: TastyReader, case HOLE => readByte() val end = readEnd() - readHole(end, isType = true) + val idx = readNat() + val tpe = readType() + val args = until(end)(readTerm()) + TreePickler.Hole(false, idx, args).withType(tpe) case _ => if (isTypeTreeTag(nextByte)) readTerm() else { @@ -1299,35 +1303,6 @@ class TreeUnpickler(reader: TastyReader, owner => new LazyReader(localReader, owner, ctx.mode, ctx.source, op) } - def readHole(end: Addr, isType: Boolean)(implicit ctx: Context): Tree = { - val idx = readNat() - val args = until(end)(readTerm()) - val splice = splices(idx) - def wrap(arg: Tree) = - if (arg.isTerm) (qctx: scala.quoted.QuoteContext) ?=> new TastyTreeExpr(arg, QuoteContext.scopeId) - else new TreeType(arg, QuoteContext.scopeId) - val reifiedArgs = args.map(wrap) - val filled = if (isType) { - val quotedType = splice.asInstanceOf[Seq[Any] => quoted.Type[?]](reifiedArgs) - PickledQuotes.quotedTypeToTree(quotedType) - } - else { - val splice1 = splice.asInstanceOf[Seq[Any] => scala.quoted.QuoteContext ?=> quoted.Expr[?]] - val quotedExpr = splice1(reifiedArgs)(using dotty.tools.dotc.quoted.QuoteContext()) - PickledQuotes.quotedExprToTree(quotedExpr) - } - // We need to make sure a hole is created with the source file of the surrounding context, even if - // it filled with contents a different source file. Otherwise nodes containing holes might end - // up without a position. PositionPickler makes sure that holes always get spans assigned, - // so we can just return the filler tree with the new source and no span here. - if (filled.source == ctx.source) filled - else { - val filled1 = filled.cloneIn(ctx.source) - filled1.span = NoSpan - filled1 - } - } - // ------ Setting positions ------------------------------------------------ /** Pickled span for `addr`. */ diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index ac9acb130ad5..d57e41ed15de 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -331,7 +331,37 @@ class ReifyQuotes extends MacroTransform { */ private def makeHole(isTermHole: Boolean, body: Tree, splices: List[Tree], tpe: Type)(implicit ctx: Context): Hole = { val idx = embedded.addTree(body, NoSymbol) - Hole(isTermHole, idx, splices).withType(tpe).asInstanceOf[Hole] + + /** Remove references to local types that will not be defined in this quote */ + def getTypeHoleType(using ctx: Context) = new TypeMap() { + override def apply(tp: Type): Type = tp match + case tp: TypeRef if tp.typeSymbol.isSplice => + apply(tp.dealias) + case tp @ TypeRef(pre, _) if pre == NoPrefix || pre.termSymbol.isLocal => + val hiBound = tp.typeSymbol.info match + case info @ ClassInfo(_, _, classParents, _, _) => classParents.reduce(_ & _) + case info => info.hiBound + apply(hiBound) + case tp => + mapOver(tp) + } + + /** Remove references to local types that will not be defined in this quote */ + def getTermHoleType(using ctx: Context) = new TypeMap() { + override def apply(tp: Type): Type = tp match + case tp @ TypeRef(NoPrefix, _) if capturers.contains(tp.symbol) => + // reference to term with a type defined in outer quote + getTypeHoleType(tp) + case tp @ TermRef(NoPrefix, _) if capturers.contains(tp.symbol) => + // widen term refs to terms defined in outer quote + apply(tp.widenTermRefExpr) + case tp => + mapOver(tp) + } + + val holeType = if isTermHole then getTermHoleType(tpe) else getTypeHoleType(tpe) + + Hole(isTermHole, idx, splices).withType(holeType).asInstanceOf[Hole] } override def transform(tree: Tree)(implicit ctx: Context): Tree = diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 9ea3966d0f99..b289ddf75671 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -249,7 +249,7 @@ Standard Section: "Comments" Comment* object TastyFormat { final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F) - val MajorVersion: Int = 21 + val MajorVersion: Int = 22 val MinorVersion: Int = 0 /** Tags used to serialize names, should update [[nameTagToString]] if a new constant is added */ diff --git a/tests/pos-macros/i7853/JsonEncoder_1.scala b/tests/disabled/pos-macros/i7853/JsonEncoder_1.scala similarity index 100% rename from tests/pos-macros/i7853/JsonEncoder_1.scala rename to tests/disabled/pos-macros/i7853/JsonEncoder_1.scala diff --git a/tests/pos-macros/i7853/SummonJsonEncoderTest_2.scala b/tests/disabled/pos-macros/i7853/SummonJsonEncoderTest_2.scala similarity index 100% rename from tests/pos-macros/i7853/SummonJsonEncoderTest_2.scala rename to tests/disabled/pos-macros/i7853/SummonJsonEncoderTest_2.scala diff --git a/tests/pos-macros/i7853/Test_3.scala b/tests/disabled/pos-macros/i7853/Test_3.scala similarity index 100% rename from tests/pos-macros/i7853/Test_3.scala rename to tests/disabled/pos-macros/i7853/Test_3.scala diff --git a/tests/pos-macros/i7110a/Macro_1.scala b/tests/pos-macros/i7110a/Macro_1.scala new file mode 100644 index 000000000000..d35f56ba36ce --- /dev/null +++ b/tests/pos-macros/i7110a/Macro_1.scala @@ -0,0 +1,15 @@ +import scala.quoted._ + +object Macros { + + inline def m[R](sym: Symantics[R]) : R = ${ mImpl[R]('{sym}) } + + def mImpl[R: Type](using qctx: QuoteContext)(sym: Expr[Symantics[R]]): Expr[R] = '{ + $sym.Meth(42) + } +} + +trait Symantics[R] { + def Meth(exp: Int): R + def Meth(): R +} diff --git a/tests/pos-macros/i7110a/Test_2.scala b/tests/pos-macros/i7110a/Test_2.scala new file mode 100644 index 000000000000..c901785a7bf1 --- /dev/null +++ b/tests/pos-macros/i7110a/Test_2.scala @@ -0,0 +1,14 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + + val sym = new Symantics[Int] { + def Meth(exp: Int): Int = exp + def Meth(): Int = 42 + } + + val test = m[Int](sym) + } +} diff --git a/tests/pos-macros/i7110b/Macro_1.scala b/tests/pos-macros/i7110b/Macro_1.scala new file mode 100644 index 000000000000..0587f403c450 --- /dev/null +++ b/tests/pos-macros/i7110b/Macro_1.scala @@ -0,0 +1,16 @@ +import scala.quoted._ + +object Macros { + + inline def m[T](sym: Symantics {type R = T}) : T = ${ mImpl[T]('{sym}) } + + def mImpl[T: Type](using qctx: QuoteContext)(sym: Expr[Symantics { type R = T }]): Expr[T] = '{ + $sym.Meth(42) + } +} + +trait Symantics { + type R + def Meth(exp: Int): R + def Meth(): R +} diff --git a/tests/pos-macros/i7110b/Test_2.scala b/tests/pos-macros/i7110b/Test_2.scala new file mode 100644 index 000000000000..b4ee47b71e14 --- /dev/null +++ b/tests/pos-macros/i7110b/Test_2.scala @@ -0,0 +1,15 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + + val sym = new Symantics { + type R = Int + def Meth(exp: Int): Int = exp + def Meth(): Int = 42 + } + + val test = m(sym) + } +} diff --git a/tests/pos-macros/i7110c/Macro_1.scala b/tests/pos-macros/i7110c/Macro_1.scala new file mode 100644 index 000000000000..b83e3aaf594a --- /dev/null +++ b/tests/pos-macros/i7110c/Macro_1.scala @@ -0,0 +1,14 @@ +import scala.quoted._ + +object Macros { + + inline def m[R](sym: Symantics[R]) : R = ${ mImpl[R]('{sym}) } + + def mImpl[R: Type](using qctx: QuoteContext)(sym: Expr[Symantics[R]]): Expr[R] = '{ + $sym.Meth(42) + } +} + +trait Symantics[R] { + def Meth(exp: Int): R +} diff --git a/tests/pos-macros/i7110c/Test_2.scala b/tests/pos-macros/i7110c/Test_2.scala new file mode 100644 index 000000000000..efb82b88f669 --- /dev/null +++ b/tests/pos-macros/i7110c/Test_2.scala @@ -0,0 +1,16 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + + val sym = new Symantics2 + + val test = m[Int](sym) + } +} + +class Symantics2 extends Symantics[Int] { + def Meth(exp: Int): Int = exp + def Meth(): Int = 42 +} \ No newline at end of file diff --git a/tests/pos-macros/i7110d/Macro_1.scala b/tests/pos-macros/i7110d/Macro_1.scala new file mode 100644 index 000000000000..fe8a8cc002b8 --- /dev/null +++ b/tests/pos-macros/i7110d/Macro_1.scala @@ -0,0 +1,14 @@ +import scala.quoted._ + +object Macros { + + inline def m(sym: Symantics) : Int = ${ mImpl('sym) } + + def mImpl(using qctx: QuoteContext)(sym: Expr[Symantics]): Expr[Int] = '{ + $sym.Meth(42) + } +} + +trait Symantics { + def Meth(exp: Int): Int +} diff --git a/tests/pos-macros/i7110d/Test_2.scala b/tests/pos-macros/i7110d/Test_2.scala new file mode 100644 index 000000000000..5582ec55a133 --- /dev/null +++ b/tests/pos-macros/i7110d/Test_2.scala @@ -0,0 +1,16 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + + val sym = new Symantics2 + + val test = m(sym) + } +} + +class Symantics2 extends Symantics { + def Meth(exp: Int): Int = exp + def Meth(): Int = 42 +} diff --git a/tests/pos-macros/i7110e/Macro_1.scala b/tests/pos-macros/i7110e/Macro_1.scala new file mode 100644 index 000000000000..eab74cdcfc1a --- /dev/null +++ b/tests/pos-macros/i7110e/Macro_1.scala @@ -0,0 +1,15 @@ +import scala.quoted._ + +object Macros { + + inline def m(sym: Symantics, x: Int) : Int = ${ mImpl('sym, 'x) } + + def mImpl(using qctx: QuoteContext)(sym: Expr[Symantics], x: Expr[Int]): Expr[Int] = '{ + $sym.Meth($x) + } +} + +trait Symantics { + def Meth[R](exp: R): Int + def Meth(): Int +} diff --git a/tests/pos-macros/i7110e/Test_2.scala b/tests/pos-macros/i7110e/Test_2.scala new file mode 100644 index 000000000000..881ce247c65c --- /dev/null +++ b/tests/pos-macros/i7110e/Test_2.scala @@ -0,0 +1,14 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + + val sym = new Symantics { + def Meth[R](exp: R): Int = 2 + def Meth(): Int = 42 + } + + val test = m(sym, 3) + } +} diff --git a/tests/pos-macros/i7110f/Macro_1.scala b/tests/pos-macros/i7110f/Macro_1.scala new file mode 100644 index 000000000000..d35f56ba36ce --- /dev/null +++ b/tests/pos-macros/i7110f/Macro_1.scala @@ -0,0 +1,15 @@ +import scala.quoted._ + +object Macros { + + inline def m[R](sym: Symantics[R]) : R = ${ mImpl[R]('{sym}) } + + def mImpl[R: Type](using qctx: QuoteContext)(sym: Expr[Symantics[R]]): Expr[R] = '{ + $sym.Meth(42) + } +} + +trait Symantics[R] { + def Meth(exp: Int): R + def Meth(): R +} diff --git a/tests/pos-macros/i7110f/Test_2.scala b/tests/pos-macros/i7110f/Test_2.scala new file mode 100644 index 000000000000..c901785a7bf1 --- /dev/null +++ b/tests/pos-macros/i7110f/Test_2.scala @@ -0,0 +1,14 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + + val sym = new Symantics[Int] { + def Meth(exp: Int): Int = exp + def Meth(): Int = 42 + } + + val test = m[Int](sym) + } +} diff --git a/tests/pos/quoted-var.scala b/tests/pos/quoted-var.scala new file mode 100644 index 000000000000..7de01c9bb540 --- /dev/null +++ b/tests/pos/quoted-var.scala @@ -0,0 +1,17 @@ +import scala.quoted._ + +class Var[T] + +object Var { + def apply[T: Type, U: Type](init: Expr[T])(body: Var[T] => Expr[U])(using qctx: QuoteContext): Expr[U] = '{ + var x = $init + ${ + body( + new Var[T] { + def get(using qctx: QuoteContext): Expr[T] = 'x + def update(e: Expr[T])(using qctx: QuoteContext): Expr[Unit] = '{ x = $e } + } + ) + } + } +} diff --git a/tests/run-staging/abstract-int-quote.scala b/tests/run-staging/abstract-int-quote.scala new file mode 100644 index 000000000000..00411042cbaf --- /dev/null +++ b/tests/run-staging/abstract-int-quote.scala @@ -0,0 +1,16 @@ +import scala.quoted._ +import scala.quoted.staging._ + +object Test: + + given Toolbox = Toolbox.make(getClass.getClassLoader) + + def main(args: Array[String]): Unit = + def reduce[T: Type](using QuoteContext)(succ: Expr[T] => Expr[T], zero: Expr[T]): Expr[T] = '{ + var z = $zero + ${ succ('z) } + } + def resCode2(using QuoteContext): Expr[Int] = + reduce[Int](x => '{$x + 1}, '{0}) + + println(withQuoteContext(resCode2.show)) diff --git a/tests/run-staging/quote-nested-6.check b/tests/run-staging/quote-nested-6.check new file mode 100644 index 000000000000..05c2bd4eb00c --- /dev/null +++ b/tests/run-staging/quote-nested-6.check @@ -0,0 +1,7 @@ +{ + type T[X] = scala.List[X] + val x: java.lang.String = "foo" + val z: T[scala.Predef.String] = scala.List.apply[java.lang.String](x) + + (x: java.lang.String) +} diff --git a/tests/run-staging/quote-nested-6.scala b/tests/run-staging/quote-nested-6.scala new file mode 100644 index 000000000000..eae08a79328b --- /dev/null +++ b/tests/run-staging/quote-nested-6.scala @@ -0,0 +1,20 @@ +import quoted._ +import scala.quoted.staging._ + +object Test { + given Toolbox = Toolbox.make(getClass.getClassLoader) + + def main(args: Array[String]): Unit = withQuoteContext { + val q = '{ + type T[X] = List[X] + val x = "foo" + ${ + val y = 'x + '{ val z: T[String] = List($y) } + } + x + } + + println(q.show) + } +} diff --git a/tests/run/i7110.scala b/tests/run/i7110.scala new file mode 100644 index 000000000000..203dcfc74e22 --- /dev/null +++ b/tests/run/i7110.scala @@ -0,0 +1,18 @@ + +trait Symantics[R] { + def Meth(exp: Int): R + def Meth(): R +} + +inline def m[R](sym: Symantics[R]) : R = + sym.Meth(42) + +@main def Test: Unit = { + + val sym = new Symantics[Int] { + def Meth(exp: Int): Int = exp + def Meth(): Int = 43 + } + + val test = m[Int](sym) +}