Skip to content

Commit 1c158b5

Browse files
committed
Allow preamble macros to introduce declarations with documented names
Preamble macros introduce code at the beginning of a function body. Allow them to introduce declarations as well, so long as the macro declaration properly declares the names it introduces. This allows all sorts of exciting macros that introduce (e.g.) new local variables that one can use.
1 parent 367a847 commit 1c158b5

File tree

7 files changed

+131
-66
lines changed

7 files changed

+131
-66
lines changed

lib/AST/ASTScopeCreation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,7 @@ AbstractPatternEntryScope::AbstractPatternEntryScope(
13231323
#pragma mark - expandBody
13241324

13251325
void FunctionBodyScope::expandBody(ScopeCreator &scopeCreator) {
1326-
scopeCreator.addToScopeTree(decl->getBody(), this);
1326+
scopeCreator.addToScopeTree(decl->getMacroExpandedBody(), this);
13271327
}
13281328

13291329
void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {}

lib/AST/Decl.cpp

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9117,24 +9117,74 @@ bool AbstractFunctionDecl::hasBody() const {
91179117
}
91189118
}
91199119

9120+
/// Expand all preamble macros attached to the given function declaration.
9121+
static std::vector<ASTNode> expandPreamble(AbstractFunctionDecl *func) {
9122+
std::vector<ASTNode> preamble;
91209123

9124+
ASTContext &ctx = func->getASTContext();
9125+
ExpandPreambleMacroRequest request{func};
9126+
auto module = func->getParentModule();
9127+
for (auto bufferID : evaluateOrDefault(ctx.evaluator, request, { })) {
9128+
auto bufferStart = ctx.SourceMgr.getLocForBufferStart(bufferID);
9129+
auto preambleSF = module->getSourceFileContainingLocation(bufferStart);
9130+
preamble.insert(preamble.end(),
9131+
preambleSF->getTopLevelItems().begin(),
9132+
preambleSF->getTopLevelItems().end());
9133+
}
9134+
9135+
return preamble;
9136+
}
9137+
9138+
/// Expand body macros and produce the resulting body.
91219139
static BraceStmt *expandBodyMacro(AbstractFunctionDecl *fn) {
91229140
ASTContext &ctx = fn->getASTContext();
91239141

9124-
auto bufferID = evaluateOrDefault(
9125-
ctx.evaluator, ExpandBodyMacroRequest{fn}, llvm::None);
9126-
if (!bufferID) {
9142+
// Expand the preamble.
9143+
auto preamble = expandPreamble(fn);
9144+
9145+
// Expand a body macro, if there is one.
9146+
if (auto bufferID = evaluateOrDefault(
9147+
ctx.evaluator, ExpandBodyMacroRequest{fn}, llvm::None)) {
9148+
CharSourceRange bufferRange = ctx.SourceMgr.getRangeForBuffer(*bufferID);
9149+
auto bufferStart = bufferRange.getStart();
9150+
auto module = fn->getParentModule();
9151+
auto macroSourceFile = module->getSourceFileContainingLocation(bufferStart);
9152+
9153+
// When there is no preamble, adopt the body itself.
9154+
if (preamble.empty()) {
9155+
return BraceStmt::create(
9156+
ctx, bufferRange.getStart(), macroSourceFile->getTopLevelItems(),
9157+
bufferRange.getEnd());
9158+
}
9159+
9160+
// Merge the preamble into the macro-produced body.
9161+
auto contents = std::move(preamble);
9162+
contents.insert(
9163+
contents.end(),
9164+
macroSourceFile->getTopLevelItems().begin(),
9165+
macroSourceFile->getTopLevelItems().end());
9166+
return BraceStmt::create(
9167+
ctx, bufferRange.getStart(), contents, bufferRange.getEnd());
9168+
}
9169+
9170+
// There is no body macro. If there's no preamble, either, then there is
9171+
// nothing to do.
9172+
if (preamble.empty())
91279173
return nullptr;
9128-
}
91299174

9130-
CharSourceRange bufferRange = ctx.SourceMgr.getRangeForBuffer(*bufferID);
9131-
auto bufferStart = bufferRange.getStart();
9132-
auto module = fn->getParentModule();
9133-
auto macroSourceFile = module->getSourceFileContainingLocation(bufferStart);
9175+
// If there is no body, the preamble has nowhere to go.
9176+
auto body = fn->getBody(/*canSynthesize=*/true);
9177+
if (!body) {
9178+
// FIXME: diagnose this
9179+
return nullptr;
9180+
}
91349181

9182+
// Merge the preamble into the existing body.
9183+
auto contents = std::move(preamble);
9184+
contents.insert(
9185+
contents.end(), body->getElements().begin(), body->getElements().end());
91359186
return BraceStmt::create(
9136-
ctx, bufferRange.getStart(), macroSourceFile->getTopLevelItems(),
9137-
bufferRange.getEnd());
9187+
ctx, body->getLBraceLoc(), contents, body->getRBraceLoc());
91389188
}
91399189

