Skip to content

[🦩] Add proof of concept for ASTGen. #703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from

Conversation

zoecarver
Copy link
Contributor

@zoecarver zoecarver commented Sep 3, 2022

Uses SyntaxTransformVisitor to start implementing ASTGen. Just a proof of concept for now.

Refs swiftlang/swift#60943

let components = visit(node.bindings)
assert(components.count == 2)
let pattern = components.first!
let initializer = components.last!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably get re-worked.

@zoecarver zoecarver force-pushed the flamingo branch 3 times, most recently from 3991c28 to 91c6311 Compare September 7, 2022 06:11
@zoecarver zoecarver marked this pull request as ready for review September 7, 2022 06:14
@zoecarver zoecarver requested a review from ahoppen as a code owner September 7, 2022 06:14

for element in node.statements {
let swiftASTNodes = visit(element)
if element.item.is(StmtSyntax.self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should encode information that elements in a source file can only be StmtSyntax, ExprSyntax or DeclSyntax in the syntax tree so we can do an exhaustive switch here.

The best idea that I’ve come up with is to code-gen enums like the following based on the node_choices of CodeBlockItem.Item in CommonNodes.py

public struct CodeBlockItemSyntax /* CodeBlockItemSyntax conformances */ {
  enum Item {
    case stmt(StmtSyntax)
    case expr(ExprSyntax)
    case decl(DeclSyntax)
    case tokenList(TokenListSyntax)
    case nonEmptyTokenList(NonEmptyTokenListSyntax)
  
    func as(_ t: StmtSyntax.Type) -> StmtSyntax? {  }
    func as<T: StmtSyntaxProtocol>(_ t: T.Type) -> T? {  }
    func as(_ t: ExprSyntax.Type) -> ExprSyntax? {  }
    func as<T: ExprSyntaxProtocol>(_ t: T.Type) -> T? {  }
    func as(_ t: DeclSyntax.Type) -> DeclSyntax? {  }
    func as<T: DeclSyntaxProtocol>(_ t: T.Type) -> T? {  }
    func as(_ t: TokenList.Type) -> TokenListSyntax? {  }
    func as(_ t: nonEmptyTokenList.Type) -> NonEmptyTokenListSyntax? {  }
  }
  // existing code in CodeBlockItemSyntax
}

and change the type of item in CodeBlockItemSyntax from Syntax to CodeBlockItemSyntax.Item.
I think (haven’t tested) that this should be mostly (i.e. covering most common API use cases) source-comaptible with the current usage of these APIs.

That way you could exhaustivly switch over it here as

switch element.item {
    case stmt(StmtSyntax):
      out.append(SwiftTopLevelCodeDecl_createStmt(ctx, declContext, loc, swiftASTNodes, loc))
    case expr(ExprSyntax):
      out.append(SwiftTopLevelCodeDecl_createExpr(ctx, declContext, loc, swiftASTNodes, loc))
    case decl(DeclSyntax):
      out.append(swiftASTNodes)
    case tokenList(TokenListSyntax):
      fatalError("Should not be generated by SwiftParser")
    case nonEmptyTokenList(NonEmptyTokenListSyntax):
      fatalError("Should not be generated by SwiftParser")
}

I would really like to see this refactoring done before we fully start ASTGen. I don’t think it needs to block this PR but it’s something we should do ASAP.

A quick improvement without this refactoring: You can use element.item.as(SyntaxEnum.self) here and then switch over the enum cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be absolutely fantastic! The hardest thing about working with the syntax tree is that you don't know what cases to cover at any given point without working back from the grammar.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All that said, I don't know that this pull request is the place for this discussion. It's a general improvement to the ergonomics of the SwiftSyntax APIs.

return out
}

public func visit(_ node: FunctionCallExprSyntax) -> UnsafeMutableRawPointer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not really a fan that all these functions return UnsafeMutableRawPointer. I imagine it will be to easy to e.g. pass a FunctionDecl to a parameter that really expects an Identifier Can we do something more typed here? Like structs that wrap a void * or something like that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to mention this on the C++ side, but since you have here - IMO it'd be good to define a bunch of opaque types (eg. typedef void *swift_source_loc_t) and use those instead of void * directly.

And another comment mentions labels - we could just add swift_name to the C functions, eg. __attribute__((swift_name("FuncDecl_create(ctx:staticLoc:....)")))


struct ASTGenVisitor: SyntaxTransformVisitor {
let ctx: UnsafeMutableRawPointer
let base: UnsafePointer<CChar>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about renaming this to `sourceFileBufferStart?

Comment on lines 33 to 36
@_disfavoredOverload
public func visit(_ node: SourceFileSyntax) -> UnsafeMutableRawPointer {
fatalError("Use other overload.")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can’t you just delete this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we have a default implementation in the protocol that will be ambiguous.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the _disfavoredOverload attribute, so we need to add this overload.

}

struct ASTGenVisitor: SyntaxTransformVisitor {
let ctx: UnsafeMutableRawPointer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is a pointer to ASTContext? In the absence of proper typing, could you rename the variable to astContext to at least give some clue what this is?

}

public func visit(_ node: IdentifierExprSyntax) -> UnsafeMutableRawPointer {
let loc = self.base.advanced(by: node.position.utf8Offset).raw
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about extracting this to a helper function?

func loc<SyntaxType: SyntaxProtocol>(of node: SyntaxType) -> UnsafePointer<CChar> {
  return self.base.advanced(by: node.position.utf8Offset).raw
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea.


let params = node.signature.input.parameterList.map { visit($0) }
return params.withBridgedArrayRef { ref in
FuncDecl_create(ctx, loc, false, loc, name, loc, false, nil, false, nil, loc, ref, loc, body, returnType, declContext)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wish we had argument labels here. Just an idea: You define FuncDecl_create on the C++ side and on the Swift side you define

struct FuncDecl {
  var pointer: UnsafeMutableRawPointer

  init(<all the parameters>) {
    self.pointer = FuncDecl_create(<forward all the parameters>)
  }
}

The downside is that if you modify the parameters on FuncDecl_create, you also need to modify them on FuncDecl.init. But we could maybe also use FuncDecl as a typed wrapper instead of UnsafeMutableRawPointer in ASTGen.

And another thought: It might be good to namespace these types in an enum AST so they don’t conflict with types from SwiftSyntax or SwiftSyntaxBuilder.

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look more into details, but from a layering perspective I don't think this ASTGen library belongs in the swift-syntax package. It's too specific to the compiler itself, since it is layered on top of the compiler's (C++) AST representation, so it's a layering violation to have it here.

@zoecarver
Copy link
Contributor Author

Closing this patch as I don't think we need it anymore.

@zoecarver zoecarver closed this Sep 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants