Skip to content

Commit 7a79d31

Browse files
authored
Merge pull request #61660 from DougGregor/evaluate-builtin-macros
[Macros] "Expand" builtin macros for magic literals.
2 parents 4912c35 + e2993ea commit 7a79d31

File tree

13 files changed

+269
-22
lines changed

13 files changed

+269
-22
lines changed

include/swift/AST/SourceFile.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ class SourceFile final : public FileUnit {
316316
/// this source file.
317317
llvm::SmallVector<Located<StringRef>, 0> VirtualFilePaths;
318318

319+
/// The \c ExportedSourceFile instance produced by ASTGen, which includes
320+
/// the SourceFileSyntax node corresponding to this source file.
321+
void *exportedSourceFile = nullptr;
322+
319323
/// Returns information about the file paths used for diagnostics and magic
320324
/// identifiers in this source file, including virtual filenames introduced by
321325
/// \c #sourceLocation(file:) declarations.

include/swift/Basic/Features.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,13 @@ EXPERIMENTAL_FEATURE(ImplicitSome)
145145
/// corresponding syntax tree.
146146
EXPERIMENTAL_FEATURE(ParserASTGen)
147147

148+
/// Enable the experimental macros feature.
148149
EXPERIMENTAL_FEATURE(Macros)
149150

151+
/// Parse using the Swift (swift-syntax) parser and use ASTGen to generate the
152+
/// corresponding syntax tree.
153+
EXPERIMENTAL_FEATURE(BuiltinMacros)
154+
150155
#undef EXPERIMENTAL_FEATURE
151156
#undef UPCOMING_FEATURE
152157
#undef SUPPRESSIBLE_LANGUAGE_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3098,6 +3098,10 @@ static bool usesFeatureParserASTGen(Decl *decl) {
30983098
return false;
30993099
}
31003100

3101+
static bool usesFeatureBuiltinMacros(Decl *decl) {
3102+
return false;
3103+
}
3104+
31013105
static void
31023106
suppressingFeatureNoAsyncAvailability(PrintOptions &options,
31033107
llvm::function_ref<void()> action) {

lib/ASTGen/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ if (SWIFT_SWIFT_PARSER)
44
Sources/ASTGen/Decls.swift
55
Sources/ASTGen/Exprs.swift
66
Sources/ASTGen/Literals.swift
7+
Sources/ASTGen/Macros.swift
78
Sources/ASTGen/Misc.swift
9+
Sources/ASTGen/SourceFile.swift
810
Sources/ASTGen/Stmts.swift
911
)
1012

@@ -39,6 +41,8 @@ if (SWIFT_SWIFT_PARSER)
3941
$<TARGET_OBJECTS:SwiftSyntax::SwiftDiagnostics>
4042
$<TARGET_OBJECTS:SwiftSyntax::SwiftSyntax>
4143
$<TARGET_OBJECTS:SwiftSyntax::SwiftOperators>
44+
$<TARGET_OBJECTS:SwiftSyntax::SwiftSyntaxBuilder>
45+
$<TARGET_OBJECTS:SwiftSyntax::_SwiftSyntaxMacros>
4246
$<TARGET_OBJECTS:SwiftSyntax::SwiftCompilerSupport>
4347

4448
swiftAST
@@ -53,6 +57,8 @@ if (SWIFT_SWIFT_PARSER)
5357
SwiftSyntax::SwiftDiagnostics
5458
SwiftSyntax::SwiftSyntax
5559
SwiftSyntax::SwiftOperators
60+
SwiftSyntax::SwiftSyntaxBuilder
61+
SwiftSyntax::_SwiftSyntaxMacros
5662
SwiftSyntax::SwiftCompilerSupport
5763
swiftAST
5864
)

lib/ASTGen/Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ let package = Package(
2525
name: "swiftASTGen",
2626
dependencies: [
2727
.product(name: "SwiftSyntax", package: "swift-syntax"),
28-
.product(name: "SwiftParser", package: "swift-syntax")
28+
.product(name: "SwiftParser", package: "swift-syntax"),
29+
.product(name: "_SwiftSyntaxMacros", package: "swift-syntax")
2930
],
3031
path: ".",
3132
exclude: ["CMakeLists.txt"],

lib/ASTGen/Sources/ASTGen/ASTGen.swift

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ extension UnsafePointer {
1717
}
1818
}
1919

