Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid leak of internal implementation in tasty.Reflection #9613

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import scala.internal.tasty.CompilerInterface

import scala.tasty.reflect.TypeTest

class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extends CompilerInterface {
// NOTE: `ReflectionCompilerInterface` should be a class to make sure that all functionality of
// `CompilerInterface` is implemented here.

class ReflectionCompilerInterface(val rootContext: Context) extends CompilerInterface {
import tpd._

private given core.Contexts.Context = rootContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import scala.quoted.show.SyntaxHighlight
object ReflectionImpl {

def apply(rootContext: Contexts.Context): scala.tasty.Reflection =
new scala.tasty.Reflection(new ReflectionCompilerInterface(rootContext))
new ReflectionImpl(rootContext)

def showTree(tree: tpd.Tree)(using Contexts.Context): String = {
val refl = new scala.tasty.Reflection(new ReflectionCompilerInterface(MacroExpansion.context(tree)))
val refl = new ReflectionImpl(MacroExpansion.context(tree))
val reflCtx = ctx.asInstanceOf[refl.Context]
val reflTree = tree.asInstanceOf[refl.Tree]
val syntaxHighlight =
Expand All @@ -22,3 +22,6 @@ object ReflectionImpl {
}
}

// NOTE: This class should only mixin the compiler interface and the reflection interface.
// We should not implement methods here, all should be implemented by `ReflectionCompilerInterface`
class ReflectionImpl(ctx: Context) extends ReflectionCompilerInterface(ctx) with scala.tasty.Reflection
5 changes: 3 additions & 2 deletions library/src-bootstrapped/scala/internal/quoted/Expr.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.internal.quoted

import scala.quoted._
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** An Expr backed by a tree. Only the current compiler trees are allowed.
*
Expand All @@ -19,7 +20,7 @@ import scala.quoted._
}

def unseal(using qctx: QuoteContext): qctx.tasty.Term =
if (qctx.tasty.internal.compilerId != scopeId)
if (quoteContextWithCompilerInterface(qctx).tasty.compilerId != scopeId)
throw new scala.quoted.ScopeException("Cannot call `scala.quoted.staging.run(...)` within a macro or another `run(...)`")
tree.asInstanceOf[qctx.tasty.Term]

Expand Down Expand Up @@ -52,7 +53,7 @@ object Expr {
*/
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeExpr: scala.quoted.Expr[Any])(using patternExpr: scala.quoted.Expr[Any],
hasTypeSplices: Boolean, qctx: QuoteContext): Option[Tup] = {
new Matcher.QuoteMatcher[qctx.type].termMatch(scrutineeExpr.unseal, patternExpr.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
new Matcher.QuoteMatcher[qctx.type](qctx).termMatch(scrutineeExpr.unseal, patternExpr.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
}

/** Returns a null expresssion equivalent to `'{null}` */
Expand Down
31 changes: 17 additions & 14 deletions library/src-bootstrapped/scala/internal/quoted/Matcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import scala.annotation.internal.sharable
import scala.annotation.{Annotation, compileTimeOnly}

import scala.quoted._
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** Matches a quoted tree against a quoted pattern tree.
* A quoted pattern tree may have type and term holes in addition to normal terms.
Expand Down Expand Up @@ -122,7 +123,9 @@ object Matcher {
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.fromAbove`")
class fromAbove extends Annotation

class QuoteMatcher[QCtx <: QuoteContext & Singleton](using val qctx: QCtx) {
class QuoteMatcher[QCtx <: QuoteContext & Singleton](val qctx0: QCtx) {
val qctx = quoteContextWithCompilerInterface(qctx0)

// TODO improve performance

// TODO use flag from qctx.tasty.rootContext. Maybe -debug or add -debug-macros
Expand All @@ -147,14 +150,14 @@ object Matcher {
def termMatch(scrutineeTerm: Term, patternTerm: Term, hasTypeSplices: Boolean): Option[Tuple] = {
given Env = Map.empty
if (hasTypeSplices) {
val ctx: Context = internal.Constraints_init(rootContext)
val ctx: Context = qctx.tasty.Constraints_init(rootContext)
given Context = ctx
val matchings = scrutineeTerm =?= patternTerm
// After matching and doing all subtype checks, we have to approximate all the type bindings
// that we have found and seal them in a quoted.Type
matchings.asOptionOfTuple.map { tup =>
Tuple.fromArray(tup.toArray.map { // TODO improve performance
case x: SymBinding => internal.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
case x: SymBinding => qctx.tasty.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
case x => x
})
}
Expand All @@ -168,14 +171,14 @@ object Matcher {
def typeTreeMatch(scrutineeTypeTree: TypeTree, patternTypeTree: TypeTree, hasTypeSplices: Boolean): Option[Tuple] = {
given Env = Map.empty
if (hasTypeSplices) {
val ctx: Context = internal.Constraints_init(rootContext)
val ctx: Context = qctx.tasty.Constraints_init(rootContext)
given Context = ctx
val matchings = scrutineeTypeTree =?= patternTypeTree
// After matching and doing all subtype checks, we have to approximate all the type bindings
// that we have found and seal them in a quoted.Type
matchings.asOptionOfTuple.map { tup =>
Tuple.fromArray(tup.toArray.map { // TODO improve performance
case x: SymBinding => internal.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
case x: SymBinding => qctx.tasty.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
case x => x
})
}
Expand All @@ -190,13 +193,13 @@ object Matcher {
private def hasFromAboveAnnotation(sym: Symbol) = sym.annots.exists(isFromAboveAnnotation)

private def isPatternTypeAnnotation(tree: Tree): Boolean = tree match {
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuotedMatcher_patternTypeAnnot
case annot => annot.symbol.owner == internal.Definitions_InternalQuotedMatcher_patternTypeAnnot
case New(tpt) => tpt.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_patternTypeAnnot
case annot => annot.symbol.owner == qctx.tasty.Definitions_InternalQuotedMatcher_patternTypeAnnot
}

private def isFromAboveAnnotation(tree: Tree): Boolean = tree match {
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuotedMatcher_fromAboveAnnot
case annot => annot.symbol.owner == internal.Definitions_InternalQuotedMatcher_fromAboveAnnot
case New(tpt) => tpt.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_fromAboveAnnot
case annot => annot.symbol.owner == qctx.tasty.Definitions_InternalQuotedMatcher_fromAboveAnnot
}

/** Check that all trees match with `mtch` and concatenate the results with &&& */
Expand Down Expand Up @@ -250,22 +253,22 @@ object Matcher {
/* Term hole */
// Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree
case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2))
if patternHole.symbol == internal.Definitions_InternalQuotedMatcher_patternHole &&
if patternHole.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_patternHole &&
s.tpe <:< tpt.tpe &&
tpt2.tpe.derivesFrom(defn.RepeatedParamClass) =>
matched(scrutinee.seal)

/* Term hole */
// Match a scala.internal.Quoted.patternHole and return the scrutinee tree
case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil))
if patternHole.symbol == internal.Definitions_InternalQuotedMatcher_patternHole &&
if patternHole.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_patternHole &&
scrutinee.tpe <:< tpt.tpe =>
matched(scrutinee.seal)

/* Higher order term hole */
// Matches an open term and wraps it into a lambda that provides the free variables
case (scrutinee, pattern @ Apply(TypeApply(Ident("higherOrderHole"), List(Inferred())), Repeated(args, _) :: Nil))
if pattern.symbol == internal.Definitions_InternalQuotedMatcher_higherOrderHole =>
if pattern.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_higherOrderHole =>

def bodyFn(lambdaArgs: List[Tree]): Tree = {
val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Term]]).toMap
Expand Down Expand Up @@ -323,7 +326,7 @@ object Matcher {
fn1 =?= fn2 &&& args1 =?= args2

case (Block(stats1, expr1), Block(binding :: stats2, expr2)) if isTypeBinding(binding) =>
qctx.tasty.internal.Constraints_add(summon[Context])(binding.symbol :: Nil)
qctx.tasty.Constraints_add(summon[Context])(binding.symbol :: Nil)
matched(new SymBinding(binding.symbol, hasFromAboveAnnotation(binding.symbol))) &&& Block(stats1, expr1) =?= Block(stats2, expr2)

/* Match block */
Expand All @@ -340,7 +343,7 @@ object Matcher {

case (scrutinee, Block(typeBindings, expr2)) if typeBindings.forall(isTypeBinding) =>
val bindingSymbols = typeBindings.map(_.symbol)
qctx.tasty.internal.Constraints_add(summon[Context])(bindingSymbols)
qctx.tasty.Constraints_add(summon[Context])(bindingSymbols)
bindingSymbols.foldRight(scrutinee =?= expr2)((x, acc) => matched(new SymBinding(x, hasFromAboveAnnotation(x))) &&& acc)

/* Match if */
Expand Down
5 changes: 3 additions & 2 deletions library/src-bootstrapped/scala/internal/quoted/Type.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.internal.quoted

import scala.quoted._
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** Quoted type (or kind) `T` backed by a tree */
final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quoted.Type[Any] {
Expand All @@ -14,7 +15,7 @@ final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quote

/** View this expression `quoted.Type[T]` as a `TypeTree` */
def unseal(using qctx: QuoteContext): qctx.tasty.TypeTree =
if (qctx.tasty.internal.compilerId != scopeId)
if (quoteContextWithCompilerInterface(qctx).tasty.compilerId != scopeId)
throw new scala.quoted.ScopeException("Cannot call `scala.quoted.staging.run(...)` within a macro or another `run(...)`")
typeTree.asInstanceOf[qctx.tasty.TypeTree]

Expand All @@ -39,7 +40,7 @@ object Type {
*/
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeType: scala.quoted.Type[_])(using patternType: scala.quoted.Type[_],
hasTypeSplices: Boolean, qctx: QuoteContext): Option[Tup] = {
new Matcher.QuoteMatcher[qctx.type].typeTreeMatch(scrutineeType.unseal, patternType.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
new Matcher.QuoteMatcher[qctx.type](qctx).typeTreeMatch(scrutineeType.unseal, patternType.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
}

def Unit: QuoteContext ?=> quoted.Type[Unit] =
Expand Down
13 changes: 7 additions & 6 deletions library/src-bootstrapped/scala/internal/quoted/Unpickler.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.internal.quoted

import scala.quoted.{Expr, QuoteContext, Type}
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** Provides methods to unpickle `Expr` and `Type` trees. */
object Unpickler {
Expand All @@ -12,16 +13,16 @@ object Unpickler {
* replacing splice nodes with `args`
*/
def unpickleExpr[T](repr: PickledQuote, args: PickledArgs): QuoteContext ?=> Expr[T] =
val ctx = summon[QuoteContext]
val tree = ctx.tasty.internal.unpickleExpr(repr, args)
new scala.internal.quoted.Expr(tree, ctx.tasty.internal.compilerId).asInstanceOf[Expr[T]]
val qctx = quoteContextWithCompilerInterface(summon[QuoteContext])
val tree = qctx.tasty.unpickleExpr(repr, args)
new scala.internal.quoted.Expr(tree, qctx.tasty.compilerId).asInstanceOf[Expr[T]]

/** Unpickle `repr` which represents a pickled `Type` tree,
* replacing splice nodes with `args`
*/
def unpickleType[T](repr: PickledQuote, args: PickledArgs): QuoteContext ?=> Type[T] =
val ctx = summon[QuoteContext]
val tree = ctx.tasty.internal.unpickleType(repr, args)
new scala.internal.quoted.Type(tree, ctx.tasty.internal.compilerId).asInstanceOf[Type[T]]
val qctx = quoteContextWithCompilerInterface(summon[QuoteContext])
val tree = qctx.tasty.unpickleType(repr, args)
new scala.internal.quoted.Type(tree, qctx.tasty.compilerId).asInstanceOf[Type[T]]

}
4 changes: 3 additions & 1 deletion library/src-bootstrapped/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.quoted

import scala.quoted.show.SyntaxHighlight
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** Quoted expression of type `T` */
abstract class Expr[+T] private[scala] {
Expand Down Expand Up @@ -75,7 +76,8 @@ object Expr {
* Otherwise returns `expr`.
*/
def betaReduce[T](expr: Expr[T])(using qctx: QuoteContext): Expr[T] =
qctx.tasty.internal.betaReduce(expr.unseal) match
val qctx2 = quoteContextWithCompilerInterface(qctx)
qctx2.tasty.betaReduce(expr.unseal) match
case Some(expr1) => expr1.seal.asInstanceOf[Expr[T]]
case _ => expr

Expand Down
6 changes: 4 additions & 2 deletions library/src-bootstrapped/scala/quoted/Lambda.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package scala.quoted

import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** Lambda expression extractor */
object Lambda {

Expand All @@ -19,12 +21,12 @@ object Lambda {
import qctx.tasty._
val argTypes = functionType.unseal.tpe match
case AppliedType(_, functionArguments) => functionArguments.init.asInstanceOf[List[Type]]
qctx.tasty.internal.lambdaExtractor(expr.unseal, argTypes).map { fn =>
val qctx2 = quoteContextWithCompilerInterface(qctx)
qctx2.tasty.lambdaExtractor(expr.unseal, argTypes).map { fn =>
def f(args: Tuple.Map[Args, Expr]): Expr[Res] =
fn(args.toArray.toList.map(_.asInstanceOf[Expr[Any]].unseal)).seal.asInstanceOf[Expr[Res]]
tg.untupled(f)
}

}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.internal.quoted

import scala.quoted._
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** An Expr backed by a tree. Only the current compiler trees are allowed.
*
Expand All @@ -19,7 +20,7 @@ import scala.quoted._
}

def unseal(using qctx: QuoteContext): qctx.tasty.Term =
if (qctx.tasty.internal.compilerId != scopeId)
if (quoteContextWithCompilerInterface(qctx).tasty.compilerId != scopeId)
throw new scala.quoted.ScopeException("Cannot call `scala.quoted.staging.run(...)` within a macro or another `run(...)`")
tree.asInstanceOf[qctx.tasty.Term]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.internal.quoted

import scala.quoted._
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface

/** Quoted type (or kind) `T` backed by a tree */
final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quoted.Type[Any] {
Expand All @@ -14,7 +15,7 @@ final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quote

/** View this expression `quoted.Type[T]` as a `TypeTree` */
def unseal(using qctx: QuoteContext): qctx.tasty.TypeTree =
if (qctx.tasty.internal.compilerId != scopeId)
if (quoteContextWithCompilerInterface(qctx).tasty.compilerId != scopeId)
throw new scala.quoted.ScopeException("Cannot call `scala.quoted.staging.run(...)` within a macro or another `run(...)`")
typeTree.asInstanceOf[qctx.tasty.TypeTree]

Expand Down
Loading