Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle @companionClass and @companionMethod meta-annotations #17091

Merged
merged 1 commit into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {}