Skip to content

Commit c5ec389

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.
1 parent 3c04cff commit c5ec389

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
@@ -7109,6 +7109,8 @@ NOTE(macro_remove_result_type,none,
71097109
())
71107110
NOTE(macro_make_freestanding_expression,none,
71117111
"make this macro a freestanding expression macro", ())
7112+
ERROR(macro_multiple_freestanding_roles,none,
7113+
"macro can only have a single freestanding role", ())
71127114
ERROR(macro_expansion_missing_pound,none,
71137115
"expansion of macro %0 requires leading '#'", (DeclName))
71147116
ERROR(macro_expansion_missing_arguments,none,

lib/Sema/TypeCheckDeclPrimary.cpp

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

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

20822100
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)