Skip to content

Commit b8d2966

Browse files
authored
Check flags for newMethod, newVal and newBind (#16565)
2 parents ebaceb8 + 3c3c0fb commit b8d2966

File tree

3 files changed

+48
-15
lines changed

3 files changed

+48
-15
lines changed

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import dotty.tools.dotc.ast.untpd
99
import dotty.tools.dotc.core.Annotations
1010
import dotty.tools.dotc.core.Contexts._
1111
import dotty.tools.dotc.core.Decorators._
12-
import dotty.tools.dotc.core.Flags._
1312
import dotty.tools.dotc.core.NameKinds
1413
import dotty.tools.dotc.core.NameOps._
1514
import dotty.tools.dotc.core.StdNames._
@@ -276,12 +275,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
276275

277276
object DefDef extends DefDefModule:
278277
def apply(symbol: Symbol, rhsFn: List[List[Tree]] => Option[Term]): DefDef =
279-
assert(symbol.isTerm, s"expected a term symbol but received $symbol")
278+
xCheckMacroAssert(symbol.isTerm, s"expected a term symbol but received $symbol")
279+
xCheckMacroAssert(symbol.flags.is(Flags.Method), "expected a symbol with `Method` flag set")
280280
withDefaultPos(tpd.DefDef(symbol.asTerm, prefss =>
281-
xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree)
281+
xCheckedMacroOwners(xCheckMacroValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree)
282282
))
283283
def copy(original: Tree)(name: String, paramss: List[ParamClause], tpt: TypeTree, rhs: Option[Term]): DefDef =
284-
tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, xCheckMacroedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree))
284+
tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, xCheckedMacroOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree))
285285
def unapply(ddef: DefDef): (String, List[ParamClause], TypeTree, Option[Term]) =
286286
(ddef.name.toString, ddef.paramss, ddef.tpt, optional(ddef.rhs))
287287
end DefDef
@@ -307,9 +307,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
307307

308308
object ValDef extends ValDefModule:
309309
def apply(symbol: Symbol, rhs: Option[Term]): ValDef =
310-
withDefaultPos(tpd.ValDef(symbol.asTerm, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree)))
310+
xCheckMacroAssert(!symbol.flags.is(Flags.Method), "expected a symbol without `Method` flag set")
311+
withDefaultPos(tpd.ValDef(symbol.asTerm, xCheckedMacroOwners(xCheckMacroValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree)))
311312
def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef =
312-
tpd.cpy.ValDef(original)(name.toTermName, tpt, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree))
313+
tpd.cpy.ValDef(original)(name.toTermName, tpt, xCheckedMacroOwners(xCheckMacroValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree))
313314
def unapply(vdef: ValDef): (String, TypeTree, Option[Term]) =
314315
(vdef.name.toString, vdef.tpt, optional(vdef.rhs))
315316

@@ -398,7 +399,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
398399
def etaExpand(owner: Symbol): Term = self.tpe.widen match {
399400
case mtpe: Types.MethodType if !mtpe.isParamDependent =>
400401
val closureResType = mtpe.resType match {
401-
case t: Types.MethodType => t.toFunctionType(isJava = self.symbol.is(JavaDefined))
402+
case t: Types.MethodType => t.toFunctionType(isJava = self.symbol.is(dotc.core.Flags.JavaDefined))
402403
case t => t
403404
}
404405
val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType)
@@ -828,7 +829,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
828829
object Lambda extends LambdaModule:
829830
def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block =
830831
val meth = dotc.core.Symbols.newAnonFun(owner, tpe)
831-
withDefaultPos(tpd.Closure(meth, tss => xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth)))
832+
withDefaultPos(tpd.Closure(meth, tss => xCheckedMacroOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth)))
832833