20-
/// Little utility wrapper that lets us
20+
/// Little utility wrapper that lets us have some mutable state within
21+
/// immutable structs, and is therefore pretty evil.
2122
@propertyWrapper
2223
class Boxed<Value> {
2324
var wrappedValue: Value
@@ -29,7 +30,7 @@ class Boxed<Value> {
2930

3031
struct ASTGenVisitor: SyntaxTransformVisitor {
3132
let ctx: UnsafeMutableRawPointer
32-
let base: UnsafePointer<CChar>
33+
let base: UnsafePointer<UInt8>
3334

3435
@Boxed var declContext: UnsafeMutableRawPointer
3536

@@ -68,15 +69,18 @@ struct ASTGenVisitor: SyntaxTransformVisitor {
6869
}
6970
}
7071

71-
@_cdecl("parseTopLevelSwift")
72-
public func parseTopLevelSwift(
73-
buffer: UnsafePointer<CChar>, dc: UnsafeMutableRawPointer,
74-
ctx: UnsafeMutableRawPointer,
75-
outputContext: UnsafeMutableRawPointer,
76-
callback: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void
72+
/// Generate AST nodes for all top-level entities in the given source file.
73+
@_cdecl("swift_ASTGen_buildTopLevelASTNodes")
74+
public func buildTopLevelASTNodes(
75+
sourceFilePtr: UnsafePointer<UInt8>,
76+
dc: UnsafeMutableRawPointer,
77+
ctx: UnsafeMutableRawPointer,
78+
outputContext: UnsafeMutableRawPointer,
79+
callback: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void
7780
) {
78-
let syntax = try! Parser.parse(source: String(cString: buffer))
79-
ASTGenVisitor(ctx: ctx, base: buffer, declContext: dc)
80-
.visit(syntax)
81-
.forEach { callback($0, outputContext) }
81+
sourceFilePtr.withMemoryRebound(to: ExportedSourceFile.self, capacity: 1) { sourceFile in
82+
ASTGenVisitor(ctx: ctx, base: sourceFile.pointee.buffer.baseAddress!, declContext: dc)
83+
.visit(sourceFile.pointee.syntax)
84+
.forEach { callback($0, outputContext) }
85+
}
8286
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import SwiftSyntax
2+
@_spi(Testing) import _SwiftSyntaxMacros
3+
4+
extension SyntaxProtocol {
5+
func token(at position: AbsolutePosition) -> TokenSyntax? {
6+
// If the position isn't within this node at all, return early.
7+
guard position >= self.position && position < self.endPosition else {
8+
return nil
9+
}
10+
11+
// If we are a token syntax, that's it!
12+
if let token = Syntax(self).as(TokenSyntax.self) {
13+
return token
14+
}
15+
16+
// Otherwise, it must be one of our children.
17+
return children(viewMode: .sourceAccurate).lazy.compactMap { child in
18+
child.token(at: position)
19+
}.first
20+
}
21+
}
22+
23+
@_cdecl("swift_ASTGen_evaluateMacro")
24+
public func evaluateMacro(
25+
sourceFilePtr: UnsafePointer<UInt8>,
26+
sourceLocationPtr: UnsafePointer<UInt8>?,
27+
expandedSourcePointer: UnsafeMutablePointer<UnsafePointer<UInt8>?>,
28+
expandedSourceLength: UnsafeMutablePointer<Int>
29+
) -> Int {
30+
// We didn't expand anything so far.
31+
expandedSourcePointer.pointee = nil
32+
expandedSourceLength.pointee = 0
33+
34+
guard let sourceLocationPtr = sourceLocationPtr else {
35+
print("NULL source location")
36+
return -1
37+
}
38+
39+
return sourceFilePtr.withMemoryRebound(
40+
to: ExportedSourceFile.self, capacity: 1
41+
) { (sourceFile: UnsafePointer<ExportedSourceFile>) -> Int in
42+
// Find the offset.
43+
let buffer = sourceFile.pointee.buffer
44+
let offset = sourceLocationPtr - buffer.baseAddress!
45+
if offset < 0 || offset >= buffer.count {
46+
print("source location isn't inside this buffer")
47+
return -1
48+
}
49+
50+
let sf = sourceFile.pointee.syntax
51+
guard let token = sf.token(at: AbsolutePosition(utf8Offset: offset)) else {
52+
print("couldn't find token at offset \(offset)")
53+
return -1
54+
}
55+
56+
guard let parentSyntax = token.parent,
57+
parentSyntax.is(MacroExpansionExprSyntax.self) else {
58+
print("not on a macro expansion node: \(token.recursiveDescription)")
59+
return -1
60+
}
61+
62+
let macroSystem = MacroSystem.exampleSystem
63+
let converter = SourceLocationConverter(
64+
file: sourceFile.pointee.fileName, tree: sf
65+
)
66+
let context = MacroEvaluationContext(
67+
moduleName: sourceFile.pointee.moduleName,
68+
sourceLocationConverter: converter
69+
)
70+
71+
let evaluatedSyntax = parentSyntax.evaluateMacro(
72+
with: macroSystem, context: context
73+
) { error in
74+
/* TODO: Report errors */
75+
}
76+
77+
var evaluatedSyntaxStr = evaluatedSyntax.withoutTrivia().description
78+
evaluatedSyntaxStr.withUTF8 { utf8 in
79+
let evaluatedResultPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: utf8.count + 1)
80+
if let baseAddress = utf8.baseAddress {
81+
evaluatedResultPtr.initialize(from: baseAddress, count: utf8.count)
82+
}
83+
evaluatedResultPtr[utf8.count] = 0
84+
85+
expandedSourcePointer.pointee = UnsafePointer(evaluatedResultPtr)
86+
expandedSourceLength.pointee = utf8.count + 1
87+
}
88+
89+
return 0
90+
}
91+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import SwiftParser
2+
import SwiftSyntax
3+
4+
/// Describes a source file that has been "exported" to the C++ part of the
5+
/// compiler, with enough information to interface with the C++ layer.
6+
struct ExportedSourceFile {
7+
/// The underlying buffer within the C++ SourceManager, which is used
8+
/// for computations of source locations.
9+
let buffer: UnsafeBufferPointer<UInt8>
10+
11+
/// The name of the enclosing module.
12+
let moduleName: String
13+
14+
/// The name of the source file being parsed.
15+
let fileName: String
16+
17+
/// The syntax tree for the complete source file.
18+
let syntax: SourceFileSyntax
19+
}
20+
21+
/// Parses the given source file and produces a pointer to a new
22+
/// ExportedSourceFile instance.
23+
@_cdecl("swift_ASTGen_parseSourceFile")
24+
public func parseSourceFile(
25+
buffer: UnsafePointer<UInt8>, bufferLength: Int,
26+
moduleName: UnsafePointer<UInt8>, filename: UnsafePointer<UInt8>
27+
) -> UnsafeRawPointer {
28+
let buffer = UnsafeBufferPointer(start: buffer, count: bufferLength)
29+
let sourceFile = Parser.parse(source: buffer)
30+
31+
let exportedPtr = UnsafeMutablePointer<ExportedSourceFile>.allocate(capacity: 1)
32+
exportedPtr.initialize(to: .init(
33+
buffer: buffer, moduleName: String(cString: moduleName),
34+
fileName: String(cString: filename), syntax: sourceFile)
35+
)
36+
37+
return UnsafeRawPointer(exportedPtr)
38+
}
39+
40+
/// Deallocate a parsed source file.
41+
@_cdecl("swift_ASTGen_destroySourceFile")
42+
public func destroySourceFile(
43+
sourceFilePtr: UnsafeMutablePointer<UInt8>
44+
) {
45+
sourceFilePtr.withMemoryRebound(to: ExportedSourceFile.self, capacity: 1) { sourceFile in
46+
sourceFile.deinitialize(count: 1)
47+
sourceFile.deallocate()
48+
}
49+
}

lib/Parse/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ if (SWIFT_SWIFT_PARSER)
5454
$<TARGET_OBJECTS:SwiftSyntax::SwiftDiagnostics>
5555
$<TARGET_OBJECTS:SwiftSyntax::SwiftSyntax>
5656
$<TARGET_OBJECTS:SwiftSyntax::SwiftOperators>
57+
$<TARGET_OBJECTS:SwiftSyntax::SwiftSyntaxBuilder>
58+
$<TARGET_OBJECTS:SwiftSyntax::_SwiftSyntaxMacros>
5759
$<TARGET_OBJECTS:SwiftSyntax::SwiftCompilerSupport>
5860
$<TARGET_OBJECTS:swiftASTGen>
5961
)
@@ -64,6 +66,8 @@ if (SWIFT_SWIFT_PARSER)
6466
SwiftSyntax::SwiftDiagnostics
6567
SwiftSyntax::SwiftSyntax
6668
SwiftSyntax::SwiftOperators
69+
SwiftSyntax::SwiftSyntaxBuilder
70+
SwiftSyntax::_SwiftSyntaxMacros
6771
SwiftSyntax::SwiftCompilerSupport
6872
swiftASTGen
6973
)

lib/Parse/ParseDecl.cpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,22 @@ static void appendToVector(void *declPtr, void *vecPtr) {
173173
vec->push_back(decl);
174174
}
175175

176+
/// Parse a source file.
177+
extern "C" void *swift_ASTGen_parseSourceFile(const char *buffer,
178+
size_t bufferLength,
179+
const char *moduleName,
180+
const char *filename);
181+
182+
/// Destroy a source file parsed with swift_ASTGen_parseSourceFile.
183+
extern "C" void swift_ASTGen_destroySourceFile(void *sourceFile);
184+
185+
// Build AST nodes for the top-level entities in the syntax.
186+
extern "C" void swift_ASTGen_buildTopLevelASTNodes(void *sourceFile,
187+
void *declContext,
188+
void *astContext,
189+
void *outputContext,
190+
void (*)(void *, void *));
191+
176192
/// Main entrypoint for the parser.
177193
///
178194
/// \verbatim
@@ -182,20 +198,36 @@ static void appendToVector(void *declPtr, void *vecPtr) {
182198
/// decl-sil-stage [[only in SIL mode]
183199
/// \endverbatim
184200
void Parser::parseTopLevel(SmallVectorImpl<Decl *> &decls) {
185-
StringRef contents =
186-
SourceMgr.extractText(SourceMgr.getRangeForBuffer(L->getBufferID()));
187-
188201
#ifdef SWIFT_SWIFT_PARSER
189-
if (Context.LangOpts.hasFeature(Feature::ParserASTGen) &&
202+
if ((Context.LangOpts.hasFeature(Feature::BuiltinMacros) ||
203+
Context.LangOpts.hasFeature(Feature::ParserASTGen)) &&
190204
!SourceMgr.hasCodeCompletionBuffer() &&
191205
SF.Kind != SourceFileKind::SIL) {
192-
parseTopLevelSwift(contents.data(), CurDeclContext, &Context, &decls, appendToVector);
206+
StringRef contents =
207+
SourceMgr.extractText(SourceMgr.getRangeForBuffer(L->getBufferID()));
208+
209+
// Parse the source file.
210+
auto exportedSourceFile = swift_ASTGen_parseSourceFile(
211+
contents.begin(), contents.size(),
212+
SF.getParentModule()->getName().str().str().c_str(),
213+
SF.getFilename().str().c_str());
214+
SF.exportedSourceFile = exportedSourceFile;
215+
Context.addCleanup([exportedSourceFile] {
216+
swift_ASTGen_destroySourceFile(exportedSourceFile);
217+
});
193218

194-
// Spin the C++ parser to the end; we won't be using it.
195-
while (!Tok.is(tok::eof)) {
196-
consumeToken();
219+
// If we want to do ASTGen, do so now.
220+
if (Context.LangOpts.hasFeature(Feature::ParserASTGen)) {
221+
swift_ASTGen_buildTopLevelASTNodes(
222+
exportedSourceFile, CurDeclContext, &Context, &decls, appendToVector);
223+
224+
// Spin the C++ parser to the end; we won't be using it.
225+
while (!Tok.is(tok::eof)) {
226+
consumeToken();
227+
}
228+
229+
return;
197230
}
198-
return;
199231
}
200232
#endif
201233

0 commit comments

Comments
 (0)