Skip to content

Commit 8569baf

Browse files
committed
Diagnose macros that have multiple freestanding macro roles.
Per SE-0397, a macro may only have a single freestanding macro role, otherwise we would have an ambiguity in how a particular freestanding macro would be expanded. Produce an error on such macro declarations. Fixes rdar://110178899. (cherry picked from commit c5ec389)
1 parent 9fd3fc6 commit 8569baf

File tree

4 files changed

+28
-0
lines changed

4 files changed

+28
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7060,6 +7060,8 @@ NOTE(macro_remove_result_type,none,
70607060
())
70617061
NOTE(macro_make_freestanding_expression,none,
70627062
"make this macro a freestanding expression macro", ())
7063+
ERROR(macro_multiple_freestanding_roles,none,
7064+
"macro can only have a single freestanding role", ())
70637065
ERROR(macro_expansion_missing_pound,none,
70647066
"expansion of macro %0 requires leading '#'", (DeclName))
70657067
ERROR(macro_expansion_missing_arguments,none,

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,6 +2018,17 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20182018
llvm_unreachable("should always be type-checked already");
20192019
}
20202020

2021+
/// Determine the number of bits set.
2022+
static unsigned numBitsSet(uint64_t value) {
2023+
unsigned count = 0;
2024+
for (uint64_t i : range(0, 63)) {
2025+
if (value & (uint64_t(1) << i))
2026+
++count;
2027+
}
2028+
2029+
return count;
2030+
}
2031+
20212032
void visitMacroDecl(MacroDecl *MD) {
20222033
TypeChecker::checkDeclAttributes(MD);
20232034
checkAccessControl(MD);
@@ -2078,6 +2089,13 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20782089
.fixItRemove(SourceRange(MD->arrowLoc, resultTypeRepr->getEndLoc()));
20792090
}
20802091
}
2092+
2093+
// A macro can only have a single freestanding macro role.
2094+
MacroRoles freestandingRolesInhabited =
2095+
MD->getMacroRoles() & getFreestandingMacroRoles();
2096+
if (numBitsSet(freestandingRolesInhabited.toRaw()) > 1) {
2097+
MD->diagnose(diag::macro_multiple_freestanding_roles);
2098+
}
20812099
}
20822100

20832101
void visitMacroExpansionDecl(MacroExpansionDecl *MED) {

test/Macros/macros_diagnostics.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,10 @@ struct SomeType {
211211

212212
@freestanding(declaration) macro nonExpressionReturnsVoid<T>(_: T) -> Void = #externalMacro(module: "A", type: "B")
213213
// expected-warning@-1{{external macro implementation type}}
214+
215+
216+
@freestanding(expression)
217+
@freestanding(declaration)
218+
macro multipleFreestandingRoles<T>(_: T) -> Void = #externalMacro(module: "A", type: "B")
219+
// expected-warning@-1{{external macro implementation type}}
220+
// expected-error@-2{{macro can only have a single freestanding role}}

test/Macros/parsing.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ protocol Q { associatedtype Assoc }
3535
@freestanding(expression) @freestanding(declaration, names: named(Foo)) @attached(accessor)
3636
macro m10(_: String) = #externalMacro(module: "A", type: "M4")
3737
// expected-warning@-1{{external macro implementation type 'A.M4' could not be found for macro 'm10'}}
38+
// expected-error@-2{{macro can only have a single freestanding role}}
3839

3940
@attached(
4041
accessor,

0 commit comments

Comments
 (0)