diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 527c15414365..c345efaf641b 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -46,7 +46,10 @@ object PickledQuotes { case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value)) case value => Literal(Constant(value)) } - case expr: TastyTreeExpr[Tree] @unchecked => healOwner(expr.tree) + case expr: TastyTreeExpr[Tree] @unchecked => + if (contextId != expr.ctxId) + throw new scala.quoted.QuoteError("Scope extrusion!") + healOwner(expr.tree) case expr: FunctionAppliedTo[_, _] => functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x)) } @@ -187,4 +190,12 @@ object PickledQuotes { case _ => tree } } + + /** Id of the current compilation (macros) or Expr[_] run */ + def contextId(implicit ctx: Context): Int = { + def root(ctx: Context): Context = + if (ctx.outer != NoContext) root(ctx.outer) else ctx + root(ctx).hashCode + } + } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 616b3bab0bc4..309e40cf89a0 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1230,7 +1230,7 @@ class TreeUnpickler(reader: TastyReader, val args = until(end)(readTerm()) val splice = splices(idx) def wrap(arg: Tree) = - if (arg.isTerm) new TastyTreeExpr(arg) + if (arg.isTerm) new TastyTreeExpr(arg, PickledQuotes.contextId) else new TreeType(arg) val reifiedArgs = args.map(wrap) if (isType) { diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala index a4c1b76d2ee8..e5c67555fd62 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala @@ -19,7 +19,7 @@ trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl { def reify[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] = { typecheck(ctx) - new scala.quoted.Exprs.TastyTreeExpr(term).asInstanceOf[scala.quoted.Expr[T]] + new scala.quoted.Exprs.TastyTreeExpr(term, PickledQuotes.contextId).asInstanceOf[scala.quoted.Expr[T]] } private def typecheck[T: scala.quoted.Type](ctx: Context): Unit = { diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index e1cc8fa34062..c64407b3cf66 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(tree, PickledQuotes.contextId) protected def interpretTypeQuote(tree: Tree)(implicit env: Env): Object = new scala.quoted.Types.TreeType(tree) diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 5368d9e632b5..cf8b7f90444a 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -49,7 +49,7 @@ object Exprs { * * May contain references to code defined outside this TastyTreeExpr instance. */ - final class TastyTreeExpr[Tree](val tree: Tree) extends quoted.Expr[Any] { + final class TastyTreeExpr[Tree](val tree: Tree, val ctxId: Int) extends quoted.Expr[Any] { override def toString: String = s"Expr()" }