833834
def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match {
834835
case Block((ddef @ DefDef(_, tpd.ValDefs(params) :: Nil, _, Some(body))) :: Nil, Closure(meth, _))
@@ -1499,6 +1500,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
14991500

15001501
object Bind extends BindModule:
15011502
def apply(sym: Symbol, pattern: Tree): Bind =
1503+
xCheckMacroAssert(sym.flags.is(Flags.Case), "expected a symbol with `Case` flag set")
15021504
withDefaultPos(tpd.Bind(sym, pattern))
15031505
def copy(original: Tree)(name: String, pattern: Tree): Bind =
15041506
withDefaultPos(tpd.cpy.Bind(original)(name.toTermName, pattern))
@@ -2539,14 +2541,23 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
25392541
newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol)
25402542
def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
25412543
assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
2544+
checkValidFlags(flags.toTermFlags, Flags.validMethodFlags)
25422545
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin)
25432546
def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
25442547
assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
2548+
checkValidFlags(flags.toTermFlags, Flags.validValFlags)
25452549
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin)
25462550
def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol =
2547-
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe)
2551+
checkValidFlags(flags.toTermFlags, Flags.validBindFlags)
2552+
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Case, tpe)
25482553
def noSymbol: Symbol = dotc.core.Symbols.NoSymbol
25492554

2555+
private inline def checkValidFlags(inline flags: Flags, inline valid: Flags): Unit =
2556+
xCheckMacroAssert(
2557+
flags <= valid,
2558+
s"Received invalid flags. Expected flags ${flags.show} to only contain a subset of ${valid.show}."
2559+
)
2560+
25502561
def freshName(prefix: String): String =
25512562
NameKinds.MacroNames.fresh(prefix.toTermName).toString
25522563
end Symbol
@@ -2619,7 +2630,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26192630
self.isTerm && !self.is(dotc.core.Flags.Method) && !self.is(dotc.core.Flags.Case/*, FIXME add this check and fix sourcecode butNot = Enum | Module*/)
26202631
def isDefDef: Boolean = self.is(dotc.core.Flags.Method)
26212632
def isBind: Boolean =
2622-
self.is(dotc.core.Flags.Case, butNot = Enum | Module) && !self.isClass
2633+
self.is(dotc.core.Flags.Case, butNot = dotc.core.Flags.Enum | dotc.core.Flags.Module) && !self.isClass
26232634
def isNoSymbol: Boolean = self == Symbol.noSymbol
26242635
def exists: Boolean = self != Symbol.noSymbol
26252636

@@ -2817,6 +2828,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
28172828

28182829
object Flags extends FlagsModule:
28192830
def Abstract: Flags = dotc.core.Flags.Abstract
2831+
def AbsOverride: Flags = dotc.core.Flags.AbsOverride
28202832
def Artifact: Flags = dotc.core.Flags.Artifact
28212833
def Case: Flags = dotc.core.Flags.Case
28222834
def CaseAccessor: Flags = dotc.core.Flags.CaseAccessor
@@ -2862,6 +2874,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
28622874
def Synthetic: Flags = dotc.core.Flags.Synthetic
28632875
def Trait: Flags = dotc.core.Flags.Trait
28642876
def Transparent: Flags = dotc.core.Flags.Transparent
2877+
2878+
// Keep: aligned with Quotes's `newMethod` doc
2879+
private[QuotesImpl] def validMethodFlags: Flags = Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | JavaStatic // Flags that could be allowed: Synthetic | ExtensionMethod | Exported | Erased | Infix | Invisible
2880+
// Keep: aligned with Quotes's `newVal` doc
2881+
private[QuotesImpl] def validValFlags: Flags = Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | JavaStatic // Flags that could be added: Synthetic | Erased | Invisible
2882+
// Keep: aligned with Quotes's `newBind` doc
2883+
private[QuotesImpl] def validBindFlags: Flags = Case // Flags that could be allowed: Implicit | Given | Erased
28652884
end Flags
28662885

