Skip to content

Commit 3a21370

Browse files
authored
Stabilise Quotes reflect methods used for creating new classes/objects (#23826)
This PR stabilizes Quotes reflect methods: * 3 overloaded versions of `Symbol.newClass` - the first one added in 3.3.0, updated in 3.7.0. Basically necessary for mock tests, and already used in `ScalaMock` via workarounds - so somewhat battle tested. * `ClassDef.apply` - added in 3.3.0, used in conjunction with `Symbol.newClass`. * The `Symbol.newModule` - added in 3.3.0, updated in 3.7.0. Not as widely used, but generally similar to the above. If we ever need more features there, we can add those via overloading. * `ClassDef.module` - added in 3.3.0, used in conjunction with `Symbol.newModule`. * `Symbol.freshName` - added in 3.3.0. Not strictly necessary, but useful even without Macro Annotations. The design is simple enough (and API identical to scala-2 counterpart), where I don't see anything that could go wrong here. Already used in some project via a workaround (Chimney). The main idea here is to be finally able to use mocking libraries without any ugly workarounds or `--experimental` options. With that said, there are more methods in Quotes reflect that have been experimental for some time now: * `Symbol.newTypeBounds`, `Symbol.newTypeAlias` - added in 3.6.0. I believe they could be stabilized, and I planned to add them in this PR, but found some leftover todos (connected to the implementation, not the API) so I'll fix those and submit separately. * `Symbol.info` - added in 3.3.0. To be honest, I'd prefer if this was removed. Naturally `info` can return a `ClassInfo` which we currently do not support in the reflection API (so this can cause crashes currently). We would also need an `asSeenFrom` method added for this to be useful. We already have a method to return types of Symbols, with `Symbol.typeMember`, which already can return complete type information, making info + asSeenFrom redundant. Currently, in tests, it is used to more easily copy type information of one Symbol, when creating another (like when overwriting a `hashCode` method). There must be a better way to do that than accessing the raw `info` of any Symbol (perhaps we could add a `methodInfo` instead, which would, by design, not work for Classes) * `GivenSelectorModule.apply`, `OmitSelectorModule.apply`, `RenameSelectorModule.apply`, `SimpleSelectorModule.apply`- Added in 3.7.0. I'd prefer if we waited with these a bit. Added by an outside contributor, I tried to look for solid arguments not to add them, couldn't really find any, merged them and I got some pushback after doing so from other contributors. Ideally, when unused-imports become more stable/complete, we'll be able to better see the impact of those methods. * other ones like `erasedParams` and `erasedArgs` are still connected to other experimental features, so they should be left as-is.
2 parents dd39e0b + df94bd8 commit 3a21370

File tree

36 files changed

+21
-83
lines changed

36 files changed

+21
-83
lines changed

library/src/scala/quoted/Quotes.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
493493
* @param body List of members of the class. The members must align with the members of `cls`.
494494
*/
495495
// TODO add selfOpt: Option[ValDef]?
496-
@experimental def apply(cls: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): ClassDef
496+
// ^ if a use-case shows up, we add this via an overloaded method
497+
def apply(cls: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): ClassDef
497498
def copy(original: Tree)(name: String, constr: DefDef, parents: List[Tree /* Term | TypeTree */], selfOpt: Option[ValDef], body: List[Statement]): ClassDef
498499
def unapply(cdef: ClassDef): (String, DefDef, List[Tree /* Term | TypeTree */], Option[ValDef], List[Statement])
499500

@@ -518,7 +519,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
518519
* @syntax markdown
519520
*/
520521
// TODO add selfOpt: Option[ValDef]?
521-
@experimental def module(module: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): (ValDef, ClassDef)
522+
// ^ if a use-case shows up, we can add this via an overloaded method
523+
def module(module: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): (ValDef, ClassDef)
522524
}
523525

524526
/** Makes extension methods on `ClassDef` available without any imports */
@@ -3878,7 +3880,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38783880
* @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
38793881
* direct or indirect children of the reflection context's owner.
38803882
*/
3881-
@experimental def newClass(owner: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr]): Symbol
3883+
def newClass(owner: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr]): Symbol
38823884

38833885
/** Generates a new class symbol for a class with a public single term clause constructor.
38843886
*
@@ -3936,7 +3938,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
39363938
* @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
39373939
* direct or indirect children of the reflection context's owner.
39383940
*/
3939-
@experimental def newClass(
3941+
def newClass(
39403942
owner: Symbol,
39413943
name: String,
39423944
parents: Symbol => List[TypeRepr],
@@ -4038,7 +4040,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
40384040
*/
40394041
// Keep doc aligned with QuotesImpl's validFlags: `clsFlags` with `validClassFlags`, `conFlags` with `validClassConstructorFlags`,
40404042
// conParamFlags with `validClassTypeParamFlags` and `validClassTermParamFlags`
4041-
@experimental def newClass(
4043+
def newClass(
40424044
owner: Symbol,
40434045
name: String,
40444046
parents: Symbol => List[TypeRepr],
@@ -4110,7 +4112,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
41104112
*
41114113
* @syntax markdown
41124114
*/
4113-
@experimental def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: Symbol => List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol
4115+
def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: Symbol => List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol
41144116

41154117
/** Generates a new method symbol with the given parent, name and type.
41164118
*
@@ -4225,7 +4227,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
42254227
*
42264228
* @param prefix Prefix of the fresh name
42274229
*/
4228-
@experimental
42294230
def freshName(prefix: String): String
42304231
}
42314232

project/MiMaFilters.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ object MiMaFilters {
138138
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeMethods.isContextual"),
139139
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#ImplicitsModule.searchIgnoring"),
140140
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#ValDefModule.let"),
141+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newClass"),
142+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newModule"),
143+
141144
// Change `experimental` annotation to a final class
142145
ProblemFilters.exclude[FinalClassProblem]("scala.annotation.experimental"),
143146

tests/neg-macros/i19842-a.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210)
1212
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:286)
1313
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:285)
14-
| at Macros$.makeSerializer(Macro.scala:25)
14+
| at Macros$.makeSerializer(Macro.scala:23)
1515
|
1616
|---------------------------------------------------------------------------------------------------------------------
1717
|Inline stack trace

tests/neg-macros/i19842-a/Macro.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//> using options -experimental
2-
31
import scala.annotation.{experimental, targetName}
42
import scala.quoted.*
53
import scala.util.Try

tests/neg-macros/i19842-b.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210)
1212
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:286)
1313
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:285)
14-
| at Macros$.makeSerializer(Macro.scala:27)
14+
| at Macros$.makeSerializer(Macro.scala:25)
1515
|
1616
|---------------------------------------------------------------------------------------------------------------------
1717
|Inline stack trace

tests/neg-macros/i19842-b/Macro.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//> using options -experimental
2-
31
import scala.annotation.{experimental, targetName}
42
import scala.quoted.*
53
import scala.util.Try

tests/neg-macros/newClassExtendsNoParents/Macro_1.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//> using options -experimental
2-
31
import scala.quoted.*
42

53
inline def makeClass(inline name: String): Any = ${ makeClassExpr('name) }
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
//> using options -experimental
2-
31
def test: Any = makeClass("foo") // error

tests/neg-macros/newClassExtendsOnlyTrait/Macro_1.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//> using options -experimental
2-
31
import scala.quoted.*
42

53
inline def makeClass(inline name: String): Foo = ${ makeClassExpr('name) }
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
//> using options -experimental
2-
31
def test: Foo = makeClass("foo") // error

0 commit comments

Comments
 (0)