Skip to content

Commit

Permalink
Handle @companionClass and @companionMethod meta-annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Mar 12, 2023
1 parent aa079a5 commit f22420f
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 10 deletions.
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,8 @@ class Definitions {
@tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter")
@tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param")
@tu lazy val SetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.setter")
@tu lazy val CompanionClassMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionClass")
@tu lazy val CompanionMethodMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionMethod")
@tu lazy val ShowAsInfixAnnot: ClassSymbol = requiredClass("scala.annotation.showAsInfix")
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
Expand All @@ -1041,7 +1043,7 @@ class Definitions {

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

Expand Down
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,13 @@ object SymDenotations {
def annotationsCarrying(meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): List[Annotation] =
annotations.filterConserve(_.hasOneOfMetaAnnotation(meta, orNoneOf = orNoneOf))

def copyAndKeepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit =
if annotations.nonEmpty then
def keepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit =
updateAnnotationsAfter(phase, annotationsCarrying(meta, orNoneOf = orNoneOf))

def updateAnnotationsAfter(phase: DenotTransformer, annots: List[Annotation])(using Context): Unit =
if annots ne annotations then
val cpy = copySymDenotation()
cpy.annotations = annotationsCarrying(meta, orNoneOf = orNoneOf)
cpy.annotations = annots
cpy.installAfter(phase)

/** Optionally, the annotation matching the given class symbol */
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Memoize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass)
else transformFollowingDeep(ref(field))(using ctx.withOwner(sym))
val getterDef = cpy.DefDef(tree)(rhs = getterRhs)
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot))
sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot))
Thicket(fieldDef, getterDef)
else if sym.isSetter then
if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs: @unchecked } // This is intended as an assertion
Expand All @@ -193,7 +193,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
then Literal(Constant(()))
else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol)))
val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym)))
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
setterDef
else
// Curiously, some accessors from Scala2 have ' ' suffixes.
Expand Down
16 changes: 12 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,20 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
checkInferredWellFormed(tree.tpt)
if sym.is(Method) then
if sym.isSetter then
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
if sym.isOneOf(GivenOrImplicit) then
val cls = sym.info.finalResultType.classSymbol
if cls.isOneOf(GivenOrImplicit) then
sym.updateAnnotationsAfter(thisPhase,
atPhase(thisPhase)(cls.annotationsCarrying(Set(defn.CompanionMethodMetaAnnot)))
++ sym.annotations)
else
if sym.is(Param) then
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
sym.keepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
else if sym.is(ParamAccessor) then
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot))
sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot))
else
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then
if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature)
// Allow scala.reflect.materializeClassTag to be able to compile scala/reflect/package.scala
Expand Down Expand Up @@ -388,6 +394,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
VarianceChecker.check(tree)
annotateExperimental(sym)
checkMacroAnnotation(sym)
if sym.isOneOf(GivenOrImplicit) then
sym.keepAnnotationsCarrying(thisPhase, Set(defn.CompanionClassMetaAnnot), orNoneOf = defn.MetaAnnots)
tree.rhs match
case impl: Template =>
for parent <- impl.parents do
Expand Down
20 changes: 20 additions & 0 deletions tests/neg/i17002.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import scala.annotation.compileTimeOnly

sealed trait Test[T]

object Test:
@compileTimeOnly("Error")
given test0[T]: Test[T] = ???

@compileTimeOnly("Error")
given test1[T]: Test[T]()

@compileTimeOnly("Error")
implicit class ic(x: Int):
def foo = 2

test0 // error

test1 // error

2.foo // error
10 changes: 10 additions & 0 deletions tests/pos/i17002.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import scala.annotation.meta.companionMethod

@companionMethod
class methOnly extends annotation.Annotation

class Test
object Test:

@methOnly
given test2[T]: Test with {}

0 comments on commit f22420f

Please sign in to comment.