Skip to content

Commit d04a7e2

Browse files
Check flags for method, val and bind symbols
Co-authored-by: Guillaume Martres <[email protected]>
1 parent 3a20cd4 commit d04a7e2

File tree

2 files changed

+42
-23
lines changed

2 files changed

+42
-23
lines changed

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

Lines changed: 33 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)
@@ -811,7 +812,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
811812
object Lambda extends LambdaModule:
812813
def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block =
813814
val meth = dotc.core.Symbols.newAnonFun(owner, tpe)
814-
withDefaultPos(tpd.Closure(meth, tss => xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth)))
815+
withDefaultPos(tpd.Closure(meth, tss => xCheckedMacroOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth)))
815816

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

14831484
object Bind extends BindModule:
14841485
def apply(sym: Symbol, pattern: Tree): Bind =
1486+
xCheckMacroAssert(sym.flags.is(Flags.Case), "expected a symbol with `Case` flag set")
14851487
withDefaultPos(tpd.Bind(sym, pattern))
14861488
def copy(original: Tree)(name: String, pattern: Tree): Bind =
14871489
withDefaultPos(tpd.cpy.Bind(original)(name.toTermName, pattern))
@@ -2522,14 +2524,23 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
25222524
newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol)
25232525
def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
25242526
assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
2527+
checkValidFlags(flags.toTermFlags, Flags.validMethodFlags)
25252528
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin)
25262529
def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
25272530
assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
2531+
checkValidFlags(flags.toTermFlags, Flags.validValFlags)
25282532
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin)
25292533
def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol =
2530-
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe)
2534+
checkValidFlags(flags.toTermFlags, Flags.validBindFlags)
2535+
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Case, tpe)
25312536
def noSymbol: Symbol = dotc.core.Symbols.NoSymbol
25322537

2538+
private inline def checkValidFlags(inline flags: Flags, inline valid: Flags): Unit =
2539+
xCheckMacroAssert(
2540+
flags <= valid,
2541+
s"Received invalid flags. Expected flags ${flags.show} to only contain a subset of ${valid.show}."
2542+
)
2543+
25332544
def freshName(prefix: String): String =
25342545
NameKinds.MacroNames.fresh(prefix.toTermName).toString
25352546
end Symbol
@@ -2602,7 +2613,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26022613
self.isTerm && !self.is(dotc.core.Flags.Method) && !self.is(dotc.core.Flags.Case/*, FIXME add this check and fix sourcecode butNot = Enum | Module*/)
26032614
def isDefDef: Boolean = self.is(dotc.core.Flags.Method)
26042615
def isBind: Boolean =
2605-
self.is(dotc.core.Flags.Case, butNot = Enum | Module) && !self.isClass
2616+
self.is(dotc.core.Flags.Case, butNot = dotc.core.Flags.Enum | dotc.core.Flags.Module) && !self.isClass
26062617
def isNoSymbol: Boolean = self == Symbol.noSymbol
26072618
def exists: Boolean = self != Symbol.noSymbol
26082619

@@ -2846,6 +2857,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
28462857
def Synthetic: Flags = dotc.core.Flags.Synthetic
28472858
def Trait: Flags = dotc.core.Flags.Trait
28482859
def Transparent: Flags = dotc.core.Flags.Transparent
2860+
2861+
// Keep: aligned with Quotes's `newMethod` doc
2862+
private[QuotesImpl] def validMethodFlags: Flags = Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride // Flags that could be allowed: Synthetic | ExtensionMethod | Exported | Erased | Infix | Invisible
2863+
// Keep: aligned with Quotes's `newVal` doc
2864+
private[QuotesImpl] def validValFlags: Flags = Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride // Flags that could be added: Synthetic | Erased | Invisible
2865+
// Keep: aligned with Quotes's `newBind` doc
2866+
private[QuotesImpl] def validBindFlags: Flags = Case // Flags that could be allowed: Implicit | Given | Erased
28492867
end Flags
28502868

28512869
given FlagsMethods: FlagsMethods with
@@ -2966,7 +2984,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
29662984
/** Checks that all definitions in this tree have the expected owner.
29672985
* Nested definitions are ignored and assumed to be correct by construction.
29682986
*/
2969-
private def xCheckMacroedOwners(tree: Option[Tree], owner: Symbol): tree.type =
2987+
private def xCheckedMacroOwners(tree: Option[Tree], owner: Symbol): tree.type =
29702988
if xCheckMacro then
29712989
tree match
29722990
case Some(tree) =>
@@ -2977,7 +2995,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
29772995
/** Checks that all definitions in this tree have the expected owner.
29782996
* Nested definitions are ignored and assumed to be correct by construction.
29792997
*/
2980-
private def xCheckMacroedOwners(tree: Tree, owner: Symbol): tree.type =
2998+
private def xCheckedMacroOwners(tree: Tree, owner: Symbol): tree.type =
29812999
if xCheckMacro then
29823000
xCheckMacroOwners(tree, owner)
29833001
tree
@@ -3048,6 +3066,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
30483066
"Reference to a method must be eta-expanded before it is used as an expression: " + term.show)
30493067
term
30503068

3069+
private inline def xCheckMacroAssert(inline cond: Boolean, inline msg: String): Unit =
3070+
assert(!xCheckMacro || cond, msg)
3071+
30513072
object Printer extends PrinterModule:
30523073

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

library/src/scala/quoted/Quotes.scala

Lines changed: 9 additions & 11 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`
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`
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,15 +4376,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
43734376
/** Is this symbol `abstract` */
43744377
def Abstract: Flags
43754378

4376-
/** Is this symbol is labeled with of abstract & override
4379+
/** Is this an abstract override method?
43774380
*
4378-
* The override modifier has an additional significance when combined with the abstract modifier.
4379-
* That modifier combination is only allowed for value members of traits.
4380-
*
4381-
* We call a member MM of a template incomplete if it is either abstract (i.e. defined by a declaration), or it is labeled abstract and override and every member overridden by MM is again incomplete.
4382-
*
4383-
* Note that the abstract override modifier combination does not influence the concept whether a member is concrete or abstract.
4384-
* A member is abstract if only a declaration is given for it; it is concrete if a full definition is given.
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.
43854383
*/
43864384
@experimental def AbsOverride: Flags
43874385

0 commit comments

Comments
 (0)