diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index e3e6e458503da..9439df3870813 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -2359,6 +2359,10 @@ class CustomAttr final : public DeclAttribute { ASTContext &getASTContext() const; + /// If \c true, we should prefer a property wrapper if one exists for the + /// given attribute over a macro. + bool shouldPreferPropertyWrapperOverMacro() const; + /// Retrieve the NominalTypeDecl the CustomAttr refers to, or \c nullptr if /// it doesn't refer to one (which can be the case for e.g macro attrs). NominalTypeDecl *getNominalDecl() const; diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index ade306cc16a5c..7cb992ea47479 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -3167,6 +3167,20 @@ ASTContext &CustomAttr::getASTContext() const { return getOwner().getDeclContext()->getASTContext(); } +bool CustomAttr::shouldPreferPropertyWrapperOverMacro() const { + // If we have a VarDecl in a local context, prefer to use a property wrapper + // 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 = getOwner().getAsDecl()) { + if ((isa(D) || isa(D)) && + D->getDeclContext()->isLocalContext()) { + return true; + } + } + return false; +} + NominalTypeDecl *CustomAttr::getNominalDecl() const { auto &eval = getASTContext().evaluator; auto *mutThis = const_cast(this); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index a2fc4c90a81f2..9f84c236b08f8 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -4020,20 +4020,6 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c parsedGenericParams->getRAngleLoc()); } -static bool shouldPreferPropertyWrapperOverMacro(CustomAttrOwner owner) { - // If we have a VarDecl in a local context, prefer to use a property wrapper - // 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 ((isa(D) || isa(D)) && - D->getDeclContext()->isLocalContext()) { - return true; - } - } - return false; -} - NominalTypeDecl *CustomAttrNominalRequest::evaluate(Evaluator &evaluator, CustomAttr *attr) const { auto owner = attr->getOwner(); @@ -4048,7 +4034,7 @@ NominalTypeDecl *CustomAttrNominalRequest::evaluate(Evaluator &evaluator, auto macroName = (macro) ? macro->getNameRef() : DeclNameRef(); auto macros = namelookup::lookupMacros(dc, moduleName, macroName, getAttachedMacroRoles()); - auto shouldPreferPropWrapper = shouldPreferPropertyWrapperOverMacro(owner); + auto shouldPreferPropWrapper = attr->shouldPreferPropertyWrapperOverMacro(); if (!macros.empty() && !shouldPreferPropWrapper) return nullptr; diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 9e57b5a012a13..16a91928898b5 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -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(), diff --git a/test/NameLookup/rdar163961797.swift b/test/NameLookup/rdar163961797.swift new file mode 100644 index 0000000000000..2fc490d21e16f --- /dev/null +++ b/test/NameLookup/rdar163961797.swift @@ -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() + } +}