[Sema] Avoid member attribute cycle in ResolveMacroRequest

If we have a custom attribute on a type that does a qualified lookup
into the same type, we need to be able to expand member attribute
macros for that type. As such, the check to see if we already have
a nominal for the attribute would hit a cycle. Limit this check such
that it only applies to local vars, which is the only case where it
actually matters.

rdar://163961797
This commit is contained in:
Hamish Knight
2025-11-06 14:39:52 +00:00
parent 9981dc8126
commit f2b0b92dc1
3 changed files with 17 additions and 3 deletions

View File

@@ -3172,7 +3172,7 @@ bool CustomAttr::shouldPreferPropertyWrapperOverMacro() const {
// if one exists. This is necessary since we don't properly support peer
// declarations in local contexts, so want to use a property wrapper if one
// exists.
if (auto *D = owner.getAsDecl()) {
if (auto *D = getOwner().getAsDecl()) {
if ((isa<VarDecl>(D) || isa<PatternBindingDecl>(D)) &&
D->getDeclContext()->isLocalContext()) {
return true;

View File

@@ -2242,8 +2242,12 @@ ResolveMacroRequest::evaluate(Evaluator &evaluator,
// So bail out to prevent diagnostics from the contraint system.
if (auto *attr = macroRef.getAttr()) {
// If we already resolved this CustomAttr to a nominal, this isn't for a
// macro.
if (attr->getNominalDecl())
// macro. This can only currently be the case for property wrappers, so
// limit the check here to avoid request cycles for member attribute macros
// in cases where the attribute refers to a nested type in the type it's
// attached to, since the qualified lookup there needs to expand member
// attributes.
if (attr->shouldPreferPropertyWrapperOverMacro() && attr->getNominalDecl())
return ConcreteDeclRef();
auto foundMacros = namelookup::lookupMacros(dc, macroRef.getModuleName(),

View File

@@ -0,0 +1,10 @@
// RUN: %target-typecheck-verify-swift
// Make sure we don't end up with a request cycle here, since looking up 'S.Actor'
// requires expanding member attribute macros for 'S'.
@S.Actor
struct S {
@globalActor actor Actor: GlobalActor {
public static let shared = S.Actor()
}
}