Skip to content

Commit f641890

Browse files
authored
Merge pull request #77517 from DougGregor/rtc-macro-expansion
Teach the TypeRefinementContext not to skip declarations within macro expansions
2 parents 8c0453d + de4d4a4 commit f641890

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
@@ -535,6 +535,69 @@ class TypeRefinementContextBuilder : private ASTWalker {
535535
return MacroWalking::Arguments;
536536
}
537537

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