diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index fca79f882644..0157a095cd0b 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -37,6 +37,7 @@ class Compiler { /** Phases dealing with the frontend up to trees ready for TASTY pickling */ protected def frontendPhases: List[List[Phase]] = List(new FrontEnd) :: // Compiler frontend: scanner, parser, namer, typer + List(new Staging) :: // Check PCP, heal quoted types and expand macros List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks @@ -45,7 +46,6 @@ class Compiler { /** Phases dealing with TASTY tree pickling and unpickling */ protected def picklerPhases: List[List[Phase]] = - List(new Staging) :: // Check PCP, heal quoted types and expand macros List(new Pickler) :: // Generate TASTY info List(new ReifyQuotes) :: // Turn quoted trees into explicit run-time data structures Nil diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 5fbb6853752d..a9313a409ab8 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -6,7 +6,7 @@ import dotty.tools.FatalError import config.CompilerCommand import core.Comments.{ContextDoc, ContextDocstrings} import core.Contexts.{Context, ContextBase} -import core.{Mode, TypeError} +import core.{MacroClassLoader, Mode, TypeError} import reporting._ import scala.util.control.NonFatal @@ -54,6 +54,7 @@ class Driver { val ctx = rootCtx.fresh val summary = CompilerCommand.distill(args)(ctx) ctx.setSettings(summary.sstate) + MacroClassLoader.init(ctx) if (!ctx.settings.YdropComments.value(ctx) || ctx.mode.is(Mode.ReadComments)) { ctx.setProperty(ContextDoc, new ContextDocstrings) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 1ada58556f6a..942298094dd5 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1325,15 +1325,6 @@ object desugar { val desugared = tree match { case SymbolLit(str) => Literal(Constant(scala.Symbol(str))) - case Quote(t) => - if (t.isType) - TypeApply(ref(defn.QuotedType_applyR), List(t)) - else - Apply(ref(defn.QuotedExpr_applyR), t) - case Splice(expr) => - Select(expr, nme.splice) - case TypSplice(expr) => - Select(expr, tpnme.splice) case InterpolatedString(id, segments) => val strs = segments map { case ts: Thicket => ts.trees.head diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 8b6fcfe03dad..bcf5b08ec2fd 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1001,6 +1001,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { foreachSubTree { tree => if (f(tree)) buf += tree } buf.toList } + + /** Set this tree as the `defTree` of its symbol and return this tree */ + def setDefTree(implicit ctx: Context): ThisTree = { + val sym = tree.symbol + if (sym.exists) sym.defTree = tree + tree + } } /** Map Inlined nodes, NamedArgs, Blocks with no statements and local references to underlying arguments. diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 31982388bcc7..dca30ce1f82c 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -608,6 +608,9 @@ object Flags { /** An inline method */ final val InlineMethod: FlagConjunction = allOf(Inline, Method) + /** An inline by-name parameter proxy */ + final val InlineByNameProxy: FlagConjunction = allOf(InlineProxy, Method) + /** An inline parameter */ final val InlineParam: FlagConjunction = allOf(Inline, Param) diff --git a/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala new file mode 100644 index 000000000000..cbddcbaa6f05 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala @@ -0,0 +1,26 @@ +package dotty.tools.dotc.core + +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.util.Property +import dotty.tools.dotc.reporting.trace + +import scala.collection.mutable + +object MacroClassLoader { + + /** A key to be used in a context property that caches the class loader used for macro expansion */ + private val MacroClassLoaderKey = new Property.Key[ClassLoader] + + /** Get the macro class loader */ + def fromContext(implicit ctx: Context): ClassLoader = + ctx.property(MacroClassLoaderKey).getOrElse(makeMacroClassLoader) + + /** Context with a cached macro class loader that can be accessed with `macroClassLoader` */ + def init(ctx: FreshContext): ctx.type = + ctx.setProperty(MacroClassLoaderKey, makeMacroClassLoader(ctx)) + + private def makeMacroClassLoader(implicit ctx: Context): ClassLoader = trace("new macro class loader") { + val urls = ctx.settings.classpath.value.split(java.io.File.pathSeparatorChar).map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + new java.net.URLClassLoader(urls, getClass.getClassLoader) + } +} diff --git a/compiler/src/dotty/tools/dotc/core/StagingContext.scala b/compiler/src/dotty/tools/dotc/core/StagingContext.scala new file mode 100644 index 000000000000..a6d0dd5dc47c --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/StagingContext.scala @@ -0,0 +1,25 @@ +package dotty.tools.dotc.core + +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.util.Property + +import scala.collection.mutable + +object StagingContext { + + /** A key to be used in a context property that tracks the quoteation level */ + private val QuotationLevel = new Property.Key[Int] + + /** All enclosing calls that are currently inlined, from innermost to outermost. */ + def level(implicit ctx: Context): Int = + ctx.property(QuotationLevel).getOrElse(0) + + /** Context with an incremented quotation level. */ + def quoteContext(implicit ctx: Context): Context = + ctx.fresh.setProperty(QuotationLevel, level + 1) + + /** Context with a decremented quotation level. */ + def spliceContext(implicit ctx: Context): Context = + ctx.fresh.setProperty(QuotationLevel, level - 1) + +} diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 3111ba3fadc4..a3e83220f91f 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -145,7 +145,7 @@ object PickledQuotes { case Block(stats, expr) => seq(stats, rec(expr)).withSpan(fn.span) case _ => - fn.select(nme.apply).appliedToArgs(argRefs()) + fn.select(nme.apply).appliedToArgs(argRefs()).withSpan(fn.span) } Block(argVals, rec(fn)) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 052497dad583..ef22e72bc82d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -851,8 +851,6 @@ class TreeUnpickler(reader: TastyReader, sym.info = ta.avoidPrivateLeaks(sym, tree.sourcePos) } - sym.defTree = tree - if (ctx.mode.is(Mode.ReadComments)) { assert(ctx.docCtx.isDefined, "Mode is `ReadComments`, but no `docCtx` is set.") commentUnpicklerOpt.foreach { commentUnpickler => @@ -862,7 +860,7 @@ class TreeUnpickler(reader: TastyReader, } } - tree + tree.setDefTree } private def readTemplate(implicit ctx: Context): Template = { diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala new file mode 100644 index 000000000000..373c107109f7 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -0,0 +1,219 @@ +package dotty.tools.dotc +package transform + +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.{TreeTypeMap, tpd, untpd} +import dotty.tools.dotc.core.Constants._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.quoted._ +import dotty.tools.dotc.core.NameKinds._ +import dotty.tools.dotc.core.StagingContext._ +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.tasty.TreePickler.Hole +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.util.SourcePosition +import dotty.tools.dotc.util.Spans._ +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.transform.TreeMapWithStages._ +import dotty.tools.dotc.typer.Implicits.SearchFailureType +import dotty.tools.dotc.typer.Inliner + +import scala.collection.mutable +import dotty.tools.dotc.util.SourcePosition + +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]]`. + */ +class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) { + import tpd._ + + override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { + case tree: DefDef if tree.symbol.is(Inline) && level > 0 => EmptyTree + case _ => checkLevel(super.transform(tree)) + } + + /** Transform quoted trees while maintaining phase correctness */ + override protected def transformQuotation(body: Tree, quote: Tree)(implicit ctx: Context): Tree = { + val body1 = transform(body)(quoteContext) + super.transformQuotation(body1, quote) + } + + /** Transform splice + * - If inside a quote, transform the contents of the splice. + * - If inside inlined code, expand the macro code. + * - If inside of a macro definition, check the validity of the macro. + */ + protected def transformSplice(splice: Select)(implicit ctx: Context): Tree = { + if (level >= 1) { + val body1 = transform(splice.qualifier)(spliceContext) + val splice1 = cpy.Select(splice)(body1, splice.name) + if (splice1.isType) splice1 + else addSpliceCast(splice1) + } + else { + assert(!enclosingInlineds.nonEmpty, "unexpanded macro") + assert(ctx.owner.isInlineMethod) + if (Splicer.canBeSpliced(splice.qualifier)) { // level 0 inside an inline definition + transform(splice.qualifier)(spliceContext) // Just check PCP + splice + } + else { // level 0 inside an inline definition + ctx.error( + "Malformed macro call. The contents of the splice ${...} must call a static method and arguments must be quoted or inline.", + splice.sourcePos) + splice + } + } + } + + + /** Add cast to force boundaries where T and $t (an alias of T) are used to ensure PCP. + * '{ ${...: T} } --> '{ ${...: T}.asInstanceOf[T] } --> '{ ${...: T}.asInstanceOf[$t] } + */ + protected def addSpliceCast(tree: Tree)(implicit ctx: Context): Tree = { + val tp = checkType(tree.sourcePos).apply(tree.tpe.widenTermRefExpr) + tree.cast(tp).withSpan(tree.span) + } + + /** 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: + * + * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with + * `${implicitly[quoted.Type[T]]}`. + */ + 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 tree: RefTree if tree.symbol.is(InlineParam) => + tree + case _: This => + assert(checkSymLevel(tree.symbol, tree.tpe, tree.sourcePos).isEmpty) + tree + case _: Ident => + 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) + ctx.error(i"splice outside quotes", pos) + tp + case tp: NamedType => + checkSymLevel(tp.symbol, tp, pos) match { + case Some(tpRef) => tpRef.tpe + case _ => + if (tp.symbol.is(Param)) tp + else mapOver(tp) + } + case tp: ThisType => + assert(checkSymLevel(tp.cls, tp, pos).isEmpty) + mapOver(tp) + 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] + + if (sym.exists && !sym.isStaticOwner && !isClassRef && !levelOK(sym)) + tryHeal(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 && ( + sym == defn.TastyReflection_macroContext || + // here we assume that Splicer.canBeSpliced was true before going to level -1, + // this implies that all non-inline arguments are quoted and that the following two cases are checked + // on inline parameters or type parameters. + sym.is(Param) || + sym.isClass // reference to this in inline methods + ) + case None => + !sym.is(Param) || levelOK(sym.owner) + } + + /** Try to heal phase-inconsistent reference to type `T` using a local type 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. + */ + protected def tryHeal(sym: Symbol, tp: Type, pos: SourcePosition)(implicit ctx: Context): Option[Tree] = { + def levelError(errMsg: String) = { + def symStr = + if (!tp.isInstanceOf[ThisType]) sym.show + else if (sym.is(ModuleClass)) sym.sourceModule.show + else i"${sym.name}.this" + ctx.error( + em"""access to $symStr from wrong staging level: + | - the definition is at level ${levelOf(sym).getOrElse(0)}, + | - but the access is at level $level.$errMsg""", pos) + None + } + tp match { + case tp: TypeRef => + if (level == -1) { + assert(ctx.inInlineMethod) + None + } else { + val reqType = defn.QuotedTypeType.appliedTo(tp) + val tag = ctx.typer.inferImplicitArg(reqType, pos.span) + tag.tpe match { + case fail: SearchFailureType => + levelError(i""" + | + | The access would be accepted with the right type tag, but + | ${ctx.typer.missingArgMsg(tag, reqType, "")}""") + case _ => + Some(tag.select(tpnme.splice)) + } + } + case _ => + levelError("") + } + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5a21cf20c59f..3749f73b6bbe 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -167,26 +167,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } } - /** 1. If we are in an inline method but not in a nested quote, mark the inline method - * as a macro. - * - * 2. If selection is a quote or splice node, record that fact in the current compilation unit. - */ - private def handleMeta(sym: Symbol)(implicit ctx: Context): Unit = { - - 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) - - if (sym.isSplice || sym.isQuote) { - markAsMacro(ctx) - ctx.compilationUnit.needsStaging = true - } - } - private object dropInlines extends TreeMap { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case Inlined(call, _, _) => @@ -198,13 +178,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transform(tree: Tree)(implicit ctx: Context): Tree = try tree match { case tree: Ident if !tree.isType => - handleMeta(tree.symbol) tree.tpe match { case tpe: ThisType => This(tpe.cls).withSpan(tree.span) case _ => tree } case tree @ Select(qual, name) => - handleMeta(tree.symbol) if (name.isTypeName) { Checking.checkRealizable(qual.tpe, qual.posd) super.transform(tree)(ctx.addMode(Mode.Type)) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 7a456c2b9efb..082292945856 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -36,7 +36,7 @@ import scala.annotation.constructorOnly * val x1 = ??? * val x2 = ??? * ... - * ~{ ... '{ ... x1 ... x2 ...} ... } + * ${ ... '{ ... x1 ... x2 ...} ... } * ... * } * ``` @@ -56,12 +56,12 @@ import scala.annotation.constructorOnly * val x1$1 = args(0).asInstanceOf[Expr[T]] * val x2$1 = args(1).asInstanceOf[Expr[T]] // can be asInstanceOf[Type[T]] * ... - * { ... '{ ... x1$1.unary_~ ... x2$1.unary_~ ...} ... } + * { ... '{ ... ${x1$1} ... ${x2$1} ...} ... } * } * ) * ) * ``` - * and then performs the same transformation on `'{ ... x1$1.unary_~ ... x2$1.unary_~ ...}`. + * and then performs the same transformation on `'{ ... ${x1$1} ... ${x2$1} ...}`. * */ class ReifyQuotes extends MacroTransform { @@ -95,9 +95,9 @@ class ReifyQuotes extends MacroTransform { * Returns a version of the reference that needs to be used in its place. * '{ * val x = ??? - * { ... '{ ... x ... } ... }.unary_~ + * ${ ... '{ ... x ... } ... } * } - * Eta expanding the `x` in `{ ... '{ ... x ... } ... }.unary_~` will return a `x$1.unary_~` for which the `x$1` + * Eta expanding the `x` in `${ ... '{ ... x ... } ... }` will return a `${x$1}` for which the `x$1` * be created by some outer reifier. * This transformation is only applied to definitions at staging level 1. * See `isCaptured`. @@ -105,7 +105,7 @@ class ReifyQuotes extends MacroTransform { private class QuoteReifier(outer: QuoteReifier, capturers: mutable.HashMap[Symbol, Tree => Tree], val embedded: Embedded, val owner: Symbol)(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) { self => - import TreeMapWithStages._ + import StagingContext._ /** A nested reifier for a quote (if `isQuote = true`) or a splice (if not) */ def nested(isQuote: Boolean)(implicit ctx: Context): QuoteReifier = { @@ -113,11 +113,11 @@ class ReifyQuotes extends MacroTransform { new QuoteReifier(this, capturers, nestedEmbedded, ctx.owner)(ctx) } - /** Assuming contains types `.unary_~, ..., .unary_~`, the expression + /** Assuming contains types `${}, ..., ${}`, the expression * - * { type = .unary_~ + * { type = ${} * ... - * type = .unary_~ + * type = ${} * * } * @@ -133,7 +133,7 @@ class ReifyQuotes extends MacroTransform { val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs) val local = ctx.newSymbol( owner = ctx.owner, - name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_~").toTermName).toTypeName, + name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_").toTermName).toTypeName, flags = Synthetic, info = TypeAlias(splicedTree.tpe.select(tpnme.splice)), coord = spliced.termSymbol.coord).asType @@ -178,7 +178,7 @@ class ReifyQuotes extends MacroTransform { else body match { case body: RefTree if isCaptured(body.symbol, level + 1) => // Optimization: avoid the full conversion when capturing `x` - // in '{ x } to '{ x$1.unary_~ } and go directly to `x$1` + // in '{ x } to '{ ${x$1} } and go directly to `x$1` capturers(body.symbol)(body) case _=> val (body1, splices) = nested(isQuote = true).splitQuote(body)(quoteContext) @@ -246,7 +246,7 @@ class ReifyQuotes extends MacroTransform { * '{ * val x = ??? * val y = ??? - * { ... '{ ... x .. y ... } ... }.unary_~ + * ${ ... '{ ... x .. y ... } ... } * } * then the spliced subexpression * { ... '{ ... x ... y ... } ... } @@ -254,7 +254,7 @@ class ReifyQuotes extends MacroTransform { * (args: Seq[Any]) => { * val x$1 = args(0).asInstanceOf[Expr[Any]] // or .asInstanceOf[Type[Any]] * val y$1 = args(1).asInstanceOf[Expr[Any]] // or .asInstanceOf[Type[Any]] - * { ... '{ ... x$1.unary_~ ... y$1.unary_~ ... } ... } + * { ... '{ ... ${x$1} ... ${y$1} ... } ... } * } * * See: `capture` @@ -347,7 +347,7 @@ class ReifyQuotes extends MacroTransform { reporting.trace(i"Reifier.transform $tree at $level", show = true) { tree match { case TypeApply(Select(spliceTree @ Spliced(_), _), tp) if tree.symbol.isTypeCast => - // Splice term which should be in the form `x.unary_~.asInstanceOf[T]` where T is an artefact of + // Splice term which should be in the form `${x}.asInstanceOf[T]` where T is an artifact of // typer to allow pickling/unpickling phase consistent types transformSplice(spliceTree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 212eef754d1d..f2af7906cff1 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -95,7 +95,7 @@ object Splicer { } protected def interpretQuote(tree: Tree)(implicit env: Env): Object = - new scala.quoted.Exprs.TastyTreeExpr(tree) + new scala.quoted.Exprs.TastyTreeExpr(Inlined(EmptyTree, Nil, tree).withSpan(tree.span)) protected def interpretTypeQuote(tree: Tree)(implicit env: Env): Object = new scala.quoted.Types.TreeType(tree) @@ -312,7 +312,14 @@ object Splicer { protected final def interpretTree(tree: Tree)(implicit env: Env): Result = tree match { case Apply(TypeApply(fn, _), quoted :: Nil) if fn.symbol == defn.QuotedExpr_apply => - interpretQuote(quoted) + val quoted1 = quoted match { + case quoted: Ident if quoted.symbol.is(InlineByNameProxy) => + // inline proxy for by-name parameter + quoted.symbol.defTree.asInstanceOf[DefDef].rhs + case Inlined(EmptyTree, _, quoted) => quoted + case _ => quoted + } + interpretQuote(quoted1) case TypeApply(fn, quoted :: Nil) if fn.symbol == defn.QuotedType_apply => interpretTypeQuote(quoted) @@ -336,6 +343,8 @@ object Splicer { interpretStaticMethodCall(module, fn.symbol, args.map(arg => interpretTree(arg))) } else if (env.contains(fn.name)) { env(fn.name) + } else if (tree.symbol.is(InlineProxy)) { + interpretTree(tree.symbol.defTree.asInstanceOf[ValOrDefDef].rhs) } else { unexpectedTree(tree) } diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 8f097da7e323..43eceac8b569 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.quoted._ import dotty.tools.dotc.core.NameKinds._ +import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.tasty.TreePickler.Hole @@ -25,28 +26,14 @@ import dotty.tools.dotc.util.SourcePosition import scala.annotation.constructorOnly - -/** Checks that the Phase Consistency Principle (PCP) holds, heals types and expand macros. - * - * Type healing consists in transforming a phase inconsistent type `T` into `implicitly[Type[T]].unary_~`. +/** Checks that the Phase Consistency Principle (PCP) holds and heals types. * - * For macro definitions we assume that we have a single ${...} directly as the RHS. - * The Splicer is used to check that the RHS will be interpretable (with the `Splicer`) once inlined. + * Type healing consists in transforming a phase inconsistent type `T` into `${ implicitly[Type[T]] }`. */ class Staging extends MacroTransform { import tpd._ import Staging._ - /** Classloader used for loading macros */ - private[this] var myMacroClassLoader: java.lang.ClassLoader = _ - private def macroClassLoader(implicit ctx: Context): ClassLoader = { - if (myMacroClassLoader == null) { - val urls = ctx.settings.classpath.value.split(java.io.File.pathSeparatorChar).map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) - } - myMacroClassLoader - } - override def phaseName: String = Staging.name override def allowsImplicitSearch: Boolean = true @@ -88,242 +75,6 @@ class Staging extends MacroTransform { new PCPCheckAndHeal(ctx).transform(tree) } - private class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) { - - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - reporting.trace(i"PCPTransformer.transform $tree at $level", show = true) { - tree match { - case tree: DefDef if tree.symbol.is(Macro) => - if (level > 0) { - super.transform(tree) // Ignore output, only check PCP - EmptyTree // Already inlined - } - else if (enclosingInlineds.nonEmpty) { - EmptyTree // Already checked at definition site and already inlined - } - else tree.rhs match { - case InlineSplice(_) => - super.transform(tree) // Ignore output, only check PCP - tree - case _ => - ctx.error( - """Malformed macro. - | - |Expected the ~ to be at the top of the RHS: - | inline def foo(inline x: X, ..., y: Y): Int = ~impl(x, ... '(y)) - | - | * The contents of the splice must call a static method - | * All arguments must be quoted or inline - """.stripMargin, tree.rhs.sourcePos) - tree - } - case _ => - checkLevel(super.transform(tree)) - } - } - } - - /** Transform quoted trees while maintaining phase correctness */ - override protected def transformQuotation(body: Tree, quote: Tree)(implicit ctx: Context): Tree = { - val body1 = transform(body)(quoteContext) - super.transformQuotation(body1, quote) - } - - /** Transform splice - * - If inside a quote, transform the contents of the splice. - * - If inside inlined code, expand the macro code. - * - If inside of a macro definition, check the validity of the macro. - */ - protected def transformSplice(splice: Select)(implicit ctx: Context): Tree = { - if (level >= 1) { - val body1 = transform(splice.qualifier)(spliceContext) - val splice1 = cpy.Select(splice)(body1, splice.name) - if (splice1.isType) splice1 - else addSpliceCast(splice1) - } - else if (enclosingInlineds.nonEmpty) { // level 0 in an inlined call - val spliceCtx = ctx.outer // drop the last `inlineContext` - val pos: SourcePosition = spliceCtx.source.atSpan(enclosingInlineds.head.span) - val evaluatedSplice = Splicer.splice(splice.qualifier, pos, macroClassLoader)(spliceCtx) - if (ctx.reporter.hasErrors) splice else transform(evaluatedSplice.withSpan(splice.span)) - } - else if (!ctx.owner.isInlineMethod) { // level 0 outside an inline method - ctx.error(i"splice outside quotes or inline method", splice.sourcePos) - splice - } - else if (Splicer.canBeSpliced(splice.qualifier)) { // level 0 inside an inline definition - transform(splice.qualifier)(spliceContext) // Just check PCP - splice - } - else { // level 0 inside an inline definition - ctx.error( - "Malformed macro call. The contents of the ~ must call a static method and arguments must be quoted or inline.", - splice.sourcePos) - splice - } - } - - - /** Add cast to force boundaries where T and ~t (an alias of T) are used to ensure PCP. - * '{ ~(...: T) } --> '{ ~(...: T).asInstanceOf[T] } --> '{ ~(...: T).asInstanceOf[~t] } - */ - protected def addSpliceCast(tree: Tree)(implicit ctx: Context): Tree = { - val tp = checkType(tree.sourcePos).apply(tree.tpe.widenTermRefExpr) - tree.cast(tp).withSpan(tree.span) - } - - /** 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: - * - * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with - * `~implicitly[quoted.Type[T]]`. - */ - 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 tree: RefTree if tree.symbol.is(InlineParam) => - tree - case _: This => - assert(checkSymLevel(tree.symbol, tree.tpe, tree.sourcePos).isEmpty) - tree - case _: Ident => - checkSymLevel(tree.symbol, tree.tpe, tree.sourcePos) match { - case Some(tpRef) => tpRef - case _ => tree - } - case _: TypeTree | _: AppliedTypeTree | _: Apply | _: TypeApply | _: UnApply => - tree.withType(checkTp(tree.tpe)) - case Select(_, OuterSelectName(_, _)) => - tree.withType(checkTp(tree.tpe.widen)) - 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) - ctx.error(i"splice outside quotes", pos) - tp - case tp: NamedType => - checkSymLevel(tp.symbol, tp, pos) match { - case Some(tpRef) => tpRef.tpe - case _ => - if (tp.symbol.is(Param)) tp - else mapOver(tp) - } - case tp: ThisType => - assert(checkSymLevel(tp.cls, tp, pos).isEmpty) - mapOver(tp) - 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] = { - val isThis = tp.isInstanceOf[ThisType] - if (!isThis && !sym.is(Param) && sym.maybeOwner.isType) - None - else if (sym.exists && !sym.isStaticOwner && !levelOK(sym)) - tryHeal(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 && ( - sym == defn.TastyReflection_macroContext || - // here we assume that Splicer.canBeSpliced was true before going to level -1, - // this implies that all non-inline arguments are quoted and that the following two cases are checked - // on inline parameters or type parameters. - sym.is(Param) || - sym.isClass // reference to this in inline methods - ) - case None => - !sym.is(Param) || levelOK(sym.owner) - } - - /** Try to heal phase-inconsistent reference to type `T` using a local type 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. - */ - protected def tryHeal(sym: Symbol, tp: Type, pos: SourcePosition)(implicit ctx: Context): Option[Tree] = { - def levelError(errMsg: String) = { - def symStr = - if (!tp.isInstanceOf[ThisType]) sym.show - else if (sym.is(ModuleClass)) sym.sourceModule.show - else i"${sym.name}.this" - ctx.error( - em"""access to $symStr from wrong staging level: - | - the definition is at level ${levelOf(sym).getOrElse(0)}, - | - but the access is at level $level.$errMsg""", pos) - None - } - tp match { - case tp: TypeRef => - if (level == -1) { - assert(ctx.inInlineMethod) - None - } else { - val reqType = defn.QuotedTypeType.appliedTo(tp) - val tag = ctx.typer.inferImplicitArg(reqType, pos.span) - tag.tpe match { - case fail: SearchFailureType => - levelError(i""" - | - | The access would be accepted with the right type tag, but - | ${ctx.typer.missingArgMsg(tag, reqType, "")}""") - case _ => - Some(tag.select(tpnme.splice)) - } - } - case _ => - levelError("") - } - } - - /** InlineSplice is used to detect cases where the expansion - * consists of a (possibly multiple & nested) block or a sole expression. - */ - private object InlineSplice { - def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match { - case Spliced(code) if Splicer.canBeSpliced(code) => Some(code) - case Block(List(stat), Literal(Constant(()))) => unapply(stat) - case Block(Nil, expr) => unapply(expr) - case Typed(expr, _) => unapply(expr) - case _ => None - } - } - - } - } object Staging { diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala index 58896d9e4c4e..fcd651dea1e2 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala @@ -10,6 +10,7 @@ import dotty.tools.dotc.core.quoted._ import dotty.tools.dotc.core.NameKinds._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.tasty.TreePickler.Hole @@ -129,21 +130,6 @@ object TreeMapWithStages { /** A key to be used in a context property that caches the `levelOf` mapping */ private val LevelOfKey = new Property.Key[mutable.HashMap[Symbol, Int]] - /** A key to be used in a context property that tracks the quoteation level */ - private val QuotationLevel = new Property.Key[Int] - - /** All enclosing calls that are currently inlined, from innermost to outermost. */ - def level(implicit ctx: Context): Int = - ctx.property(QuotationLevel).getOrElse(0) - - /** Context with an incremented quotation level. */ - def quoteContext(implicit ctx: Context): Context = - ctx.fresh.setProperty(QuotationLevel, level + 1) - - /** Context with a decremented quotation level. */ - def spliceContext(implicit ctx: Context): Context = - ctx.fresh.setProperty(QuotationLevel, level - 1) - /** Initial context for a StagingTransformer transformation. */ def freshStagingContext(implicit ctx: Context): Context = ctx.fresh.setProperty(LevelOfKey, new mutable.HashMap[Symbol, Int]) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 6ae5ae5b04a7..984648f77410 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -185,6 +185,30 @@ object Applications { case _ => None } } + + /** 1. If we are in an inline method but not in a nested quote, mark the inline method + * as a macro. + * + * 2. If selection is a quote or splice node, record that fact in the current compilation unit. + */ + def handleMeta(tree: Tree)(implicit ctx: Context): tree.type = { + import transform.SymUtils._ + + 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) + val sym = tree.symbol + if (sym.isSplice) { + if (StagingContext.level == 0) + markAsMacro(ctx) + ctx.compilationUnit.needsStaging = true + } else if (sym.isQuote) { + ctx.compilationUnit.needsStaging = true + } + + tree + } } trait Applications extends Compatibility { self: Typer with Dynamic => @@ -754,7 +778,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * or, if application is an operator assignment, also an `Assign` or * Block node. */ - def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { + def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = handleMeta { def realApply(implicit ctx: Context): Tree = track("realApply") { val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isContextual)(argCtx(tree)) @@ -892,7 +916,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") { val isNamed = hasNamedArg(tree.args) val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) - typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { + handleMeta(typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { case ExtMethodApply(app) => app case _: TypeApply if !ctx.isAfterTyper => @@ -914,7 +938,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply() else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) - } + }) } /** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray. diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 4525f0a1be65..0156e0c6f340 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -47,7 +47,7 @@ abstract class Lifter { var liftedType = expr.tpe.widen if (liftedFlags.is(Method)) liftedType = ExprType(liftedType) val lifted = ctx.newSymbol(ctx.owner, name, liftedFlags | Synthetic, liftedType, coord = spanCoord(expr.span)) - defs += liftedDef(lifted, expr).withSpan(expr.span) + defs += liftedDef(lifted, expr).withSpan(expr.span).setDefTree ref(lifted.termRef).withSpan(expr.span.focus) } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 6105babde61c..e47cd0f24ac2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -2,7 +2,7 @@ package dotty.tools package dotc package typer -import ast._ +import ast.{TreeInfo, tpd, _} import Trees._ import core._ import Flags._ @@ -10,6 +10,7 @@ import Symbols._ import Types._ import Decorators._ import Constants._ +import StagingContext._ import StdNames._ import transform.SymUtils._ import Contexts.Context @@ -25,6 +26,7 @@ import dotty.tools.dotc.util.{SimpleIdentityMap, SimpleIdentitySet, SourceFile, import collection.mutable import reporting.trace import util.Spans.Span +import dotty.tools.dotc.transform.{Splicer, TreeMapWithStages} object Inliner { import tpd._ @@ -104,7 +106,7 @@ object Inliner { else if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) { val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors if (ctx.reporter.hasErrors) tree - else new Inliner(tree, body).inlined(pt) + else new Inliner(tree, body).inlined(pt, tree.sourcePos) } else errorTree( @@ -251,15 +253,14 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { var inlineFlag = InlineProxy if (paramtp.hasAnnotation(defn.InlineParamAnnot)) inlineFlag |= Inline val (bindingFlags, bindingType) = - if (isByName) (Method | InlineProxy, ExprType(argtpe.widen)) + if (isByName) (InlineByNameProxy.toTermFlags, ExprType(argtpe.widen)) else (inlineFlag, argtpe.widen) val boundSym = newSym(name, bindingFlags, bindingType).asTerm val binding = { if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym)) else ValDef(boundSym, arg) }.withSpan(boundSym.span) - boundSym.defTree = binding - bindingsBuf += binding + bindingsBuf += binding.setDefTree binding } @@ -311,9 +312,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { ref(rhsClsSym.sourceModule) else inlineCallPrefix - val binding = ValDef(selfSym.asTerm, rhs).withSpan(selfSym.span) + val binding = ValDef(selfSym.asTerm, rhs).withSpan(selfSym.span).setDefTree bindingsBuf += binding - selfSym.defTree = binding inlining.println(i"proxy at $level: $selfSym = ${bindingsBuf.last}") lastSelf = selfSym lastLevel = level @@ -381,7 +381,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { } /** The Inlined node representing the inlined call */ - def inlined(pt: Type): Tree = { + def inlined(pt: Type, sourcePos: SourcePosition): Tree = { if (callTypeArgs.length == 1) if (inlinedMethod == defn.Compiletime_constValue) { @@ -487,7 +487,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { trace(i"inlining $call", inlining, show = true) { // The normalized bindings collected in `bindingsBuf` - bindingsBuf.transform(reducer.normalizeBinding(_)(inlineCtx)) + bindingsBuf.transform { binding => + // Set trees to symbols allow macros to see the definition tree. + // This is used by `underlyingArgument`. + reducer.normalizeBinding(binding)(inlineCtx).setDefTree + } // Run a typing pass over the inlined tree. See InlineTyper for details. val expansion1 = inlineTyper.typed(expansion, pt)(inlineCtx) @@ -949,11 +953,21 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { assert(tree.hasType, tree) val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this)) - val res = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) + val res = + if (tree.symbol == defn.QuotedExpr_splice && level == 0) expandMacro(qual1, tree.span) + else untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.sourcePos) res } + 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)) + if (ctx.reporter.hasErrors) EmptyTree + else evaluatedSplice.withSpan(span) + } + override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = typed(tree.cond, defn.BooleanType) match { case cond1 @ ConstantValue(b: Boolean) => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6744647d97f1..f1ba950f1cd2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -815,7 +815,6 @@ class Namer { typer: Typer => case original: untpd.DefDef if sym.isInlineMethod => PrepareInlineable.registerInlineInfo( sym, - original.rhs, implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs )(localContext(sym)) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala index f43120efc6c5..e361f2137f89 100644 --- a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala @@ -2,7 +2,7 @@ package dotty.tools package dotc package typer -import dotty.tools.dotc.ast.{Trees, untpd, tpd} +import dotty.tools.dotc.ast.{Trees, tpd, untpd} import Trees._ import core._ import Flags._ @@ -15,9 +15,11 @@ import Contexts.Context import Names.Name import NameKinds.{InlineAccessorName, UniqueInlineName} import Annotations._ -import transform.AccessProxies +import transform.{AccessProxies, PCPCheckAndHeal, Splicer, TreeMapWithStages} import config.Printers.inlining -import util.Property +import util.{Property, SourcePosition} +import dotty.tools.dotc.core.StagingContext._ +import dotty.tools.dotc.transform.TreeMapWithStages._ object PrepareInlineable { import tpd._ @@ -219,7 +221,7 @@ object PrepareInlineable { * to have the inline method as owner. */ def registerInlineInfo( - inlined: Symbol, originalBody: untpd.Tree, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { + inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { inlined.unforcedAnnotation(defn.BodyAnnot) match { case Some(ann: ConcreteBodyAnnotation) => case Some(ann: LazyBodyAnnotation) if ann.isEvaluated => @@ -249,4 +251,52 @@ object PrepareInlineable { em"inline unapply method can be rewritten only if its right hand side is a tuple (e1, ..., eN)", body.sourcePos) } + + def checkInlineMacro(sym: Symbol, rhs: Tree, pos: SourcePosition)(implicit ctx: Context) = { + if (!ctx.isAfterTyper) { + + /** InlineSplice is used to detect cases where the expansion + * consists of a (possibly multiple & nested) block or a sole expression. + */ + object InlineSplice { + def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match { + case Spliced(code) if Splicer.canBeSpliced(code) => Some(code) + case Block(List(stat), Literal(Constants.Constant(()))) => unapply(stat) + case Block(Nil, expr) => unapply(expr) + case Typed(expr, _) => unapply(expr) + case _ => None + } + } + + var isMacro = false + new TreeMapWithStages(freshStagingContext) { + override protected def transformSplice(splice: tpd.Select)(implicit ctx: Context): tpd.Tree = { + isMacro = true + splice + } + override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = + if (isMacro) tree else super.transform(tree) + }.transform(rhs) + + if (isMacro) { + sym.setFlag(Macro) + if (level == 0) + rhs match { + case InlineSplice(_) => + new PCPCheckAndHeal(freshStagingContext).transform(rhs) // Ignore output, only check PCP + case _ => + ctx.error( + """Malformed macro. + | + |Expected the splice ${...} to be at the top of the RHS: + | inline def foo(inline x: X, ..., y: Y): Int = ${impl(x, ... '{y}}) + | + | * The contents of the splice must call a static method + | * All arguments must be quoted or inline + """.stripMargin, pos) + } + } + } + } + } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c5c98812bf53..da69b30f354e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -27,7 +27,7 @@ import EtaExpansion.etaExpand import util.Spans._ import util.common._ import util.Property -import Applications.{ExtMethodApply, wrapDefs, productSelectorTypes} +import Applications.{ExtMethodApply, productSelectorTypes, wrapDefs} import collection.mutable import annotation.tailrec @@ -36,6 +36,8 @@ import util.Stats.{record, track} import config.Printers.{gadts, typr} import rewrites.Rewrites.patch import NavigateAST._ +import dotty.tools.dotc.transform.{PCPCheckAndHeal, Staging, TreeMapWithStages} +import dotty.tools.dotc.core.StagingContext._ import transform.SymUtils._ import transform.TypeUtils._ import reporting.trace @@ -440,7 +442,7 @@ class Typer extends Namer } private def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(implicit ctx: Context): Select = - checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt) + Applications.handleMeta(checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt)) def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { @@ -1489,7 +1491,7 @@ class Typer extends Namer checkInlineConformant(rhs1, isFinal = sym.is(Final), em"right-hand side of inline $sym") patchIfLazy(vdef1) patchFinalVals(vdef1) - vdef1 + vdef1.setDefTree } /** Add a @volitile to lazy vals when rewriting from Scala2 */ @@ -1551,13 +1553,16 @@ class Typer extends Namer if (sym.isInlineMethod) rhsCtx = rhsCtx.addMode(Mode.InlineableBody) val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx) - if (sym.isInlineMethod) PrepareInlineable.registerInlineInfo(sym, ddef.rhs, _ => rhs1) + if (sym.isInlineMethod) { + PrepareInlineable.checkInlineMacro(sym, rhs1, ddef.sourcePos) + PrepareInlineable.registerInlineInfo(sym, _ => rhs1) + } if (sym.isConstructor && !sym.isPrimaryConstructor) for (param <- tparams1 ::: vparamss1.flatten) checkRefsLegal(param, sym.owner, (name, sym) => sym.is(TypeParam), "secondary constructor") - assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) + assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym).setDefTree //todo: make sure dependent method types do not depend on implicits or by-name params } @@ -1924,6 +1929,35 @@ class Typer extends Namer } } + /** Translate '{ t }` into `scala.quoted.Expr.apply(t)` and `'[T]` into `scala.quoted.Type.apply[T]` + * while tracking the quotation level in the context. + */ + def typedQuote(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = track("typedQuote") { + val tree1 = + if (tree.t.isType) + typedTypeApply(untpd.TypeApply(untpd.ref(defn.QuotedType_applyR), List(tree.t)), pt)(quoteContext) + else + typedApply(untpd.Apply(untpd.ref(defn.QuotedExpr_applyR), tree.t), pt)(quoteContext) + tree1.withSpan(tree.span) + } + + /** Translate `${ t: Expr[T] }` into expresiion `t.splice` while tracking the quotation level in the context */ + def typedSplice(tree: untpd.Splice, pt: Type)(implicit ctx: Context): Tree = track("typedSplice") { + checkSpliceOutsideQuote(tree) + typedSelect(untpd.Select(tree.expr, nme.splice), pt)(spliceContext).withSpan(tree.span) + } + + /** Translate ${ t: Type[T] }` into type `t.splice` while tracking the quotation level in the context */ + def typedTypSplice(tree: untpd.TypSplice, pt: Type)(implicit ctx: Context): Tree = track("typedTypSplice") { + checkSpliceOutsideQuote(tree) + typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span) + } + + private def checkSpliceOutsideQuote(tree: untpd.Tree)(implicit ctx: Context): Unit = { + if (level == 0 && !ctx.owner.isInlineMethod) + ctx.error("splice outside quotes or inline method", tree.sourcePos) + } + /** Retrieve symbol attached to given tree */ protected def retrieveSym(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.removeAttachment(SymOfTree) match { case Some(sym) => @@ -2014,6 +2048,9 @@ class Typer extends Namer case tree: untpd.InfixOp if ctx.mode.isExpr => typedInfixOp(tree, pt) case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree + case tree: untpd.Quote => typedQuote(tree, pt) + case tree: untpd.Splice => typedSplice(tree, pt) + case tree: untpd.TypSplice => typedTypSplice(tree, pt) case _ => typedUnadapted(desugar(tree), pt, locked) } diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index f32d247c7635..3c4f38cc6170 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -12,7 +12,7 @@ import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.reporting.diagnostic.messages -import dotty.tools.dotc.transform.PostTyper +import dotty.tools.dotc.transform.{PostTyper, Staging} import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.util.{ParsedComment, SourceFile} @@ -34,6 +34,7 @@ class ReplCompiler extends Compiler { override protected def frontendPhases: List[List[Phase]] = List( List(new REPLFrontEnd), List(new CollectTopLevelImports), + List(new Staging), List(new PostTyper) ) diff --git a/tests/neg/quote-this-b.scala b/tests/neg/quote-this-b.scala new file mode 100644 index 000000000000..0e5078d786e1 --- /dev/null +++ b/tests/neg/quote-this-b.scala @@ -0,0 +1,10 @@ +import scala.quoted._ + +class Foo { + inline def k(): Unit = ${ Foo.impl[Any](this) } // error + inline def l(that: Foo): Unit = ${ Foo.impl[Any](that) } // error +} + +object Foo { + def impl[T](x: Any): Expr[Unit] = '{} +} diff --git a/tests/neg/quote-this.scala b/tests/neg/quote-this.scala index ddfc6961661c..733e9da9fab3 100644 --- a/tests/neg/quote-this.scala +++ b/tests/neg/quote-this.scala @@ -19,9 +19,6 @@ class Foo { 'that // error }) } - inline def k(): Unit = ${ Foo.impl[Any](this) } // error - inline def l(that: Foo): Unit = ${ Foo.impl[Any](that) } // error - } object Foo { diff --git a/tests/neg/quote-whitebox/Macro_1.scala b/tests/neg/quote-whitebox/Macro_1.scala new file mode 100644 index 000000000000..11eb26bf60df --- /dev/null +++ b/tests/neg/quote-whitebox/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object Macros { + inline def defaultOf(inline str: String) <: Any = ${ defaultOfImpl(str) } + def defaultOfImpl(str: String): Expr[Any] = str match { + case "int" => '{1} + case "string" => '{"a"} + } +} diff --git a/tests/neg/quote-whitebox/Test_2.scala b/tests/neg/quote-whitebox/Test_2.scala new file mode 100644 index 000000000000..984e741af216 --- /dev/null +++ b/tests/neg/quote-whitebox/Test_2.scala @@ -0,0 +1,8 @@ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + val a: String = defaultOf("int") // error + val b: Int = defaultOf("string") // error + } +} diff --git a/tests/neg/splice-in-top-level-splice-1.scala b/tests/neg/splice-in-top-level-splice-1.scala index 921007e2d292..815b764ba919 100644 --- a/tests/neg/splice-in-top-level-splice-1.scala +++ b/tests/neg/splice-in-top-level-splice-1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object Foo { - inline def foo(): Int = ${bar($x)} // error + inline def foo(): Int = ${bar(${x})} // error def x: Expr[Int] = '{1} def bar(i: Int): Expr[Int] = i.toExpr } diff --git a/tests/neg/tasty-macro-assert/quoted_1.scala b/tests/neg/tasty-macro-assert-1/quoted_1.scala similarity index 100% rename from tests/neg/tasty-macro-assert/quoted_1.scala rename to tests/neg/tasty-macro-assert-1/quoted_1.scala diff --git a/tests/neg/tasty-macro-assert-1/quoted_2.scala b/tests/neg/tasty-macro-assert-1/quoted_2.scala new file mode 100644 index 000000000000..25ef0291b120 --- /dev/null +++ b/tests/neg/tasty-macro-assert-1/quoted_2.scala @@ -0,0 +1,10 @@ + +import Asserts._ + +object Test { + def main(args: Array[String]): Unit = { + macroAssert(true === "cde") + macroAssert("acb" === "cde") // error + } + +} diff --git a/tests/neg/tasty-macro-assert-2/quoted_1.scala b/tests/neg/tasty-macro-assert-2/quoted_1.scala new file mode 100644 index 000000000000..f3796a7a31f4 --- /dev/null +++ b/tests/neg/tasty-macro-assert-2/quoted_1.scala @@ -0,0 +1,69 @@ +import scala.quoted._ + +import scala.tasty._ + +object Asserts { + + implicit class Ops[T](left: T) { + def ===(right: T): Boolean = left == right + def !==(right: T): Boolean = left != right + } + + object Ops + + inline def macroAssert(cond: => Boolean): Unit = + ${ impl('cond) } + + def impl(cond: Expr[Boolean])(implicit reflect: Reflection): Expr[Unit] = { + import reflect._ + + val tree = cond.unseal + + def isOps(tpe: TypeOrBounds): Boolean = tpe match { + case Type.SymRef(IsDefSymbol(sym), _) => sym.name == "Ops" // TODO check that the parent is Asserts + case _ => false + } + + object OpsTree { + def unapply(arg: Term): Option[Term] = arg match { + case Term.Apply(Term.TypeApply(term, _), left :: Nil) if isOps(term.tpe) => + Some(left) + case _ => None + } + } + + tree match { + case Term.Inlined(_, Nil, Term.Apply(Term.Select(OpsTree(left), op), right :: Nil)) => + '{assertTrue(${left.seal[Boolean]})} // Buggy code. To generate the errors + case _ => + '{assertTrue($cond)} + } + + } + + def assertEquals[T](left: T, right: T): Unit = { + if (left != right) { + println( + s"""Error left did not equal right: + | left = $left + | right = $right""".stripMargin) + } + + } + + def assertNotEquals[T](left: T, right: T): Unit = { + if (left == right) { + println( + s"""Error left was equal to right: + | left = $left + | right = $right""".stripMargin) + } + + } + + def assertTrue(cond: Boolean): Unit = { + if (!cond) + println("Condition was false") + } + +} diff --git a/tests/neg/tasty-macro-assert/quoted_2.scala b/tests/neg/tasty-macro-assert-2/quoted_2.scala similarity index 67% rename from tests/neg/tasty-macro-assert/quoted_2.scala rename to tests/neg/tasty-macro-assert-2/quoted_2.scala index b94c98ab6b70..b43e3cc84dde 100644 --- a/tests/neg/tasty-macro-assert/quoted_2.scala +++ b/tests/neg/tasty-macro-assert-2/quoted_2.scala @@ -3,8 +3,6 @@ import Asserts._ object Test { def main(args: Array[String]): Unit = { - macroAssert(true === "cde") - macroAssert("acb" === "cde") // error macroAssert(false !== "acb") macroAssert("acb" !== "acb") // error } diff --git a/tests/run/tasty-comments/quoted_1.scala b/tests/pending/run/tasty-comments/quoted_1.scala similarity index 100% rename from tests/run/tasty-comments/quoted_1.scala rename to tests/pending/run/tasty-comments/quoted_1.scala diff --git a/tests/run/tasty-comments/quoted_2.scala b/tests/pending/run/tasty-comments/quoted_2.scala similarity index 100% rename from tests/run/tasty-comments/quoted_2.scala rename to tests/pending/run/tasty-comments/quoted_2.scala diff --git a/tests/pos/quote-whitebox/Macro_1.scala b/tests/pos/quote-whitebox/Macro_1.scala new file mode 100644 index 000000000000..2f21ccdabc21 --- /dev/null +++ b/tests/pos/quote-whitebox/Macro_1.scala @@ -0,0 +1,10 @@ + +import scala.quoted._ + +object Macro { + + inline def charOrString(inline str: String) <: Char | String = ${ impl(str) } + + def impl(str: String) = if (str.length == 1) str.charAt(0).toExpr else str.toExpr + +} diff --git a/tests/pos/quote-whitebox/Test_2.scala b/tests/pos/quote-whitebox/Test_2.scala new file mode 100644 index 000000000000..da5757f22d79 --- /dev/null +++ b/tests/pos/quote-whitebox/Test_2.scala @@ -0,0 +1,8 @@ +import Macro._ + +object Test { + + val x: Char = charOrString("1") + val y: String = charOrString("123") + +} diff --git a/tests/pos/quoted-inline-quote.scala b/tests/pos/quoted-inline-quote.scala new file mode 100644 index 000000000000..70212ca52582 --- /dev/null +++ b/tests/pos/quoted-inline-quote.scala @@ -0,0 +1,4 @@ +class Foo { + inline def foo(x: quoted.Expr[String]) = '{ println(${x}) } + foo('{"abc"}) +} \ No newline at end of file diff --git a/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check b/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check index a67242c349e8..466e73606bac 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check +++ b/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check @@ -1,3 +1,3 @@ -DefDef("foo", Nil, Nil, TypeTree.Ident("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(1)), "+"), List(Term.Literal(Constant.Int(2)))))) -ValDef("bar", TypeTree.Ident("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(2)), "+"), List(Term.Literal(Constant.Int(3)))))) +DefDef("foo", Nil, Nil, TypeTree.Inferred(), None) +ValDef("bar", TypeTree.Inferred(), None) Pattern.Bind("x", Pattern.Value(Term.Ident("_"))) diff --git a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check index ad142704cb0a..e2d34376e9a5 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check +++ b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check @@ -1,27 +1,27 @@ foo -DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Typed(Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Inferred())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Inferred())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))), ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "$splice"), TypeTree.Ident("Unit")))))) +DefDef("main", Nil, List(List(ValDef("args", TypeTree.Inferred(), None))), TypeTree.Inferred(), None) bar -DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))) +DefDef("foo", Nil, Nil, TypeTree.Inferred(), None) bar2 -DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))) +DefDef("foo", Nil, Nil, TypeTree.Inferred(), None) foo2 -DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Typed(Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Inferred())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Inferred())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))), ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "$splice"), TypeTree.Ident("Unit")))))) +DefDef("main", Nil, List(List(ValDef("args", TypeTree.Inferred(), None))), TypeTree.Inferred(), None) baz -ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))) +ValDef("foo2", TypeTree.Inferred(), None) baz2 -ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))) +ValDef("foo2", TypeTree.Inferred(), None) -ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) +ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(TypeTree.Inferred()), Nil, None, List(TypeDef("B", TypeBoundsTree(TypeTree.Inferred(), TypeTree.Inferred())), DefDef("b", Nil, Nil, TypeTree.Inferred(), None), ValDef("b2", TypeTree.Inferred(), None))) b -ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) +ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(TypeTree.Inferred()), Nil, None, List(TypeDef("B", TypeBoundsTree(TypeTree.Inferred(), TypeTree.Inferred())), DefDef("b", Nil, Nil, TypeTree.Inferred(), None), ValDef("b2", TypeTree.Inferred(), None))) b2 -ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) +ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(TypeTree.Inferred()), Nil, None, List(TypeDef("B", TypeBoundsTree(TypeTree.Inferred(), TypeTree.Inferred())), DefDef("b", Nil, Nil, TypeTree.Inferred(), None), ValDef("b2", TypeTree.Inferred(), None))) diff --git a/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check b/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check index fa4dada79aef..6438ecc51781 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check +++ b/tests/run-custom-args/Yretain-trees/tasty-load-tree-2.check @@ -1,2 +1,2 @@ -DefDef("foo", Nil, Nil, TypeTree.Ident("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(1)), "+"), List(Term.Literal(Constant.Int(2)))))) -ValDef("bar", TypeTree.Ident("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(2)), "+"), List(Term.Literal(Constant.Int(3)))))) +DefDef("foo", Nil, Nil, TypeTree.Inferred(), None) +ValDef("bar", TypeTree.Inferred(), None) diff --git a/tests/run-with-compiler/quote-macro-in-splice.check b/tests/run-with-compiler/quote-macro-in-splice.check new file mode 100644 index 000000000000..b3982e366180 --- /dev/null +++ b/tests/run-with-compiler/quote-macro-in-splice.check @@ -0,0 +1,4 @@ +{ + val y: scala.Int = 1 + y.+(8) +} diff --git a/tests/run-with-compiler/quote-macro-in-splice/quoted_1.scala b/tests/run-with-compiler/quote-macro-in-splice/quoted_1.scala new file mode 100644 index 000000000000..762161a14d2d --- /dev/null +++ b/tests/run-with-compiler/quote-macro-in-splice/quoted_1.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +object Macros { + def impl(x: Expr[Int]): Expr[Int] = '{ $x + 1 } +} diff --git a/tests/run-with-compiler/quote-macro-in-splice/quoted_2.scala b/tests/run-with-compiler/quote-macro-in-splice/quoted_2.scala new file mode 100644 index 000000000000..9958f623c754 --- /dev/null +++ b/tests/run-with-compiler/quote-macro-in-splice/quoted_2.scala @@ -0,0 +1,19 @@ +import scala.quoted._ +import Macros._ + +import scala.quoted.Toolbox.Default._ + +object Test { + def main(args: Array[String]): Unit = { + val x = '{ + val y = 1 + ${ + inline def a(z: Int): Int = ${ impl('z) } + val b = a(7).toExpr + '{ y + $b } + } + } + println(x.show) + } + +} diff --git a/tests/run/quote-whitebox.check b/tests/run/quote-whitebox.check new file mode 100644 index 000000000000..e439850d6fa5 --- /dev/null +++ b/tests/run/quote-whitebox.check @@ -0,0 +1,2 @@ +1 +a diff --git a/tests/run/quote-whitebox/Macro_1.scala b/tests/run/quote-whitebox/Macro_1.scala new file mode 100644 index 000000000000..11eb26bf60df --- /dev/null +++ b/tests/run/quote-whitebox/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object Macros { + inline def defaultOf(inline str: String) <: Any = ${ defaultOfImpl(str) } + def defaultOfImpl(str: String): Expr[Any] = str match { + case "int" => '{1} + case "string" => '{"a"} + } +} diff --git a/tests/run/quote-whitebox/Test_2.scala b/tests/run/quote-whitebox/Test_2.scala new file mode 100644 index 000000000000..105fcad244b7 --- /dev/null +++ b/tests/run/quote-whitebox/Test_2.scala @@ -0,0 +1,10 @@ +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + val a: Int = defaultOf("int") + val b: String = defaultOf("string") + println(a) + println(b) + } +} diff --git a/tests/run/tasty-argument-tree-1.check b/tests/run/tasty-argument-tree-1.check index b6b86afcffc1..8d50036bcadc 100644 --- a/tests/run/tasty-argument-tree-1.check +++ b/tests/run/tasty-argument-tree-1.check @@ -1,33 +1,33 @@ -tree: Term.Literal(Constant.Int(3)) +tree: Term.Inlined(None, Nil, Term.Literal(Constant.Int(3))) tree deref. vals: Term.Literal(Constant.Int(3)) -tree: Term.Ident("v") +tree: Term.Inlined(None, Nil, Term.Ident("v")) tree deref. vals: Term.Literal(Constant.Int(1)) -tree: Term.Ident("x") +tree: Term.Inlined(None, Nil, Term.Ident("x")) tree deref. vals: Term.Literal(Constant.Int(2)) -tree: Term.Ident("l") +tree: Term.Inlined(None, Nil, Term.Ident("l")) tree deref. vals: Term.Literal(Constant.Int(3)) -tree: Term.Ident("a") +tree: Term.Inlined(None, Nil, Term.Ident("a")) tree deref. vals: Term.Ident("a") -tree: Term.Ident("x") +tree: Term.Inlined(None, Nil, Term.Ident("x")) tree deref. vals: Term.Ident("b") -tree: Term.Ident("vv") +tree: Term.Inlined(None, Nil, Term.Ident("vv")) tree deref. vals: Term.Literal(Constant.Int(1)) -tree: Term.Ident("x") +tree: Term.Inlined(None, Nil, Term.Ident("x")) tree deref. vals: Term.Literal(Constant.Int(1)) -tree: Term.Ident("vd") +tree: Term.Inlined(None, Nil, Term.Ident("vd")) tree deref. vals: Term.Literal(Constant.Int(2)) -tree: Term.Ident("x") +tree: Term.Inlined(None, Nil, Term.Ident("x")) tree deref. vals: Term.Literal(Constant.Int(2)) -tree: Term.Ident("x") +tree: Term.Inlined(None, Nil, Term.Ident("x")) tree deref. vals: Term.Apply(Term.TypeApply(Term.Select(Term.Ident("Tuple2"), "apply"), List(TypeTree.Inferred(), TypeTree.Inferred())), List(Term.Literal(Constant.Int(1)), Term.Literal(Constant.Int(2)))) diff --git a/tests/run/tasty-extractors-1.check b/tests/run/tasty-extractors-1.check index b04fa977ed07..bc4db2f21ce5 100644 --- a/tests/run/tasty-extractors-1.check +++ b/tests/run/tasty-extractors-1.check @@ -1,19 +1,19 @@ -Term.Literal(Constant.Boolean(true)) +Term.Inlined(None, Nil, Term.Literal(Constant.Boolean(true))) Type.ConstantType(Constant.Boolean(true)) -Term.Literal(Constant.Int(1)) +Term.Inlined(None, Nil, Term.Literal(Constant.Int(1))) Type.ConstantType(Constant.Int(1)) -Term.Literal(Constant.Long(2)) +Term.Inlined(None, Nil, Term.Literal(Constant.Long(2))) Type.ConstantType(Constant.Long(2)) -Term.Literal(Constant.Float(2.1)) +Term.Inlined(None, Nil, Term.Literal(Constant.Float(2.1))) Type.ConstantType(Constant.Float(2.1)) -Term.Literal(Constant.Double(2.2)) +Term.Inlined(None, Nil, Term.Literal(Constant.Double(2.2))) Type.ConstantType(Constant.Double(2.2)) -Term.Literal(Constant.String("abc")) +Term.Inlined(None, Nil, Term.Literal(Constant.String("abc"))) Type.ConstantType(Constant.String("abc")) Term.Inlined(None, Nil, Term.Apply(Term.Ident("println"), List(Term.Literal(Constant.String("abc"))))) @@ -28,7 +28,7 @@ Type.SymRef(IsClassSymbol(), Type.SymRef(IsPackageSymbol(), T Term.Inlined(None, Nil, Term.Typed(Term.Literal(Constant.Short(8)), TypeTree.Ident("Short"))) Type.SymRef(IsClassSymbol(), Type.SymRef(IsPackageSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(<>), NoPrefix())))) -Term.Literal(Constant.Char(a)) +Term.Inlined(None, Nil, Term.Literal(Constant.Char(a))) Type.ConstantType(Constant.Char(a)) Term.Inlined(None, Nil, Term.Block(List(Term.Literal(Constant.Int(1)), Term.Literal(Constant.Int(2))), Term.Literal(Constant.Int(3)))) @@ -37,40 +37,40 @@ Type.ConstantType(Constant.Int(3)) Term.Inlined(None, Nil, Term.If(Term.Typed(Term.Literal(Constant.Boolean(true)), TypeTree.Ident("Boolean")), Term.Literal(Constant.Int(1)), Term.Literal(Constant.Int(2)))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("a")), List(CaseDef(Pattern.Value(Term.Literal(Constant.String("a"))), None, Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("a")), List(CaseDef(Pattern.Value(Term.Literal(Constant.String("a"))), None, Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("b")), List(CaseDef(Pattern.Bind("n", Pattern.Value(Term.Ident("_"))), None, Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("b")), List(CaseDef(Pattern.Bind("n", Pattern.Value(Term.Ident("_"))), None, Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("c")), List(CaseDef(Pattern.Bind("n", Pattern.TypeTest(TypeTree.Ident("String"))), None, Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("c")), List(CaseDef(Pattern.Bind("n", Pattern.TypeTest(TypeTree.Ident("String"))), None, Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("e")), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("e")), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("f")), List(CaseDef(Pattern.TypeTest(TypeTree.Ident("String")), None, Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("f")), List(CaseDef(Pattern.TypeTest(TypeTree.Ident("String")), None, Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Typed(Term.Literal(Constant.String("g")), TypeTree.Ident("Any")), List(CaseDef(Pattern.Alternative(List(Pattern.TypeTest(TypeTree.Ident("String")), Pattern.TypeTest(TypeTree.Ident("Int")))), None, Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Typed(Term.Literal(Constant.String("g")), TypeTree.Ident("Any")), List(CaseDef(Pattern.Alternative(List(Pattern.TypeTest(TypeTree.Ident("String")), Pattern.TypeTest(TypeTree.Ident("Int")))), None, Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("h")), List(CaseDef(Pattern.Value(Term.Ident("_")), Some(Term.Literal(Constant.Boolean(false))), Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Literal(Constant.String("h")), List(CaseDef(Pattern.Value(Term.Ident("_")), Some(Term.Literal(Constant.Boolean(false))), Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.String("o"))))), Term.Match(Term.Literal(Constant.String("i")), List(CaseDef(Pattern.Bind("a", Pattern.Value(Term.Ident("_"))), None, Term.Literal(Constant.Unit())))))) +Term.Inlined(None, Nil, Term.Block(List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.String("o"))))), Term.Match(Term.Literal(Constant.String("i")), List(CaseDef(Pattern.Bind("a", Pattern.Value(Term.Ident("_"))), None, Term.Block(Nil, Term.Literal(Constant.Unit()))))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Match(Term.Ident("Nil"), List(CaseDef(Pattern.Unapply(Term.TypeApply(Term.Select(Term.Ident("List"), "unapplySeq"), List(TypeTree.Inferred())), Nil, List(Pattern.Bind("a", Pattern.Value(Term.Ident("_"))), Pattern.Bind("b", Pattern.Value(Term.Ident("_"))), Pattern.Bind("c", Pattern.Value(Term.Ident("_"))))), None, Term.Literal(Constant.Unit()))))) +Term.Inlined(None, Nil, Term.Match(Term.Ident("Nil"), List(CaseDef(Pattern.Unapply(Term.TypeApply(Term.Select(Term.Ident("List"), "unapplySeq"), List(TypeTree.Inferred())), Nil, List(Pattern.Bind("a", Pattern.Value(Term.Ident("_"))), Pattern.Bind("b", Pattern.Value(Term.Ident("_"))), Pattern.Bind("c", Pattern.Value(Term.Ident("_"))))), None, Term.Block(Nil, Term.Literal(Constant.Unit())))))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Try(Term.Literal(Constant.Int(1)), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Unit()))), None)) +Term.Inlined(None, Nil, Term.Try(Term.Literal(Constant.Int(1)), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Block(Nil, Term.Literal(Constant.Unit())))), None)) Type.OrType(Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))), Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix())))) Term.Inlined(None, Nil, Term.Try(Term.Literal(Constant.Int(2)), Nil, Some(Term.Literal(Constant.Unit())))) Type.ConstantType(Constant.Int(2)) -Term.Inlined(None, Nil, Term.Try(Term.Literal(Constant.Int(3)), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Unit()))), Some(Term.Literal(Constant.Unit())))) +Term.Inlined(None, Nil, Term.Try(Term.Literal(Constant.Int(3)), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Block(Nil, Term.Literal(Constant.Unit())))), Some(Term.Literal(Constant.Unit())))) Type.OrType(Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))), Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix())))) Term.Inlined(None, Nil, Term.Apply(Term.Select(Term.Literal(Constant.String("a")), "=="), List(Term.Literal(Constant.String("b"))))) diff --git a/tests/run/tasty-extractors-2.check b/tests/run/tasty-extractors-2.check index d6b33232dacf..9cfe4460e1ff 100644 --- a/tests/run/tasty-extractors-2.check +++ b/tests/run/tasty-extractors-2.check @@ -7,7 +7,7 @@ Type.AppliedType(Type.SymRef(IsClassSymbol(), Type.ThisType(Typ Term.Inlined(None, Nil, Term.Ident("???")) Type.SymRef(IsDefSymbol(), Type.SymRef(IsValSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix())))) -Term.Literal(Constant.Int(1)) +Term.Inlined(None, Nil, Term.Literal(Constant.Int(1))) Type.ConstantType(Constant.Int(1)) Term.Inlined(None, Nil, Term.Typed(Term.Literal(Constant.Int(1)), TypeTree.Ident("Int"))) @@ -49,7 +49,7 @@ Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymb Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Product"), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Serializable")), Nil, None, List(DefDef("hashCode", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Literal(Constant.Int(394005536)))), DefDef("equals", Nil, List(List(ValDef("x$0", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.Apply(Term.Select(Term.This(Some(Id("Foo"))), "eq"), List(Term.TypeApply(Term.Select(Term.Ident("x$0"), "$asInstanceOf$"), List(TypeTree.Inferred())))), "||"), List(Term.Match(Term.Ident("x$0"), List(CaseDef(Pattern.Bind("x$0", Pattern.TypeTest(TypeTree.Inferred())), None, Term.Literal(Constant.Boolean(true))), CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Boolean(false))))))))), DefDef("toString", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Ident("_toString"), List(Term.This(Some(Id("Foo"))))))), DefDef("canEqual", Nil, List(List(ValDef("that", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.TypeApply(Term.Select(Term.Ident("that"), "isInstanceOf"), List(TypeTree.Inferred())))), DefDef("productArity", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0)))), DefDef("productPrefix", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.String("Foo")))), DefDef("productElement", Nil, List(List(ValDef("n", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Match(Term.Ident("n"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), List(Term.Apply(Term.Select(Term.Ident("n"), "toString"), Nil)))))))))), DefDef("productElementName", Nil, List(List(ValDef("x$1", TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Int"), None))), TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "String"), Some(Term.Match(Term.Ident("x$1"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "IndexOutOfBoundsException")), ""), List(Term.Apply(Term.Select(Term.Select(Term.Select(Term.Ident("java"), "lang"), "String"), "valueOf"), List(Term.Ident("x$1")))))))))))), DefDef("copy", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))))), ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Applied(TypeTree.Inferred(), List(TypeTree.Inferred())), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Serializable")), Nil, Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Product"), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Serializable")), Nil, None, List(DefDef("productElementName", Nil, List(List(ValDef("x$1", TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Int"), None))), TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "String"), Some(Term.Match(Term.Ident("x$1"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "IndexOutOfBoundsException")), ""), List(Term.Apply(Term.Select(Term.Select(Term.Select(Term.Ident("java"), "lang"), "String"), "valueOf"), List(Term.Ident("x$1")))))))))))), DefDef("copy", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))))), ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Applied(TypeTree.Inferred(), List(TypeTree.Inferred())), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Serializable")), Nil, Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo1", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) diff --git a/tests/run/tasty-macro-const/quoted_1.scala b/tests/run/tasty-macro-const/quoted_1.scala index f48da1280d5f..20f419545bf0 100644 --- a/tests/run/tasty-macro-const/quoted_1.scala +++ b/tests/run/tasty-macro-const/quoted_1.scala @@ -9,7 +9,7 @@ object Macros { import reflection._ val xTree: Term = x.unseal xTree match { - case Term.Literal(Constant.Int(n)) => + case Term.Inlined(_, _, Term.Literal(Constant.Int(n))) => if (n <= 0) throw new QuoteError("Parameter must be natural number") xTree.seal[Int] diff --git a/tests/run/tasty-positioned.check b/tests/run/tasty-positioned.check index 3005121fb971..d007e4366bbe 100644 --- a/tests/run/tasty-positioned.check +++ b/tests/run/tasty-positioned.check @@ -4,5 +4,5 @@ acbvasdfa columns:13-36 lines:12-12 acbvasdfa columns:13-24 lines:13-13 a -b columns:6-25 lines:15-16 -Foo columns:12-19 lines:17-17 +b columns:6-33 lines:15-16 +Foo columns:13-20 lines:17-17 diff --git a/tests/run/tasty-positioned/quoted_2.scala b/tests/run/tasty-positioned/quoted_2.scala index 4026b53b3ff2..2ef194c72f63 100644 --- a/tests/run/tasty-positioned/quoted_2.scala +++ b/tests/run/tasty-positioned/quoted_2.scala @@ -14,8 +14,8 @@ object Test { printPos("acbvasdfa") printPos( """a - |b""".stripMargin) - printPos(new Foo) + |b""".stripMargin: String) + printPos(new Foo) } class Foo { override def toString: String = "Foo"