Skip to content

Commit 20dd9a0

Browse files
authored
Handle @companionClass and @companionMethod meta-annotations (#17091)
2 parents 89a744f + f22420f commit 20dd9a0

File tree

6 files changed

+53
-10
lines changed

6 files changed

+53
-10
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,8 @@ class Definitions {
10281028
@tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter")
10291029
@tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param")
10301030
@tu lazy val SetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.setter")
1031+
@tu lazy val CompanionClassMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionClass")
1032+
@tu lazy val CompanionMethodMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionMethod")
10311033
@tu lazy val ShowAsInfixAnnot: ClassSymbol = requiredClass("scala.annotation.showAsInfix")
10321034
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
10331035
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
@@ -1041,7 +1043,7 @@ class Definitions {
10411043

10421044
// A list of meta-annotations that are relevant for fields and accessors
10431045
@tu lazy val NonBeanMetaAnnots: Set[Symbol] =
1044-
Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot)
1046+
Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot, CompanionClassMetaAnnot, CompanionMethodMetaAnnot)
10451047
@tu lazy val MetaAnnots: Set[Symbol] =
10461048
NonBeanMetaAnnots + BeanGetterMetaAnnot + BeanSetterMetaAnnot
10471049

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,13 @@ object SymDenotations {
255255
def annotationsCarrying(meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): List[Annotation] =
256256
annotations.filterConserve(_.hasOneOfMetaAnnotation(meta, orNoneOf = orNoneOf))
257257

258-
def copyAndKeepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit =
259-
if annotations.nonEmpty then
258+
def keepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit =
259+
updateAnnotationsAfter(phase, annotationsCarrying(meta, orNoneOf = orNoneOf))
260+
261+
def updateAnnotationsAfter(phase: DenotTransformer, annots: List[Annotation])(using Context): Unit =
262+
if annots ne annotations then
260263
val cpy = copySymDenotation()
261-
cpy.annotations = annotationsCarrying(meta, orNoneOf = orNoneOf)
264+
cpy.annotations = annots
262265
cpy.installAfter(phase)
263266

264267
/** Optionally, the annotation matching the given class symbol */

compiler/src/dotty/tools/dotc/transform/Memoize.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
167167
if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass)
168168
else transformFollowingDeep(ref(field))(using ctx.withOwner(sym))
169169
val getterDef = cpy.DefDef(tree)(rhs = getterRhs)
170-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot))
170+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot))
171171
Thicket(fieldDef, getterDef)
172172
else if sym.isSetter then
173173
if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs: @unchecked } // This is intended as an assertion
@@ -193,7 +193,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
193193
then Literal(Constant(()))
194194
else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol)))
195195
val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym)))
196-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
196+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
197197
setterDef
198198
else
199199
// Curiously, some accessors from Scala2 have ' ' suffixes.

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,20 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
157157
checkInferredWellFormed(tree.tpt)
158158
if sym.is(Method) then
159159
if sym.isSetter then
160-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
160+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
161+
if sym.isOneOf(GivenOrImplicit) then
162+
val cls = sym.info.finalResultType.classSymbol
163+
if cls.isOneOf(GivenOrImplicit) then
164+
sym.updateAnnotationsAfter(thisPhase,
165+
atPhase(thisPhase)(cls.annotationsCarrying(Set(defn.CompanionMethodMetaAnnot)))
166+
++ sym.annotations)
161167
else
162168
if sym.is(Param) then
163-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
169+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
164170
else if sym.is(ParamAccessor) then
165-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot))
171+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot))
166172
else
167-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
173+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
168174
if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then
169175
if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature)
170176
// Allow scala.reflect.materializeClassTag to be able to compile scala/reflect/package.scala
@@ -388,6 +394,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
388394
VarianceChecker.check(tree)
389395
annotateExperimental(sym)
390396
checkMacroAnnotation(sym)
397+
if sym.isOneOf(GivenOrImplicit) then
398+
sym.keepAnnotationsCarrying(thisPhase, Set(defn.CompanionClassMetaAnnot), orNoneOf = defn.MetaAnnots)
391399
tree.rhs match
392400
case impl: Template =>
393401
for parent <- impl.parents do

tests/neg/i17002.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import scala.annotation.compileTimeOnly
2+
3+
sealed trait Test[T]
4+
5+
object Test:
6+
@compileTimeOnly("Error")
7+
given test0[T]: Test[T] = ???
8+
9+
@compileTimeOnly("Error")
10+
given test1[T]: Test[T]()
11+
12+
@compileTimeOnly("Error")
13+
implicit class ic(x: Int):
14+
def foo = 2
15+
16+
test0 // error
17+
18+
test1 // error
19+
20+
2.foo // error

tests/pos/i17002.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.annotation.meta.companionMethod
2+
3+
@companionMethod
4+
class methOnly extends annotation.Annotation
5+
6+
class Test
7+
object Test:
8+
9+
@methOnly
10+
given test2[T]: Test with {}

0 commit comments

Comments
 (0)