Skip to content

Commit ed5b119

Browse files
authored
Make sure annotations are typed in expression contexts (#16699)
The previous logic tried but had holes. A local context was established in `typedStats#localCtx` but that context was not found in annotations that were typechecked in the completer of the definition they decorated. We are now more thorough and always allocate a localDummy as new owner if the owner would otherwise be a class. This means that closures in annotation arguments will have a term as owner, and therefore will be allocated in distinct local scopes. Fixes #15054
2 parents b195113 + 4cfc06b commit ed5b119

File tree

3 files changed

+31
-30
lines changed

3 files changed

+31
-30
lines changed

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,9 @@ class TreeUnpickler(reader: TastyReader,
625625
else
626626
newSymbol(ctx.owner, name, flags, completer, privateWithin, coord)
627627
}
628-
val annots = annotFns.map(_(sym.owner))
628+
val annotOwner =
629+
if sym.owner.isClass then newLocalDummy(sym.owner) else sym.owner
630+
val annots = annotFns.map(_(annotOwner))
629631
sym.annotations = annots
630632
if sym.isOpaqueAlias then sym.setFlag(Deferred)
631633
val isScala2MacroDefinedInScala3 = flags.is(Macro, butNot = Inline) && flags.is(Erased)

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

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,6 @@ object Typer {
7373
/** An attachment for GADT constraints that were inferred for a pattern. */
7474
val InferredGadtConstraints = new Property.StickyKey[core.GadtConstraint]
7575

76-
/** A context property that indicates the owner of any expressions to be typed in the context
77-
* if that owner is different from the context's owner. Typically, a context with a class
78-
* as owner would have a local dummy as ExprOwner value.
79-
*/
80-
private val ExprOwner = new Property.Key[Symbol]
81-
8276
/** An attachment on a Select node with an `apply` field indicating that the `apply`
8377
* was inserted by the Typer.
8478
*/
@@ -2234,29 +2228,23 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
22342228
/** The context to be used for an annotation of `mdef`.
22352229
* This should be the context enclosing `mdef`, or if `mdef` defines a parameter
22362230
* the context enclosing the owner of `mdef`.
2237-
* Furthermore, we need to evaluate annotation arguments in an expression context,
2238-
* since classes defined in a such arguments should not be entered into the
2239-
* enclosing class.
2231+
* Furthermore, we need to make sure that annotation trees are evaluated
2232+
* with an owner that is not the enclosing class since otherwise locally
2233+
* defined symbols would be entered as class members.
22402234
*/
2241-
def annotContext(mdef: untpd.Tree, sym: Symbol)(using Context): Context = {
2235+
def annotContext(mdef: untpd.Tree, sym: Symbol)(using Context): Context =
22422236
def isInner(owner: Symbol) = owner == sym || sym.is(Param) && owner == sym.owner
22432237
val outer = ctx.outersIterator.dropWhile(c => isInner(c.owner)).next()
2244-
var adjusted = outer.property(ExprOwner) match {
2245-
case Some(exprOwner) if outer.owner.isClass => outer.exprContext(mdef, exprOwner)
2246-
case _ => outer
2247-
}
2238+
def local: FreshContext = outer.fresh.setOwner(newLocalDummy(sym.owner))
22482239
sym.owner.infoOrCompleter match
2249-
case completer: Namer#Completer if sym.is(Param) =>
2250-
val tparams = completer.completerTypeParams(sym)
2251-
if tparams.nonEmpty then
2252-
// Create a new local context with a dummy owner and a scope containing the
2253-
// type parameters of the enclosing method or class. Thus annotations can see
2254-
// these type parameters. See i12953.scala for a test case.
2255-
val dummyOwner = newLocalDummy(sym.owner)
2256-
adjusted = adjusted.fresh.setOwner(dummyOwner).setScope(newScopeWith(tparams*))
2240+
case completer: Namer#Completer
2241+
if sym.is(Param) && completer.completerTypeParams(sym).nonEmpty =>
2242+
// Create a new local context with a dummy owner and a scope containing the
2243+
// type parameters of the enclosing method or class. Thus annotations can see
2244+
// these type parameters. See i12953.scala for a test case.
2245+
local.setScope(newScopeWith(completer.completerTypeParams(sym)*))
22572246
case _ =>
2258-
adjusted
2259-
}
2247+
if outer.owner.isClass then local else outer
22602248

22612249
def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(using Context): Unit = {
22622250
// necessary to force annotation trees to be computed.
@@ -3101,10 +3089,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
31013089
case nil =>
31023090
(buf.toList, ctx)
31033091
}
3104-
val localCtx = {
3105-
val exprOwnerOpt = if (exprOwner == ctx.owner) None else Some(exprOwner)
3106-
ctx.withProperty(ExprOwner, exprOwnerOpt)
3107-
}
31083092
def finalize(stat: Tree)(using Context): Tree = stat match {
31093093
case stat: TypeDef if stat.symbol.is(Module) =>
31103094
val enumContext = enumContexts(stat.symbol.linkedClass)
@@ -3117,7 +3101,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
31173101
case _ =>
31183102
stat
31193103
}
3120-
val (stats0, finalCtx) = traverse(stats)(using localCtx)
3104+
val (stats0, finalCtx) = traverse(stats)
31213105
val stats1 = stats0.mapConserve(finalize)
31223106
if ctx.owner == exprOwner then checkNoTargetNameConflict(stats1)
31233107
(stats1, finalCtx)

tests/pos/i15054.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

0 commit comments

Comments
 (0)