91409190
BraceStmt *AbstractFunctionDecl::getMacroExpandedBody() const {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7146,7 +7146,6 @@ void AttributeChecker::visitMacroRoleAttr(MacroRoleAttr *attr) {
71467146
// TODO: Check property observer names?
71477147
break;
71487148
case MacroRole::MemberAttribute:
7149-
case MacroRole::Preamble:
71507149
case MacroRole::Body:
71517150
if (!attr->getNames().empty())
71527151
diagnoseAndRemoveAttr(attr, diag::macro_cannot_introduce_names,
@@ -7168,6 +7167,7 @@ void AttributeChecker::visitMacroRoleAttr(MacroRoleAttr *attr) {
71687167
break;
71697168
}
71707169
case MacroRole::Extension:
7170+
case MacroRole::Preamble:
71717171
break;
71727172
default:
71737173
diagnoseAndRemoveAttr(attr, diag::invalid_macro_role_for_macro_syntax,

lib/Sema/TypeCheckMacros.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,29 @@ getExplicitInitializerRange(AbstractStorageDecl *storage) {
834834
return SourceRange(equalLoc, initRange.End);
835835
}
836836

837+
/// Compute the original source range for expanding a preamble macro.
838+
static CharSourceRange getPreambleMacroOriginalRange(AbstractFunctionDecl *fn) {
839+
ASTContext &ctx = fn->getASTContext();
840+
841+
SourceLoc insertionLoc;
842+
843+
// If there is a body macro, start at the beginning of it.
844+
if (auto bodyBufferID = evaluateOrDefault(
845+
ctx.evaluator, ExpandBodyMacroRequest{fn}, llvm::None)) {
846+
insertionLoc = ctx.SourceMgr.getRangeForBuffer(*bodyBufferID).getStart();
847+
} else {
848+
// If there is a parsed body, start at the beginning of it.
849+
SourceRange range = fn->getBodySourceRange();
850+
if (range.isValid()) {
851+
insertionLoc = range.Start;
852+
} else {
853+
insertionLoc = fn->getEndLoc();
854+
}
855+
}
856+
857+
return CharSourceRange(insertionLoc, 0);
858+
}
859+
837860
static CharSourceRange getExpansionInsertionRange(MacroRole role,
838861
ASTNode target,
839862
SourceManager &sourceMgr) {
@@ -907,15 +930,11 @@ static CharSourceRange getExpansionInsertionRange(MacroRole role,
907930
}
908931

909932
case MacroRole::Preamble: {
910-
SourceLoc inBodyLoc;
911933
if (auto fn = dyn_cast<AbstractFunctionDecl>(target.get<Decl *>())) {
912-
inBodyLoc = fn->getMacroExpandedBody()->getStartLoc();
934+
return getPreambleMacroOriginalRange(fn);
913935
}
914936

915-
if (inBodyLoc.isInvalid())
916-
inBodyLoc = target.getEndLoc();
917-
918-
return CharSourceRange(Lexer::getLocForEndOfToken(sourceMgr, inBodyLoc), 0);
937+
return CharSourceRange(Lexer::getLocForEndOfToken(sourceMgr, target.getEndLoc()), 0);
919938
}
920939

921940
case MacroRole::Body: {

lib/Sema/TypeCheckStmt.cpp

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,24 +2764,6 @@ static bool requiresNoDefinition(Decl *decl) {
27642764
return false;
27652765
}
27662766

2767-
/// Expand all preamble macros attached to the given function declaration.
2768-
static std::vector<ASTNode> expandPreamble(AbstractFunctionDecl *func) {
2769-
std::vector<ASTNode> preamble;
2770-
2771-
ASTContext &ctx = func->getASTContext();
2772-
ExpandPreambleMacroRequest request{func};
2773-
auto module = func->getParentModule();
2774-
for (auto bufferID : evaluateOrDefault(ctx.evaluator, request, { })) {
2775-
auto bufferStart = ctx.SourceMgr.getLocForBufferStart(bufferID);
2776-
auto preambleSF = module->getSourceFileContainingLocation(bufferStart);
2777-
preamble.insert(preamble.end(),
2778-
preambleSF->getTopLevelItems().begin(),
2779-
preambleSF->getTopLevelItems().end());
2780-
}
2781-
2782-
return preamble;
2783-
}
2784-
27852767
BraceStmt *
27862768
TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval,
27872769
AbstractFunctionDecl *AFD) const {
@@ -2857,17 +2839,6 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval,
28572839
}
28582840
}
28592841

2860-
// Expand any preamble macros and introduce them into the body.
2861-
auto preamble = expandPreamble(AFD);
2862-
if (!preamble.empty()) {
2863-
auto newBody = std::move(preamble);
2864-
newBody.insert(
2865-
newBody.end(), body->getElements().begin(), body->getElements().end());
2866-
2867-
body = BraceStmt::create(
2868-
ctx, body->getLBraceLoc(), newBody, body->getRBraceLoc());
2869-
}
2870-
28712842
// Typechecking, in particular ApplySolution is going to replace closures
28722843
// with OpaqueValueExprs and then try to do lookups into the closures.
28732844
// So, build out the body now.

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,7 +2183,7 @@ public struct TracedPreambleMacro: PreambleMacro {
21832183
}
21842184

21852185
@_spi(ExperimentalLanguageFeature)
2186-
public struct Log2PreambleMacro: PreambleMacro {
2186+
public struct LoggerMacro: PreambleMacro {
21872187
public static func expansion(
21882188
of node: AttributeSyntax,
21892189
providingPreambleFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
@@ -2203,16 +2203,19 @@ public struct Log2PreambleMacro: PreambleMacro {
22032203
let passedArgs = paramNames.map { "\($0): \\(\($0))" }.joined(separator: ", ")
22042204

22052205
let entry: CodeBlockItemSyntax = """
2206-
log2("Entering \(funcBaseName)(\(raw: passedArgs))")
2206+
logger.log(entering: "\(funcBaseName)(\(raw: passedArgs))")
22072207
"""
22082208

22092209
let argLabels = paramNames.map { "\($0):" }.joined()
22102210

22112211
let exit: CodeBlockItemSyntax = """
2212-
log2("Exiting \(funcBaseName)(\(raw: argLabels))")
2212+
logger.log(exiting: "\(funcBaseName)(\(raw: argLabels))")
22132213
"""
22142214

22152215
return [
2216+
"""
2217+
let logger = Logger()
2218+
""",
22162219
entry,
22172220
"""
22182221
defer {

test/Macros/macro_expand_body.swift

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ macro Remote() = #externalMacro(module: "MacroDefinition", type: "RemoteBodyMacr
1616
@attached(preamble)
1717
macro Traced() = #externalMacro(module: "MacroDefinition", type: "TracedPreambleMacro")
1818

19-
@attached(preamble)
20-
macro Log2() = #externalMacro(module: "MacroDefinition", type: "Log2PreambleMacro")
19+
@attached(preamble, names: named(logger))
20+
macro Logged() = #externalMacro(module: "MacroDefinition", type: "LoggerMacro")
2121

2222
protocol ConjureRemoteValue {
2323
static func conjureValue() -> Self
@@ -27,36 +27,53 @@ extension String: ConjureRemoteValue {
2727
static func conjureValue() -> String { "" }
2828
}
2929

30+
struct Logger {
31+
func log(entering function: String) {
32+
print("Logger entering \(function)")
33+
}
34+
35+
func log(_ message: String) {
36+
print("--- \(message)")
37+
}
38+
39+
func log(exiting function: String) {
40+
print("Logger exiting \(function)")
41+
}
42+
}
43+
44+
func log(_ message: String) {
45+
print(message)
46+
}
47+
3048
@available(SwiftStdlib 5.1, *)
3149
func remoteCall<Result: ConjureRemoteValue>(function: String, arguments: [String: Any]) async throws -> Result {
32-
let printedArgs = arguments.keys.sorted().map { key in
50+
let printedArgs = arguments.keys.sorted().map { key in
3351
"\(key): \(arguments[key]!)"
3452
}.joined(separator: ", ")
3553
print("Remote call \(function)(\(printedArgs))")
3654
return Result.conjureValue()
3755
}
3856

39-
func log(_ value: String) {
40-
print(value)
41-
}
42-
43-
func log2(_ value: String) {
44-
print("log2(\(value))")
45-
}
57+
@available(SwiftStdlib 5.1, *)
58+
@Remote
59+
func f(a: Int, b: String) async throws -> String
4660

4761
@Traced
4862
func doubleTheValue(value: Int) -> Int {
4963
return value * 2
5064
}
5165

52-
@available(SwiftStdlib 5.1, *)
53-
@Remote
54-
func f(a: Int, b: String) async throws -> String
66+
@Logged
67+
func useLogger() {
68+
let x = 1
69+
logger.log("use it")
70+
print(x)
71+
}
5572

5673
@available(SwiftStdlib 5.1, *)
5774
@Remote
5875
@Traced
59-
@Log2
76+
@Logged
6077
func g(a: Int, b: String) async throws -> String {
6178
doesNotTypeCheck()
6279
}
@@ -80,9 +97,14 @@ if #available(SwiftStdlib 5.1, *) {
8097
print(try await f(a: 5, b: "Hello"))
8198

8299
// CHECK: Entering g(a: 5, b: World)
83-
// CHECK: log2(Entering g(a: 5, b: World))
100+
// CHECK: Logger entering g(a: 5, b: World)
84101
// CHECK: Remote call g(a: 5, b: World)
85-
// CHECK: log2(Exiting g(a:b:))
102+
// CHECK: Logger exiting g(a:b:)
86103
// CHECK: Exiting g(a:b:)
87104
print(try await g(a: 5, b: "World"))
88105
}
106+
107+
// CHECK: Logger entering useLogger()
108+
// CHECK: --- use it
109+
// CHECK: Logger exiting useLogger()
110+
useLogger()

0 commit comments

Comments
 (0)