28672886
given FlagsMethods: FlagsMethods with
@@ -2982,7 +3001,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
29823001
/** Checks that all definitions in this tree have the expected owner.
29833002
* Nested definitions are ignored and assumed to be correct by construction.
29843003
*/
2985-
private def xCheckMacroedOwners(tree: Option[Tree], owner: Symbol): tree.type =
3004+
private def xCheckedMacroOwners(tree: Option[Tree], owner: Symbol): tree.type =
29863005
if xCheckMacro then
29873006
tree match
29883007
case Some(tree) =>
@@ -2993,7 +3012,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
29933012
/** Checks that all definitions in this tree have the expected owner.
29943013
* Nested definitions are ignored and assumed to be correct by construction.
29953014
*/
2996-
private def xCheckMacroedOwners(tree: Tree, owner: Symbol): tree.type =
3015+
private def xCheckedMacroOwners(tree: Tree, owner: Symbol): tree.type =
29973016
if xCheckMacro then
29983017
xCheckMacroOwners(tree, owner)
29993018
tree
@@ -3064,6 +3083,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
30643083
"Reference to a method must be eta-expanded before it is used as an expression: " + term.show)
30653084
term
30663085

3086+
private inline def xCheckMacroAssert(inline cond: Boolean, inline msg: String): Unit =
3087+
assert(!xCheckMacro || cond, msg)
3088+
30673089
object Printer extends PrinterModule:
30683090

30693091
lazy val TreeCode: Printer[Tree] = new Printer[Tree]:

library/src/scala/quoted/Quotes.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3785,9 +3785,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
37853785
* @param parent The owner of the method
37863786
* @param name The name of the method
37873787
* @param tpe The type of the method (MethodType, PolyType, ByNameType)
3788-
* @param flags extra flags to with which the symbol should be constructed
3788+
* @param flags extra flags to with which the symbol should be constructed. `Method` flag will be added. Can be `Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | JavaStatic`
37893789
* @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol.
37903790
*/
3791+
// Keep: `flags` doc aligned with QuotesImpl's `validMethodFlags`
37913792
def newMethod(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol
37923793

37933794
/** Generates a new val/var/lazy val symbol with the given parent, name and type.
@@ -3801,11 +3802,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38013802
* @param parent The owner of the val/var/lazy val
38023803
* @param name The name of the val/var/lazy val
38033804
* @param tpe The type of the val/var/lazy val
3804-
* @param flags extra flags to with which the symbol should be constructed
3805+
* @param flags extra flags to with which the symbol should be constructed. Can be `Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | JavaStatic`
38053806
* @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol.
38063807
* @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
38073808
* direct or indirect children of the reflection context's owner.
38083809
*/
3810+
// Keep: `flags` doc aligned with QuotesImpl's `validValFlags`
38093811
def newVal(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol
38103812

38113813
/** Generates a pattern bind symbol with the given parent, name and type.
@@ -3816,11 +3818,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38163818
*
38173819
* @param parent The owner of the binding
38183820
* @param name The name of the binding
3819-
* @param flags extra flags to with which the symbol should be constructed
3821+
* @param flags extra flags to with which the symbol should be constructed. `Case` flag will be added. Can be `Case`
38203822
* @param tpe The type of the binding
38213823
* @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
38223824
* direct or indirect children of the reflection context's owner.
38233825
*/
3826+
// Keep: `flags` doc aligned with QuotesImpl's `validBindFlags`
38243827
def newBind(parent: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol
38253828

38263829
/** Definition not available */
@@ -4373,6 +4376,13 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
43734376
/** Is this symbol `abstract` */
43744377
def Abstract: Flags
43754378

4379+
/** Is this an abstract override method?
4380+
*
4381+
* This corresponds to a definition declared as "abstract override def" in the source.
4382+
* See https://stackoverflow.com/questions/23645172/why-is-abstract-override-required-not-override-alone-in-subtrait for examples.
4383+
*/
4384+
@experimental def AbsOverride: Flags
4385+
43764386
/** Is this generated by Scala compiler.
43774387
* Corresponds to ACC_SYNTHETIC in the JVM.
43784388
*/

tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ val experimentalDefinitionInLibrary = Set(
6565
//// New APIs: Quotes
6666
// Should be stabilized in 3.4.0
6767
"scala.quoted.Quotes.reflectModule.defnModule.FunctionClass",
68+
"scala.quoted.Quotes.reflectModule.FlagsModule.AbsOverride",
6869
// Can be stabilized in 3.4.0 (unsure) or later
6970
"scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings",
7071
"scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation",

0 commit comments

Comments
 (0)