Skip to content

Commit de0e3e7

Browse files
committed
Allow lambdas in annotations
1 parent a4afa21 commit de0e3e7

File tree

6 files changed

+58
-64
lines changed

6 files changed

+58
-64
lines changed

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,7 +1430,7 @@ trait Checking {
14301430
report.error(em"@${cls.name} needs a string literal as argument", arg.srcPos)
14311431
tree
14321432
case _ =>
1433-
tree.find(!isValidAnnotSubtree(_)) match
1433+
findInvalidAnnotSubTree(tree) match
14341434
case None => tree
14351435
case Some(invalidSubTree) =>
14361436
errorTree(
@@ -1441,11 +1441,9 @@ trait Checking {
14411441
invalidSubTree.srcPos
14421442
)
14431443

1444-
/** Returns `true` if this tree can appear inside an annotation argument. */
1445-
private def isValidAnnotSubtree(subTree: Tree) =
1446-
subTree.isType || subTree.isInstanceOf[
1447-
EmptyTree.type
1448-
| Ident
1444+
private def findInvalidAnnotSubTree(tree: Tree)(using Context): Option[Tree] =
1445+
type ValidAnnotTree =
1446+
Ident
14491447
| Select
14501448
| This
14511449
| Super
@@ -1461,7 +1459,25 @@ trait Checking {
14611459
| Inlined
14621460
| Hole
14631461
| Annotated
1464-
]
1462+
| EmptyTree.type
1463+
1464+
val accumulator = new TreeAccumulator[Option[Tree]]:
1465+
def apply(acc: Option[Tree], tree: Tree)(using Context): Option[Tree] =
1466+
if acc.isDefined then acc
1467+
else
1468+
tree match
1469+
case tree if tree.isType => foldOver(acc, tree)
1470+
case closureDef(meth) =>
1471+
val paramsRes =
1472+
meth.paramss.foldLeft(acc): (acc: Option[Tree], params: List[ValDef] | List[TypeDef]) =>
1473+
params.foldLeft(acc): (acc: Option[Tree], param: ValDef | TypeDef) =>
1474+
foldOver(acc, param)
1475+
foldOver(paramsRes, meth.rhs)
1476+
case tree: ValidAnnotTree => foldOver(acc, tree)
1477+
case _ => Some(tree)
1478+
1479+
accumulator(None, tree)
1480+
14651481

14661482
/** 1. Check that all case classes that extend `scala.reflect.Enum` are `enum` cases
14671483
* 2. Check that parameterised `enum` cases do not extend java.lang.Enum.

tests/neg/annot-invalid.check

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,62 +22,38 @@
2222
| Implementation restriction: expression cannot be used inside an annotation argument.
2323
| Tree: def f: Int = 2
2424
| Type: (f : => Int)
25-
-- Error: tests/neg/annot-invalid.scala:13:30 --------------------------------------------------------------------------
26-
13 | val x5: Int @annot((x: Int) => x) = 0 // error
27-
| ^^^^^^^^^^^^^
28-
| Implementation restriction: expression cannot be used inside an annotation argument.
29-
| Tree: closure($anonfun)
30-
| Type: Int => Int
31-
-- Error: tests/neg/annot-invalid.scala:14:21 --------------------------------------------------------------------------
32-
14 | val x6: Int @annot(O.g) = 0 // error
33-
| ^^^
34-
| Implementation restriction: expression cannot be used inside an annotation argument.
35-
| Tree: closure($anonfun)
36-
| Type: Int => Int
37-
-- Error: tests/neg/annot-invalid.scala:16:25 --------------------------------------------------------------------------
38-
16 | val x7: Int @annot('{4}) = 0 // error
25+
-- Error: tests/neg/annot-invalid.scala:14:25 --------------------------------------------------------------------------
26+
14 | val x5: Int @annot('{4}) = 0 // error
3927
| ^
4028
| Implementation restriction: expression cannot be used inside an annotation argument.
4129
| Tree: '{4}
4230
| Type: (scala.quoted.Quotes) ?=> scala.quoted.Expr[Int]
43-
-- Error: tests/neg/annot-invalid.scala:18:9 ---------------------------------------------------------------------------
44-
18 | @annot(new Object {}) val y1: Int = 0 // error
31+
-- Error: tests/neg/annot-invalid.scala:16:9 ---------------------------------------------------------------------------
32+
16 | @annot(new Object {}) val y1: Int = 0 // error
4533
| ^^^^^^^^^^^^^
4634
| Implementation restriction: expression cannot be used inside an annotation argument.
4735
| Tree: final class $anon() extends Object() {}
4836
| Type: Object {...}
49-
-- Error: tests/neg/annot-invalid.scala:19:16 --------------------------------------------------------------------------
50-
19 | @annot({class C}) val y2: Int = 0 // error
37+
-- Error: tests/neg/annot-invalid.scala:17:16 --------------------------------------------------------------------------
38+
17 | @annot({class C}) val y2: Int = 0 // error
5139
| ^^^^^^^
5240
| Implementation restriction: expression cannot be used inside an annotation argument.
5341
| Tree: class C() extends Object() {}
5442
| Type: C
55-
-- Error: tests/neg/annot-invalid.scala:20:14 --------------------------------------------------------------------------
56-
20 | @annot({val y: Int = 2}) val y3: Int = 0 // error
43+
-- Error: tests/neg/annot-invalid.scala:18:14 --------------------------------------------------------------------------
44+
18 | @annot({val y: Int = 2}) val y3: Int = 0 // error
5745
| ^^^^^^^^^^^^^^
5846
| Implementation restriction: expression cannot be used inside an annotation argument.
5947
| Tree: val y: Int = 2
6048
| Type: (y : Int)
61-
-- Error: tests/neg/annot-invalid.scala:21:14 --------------------------------------------------------------------------
62-
21 | @annot({def f = 2}) val y4: Int = 0 // error
49+
-- Error: tests/neg/annot-invalid.scala:19:14 --------------------------------------------------------------------------
50+
19 | @annot({def f = 2}) val y4: Int = 0 // error
6351
| ^^^^^^^^^
6452
| Implementation restriction: expression cannot be used inside an annotation argument.
6553
| Tree: def f: Int = 2
6654
| Type: (f : => Int)
67-
-- Error: tests/neg/annot-invalid.scala:22:18 --------------------------------------------------------------------------
68-
22 | @annot((x: Int) => x) val y5: Int = 0 // error
69-
| ^^^^^^^^^^^^^
70-
| Implementation restriction: expression cannot be used inside an annotation argument.
71-
| Tree: closure($anonfun)
72-
| Type: Int => Int
73-
-- Error: tests/neg/annot-invalid.scala:23:9 ---------------------------------------------------------------------------
74-
23 | @annot(O.g) val y6: Int = 0 // error
75-
| ^^^
76-
| Implementation restriction: expression cannot be used inside an annotation argument.
77-
| Tree: closure($anonfun)
78-
| Type: Int => Int
79-
-- Error: tests/neg/annot-invalid.scala:25:13 --------------------------------------------------------------------------
80-
25 | @annot('{4}) val y7: Int = 0 // error
55+
-- Error: tests/neg/annot-invalid.scala:21:13 --------------------------------------------------------------------------
56+
21 | @annot('{4}) val y5: Int = 0 // error
8157
| ^
8258
| Implementation restriction: expression cannot be used inside an annotation argument.
8359
| Tree: '{4}

tests/neg/annot-invalid.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,14 @@ def main =
1010
val x2: Int @annot({class C}) = 0 // error
1111
val x3: Int @annot({val y: Int = 2}) = 0 // error
1212
val x4: Int @annot({def f = 2}) = 0 // error
13-
val x5: Int @annot((x: Int) => x) = 0 // error
14-
val x6: Int @annot(O.g) = 0 // error
1513
def withQuotes(using Quotes) =
16-
val x7: Int @annot('{4}) = 0 // error
14+
val x5: Int @annot('{4}) = 0 // error
1715

1816
@annot(new Object {}) val y1: Int = 0 // error
1917
@annot({class C}) val y2: Int = 0 // error
2018
@annot({val y: Int = 2}) val y3: Int = 0 // error
2119
@annot({def f = 2}) val y4: Int = 0 // error
22-
@annot((x: Int) => x) val y5: Int = 0 // error
23-
@annot(O.g) val y6: Int = 0 // error
2420
def withQuotes2(using Quotes) =
25-
@annot('{4}) val y7: Int = 0 // error
21+
@annot('{4}) val y5: Int = 0 // error
2622

2723
()

tests/neg/i15054.scala

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/pos/annot-15054.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.annotation.Annotation
2+
3+
class AnAnnotation(function: Int => String) extends Annotation
4+
5+
@AnAnnotation(_.toString)
6+
val a = 1
7+
@AnAnnotation(_.toString.length.toString)
8+
val b = 2
9+
10+
def test =
11+
@AnAnnotation(_.toString)
12+
val a = 1
13+
@AnAnnotation(_.toString.length.toString)
14+
val b = 2
15+
a + b

tests/pos/annot-valid.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ def main =
2222
val x13: Int @annot(throw new Error()) = 0
2323
val x14: Int @annot(42: Double) = 0
2424
val x15: Int @annot(O.g(2)) = 0
25+
val x16: Int @annot((x: Int) => x) = 0
26+
val x17: Int @annot([T] => (x: T) => x) = 0
27+
val x18: Int @annot(O.g) = 0
2528

2629
@annot(42) val y1: Int = 0
2730
@annot("hello") val y2: Int = 0
@@ -38,5 +41,8 @@ def main =
3841
@annot(throw new Error()) val y13: Int = 0
3942
@annot(42: Double) val y14: Int = 0
4043
@annot(O.g(2)) val y15: Int = 0
44+
@annot((x: Int) => x) val y16: Int = 0
45+
@annot([T] => (x: T) => x) val y17: Int = 0
46+
@annot(O.g) val y18: Int = 0
4147

4248
()

0 commit comments

Comments
 (0)