Skip to content

Commit edd87d6

Browse files
committed
[ASTGen] Add experimental feature to use ASTGen in lieu of parsing types
Introduce a new experimental feature `ASTGenTypes` that uses ASTGen to translate the Swift syntax tree (produced by the new Swift parser) into C++ `TypeRepr` nodes instead of having the C++ parser create the nodes. The approach here is to intercept the C++ parser's `parseType` operation to find the Swift syntax node at the given position (where the lexer currently is) and have ASTGen translate that into the corresponding C++ AST node. Then, we spin the lexer forward to the token immediately following the end of the syntax node and continue parsing.
1 parent 8d02e39 commit edd87d6

File tree

7 files changed

+136
-8
lines changed

7 files changed

+136
-8
lines changed

include/swift/Basic/Features.def

+4
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ EXPERIMENTAL_FEATURE(ImplicitSome, false)
173173
/// corresponding syntax tree.
174174
EXPERIMENTAL_FEATURE(ParserASTGen, false)
175175

176+
/// Use the syntax tree produced by the Swift (swift-syntax) parser for type
177+
/// parsing, using ASTGen to translate them into AST nodes.
178+
EXPERIMENTAL_FEATURE(ASTGenTypes, false)
179+
176180
/// Parse using the Swift (swift-syntax) parser and use ASTGen to generate the
177181
/// corresponding syntax tree.
178182
EXPERIMENTAL_FEATURE(BuiltinMacros, false)

include/swift/Parse/Parser.h

+44
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/LayoutConstraint.h"
2525
#include "swift/AST/ParseRequests.h"
2626
#include "swift/AST/Pattern.h"
27+
#include "swift/AST/SourceFile.h"
2728
#include "swift/AST/Stmt.h"
2829
#include "swift/Basic/OptionSet.h"
2930
#include "swift/Config.h"
@@ -1325,6 +1326,49 @@ class Parser {
13251326
/// Get the location for a type error.
13261327
SourceLoc getTypeErrorLoc() const;
13271328

1329+
/// Callback function used for creating a C++ AST from the syntax node at the given source location.
1330+
///
1331+
/// The arguments to this callback are the source file to pass into ASTGen (the exported source file)
1332+
/// and the source location pointer to pass into ASTGen (to find the syntax node).
1333+
///
1334+
/// The callback returns the new AST node and the ending location of the syntax node. If the AST node
1335+
/// is NULL, something went wrong.
1336+
template<typename T>
1337+
using ASTFromSyntaxTreeCallback = std::pair<T*, const void *>(
1338+
void *sourceFile, const void *sourceLoc
1339+
);
1340+
1341+
/// Parse by constructing a C++ AST node from the Swift syntax tree via ASTGen.
1342+
template<typename T>
1343+
ParserResult<T> parseASTFromSyntaxTree(
1344+
llvm::function_ref<ASTFromSyntaxTreeCallback<T>> body
1345+
) {
1346+
if (!Context.LangOpts.hasFeature(Feature::ASTGenTypes))
1347+
return nullptr;
1348+
1349+
auto exportedSourceFile = SF.exportedSourceFile;
1350+
if (!exportedSourceFile)
1351+
return nullptr;
1352+
1353+
// Perform the translation.
1354+
auto sourceLoc = Tok.getLoc().getOpaquePointerValue();
1355+
T* astNode;
1356+
const void *endLocPtr;
1357+
std::tie(astNode, endLocPtr) = body(exportedSourceFile, sourceLoc);
1358+
1359+
if (!astNode) {
1360+
assert(false && "Could not build AST node from syntax tree");
1361+
return nullptr;
1362+
}
1363+
1364+
// Spin the lexer until we get to the ending location.
1365+
while (Tok.getLoc().getOpaquePointerValue() < endLocPtr &&
1366+
!Tok.is(tok::eof))
1367+
consumeToken();
1368+
1369+
return makeParserResult(astNode);
1370+
}
1371+
13281372
//===--------------------------------------------------------------------===//
13291373
// Type Parsing
13301374

lib/AST/ASTPrinter.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -3322,6 +3322,10 @@ static bool usesFeatureParserASTGen(Decl *decl) {
33223322
return false;
33233323
}
33243324

3325+
static bool usesFeatureASTGenTypes(Decl *decl) {
3326+
return false;
3327+
}
3328+
33253329
static bool usesFeatureBuiltinMacros(Decl *decl) {
33263330
return false;
33273331
}

lib/ASTGen/Sources/ASTGen/Macros.swift

+25-5
Original file line numberDiff line numberDiff line change
@@ -635,10 +635,11 @@ func expandFreestandingMacroInProcess(
635635
}
636636

637637
/// Retrieve a syntax node in the given source file, with the given type.
638-
private func findSyntaxNodeInSourceFile<Node: SyntaxProtocol>(
638+
func findSyntaxNodeInSourceFile<Node: SyntaxProtocol>(
639639
sourceFilePtr: UnsafeRawPointer,
640640
sourceLocationPtr: UnsafePointer<UInt8>?,
641-
type: Node.Type
641+
type: Node.Type,
642+
wantOutermost: Bool = false
642643
) -> Node? {
643644
guard let sourceLocationPtr = sourceLocationPtr else {
644645
return nil
@@ -664,16 +665,35 @@ private func findSyntaxNodeInSourceFile<Node: SyntaxProtocol>(
664665
}
665666

666667
var currentSyntax = Syntax(token)
668+
var resultSyntax: Node? = nil
667669
while let parentSyntax = currentSyntax.parent {
668670
if let typedParent = parentSyntax.as(type) {
669-
return typedParent
671+
resultSyntax = typedParent
672+
break
670673
}
671674

672675
currentSyntax = parentSyntax
673676
}
674677

675-
print("unable to find node: \(token.debugDescription)")
676-
return nil
678+
// If we didn't find anything, complain and fail.
679+
guard var resultSyntax else {
680+
print("unable to find node: \(token.debugDescription)")
681+
return nil
682+
}
683+
684+
// If we want the outermost node, keep looking.
685+
if wantOutermost {
686+
while let parentSyntax = resultSyntax.parent {
687+
guard let typedParent = parentSyntax.as(type),
688+
typedParent.position == resultSyntax.position else {
689+
break;
690+
}
691+
692+
resultSyntax = typedParent
693+
}
694+
}
695+
696+
return resultSyntax
677697
}
678698

679699
@_cdecl("swift_ASTGen_expandAttachedMacro")

lib/ASTGen/Sources/ASTGen/Types.swift

+34
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,37 @@ extension ASTGenVisitor {
207207
}
208208
}
209209
}
210+
211+
@_cdecl("swift_ASTGen_buildTypeRepr")
212+
@usableFromInline
213+
func buildTypeRepr(
214+
sourceFilePtr: UnsafeRawPointer,
215+
typeLocPtr: UnsafePointer<UInt8>,
216+
dc: UnsafeMutableRawPointer,
217+
ctx: UnsafeMutableRawPointer,
218+
endTypeLocPtr: UnsafeMutablePointer<UnsafePointer<UInt8>?>
219+
) -> UnsafeMutableRawPointer? {
220+
let sourceFile = sourceFilePtr.bindMemory(
221+
to: ExportedSourceFile.self, capacity: 1
222+
)
223+
224+
// Find the type syntax node.
225+
guard let typeSyntax = findSyntaxNodeInSourceFile(
226+
sourceFilePtr: sourceFilePtr,
227+
sourceLocationPtr: typeLocPtr,
228+
type: TypeSyntax.self,
229+
wantOutermost: true
230+
) else {
231+
// FIXME: Produce an error
232+
return nil
233+
}
234+
235+
// Fill in the end location.
236+
endTypeLocPtr.pointee = sourceFile.pointee.buffer.baseAddress!.advanced(by: typeSyntax.endPosition.utf8Offset)
237+
238+
// Convert the type syntax node.
239+
let typeReprNode = ASTGenVisitor(ctx: ctx, base: sourceFile.pointee.buffer.baseAddress!, declContext: dc)
240+
.visit(typeSyntax)
241+
242+
return typeReprNode.rawValue
243+
}

lib/Parse/ParseType.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,14 @@ ParserResult<TypeRepr> Parser::parseTypeScalar(
572572
constLoc));
573573
}
574574

