diff --git a/community-build/community-projects/dotty-cps-async b/community-build/community-projects/dotty-cps-async index 13e2d4e14ac3..0e023dd2395c 160000 --- a/community-build/community-projects/dotty-cps-async +++ b/community-build/community-projects/dotty-cps-async @@ -1 +1 @@ -Subproject commit 13e2d4e14ac3b9be402fa02e056980abc7a1cb5a +Subproject commit 0e023dd2395cb9521c34a11e21541bbc30839074 diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index d3f8718ef525..7e4ee3c3dbb9 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -31,7 +31,29 @@ import scala.annotation.constructorOnly /** Checks that the Phase Consistency Principle (PCP) holds and heals types. * - * Type healing consists in transforming a phase inconsistent type `T` into a splice of `implicitly[Type[T]]`. + * Local term references are phase consistent if and only if they are used at the same level as their definition. + * + * Local type references can be used at the level of their definition or lower. If used used at a higher level, + * it will be healed if possible, otherwise it is inconsistent. + * + * Type healing consists in transforming a phase inconsistent type `T` into a splice of `${summon[Type[T]]}`. + * + * As references to types do not necessarily have an assosiated tree it is not always possible to replace the types directly. + * Instead we always generate a type alias for it and palce it at the start of the surounding quote. This also avoids duplication. + * For example: + * '{ + * val x: List[T] = List[T]() + * () + * } + * + * is transformed to + * + * '{ + * type t$1 = ${summon[Type[T]]} + * val x: List[t$1] = List[t$1](); + * () + * } + * */ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) with Checking { import tpd._ @@ -41,16 +63,40 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( override def transform(tree: Tree)(implicit ctx: Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(ctx.withSource(tree.source)) + else if !isInQuoteOrSplice then + checkAnnotations(tree) + super.transform(tree) else tree match { - case tree: DefDef if tree.symbol.is(Inline) && level > 0 => EmptyTree - case tree: DefTree => - lazy val annotCtx = ctx.fresh.setProperty(InAnnotation, true).withOwner(tree.symbol) - for (annot <- tree.symbol.annotations) annot match { - case annot: BodyAnnotation => annot // already checked in PrepareInlineable before the creation of the BodyAnnotation - case annot => transform(annot.tree)(using annotCtx) - } - checkLevel(super.transform(tree)) - case _ => checkLevel(super.transform(tree)) + + case _: TypeTree | _: RefTree if tree.isType => + val healedType = healType(tree.sourcePos)(tree.tpe) + if healedType == tree.tpe then tree + else TypeTree(healedType).withSpan(tree.span) + case _: AppliedTypeTree => + super.transform(tree) match + case tree1: AppliedTypeTree if tree1 ne tree => + // propagate healed types + tree1.withType(tree1.tpt.tpe.appliedTo(tree1.args.map(_.tpe))) + case tree1 => tree1 + + case _: Ident | _: This => + tree.withType(healTypeOfTerm(tree.sourcePos)(tree.tpe)) + + // Remove inline defs in quoted code. Already fully inlined. + case tree: DefDef if tree.symbol.is(Inline) && level > 0 => + EmptyTree + + case tree: ValOrDefDef => + checkAnnotations(tree) + healInfo(tree, tree.tpt.sourcePos) + super.transform(tree) + case tree: Bind => + checkAnnotations(tree) + healInfo(tree, tree.sourcePos) + super.transform(tree) + + case _ => + super.transform(tree) } /** Transform quoted trees while maintaining phase correctness */ @@ -83,12 +129,12 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( case Apply(fun @ TypeApply(_, _ :: Nil), _) if splice.isTerm => // Type of the splice itsel must also be healed // internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...) - val tp = checkType(splice.sourcePos).apply(splice.tpe.widenTermRefExpr) + val tp = healType(splice.sourcePos)(splice.tpe.widenTermRefExpr) cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil) case Apply(f @ Apply(fun @ TypeApply(_, _), qctx :: Nil), _) if splice.isTerm => // Type of the splice itsel must also be healed // internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...) - val tp = checkType(splice.sourcePos).apply(splice.tpe.widenTermRefExpr) + val tp = healType(splice.sourcePos)(splice.tpe.widenTermRefExpr) cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), qctx :: Nil), body1 :: Nil) case splice: Select => val tagRef = getQuoteTypeTags.getTagRef(splice.qualifier.tpe.asInstanceOf[TermRef]) @@ -96,175 +142,109 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( } } - /** If `tree` refers to a locally defined symbol (either directly, or in a pickled type), - * check that its staging level matches the current level. References to types - * that are phase-incorrect can still be healed as follows: + /** Check that annotations do not contain quotes and and that splices are valid */ + private def checkAnnotations(tree: Tree)(using Context): Unit = + tree match + case tree: DefTree => + lazy val annotCtx = ctx.fresh.setProperty(InAnnotation, true).withOwner(tree.symbol) + for (annot <- tree.symbol.annotations) annot match + case annot: BodyAnnotation => annot // already checked in PrepareInlineable before the creation of the BodyAnnotation + case annot => transform(annot.tree)(using annotCtx) + case _ => + + /** Heal types in the info of the given tree */ + private def healInfo(tree: Tree, pos: SourcePosition)(using Context): Unit = + tree.symbol.info = healType(pos)(tree.symbol.info) + + /** If the type refers to a locally defined symbol (either directly, or in a pickled type), + * check that its staging level matches the current level. + * - Static types and term are allowed at any level. + * - If a type reference is used a higher level, then it is insosistent. Will atempt to heal before failing. + * - If a term reference is used a different level, then it is insosistent. * * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with - * `${implicitly[quoted.Type[T]]}`. + * a type tag of type `quoted.Type[T]`. + * The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit + * or indirectly by `tryHeal`. */ - protected def checkLevel(tree: Tree)(implicit ctx: Context): Tree = { - def checkTp(tp: Type): Type = checkType(tree.sourcePos).apply(tp) - tree match { - case Quoted(_) | Spliced(_) => - tree - case _: This => - assert(checkSymLevel(tree.symbol, tree.tpe, tree.sourcePos).isEmpty) - tree - case Ident(name) => - if (name == nme.WILDCARD) - untpd.Ident(name).withType(checkType(tree.sourcePos).apply(tree.tpe)).withSpan(tree.span) - else - checkSymLevel(tree.symbol, tree.tpe, tree.sourcePos) match { - case Some(tpRef) => tpRef - case _ => tree - } - case _: TypeTree | _: AppliedTypeTree | _: Apply | _: TypeApply | _: UnApply | Select(_, OuterSelectName(_, _)) => - tree.withType(checkTp(tree.tpe)) - case _: ValOrDefDef | _: Bind => - tree.symbol.info = checkTp(tree.symbol.info) - tree - case _: Template => - checkTp(tree.symbol.owner.asClass.givenSelfType) - tree - case _ => - tree - } - } - - /** Check and heal all named types and this-types in a given type for phase consistency. */ - private def checkType(pos: SourcePosition)(implicit ctx: Context): TypeMap = new TypeMap { - def apply(tp: Type): Type = reporting.trace(i"check type level $tp at $level") { - tp match { - case tp: TypeRef if tp.symbol.isSplice => - if (tp.isTerm) - mapCtx.error(i"splice outside quotes", pos) - if level > 0 then getQuoteTypeTags.getTagRef(tp.prefix.asInstanceOf[TermRef]) - else tp - case tp: TypeRef if tp.symbol == defn.QuotedTypeClass.typeParams.head => - if level > 0 then - // Adapt direct references to the type of the type parameter T of a quoted.Type[T]. - // Replace it with a properly encoded type splice. This is the normal form expected for type splices. - getQuoteTypeTags.getTagRef(tp.prefix.asInstanceOf[TermRef]) - else tp - case tp: NamedType => - checkSymLevel(tp.symbol, tp, pos) match { - case Some(tpRef) => tpRef.tpe + private def healType(pos: SourcePosition)(using Context) = new TypeMap { + def apply(tp: Type): Type = + tp match + case tp: TypeRef => + tp.prefix match + case NoPrefix if level > levelOf(tp.symbol) => + tryHeal(tp.symbol, tp, pos) + case prefix: ThisType if !tp.symbol.isStatic && level > levelOf(prefix.cls) => + tryHeal(tp.symbol, tp, pos) + case prefix: TermRef if tp.symbol.isSplice => + // Heal explicit type splice in the code + if level > 0 then getQuoteTypeTags.getTagRef(prefix) else tp + case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => + tryHeal(prefix.symbol, tp, pos) case _ => - if (tp.symbol.is(Param)) tp - else mapOver(tp) - } - case tp: ThisType => - assert(checkSymLevel(tp.cls, tp, pos).isEmpty) - mapOver(tp) + mapOver(tp) + case tp: ThisType if level != -1 && level != levelOf(tp.cls) => + levelError(tp.cls, tp, pos, "") case tp: AnnotatedType => - derivedAnnotatedType(tp, apply(tp.parent), tp.annot) + val newAnnotTree = transform(tp.annot.tree) + derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree)) case _ => mapOver(tp) - } - } } - /** Check reference to `sym` for phase consistency, where `tp` is the underlying type - * by which we refer to `sym`. If it is an inconsistent type try construct a healed type for it. - * - * @return `None` if the phase is correct or cannot be healed - * `Some(tree)` with the `tree` of the healed type tree for `${implicitly[quoted.Type[T]]}` - */ - private def checkSymLevel(sym: Symbol, tp: Type, pos: SourcePosition)(implicit ctx: Context): Option[Tree] = { - /** Is a reference to a class but not `this.type` */ - def isClassRef = sym.isClass && !tp.isInstanceOf[ThisType] - - /** Is this a static path or a type porjection with a static prefix */ - def isStaticPathOK(tp1: Type): Boolean = - tp1.stripTypeVar match - case tp1: TypeRef => tp1.symbol.is(Package) || isStaticPathOK(tp1.prefix) - case tp1: TermRef => - def isStaticTermPathOK(sym: Symbol): Boolean = - (sym.is(Module) && sym.isStatic) || - (sym.isStableMember && isStaticTermPathOK(sym.owner)) - isStaticTermPathOK(tp1.symbol) - case tp1: ThisType => tp1.cls.isStaticOwner - case tp1: AppliedType => isStaticPathOK(tp1.tycon) - case tp1: SkolemType => isStaticPathOK(tp1.info) - case _ => false - - /* Is a reference to an `` method on a class with a static path */ - def isStaticNew(tp1: Type): Boolean = tp1 match - case tp1: TermRef => tp1.symbol.isConstructor && isStaticPathOK(tp1.prefix) - case _ => false - - if (!sym.exists || levelOK(sym) || isStaticPathOK(tp) || isStaticNew(tp)) - None - else if (!sym.isStaticOwner && !isClassRef) + /** Check phase consistency of terms and heal incosistent type references. */ + private def healTypeOfTerm(pos: SourcePosition)(using Context) = new TypeMap { + def apply(tp: Type): Type = tp match - case tp: TypeRef => - if levelOf(sym).getOrElse(0) < level then tryHeal(sym, tp, pos) - else None + case tp @ TypeRef(NoPrefix, _) if level > levelOf(tp.symbol) => + tryHeal(tp.symbol, tp, pos) + case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) => + levelError(tp.symbol, tp, pos, "") + case tp: ThisType if level != -1 && level != levelOf(tp.cls) => + levelError(tp.cls, tp, pos, "") case _ => - levelError(sym, tp, pos, "") - else if (!sym.owner.isStaticOwner) // non-top level class reference that is phase inconsistent - levelError(sym, tp, pos, "") - else - None - } - - /** Does the level of `sym` match the current level? - * An exception is made for inline vals in macros. These are also OK if their level - * is one higher than the current level, because on execution such values - * are constant expression trees and we can pull out the constant from the tree. - */ - private def levelOK(sym: Symbol)(implicit ctx: Context): Boolean = levelOf(sym) match { - case Some(l) => - l == level || - level == -1 && ( - // here we assume that Splicer.checkValidMacroBody was true before going to level -1, - // this implies that all arguments are quoted. - sym.isClass // reference to this in inline methods - ) - case None => - sym.is(Package) || sym.owner.isStaticOwner || - (sym.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) && level > 0) || - levelOK(sym.owner) + if tp.typeSymbol.is(Package) then tp + else mapOver(tp) } /** Try to heal reference to type `T` used in a higher level than its definition. - * @return None if successful - * @return Some(msg) if unsuccessful where `msg` is a potentially empty error message - * to be added to the "inconsistent phase" message. + * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a + * refercence to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. + * Emits and error if `T` cannot be healed and returns `T`. */ - protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(implicit ctx: Context): Option[Tree] = { + protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(implicit ctx: Context): TypeRef = { val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) val tag = ctx.typer.inferImplicitArg(reqType, pos.span) - tag.tpe match + case tp: TermRef => checkStable(tp, pos, "type witness") - Some(ref(getQuoteTypeTags.getTagRef(tp))) + getQuoteTypeTags.getTagRef(tp) case _: SearchFailureType => levelError(sym, tp, pos, - i""" - | - | The access would be accepted with the right type tag, but - | ${ctx.typer.missingArgMsg(tag, reqType, "")}""") + i""" + | + | The access would be accepted with the right type tag, but + | ${ctx.typer.missingArgMsg(tag, reqType, "")}""") case _ => levelError(sym, tp, pos, - i""" - | - | The access would be accepted with a given $reqType""") + i""" + | + | The access would be accepted with a given $reqType""") } - private def levelError(sym: Symbol, tp: Type, pos: SourcePosition, errMsg: String)(using Context) = { + private def levelError(sym: Symbol, tp: Type, pos: SourcePosition, errMsg: String)(using Context): tp.type = { def symStr = if (!tp.isInstanceOf[ThisType]) sym.show else if (sym.is(ModuleClass)) sym.sourceModule.show else i"${sym.name}.this" - summon[Context].error( + ctx.error( em"""access to $symStr from wrong staging level: - | - the definition is at level ${levelOf(sym).getOrElse(0)}, + | - the definition is at level ${levelOf(sym)}, | - but the access is at level $level.$errMsg""", pos) - None + tp } + } object PCPCheckAndHeal { diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index b245b9ac41f7..22cfd2d588c1 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -301,7 +301,7 @@ class ReifyQuotes extends MacroTransform { /** Returns true if this tree will be captured by `makeLambda`. Checks phase consistency and presence of capturer. */ private def isCaptured(sym: Symbol, level: Int)(implicit ctx: Context): Boolean = - level == 1 && levelOf(sym).contains(1) && capturers.contains(sym) + level == 1 && levelOf(sym) == 1 && capturers.contains(sym) /** Transform `tree` and return the resulting tree and all `embedded` quotes * or splices as a pair. diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 708bf418925d..7f5547239b92 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -43,19 +43,19 @@ class Staging extends MacroTransform { tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => val checker = new PCPCheckAndHeal(freshStagingContext) { - override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(implicit ctx: Context): Option[tpd.Tree] = { + override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(implicit ctx: Context): TypeRef = { def symStr = if (sym.is(ModuleClass)) sym.sourceModule.show else i"${sym.name}.this" val errMsg = s"\nin ${ctx.owner.fullName}" assert( ctx.owner.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) || - (sym.isType && levelOf(sym).getOrElse(0) > 0), + (sym.isType && levelOf(sym) > 0), em"""access to $symStr from wrong staging level: - | - the definition is at level ${levelOf(sym).getOrElse(0)}, + | - the definition is at level ${levelOf(sym)}, | - but the access is at level $level.$errMsg""") - None + tp } } checker.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala index 6e86c25d526a..e36b97cf9641 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala @@ -35,23 +35,28 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap import TreeMapWithStages._ /** A map from locally defined symbols to their definition quotation level */ - private val levelOfMap: mutable.HashMap[Symbol, Int] = ictx.property(LevelOfKey).get + private[this] val levelOfMap: mutable.HashMap[Symbol, Int] = ictx.property(LevelOfKey).get /** A stack of entered symbols, to be unwound after scope exit */ - private var enteredSyms: List[Symbol] = Nil + private[this] var enteredSyms: List[Symbol] = Nil + + /** If we are inside a quote or a splice */ + private[this] var inQuoteOrSplice = false /** The quotation level of the definition of the locally defined symbol */ - protected def levelOf(sym: Symbol): Option[Int] = levelOfMap.get(sym) + protected def levelOf(sym: Symbol): Int = levelOfMap.getOrElse(sym, 0) /** Localy defined symbols seen so far by `StagingTransformer.transform` */ protected def localSymbols: List[Symbol] = enteredSyms - /** Enter staging level of symbol defined by `tree`, if applicable. */ + /** If we are inside a quote or a splice */ + protected def isInQuoteOrSplice: Boolean = inQuoteOrSplice + + /** Enter staging level of symbol defined by `tree` */ private def markSymbol(sym: Symbol)(implicit ctx: Context): Unit = - if ((sym.isClass || sym.maybeOwner.isTerm) && !levelOfMap.contains(sym)) { + if level != 0 && !levelOfMap.contains(sym) then levelOfMap(sym) = level enteredSyms = sym :: enteredSyms - } /** Enter staging level of symbol defined by `tree`, if applicable. */ private def markDef(tree: Tree)(implicit ctx: Context): Unit = tree match { @@ -89,19 +94,25 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap tree match { case Quoted(quotedTree) => - dropEmptyBlocks(quotedTree) match { + val old = inQuoteOrSplice + inQuoteOrSplice = true + try dropEmptyBlocks(quotedTree) match { case Spliced(t) => // '{ $x } --> x // and adapt the refinment of `QuoteContext { type tasty: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) case _ => transformQuotation(quotedTree, tree) } + finally inQuoteOrSplice = old case tree @ Spliced(splicedTree) => - dropEmptyBlocks(splicedTree) match { + val old = inQuoteOrSplice + inQuoteOrSplice = true + try dropEmptyBlocks(splicedTree) match { case Quoted(t) => transform(t) // ${ 'x } --> x case _ => transformSplice(splicedTree, tree) } + finally inQuoteOrSplice = old case Block(stats, _) => val last = enteredSyms diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9c4ba0b47128..50ad60048a00 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -38,7 +38,6 @@ import config.Feature._ import config.SourceVersion._ import rewrites.Rewrites.patch import NavigateAST._ -import dotty.tools.dotc.transform.{PCPCheckAndHeal, Staging, TreeMapWithStages} import transform.SymUtils._ import transform.TypeUtils._ import reporting.trace diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index a4cdd5a650de..564488b3d032 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -40,3 +40,6 @@ nullable.scala # parameter untupling with overloaded functions (see comment in Applications.normArg) i7757.scala + +# splice type tag dealiased in this reference +i8651b.scala diff --git a/tests/neg-macros/quote-this-a.scala b/tests/neg-macros/quote-this-a.scala new file mode 100644 index 000000000000..281aaea81a9b --- /dev/null +++ b/tests/neg-macros/quote-this-a.scala @@ -0,0 +1,14 @@ +import scala.quoted._ + +class Foo { + + def f(using QuoteContext): Unit = '{ + def bar[T](x: T): T = x + bar[ + this.type // error + ] { + this // error + } + } + +} diff --git a/tests/neg-macros/quote-this.scala b/tests/neg-macros/quote-this-c.scala similarity index 71% rename from tests/neg-macros/quote-this.scala rename to tests/neg-macros/quote-this-c.scala index 6b5ea97053c0..8022bd6360ce 100644 --- a/tests/neg-macros/quote-this.scala +++ b/tests/neg-macros/quote-this-c.scala @@ -2,15 +2,6 @@ import scala.quoted._ class Foo { - def f(using QuoteContext): Unit = '{ - def bar[T](x: T): T = x - bar[ - this.type // error - ] { - this // error - } - } - inline def i(): Unit = ${ Foo.impl[Any]('{ val x: QuoteContext = ??? given x.type = x diff --git a/tests/neg/i7892.scala b/tests/neg/i7892.scala new file mode 100644 index 000000000000..b738761a2a88 --- /dev/null +++ b/tests/neg/i7892.scala @@ -0,0 +1,12 @@ +import scala.quoted._ + +package x { + class CExprResult1[T] +} + +def run(using qctx: QuoteContext): Unit = { + val cpsLeft: x.CExprResult1[?] = ??? + run1(cpsLeft) // error +} + +def run1[L:Type](cpsLeft: x.CExprResult1[L]): Unit = ??? diff --git a/tests/pos-macros/i6140.scala b/tests/pos-macros/i6140.scala new file mode 100644 index 000000000000..96247819bc1f --- /dev/null +++ b/tests/pos-macros/i6140.scala @@ -0,0 +1,8 @@ +import scala.quoted._ +sealed trait Trait[T] { + type t = T +} + +object O { + def fn[T:Type](t : Trait[T])(using QuoteContext): Type[T] = '[t.t] +} diff --git a/tests/pos-macros/i7030/Macros_1.scala b/tests/pos-macros/i7030/Macros_1.scala new file mode 100644 index 000000000000..a529538e08a7 --- /dev/null +++ b/tests/pos-macros/i7030/Macros_1.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +inline def inner(exprs: Any): Any = ${innerImpl('exprs)} +def innerImpl(exprs: Expr[Any])(using QuoteContext): Expr[Any] = + '{$exprs ; ()} + +inline def outer(expr: => Any): Any = ${outerImpl('expr)} +def outerImpl(body: Expr[Any])(using ctx: QuoteContext): Expr[Any] = { + import ctx.tasty._ + body.unseal.underlyingArgument.seal +} diff --git a/tests/pos-macros/i7030/Test_2.scala b/tests/pos-macros/i7030/Test_2.scala new file mode 100644 index 000000000000..2271ddfc7768 --- /dev/null +++ b/tests/pos-macros/i7030/Test_2.scala @@ -0,0 +1 @@ +val x = outer(inner(???)) diff --git a/tests/pos-macros/i8100.scala b/tests/pos-macros/i8100.scala new file mode 100644 index 000000000000..374d9e086ac7 --- /dev/null +++ b/tests/pos-macros/i8100.scala @@ -0,0 +1,21 @@ +import scala.quoted._ + +class M { + type E +} + +def f[T: Type](using QuoteContext) = + Expr.summon[M] match + case Some('{ $mm : $tt }) => + '{ + val m = $mm + type ME = m.E + ${ g[ME](using '[ME]) } + ${ g[m.E](using '[ME]) } + ${ g[ME](using '[m.E]) } + ${ g[m.E](using '[m.E]) } + // ${ g[ME] } // FIXME: issue seems to be in ReifyQuotes + // ${ g[m.E] } // FIXME: issue seems to be in ReifyQuotes + } + +def g[T](using Type[T]) = ??? diff --git a/tests/pos/i7997.scala b/tests/pos/i7997.scala new file mode 100644 index 000000000000..bc90850e62a4 --- /dev/null +++ b/tests/pos/i7997.scala @@ -0,0 +1,4 @@ +import scala.quoted._ +def oops(using QuoteContext) = { + val q = '{ class Foo { val x = 3; ${ val v = 'x; '{} } }} +} diff --git a/tests/pos/i8651a.scala b/tests/pos/i8651a.scala new file mode 100644 index 000000000000..4e342cc8451f --- /dev/null +++ b/tests/pos/i8651a.scala @@ -0,0 +1,8 @@ +import scala.quoted._ +def coroutineImpl(using QuoteContext): Expr[Any] = + '{ + new { + def state: Int = 0 + ${identity('state)} + } + } diff --git a/tests/pos/i8651b.scala b/tests/pos/i8651b.scala new file mode 100644 index 000000000000..b4fc26663cbd --- /dev/null +++ b/tests/pos/i8651b.scala @@ -0,0 +1,31 @@ +abstract class Coroutine[+T] { + def continue: Option[T] +} + +object Macros { + + import scala.quoted._ + import scala.quoted.matching._ + + + inline def coroutine[T](inline body: Any): Coroutine[T] = ${ coroutineImpl('{body}) } + + def coroutineImpl[T: Type](expr: Expr[_ <: Any])(implicit qtx: QuoteContext): Expr[Coroutine[T]] = { + import qtx.tasty.{_, given _} + + '{ + new Coroutine[T] { + var state: Int = 0 + + def continue: Option[T] = ${ + '{ + state = 1 //if this line is commented there are no compile errors anymore!!! + None + } + } + + } + } + + } +} diff --git a/tests/pos/quote-type-with-param.scala b/tests/pos/quote-type-with-param.scala new file mode 100644 index 000000000000..c639f8129860 --- /dev/null +++ b/tests/pos/quote-type-with-param.scala @@ -0,0 +1,4 @@ +import scala.quoted._ + +def f(using QuoteContext): Unit = + '{ type T[X] = List[X] } diff --git a/tests/run-macros/i6772/Macro_1.scala b/tests/run-macros/i6772/Macro_1.scala new file mode 100644 index 000000000000..0cf3c4acca12 --- /dev/null +++ b/tests/run-macros/i6772/Macro_1.scala @@ -0,0 +1,14 @@ +import scala.quoted._ + +object Macros { + + inline def m() : Any = ${ mImpl() } + + def mImpl()(using QuoteContext): Expr[Any] = + List(Expr(1), Expr(2), Expr(3)).toExprOfList + + def (list: List[Expr[T]]).toExprOfList[T](using Type[T], QuoteContext): Expr[List[T]] = '{ + val buff = List.newBuilder[T] + ${ Expr.block(list.map(v => '{ buff += $v }), '{ buff.result() }) } + } +} diff --git a/tests/run-macros/i6772/Test_2.scala b/tests/run-macros/i6772/Test_2.scala new file mode 100644 index 000000000000..23805df36462 --- /dev/null +++ b/tests/run-macros/i6772/Test_2.scala @@ -0,0 +1,8 @@ +import scala.quoted._ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + println(m()) + } +}