Skip to content

Commit 43f8cdb

Browse files
authored
Lift arguments of explicitly constructed annotations (#22553)
2 parents a601e83 + 337856c commit 43f8cdb

File tree

6 files changed

+132
-14
lines changed

6 files changed

+132
-14
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/Mode.scala

+30
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,36 @@ object Mode {
166166
*/
167167
val ForceInline: Mode = newMode(29, "ForceInline")
168168

169+
/** Are we typing the argument of an annotation?
170+
*
171+
* This mode is used through [[Applications.isAnnotConstr]] to avoid lifting
172+
* arguments of annotation constructors. This mode is disabled in nested
173+
* applications (from [[ProtoTypes.typedArg]]) and in "explicit" annotation
174+
* constructors applications (annotation classes constructed with `new`).
175+
*
176+
* In the following example:
177+
*
178+
* ```scala
179+
* @annot(y = new annot(y = Array("World"), x = 1), x = 2)
180+
* ```
181+
*
182+
* the mode will be set when typing `@annot(...)` but not when typing
183+
* `new annot(...)`, such that the arguments of the former are not lifted but
184+
* the arguments of the later can be:
185+
*
186+
* ```scala
187+
* @annot(x = 2, y = {
188+
* val y$3: Array[String] =
189+
* Array.apply[String](["World" : String]*)(
190+
* scala.reflect.ClassTag.apply[String](classOf[String]))
191+
* new annot(x = 1, y = y$3)
192+
* })
193+
* ```
194+
*
195+
* See #22035, #22526, #22553 and `dependent-annot-default-args.scala`.
196+
*/
197+
val InAnnotation: Mode = newMode(30, "InAnnotation")
198+
169199
/** Skip inlining of methods. */
170200
val NoInline: Mode = newMode(31, "NoInline")
171201
}

Diff for: compiler/src/dotty/tools/dotc/typer/Applications.scala

+6-6
Original file line numberDiff line numberDiff line change
@@ -694,9 +694,11 @@ trait Applications extends Compatibility {
694694
sym.is(JavaDefined) && sym.isConstructor && sym.owner.is(JavaAnnotation)
695695

696696

697-
/** Is `sym` a constructor of an annotation? */
698-
def isAnnotConstr(sym: Symbol): Boolean =
699-
sym.isConstructor && sym.owner.isAnnotation
697+
/** Is `sym` a constructor of an annotation class, and are we in an
698+
* annotation? If so, we don't lift arguments. See [[Mode.InAnnotation]].
699+
*/
700+
protected final def isAnnotConstr(sym: Symbol): Boolean =
701+
ctx.mode.is(Mode.InAnnotation) && sym.isConstructor && sym.owner.isAnnotation
700702

701703
/** Match re-ordered arguments against formal parameters
702704
* @param n The position of the first parameter in formals in `methType`.
@@ -994,9 +996,7 @@ trait Applications extends Compatibility {
994996
case (arg: NamedArg, _) => arg
995997
case (arg, name) => NamedArg(name, arg)
996998
}
997-
else if isAnnotConstr(methRef.symbol) then
998-
typedArgs
999-
else if !sameSeq(args, orderedArgs) && !typedArgs.forall(isSafeArg) then
999+
else if !isAnnotConstr(methRef.symbol) && !sameSeq(args, orderedArgs) && !typedArgs.forall(isSafeArg) then
10001000
// need to lift arguments to maintain evaluation order in the
10011001
// presence of argument reorderings.
10021002
// (never do this for Java annotation constructors, hence the 'else if')

Diff for: compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -536,8 +536,8 @@ object ProtoTypes {
536536
def typedArg(arg: untpd.Tree, formal: Type)(using Context): Tree = {
537537
val wideFormal = formal.widenExpr
538538
val argCtx =
539-
if wideFormal eq formal then ctx
540-
else ctx.withNotNullInfos(ctx.notNullInfos.retractMutables)
539+
if wideFormal eq formal then ctx.retractMode(Mode.InAnnotation)
540+
else ctx.retractMode(Mode.InAnnotation).withNotNullInfos(ctx.notNullInfos.retractMutables)
541541
val locked = ctx.typerState.ownedVars
542542
val targ = cacheTypedArg(arg,
543543
typer.typedUnadapted(_, wideFormal, locked)(using argCtx),

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+6-3
Original file line numberDiff line numberDiff line change
@@ -2779,7 +2779,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
27792779
def isInner(owner: Symbol) = owner == sym || sym.is(Param) && owner == sym.owner
27802780
val outer = ctx.outersIterator.dropWhile(c => isInner(c.owner)).next()
27812781
def local: FreshContext = outer.fresh.setOwner(newLocalDummy(sym.owner))
2782-
sym.owner.infoOrCompleter match
2782+
val ctx0 = sym.owner.infoOrCompleter match
27832783
case completer: Namer#Completer
27842784
if sym.is(Param) && completer.completerTypeParams(sym).nonEmpty =>
27852785
// Create a new local context with a dummy owner and a scope containing the
@@ -2788,6 +2788,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
27882788
local.setScope(newScopeWith(completer.completerTypeParams(sym)*))
27892789
case _ =>
27902790
if outer.owner.isClass then local else outer
2791+
ctx0.addMode(Mode.InAnnotation)
27912792

27922793
def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(using Context): Unit = {
27932794
// necessary to force annotation trees to be computed.
@@ -2802,7 +2803,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
28022803
}
28032804

28042805
def typedAnnotation(annot: untpd.Tree)(using Context): Tree =
2805-
checkAnnotClass(checkAnnotArgs(typed(annot)))
2806+
val typedAnnot = withMode(Mode.InAnnotation)(typed(annot))
2807+
checkAnnotClass(checkAnnotArgs(typedAnnot))
28062808

28072809
def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit =
28082810
val annot = Annotations.Annotation(tree)
@@ -3335,7 +3337,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
33353337
end typedPackageDef
33363338

33373339
def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = {
3338-
val annot1 = checkAnnotClass(typedExpr(tree.annot))
3340+
val annot0 = withMode(Mode.InAnnotation)(typedExpr(tree.annot))
3341+
val annot1 = checkAnnotClass(annot0)
33393342
val annotCls = Annotations.annotClass(annot1)
33403343
if annotCls == defn.NowarnAnnot then
33413344
registerNowarn(annot1, tree)

Diff for: tests/printing/dependent-annot-default-args.check

+75-2
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@ package <empty> {
2323
new dependent-annot-default-args$package()
2424
final module class dependent-annot-default-args$package() extends Object() {
2525
this: dependent-annot-default-args$package.type =>
26-
def f(x: Int): Int @annot(x) = x
26+
def f(x: Any): Any @annot(x) = x
2727
def f2(x: Int):
2828
Int @annot2(
2929
y = Array.apply[Any](["Hello",x : Any]*)(scala.reflect.ClassTag.Any))
3030
= x
31+
def f3(x: Any, y: Any): Any @annot(x = x, y = y) = x
3132
def test: Unit =
3233
{
3334
val y: Int = ???
34-
val z: Int @annot(y) = f(y)
35+
val z: Any @annot(y) = f(y)
3536
val z2:
3637
Int @annot2(
3738
y = Array.apply[Any](["Hello",y : Any]*)(scala.reflect.ClassTag.Any)
@@ -41,6 +42,78 @@ package <empty> {
4142
@annot2(
4243
y = Array.apply[Any](["Hello",y : Any]*)(scala.reflect.ClassTag.Any))
4344
val z4: Int = 45
45+
val z5: annot =
46+
{
47+
val y$1: Array[String] =
48+
Array.apply[String](["World" : String]*)(
49+
scala.reflect.ClassTag.apply[String](classOf[String]))
50+
new annot(x = 1, y = y$1)
51+
}
52+
val z6: annot2 =
53+
{
54+
val y$2: Array[Any] =
55+
Array.apply[Any](["World" : Any]*)(scala.reflect.ClassTag.Any)
56+
new annot2(x = 1, y = y$2)
57+
}
58+
@annot(x = 2,
59+
y =
60+
{
61+
val y$3: Array[String] =
62+
Array.apply[String](["World" : String]*)(
63+
scala.reflect.ClassTag.apply[String](classOf[String]))
64+
new annot(x = 1, y = y$3)
65+
}
66+
) val z7: Int = 45
67+
@annot(x = 4,
68+
y =
69+
3:
70+
Int @annot(x = 1,
71+
y =
72+
Array.apply[String](["World" : String]*)(
73+
scala.reflect.ClassTag.apply[String](classOf[String]))
74+
)
75+
) val z8: Int = 45
76+
val z9:
77+
Int @annot(x = 2,
78+
y =
79+
{
80+
val y$4: Array[String] =
81+
Array.apply[String](["World" : String]*)(
82+
scala.reflect.ClassTag.apply[String](classOf[String]))
83+
new annot(x = 1, y = y$4)
84+
}
85+
)
86+
= 46
87+
@annot(x = 4,
88+
y =
89+
3:
90+
Int @annot(x = 1,
91+
y =
92+
Array.apply[String](["World" : String]*)(
93+
scala.reflect.ClassTag.apply[String](classOf[String]))
94+
)
95+
) val z10: Int = 45
96+
val z11: Any @annot(annot) =
97+
f(
98+
{
99+
val y$5: Array[String] =
100+
Array.apply[String](["World" : String]*)(
101+
scala.reflect.ClassTag.apply[String](classOf[String]))
102+
new annot(x = 1, y = y$5)
103+
}
104+
)
105+
val z12: Any @annot(x = x, y = y) =
106+
f3(
107+
Array.apply[String](["World" : String]*)(
108+
scala.reflect.ClassTag.apply[String](classOf[String])),
109+
1)
110+
val z13: Any @annot(x = x, y = y) =
111+
{
112+
val y$6: Array[String] =
113+
Array.apply[String](["World" : String]*)(
114+
scala.reflect.ClassTag.apply[String](classOf[String]))
115+
f3(x = 1, y = y$6)
116+
}
44117
()
45118
}
46119
}

Diff for: tests/printing/dependent-annot-default-args.scala

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
class annot(x: Any, y: Any = 42) extends annotation.Annotation
22
class annot2(x: Any = -1, y: Array[Any] = Array("Hello")) extends annotation.Annotation
33

4-
def f(x: Int): Int @annot(x) = x
4+
def f(x: Any): Any @annot(x) = x
55
def f2(x: Int): Int @annot2(y = Array("Hello", x)) = x
6+
def f3(x: Any, y: Any): Any @annot(y=y, x=x) = x
67

78
def test =
89
val y: Int = ???
@@ -13,3 +14,14 @@ def test =
1314
@annot(44) val z3 = 45
1415
@annot2(y = Array("Hello", y)) val z4 = 45
1516

17+
// Arguments are still lifted if the annotation class is instantiated
18+
// explicitly. See #22526.
19+
val z5 = new annot(y = Array("World"), x = 1)
20+
val z6 = new annot2(y = Array("World"), x = 1)
21+
@annot(y = new annot(y = Array("World"), x = 1), x = 2) val z7 = 45
22+
@annot(y = 3: Int @annot(y = Array("World"), x = 1), x = 4) val z8 = 45
23+
val z9: Int @annot(y = new annot(y = Array("World"), x = 1), x = 2) = 46
24+
@annot(y = 3: Int @annot(y = Array("World"), x = 1), x = 4) val z10 = 45
25+
val z11 = f(new annot(y = Array("World"), x = 1))
26+
val z12 = f3(Array("World"), 1)
27+
val z13 = f3(y=Array("World"), x=1)

0 commit comments

Comments
 (0)