575+
/// Build a TypeRepr for AST node for the type at the given source location in the specified file.
576+
///
577+
/// \param sourceLoc The source location at which to start processing a type.
578+
/// \param endSourceLoc Will receive the source location immediately following the type.
579+
extern "C" TypeRepr *swift_ASTGen_buildTypeRepr(
580+
void *sourceFile, const void *_Nullable sourceLoc,
581+
void *declContext, void *astContext, const void *_Nullable *endSourceLoc);
582+
575583
/// parseType
576584
/// type:
577585
/// type-scalar
@@ -582,6 +590,19 @@ ParserResult<TypeRepr> Parser::parseTypeScalar(
582590
///
583591
ParserResult<TypeRepr> Parser::parseType(
584592
Diag<> MessageID, ParseTypeReason reason) {
593+
#if SWIFT_SWIFT_PARSER
594+
auto astGenResult = parseASTFromSyntaxTree<TypeRepr>(
595+
[&](void *exportedSourceFile, const void *sourceLoc) {
596+
const void *endLocPtr = nullptr;
597+
TypeRepr *typeRepr = swift_ASTGen_buildTypeRepr(
598+
exportedSourceFile, Tok.getLoc().getOpaquePointerValue(),
599+
CurDeclContext, &Context, &endLocPtr);
600+
return std::make_pair(typeRepr, endLocPtr);
601+
});
602+
if (astGenResult.isNonNull())
603+
return astGenResult;
604+
#endif
605+
585606
// Parse pack expansion 'repeat T'
586607
if (Tok.is(tok::kw_repeat)) {
587608
SourceLoc repeatLoc = consumeToken(tok::kw_repeat);

test/ASTGen/verify-parse.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-run-simple-swift(-enable-experimental-feature SwiftParser -enable-experimental-feature ParserASTGen)
2+
// RUN: %target-run-simple-swift(-enable-experimental-feature ASTGenTypes)
23

34
// REQUIRES: executable_test
45

@@ -25,11 +26,11 @@ func test3(y: Int) -> Int {
2526
return x
2627
}
2728

28-
func test4(_ b: Bool) -> Int {
29-
if b { 0 } else { 1 }
29+
func test4(_ b: [Bool]) -> Int {
30+
if b.isEmpty { 0 } else { 1 }
3031
}
3132

32-
func test5(_ b: Bool) -> Int {
33+
func test5(_ b: Swift.Bool) -> Int {
3334
return if b { 0 } else { 1 }
3435
}
3536

0 commit comments

Comments
 (0)