Skip to content

Commit de4d4a4

Browse files
committed
Teach the TypeRefinementContext not to skip declarations within macro expansions
The construction of type refinement contexts performs lazy expansion for the contents of macro expansions, so that TRC creation doesn't force all macros to be expanded. However, the logic that skips macro expansions would *also* skip some declarations produced within a macro expansion, even when building the TRC specifically for that macro expansion buffer. This manifest as missing some availability information within the TRC, rejecting some well-formed code. Tune the logic for "don't visit macro expansions when building a TRC" to recognize when we're building a TRC for that macro expansion. Fixes rdar://128400301.
1 parent f892189 commit de4d4a4

File tree

6 files changed

+117
-4
lines changed

6 files changed

+117
-4
lines changed

include/swift/AST/ASTWalker.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,11 @@ class ASTWalker {
616616
return MacroWalking::ArgumentsAndExpansion;
617617
}
618618

619+
/// This method determines whether the given declaration should be
620+
/// considered to be in a macro expansion context. It can be configured
621+
/// by subclasses.
622+
virtual bool isDeclInMacroExpansion(Decl *decl) const;
623+
619624
/// Determine whether we should walk macro arguments (as they appear in
620625
/// source) and the expansion (which is semantically part of the program).
621626
std::pair<bool, bool> shouldWalkMacroArgumentsAndExpansion() const {

lib/AST/ASTWalker.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ using namespace swift;
6363

6464
void ASTWalker::anchor() {}
6565

66+
bool ASTWalker::isDeclInMacroExpansion(Decl *decl) const {
67+
return decl->isInMacroExpansionInContext();
68+
}
69+
6670
namespace {
6771

6872
/// Traversal - This class implements a simple expression/statement
@@ -1540,7 +1544,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
15401544

15411545
bool shouldSkip(Decl *D) {
15421546
if (!Walker.shouldWalkMacroArgumentsAndExpansion().second &&
1543-
D->isInMacroExpansionInContext() && !Walker.Parent.isNull())
1547+
Walker.isDeclInMacroExpansion(D) && !Walker.Parent.isNull())
15441548
return true;
15451549

15461550
if (auto *VD = dyn_cast<VarDecl>(D)) {

lib/AST/SwiftNameTranslation.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ getErrorDomainStringForObjC(const EnumDecl *ED) {
6363
// Should have already been diagnosed as diag::objc_enum_generic.
6464
assert(!ED->isGenericContext() && "Trying to bridge generic enum error to Obj-C");
6565

66-
// Clang decls have custom domains, but we shouldn't see them here anyway.
67-
assert(!ED->getClangDecl() && "clang decls shouldn't be re-exported");
68-
6966
SmallVector<const NominalTypeDecl *, 4> outerTypes;
7067
for (const NominalTypeDecl * D = ED;
7168
D != nullptr;

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,69 @@ class TypeRefinementContextBuilder : private ASTWalker {
538538
return MacroWalking::Arguments;
539539
}
540540

541+
/// Check whether this declaration is in a source file buried within
542+
/// a macro expansion of the
543+
bool isDeclInMacroExpansion(Decl *decl) const override {
544+
// If it's not in a macro expansion relative to its context, it's not
545+
// considered to be in a macro expansion.
546+
if (!decl->isInMacroExpansionInContext())
547+
return false;
548+
549+
auto module = decl->getDeclContext()->getParentModule();
550+
auto *declFile = module->getSourceFileContainingLocation(decl->getLoc());
551+
if (!declFile)
552+
return false;
553+
554+
// Look for a parent context that implies that we are producing a
555+
// type refinement context for this expansion.
556+
for (auto iter = ContextStack.rbegin(), endIter = ContextStack.rend();
557+
iter != endIter; ++iter) {
558+
const auto &context = *iter;
559+
if (auto contextRTC = context.TRC) {
560+
// If the context is the same source file, don't treat it as an
561+
// expansion.
562+
auto introNode = contextRTC->getIntroductionNode();
563+
switch (auto reason = contextRTC->getReason()) {
564+
case TypeRefinementContext::Reason::Root:
565+
if (auto contextFile = introNode.getAsSourceFile())
566+
if (declFile == contextFile)
567+
return false;
568+
569+
break;
570+
571+
case TypeRefinementContext::Reason::Decl:
572+
case TypeRefinementContext::Reason::DeclImplicit:
573+
// If the context is a declaration, check whether the declaration
574+
// is in the same source file as this declaration.
575+
if (auto contextDecl = introNode.getAsDecl()) {
576+
if (decl == contextDecl)
577+
return false;
578+
579+
auto contextModule =
580+
contextDecl->getDeclContext()->getParentModule();
581+
SourceLoc contextDeclLoc = contextDecl->getLoc();
582+
auto contextDeclFile =
583+
contextModule->getSourceFileContainingLocation(contextDeclLoc);
584+
if (declFile == contextDeclFile)
585+
return false;
586+
}
587+
break;
588+
589+
case TypeRefinementContext::Reason::IfStmtThenBranch:
590+
case TypeRefinementContext::Reason::IfStmtElseBranch:
591+
case TypeRefinementContext::Reason::ConditionFollowingAvailabilityQuery:
592+
case TypeRefinementContext::Reason::GuardStmtFallthrough:
593+
case TypeRefinementContext::Reason::GuardStmtElseBranch:
594+
case TypeRefinementContext::Reason::WhileStmtBody:
595+
// Nothing to check here.
596+
break;
597+
}
598+
}
599+
}
600+
601+
return true;
602+
}
603+
541604
bool shouldSkipDecl(Decl *D) const {
542605
// Only visit a node that has a corresponding concrete syntax node if we are
543606
// already walking that concrete syntax node.

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,39 @@ public struct AddArbitraryMembers: MemberMacro {
758758
}
759759
}
760760

761+
public struct MemberThatCallsCodeMacro: MemberMacro {
762+
public static func expansion(
763+
of node: AttributeSyntax,
764+
providingMembersOf decl: some DeclGroupSyntax,
765+
in context: some MacroExpansionContext
766+
) throws -> [DeclSyntax] {
767+
guard case let .argumentList(arguments) = node.arguments,
768+
let firstElement = arguments.first,
769+
let stringLiteral = firstElement.expression.as(StringLiteralExprSyntax.self) else {
770+
throw CustomError.message("Macro requires a string literal")
771+
}
772+
773+
let codeString = try stringLiteral.segments.map { segment in
774+
switch segment {
775+
case .stringSegment(let string):
776+
return string.content.text
777+
778+
case .expressionSegment(_):
779+
throw CustomError.message("Macro cannot handle string interpolation")
780+
}
781+
}.joined(separator: "")
782+
return [
783+
"""
784+
static var synthesizedMember: String {
785+
\(raw: codeString)
786+
787+
return "hello"
788+
}
789+
""",
790+
]
791+
}
792+
}
793+
761794
/// Implementation of the `wrapStoredProperties` macro, which can be
762795
/// used to apply an attribute to all of the stored properties of a type.
763796
///

test/Macros/macro_availability_macosx.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ struct X { }
1717

1818
@freestanding(expression) macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro")
1919

20+
@attached(member, names: named(synthesizedMember))
21+
macro MemberThatCallsCode(_ codeString: String) = #externalMacro(module: "MacroDefinition", type: "MemberThatCallsCodeMacro")
22+
2023
@available(macOS 12.0, *)
2124
func onlyInMacOS12() { }
2225

@@ -27,3 +30,11 @@ func test() {
2730
}
2831
})
2932
}
33+
34+
@MemberThatCallsCode("""
35+
if #available(macOS 12.0, *) {
36+
onlyInMacOS12()
37+
}
38+
""")
39+
struct HasMembers {
40+
}

0 commit comments

Comments
 (0)