Skip to content

Commit a3e7636

Browse files
Merge pull request #9613 from dotty-staging/link-quoted-and-tasty-context
Avoid leak of internal implementation in tasty.Reflection
2 parents 5dbd4ed + a2d3821 commit a3e7636

File tree

13 files changed

+1052
-1414
lines changed

13 files changed

+1052
-1414
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import scala.internal.tasty.CompilerInterface
2121

2222
import scala.tasty.reflect.TypeTest
2323

24-
class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extends CompilerInterface {
24+
// NOTE: `ReflectionCompilerInterface` should be a class to make sure that all functionality of
25+
// `CompilerInterface` is implemented here.
26+
27+
class ReflectionCompilerInterface(val rootContext: Context) extends CompilerInterface {
2528
import tpd._
2629

2730
private given core.Contexts.Context = rootContext

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import scala.quoted.show.SyntaxHighlight
99
object ReflectionImpl {
1010

1111
def apply(rootContext: Contexts.Context): scala.tasty.Reflection =
12-
new scala.tasty.Reflection(new ReflectionCompilerInterface(rootContext))
12+
new ReflectionImpl(rootContext)
1313

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

25+
// NOTE: This class should only mixin the compiler interface and the reflection interface.
26+
// We should not implement methods here, all should be implemented by `ReflectionCompilerInterface`
27+
class ReflectionImpl(ctx: Context) extends ReflectionCompilerInterface(ctx) with scala.tasty.Reflection

library/src-bootstrapped/scala/internal/quoted/Expr.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.internal.quoted
22

33
import scala.quoted._
4+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
45

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

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

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

5859
/** Returns a null expresssion equivalent to `'{null}` */

library/src-bootstrapped/scala/internal/quoted/Matcher.scala

+17-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import scala.annotation.internal.sharable
44
import scala.annotation.{Annotation, compileTimeOnly}
55

66
import scala.quoted._
7+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
78

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

125-
class QuoteMatcher[QCtx <: QuoteContext & Singleton](using val qctx: QCtx) {
126+
class QuoteMatcher[QCtx <: QuoteContext & Singleton](val qctx0: QCtx) {
127+
val qctx = quoteContextWithCompilerInterface(qctx0)
128+
126129
// TODO improve performance
127130

128131
// TODO use flag from qctx.tasty.rootContext. Maybe -debug or add -debug-macros
@@ -147,14 +150,14 @@ object Matcher {
147150
def termMatch(scrutineeTerm: Term, patternTerm: Term, hasTypeSplices: Boolean): Option[Tuple] = {
148151
given Env = Map.empty
149152
if (hasTypeSplices) {
150-
val ctx: Context = internal.Constraints_init(rootContext)
153+
val ctx: Context = qctx.tasty.Constraints_init(rootContext)
151154
given Context = ctx
152155
val matchings = scrutineeTerm =?= patternTerm
153156
// After matching and doing all subtype checks, we have to approximate all the type bindings
154157
// that we have found and seal them in a quoted.Type
155158
matchings.asOptionOfTuple.map { tup =>
156159
Tuple.fromArray(tup.toArray.map { // TODO improve performance
157-
case x: SymBinding => internal.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
160+
case x: SymBinding => qctx.tasty.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
158161
case x => x
159162
})
160163
}
@@ -168,14 +171,14 @@ object Matcher {
168171
def typeTreeMatch(scrutineeTypeTree: TypeTree, patternTypeTree: TypeTree, hasTypeSplices: Boolean): Option[Tuple] = {
169172
given Env = Map.empty
170173
if (hasTypeSplices) {
171-
val ctx: Context = internal.Constraints_init(rootContext)
174+
val ctx: Context = qctx.tasty.Constraints_init(rootContext)
172175
given Context = ctx
173176
val matchings = scrutineeTypeTree =?= patternTypeTree
174177
// After matching and doing all subtype checks, we have to approximate all the type bindings
175178
// that we have found and seal them in a quoted.Type
176179
matchings.asOptionOfTuple.map { tup =>
177180
Tuple.fromArray(tup.toArray.map { // TODO improve performance
178-
case x: SymBinding => internal.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
181+
case x: SymBinding => qctx.tasty.Constraints_approximation(summon[Context])(x.sym, !x.fromAbove).seal
179182
case x => x
180183
})
181184
}
@@ -190,13 +193,13 @@ object Matcher {
190193
private def hasFromAboveAnnotation(sym: Symbol) = sym.annots.exists(isFromAboveAnnotation)
191194

192195
private def isPatternTypeAnnotation(tree: Tree): Boolean = tree match {
193-
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuotedMatcher_patternTypeAnnot
194-
case annot => annot.symbol.owner == internal.Definitions_InternalQuotedMatcher_patternTypeAnnot
196+
case New(tpt) => tpt.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_patternTypeAnnot
197+
case annot => annot.symbol.owner == qctx.tasty.Definitions_InternalQuotedMatcher_patternTypeAnnot
195198
}
196199

197200
private def isFromAboveAnnotation(tree: Tree): Boolean = tree match {
198-
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuotedMatcher_fromAboveAnnot
199-
case annot => annot.symbol.owner == internal.Definitions_InternalQuotedMatcher_fromAboveAnnot
201+
case New(tpt) => tpt.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_fromAboveAnnot
202+
case annot => annot.symbol.owner == qctx.tasty.Definitions_InternalQuotedMatcher_fromAboveAnnot
200203
}
201204

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

258261
/* Term hole */
259262
// Match a scala.internal.Quoted.patternHole and return the scrutinee tree
260263
case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil))
261-
if patternHole.symbol == internal.Definitions_InternalQuotedMatcher_patternHole &&
264+
if patternHole.symbol == qctx.tasty.Definitions_InternalQuotedMatcher_patternHole &&
262265
scrutinee.tpe <:< tpt.tpe =>
263266
matched(scrutinee.seal)
264267

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

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

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

329332
/* Match block */
@@ -340,7 +343,7 @@ object Matcher {
340343

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

346349
/* Match if */

library/src-bootstrapped/scala/internal/quoted/Type.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.internal.quoted
22

33
import scala.quoted._
4+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
45

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

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

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

4546
def Unit: QuoteContext ?=> quoted.Type[Unit] =
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.internal.quoted
22

33
import scala.quoted.{Expr, QuoteContext, Type}
4+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
45

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

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

2728
}

library/src-bootstrapped/scala/quoted/Expr.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.quoted
22

33
import scala.quoted.show.SyntaxHighlight
4+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
45

56
/** Quoted expression of type `T` */
67
abstract class Expr[+T] private[scala] {
@@ -75,7 +76,8 @@ object Expr {
7576
* Otherwise returns `expr`.
7677
*/
7778
def betaReduce[T](expr: Expr[T])(using qctx: QuoteContext): Expr[T] =
78-
qctx.tasty.internal.betaReduce(expr.unseal) match
79+
val qctx2 = quoteContextWithCompilerInterface(qctx)
80+
qctx2.tasty.betaReduce(expr.unseal) match
7981
case Some(expr1) => expr1.seal.asInstanceOf[Expr[T]]
8082
case _ => expr
8183

library/src-bootstrapped/scala/quoted/Lambda.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package scala.quoted
22

3+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
4+
35
/** Lambda expression extractor */
46
object Lambda {
57

@@ -19,12 +21,12 @@ object Lambda {
1921
import qctx.tasty._
2022
val argTypes = functionType.unseal.tpe match
2123
case AppliedType(_, functionArguments) => functionArguments.init.asInstanceOf[List[Type]]
22-
qctx.tasty.internal.lambdaExtractor(expr.unseal, argTypes).map { fn =>
24+
val qctx2 = quoteContextWithCompilerInterface(qctx)
25+
qctx2.tasty.lambdaExtractor(expr.unseal, argTypes).map { fn =>
2326
def f(args: Tuple.Map[Args, Expr]): Expr[Res] =
2427
fn(args.toArray.toList.map(_.asInstanceOf[Expr[Any]].unseal)).seal.asInstanceOf[Expr[Res]]
2528
tg.untupled(f)
2629
}
27-
2830
}
2931

3032
}

library/src-non-bootstrapped/scala/internal/quoted/Expr.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.internal.quoted
22

33
import scala.quoted._
4+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
45

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

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

library/src-non-bootstrapped/scala/internal/quoted/Type.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.internal.quoted
22

33
import scala.quoted._
4+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
45

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

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

0 commit comments

Comments
 (0)