Skip to content
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

ASTGen: An assortment of strides and improvements #67111

Merged
merged 32 commits into from
Sep 16, 2023

Conversation

AnthonyLatsis
Copy link
Collaborator

This pull request focuses on declaration translation, extrapolating preparatory work to the rest of the module for consistency and testing.

Comment on lines +2 to +5
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -dump-parse -disable-availability-checking -enable-experimental-feature ParserASTGen > %t/astgen.ast
// RUN: %target-swift-frontend %s -dump-parse -disable-availability-checking > %t/cpp-parser.ast
// RUN: %diff -u %t/astgen.ast %t/cpp-parser.ast
Copy link
Collaborator Author

@AnthonyLatsis AnthonyLatsis Jul 4, 2023

Choose a reason for hiding this comment

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

This is of course ersatz diffing — we ought to implement an AST matcher.

Copy link
Member

Choose a reason for hiding this comment

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

We should, but it's a big task and the approach you're using here can provide real value along the way.

@slavapestov
Copy link
Contributor

Nice!

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test

@AnthonyLatsis

This comment was marked as duplicate.

@AnthonyLatsis

This comment was marked as duplicate.

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

Copy link
Contributor

@bnbarham bnbarham left a comment

Choose a reason for hiding this comment

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

Thanks @AnthonyLatsis. Big fan of your operator test choices :)

lib/ASTGen/Sources/ASTGen/Misc.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Generics.swift Show resolved Hide resolved
include/swift/AST/CASTBridging.h Outdated Show resolved Hide resolved

// REQUIRES: executable_test

// -enable-experimental-feature requires an asserts build
// REQUIRES: asserts

func test1(x: Int, fn: (Int) -> Int) -> Int {
let xx = fn(42)
// NB: Ridiculous formatting to test that we do not include leading trivia in locations.
Copy link
Contributor

Choose a reason for hiding this comment

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

Could use inline comments instead , I'm not sure which is more readable though to be honest.

include/swift/AST/CASTBridging.h Show resolved Hide resolved
lib/AST/Decl.cpp Outdated Show resolved Hide resolved
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.

This is really quite awesome! A few minor comments here and there, but I love that you've increased the coverage of ASTGen so far.

It would be fun to follow this up with the infrastructure to redirect the C++ parser over to ASTGen for specific declaration kinds (e.g., import decls, operators, other small stuff), like I did in #66033, which lets you run a whole bunch of code through a narrow slice of ASTGen.

lib/AST/ASTDumper.cpp Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Decls.swift Outdated Show resolved Hide resolved
case .keyword(.none): self = .none
case .keyword(.left): self = .left
case .keyword(.right): self = .right
default: fatalError("Unknown precedence group associativity value")
Copy link
Member

Choose a reason for hiding this comment

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

Should this produce a diagnostic rather than failing? The incoming syntax tree could be ill-formed.

Copy link
Collaborator Author

@AnthonyLatsis AnthonyLatsis Jul 5, 2023

Choose a reason for hiding this comment

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

Do we not expect a SwiftParserDiagnostics pass to catch this sort of thing? Why do we want to allow for translation of ill-formed syntax trees?

Copy link
Contributor

Choose a reason for hiding this comment

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

ASTGen should handle any swift-syntax tree, whether from the parser or not. There's a few cases that currently have ! that really should not as well. @ahoppen as I know he has strong opinions on this :)

Copy link
Collaborator Author

@AnthonyLatsis AnthonyLatsis Jul 7, 2023

Choose a reason for hiding this comment

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

I see, so what we want is to simply emit a generic error about the syntax tree being invalid, I suppose? We don’t seem to currently have a way to bail out without constructing a node either.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ASTGen should handle any swift-syntax tree, whether from the parser or not. There's a few cases that currently have ! that really should not as well.

Does SwiftSyntax have a way to verify nodes? We could have an interceptor or verifier pre-pass rather than having to recover from every corner where the model is too permissive in the middle of translation.

Copy link
Member

Choose a reason for hiding this comment

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

Edit: I just thought about this a little bit more and it’s hard to model the groupAttributes with an optional child for each of the attribute kinds because we don’t enforce an order of the group attributes. I.e. we need to be able to represent both

precedencegroup T {
  assignment: true
  higherThan: AdditionPrecedence
}

and

precedencegroup T {
  higherThan: AdditionPrecedence
  assignment: true
}

And that basically requires a SyntaxCollection, unfortunately.

Copy link
Collaborator Author

@AnthonyLatsis AnthonyLatsis Sep 6, 2023

Choose a reason for hiding this comment

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

Ah, right. How ugly would it be to not actually enforce their order in the layout? Say, the initializer always inserts the attributes per the parameter order, but one could pass nils in the initializer and then insert the attributes in the appropriate order via the setters if necessary. The getters and setters would then work with a specific subrange of the layout of size 4 which is nilled out by default.

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 entirely sure what you mean. Do you have a short example of how you think the API should look like?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

An optional child for each attribute kind as originally suggested, with accessors similar to these:

  public var higherThanRelation: PrecedenceGroupRelationSyntax? {
      get {
        for index in 11...14 {
          guard let childData = data.child(at: index) else {
            // No data for this attribute (attributes are stored contiguously, i.e., no 'nil' data in between attributes).
            break
          }

          if let value = PrecedenceGroupRelationSyntax(Syntax(childData)) {
            return value
          }
        }

        return nil
      }

      set(value) {
        for index in 11...14 {
          guard let rawChild = data.rawChild(at: index) else {
            // No data for this attribute (attributes are stored contiguously, i.e., no 'nil' data in between attributes).
            // Stick it in at this index.
            self = PrecedenceGroupDeclSyntax(data.replacingChild(at: index, with: value?.data, arena: SyntaxArena()))
            return
          }

          if rawChild.kind == .precedenceGroupRelation {
            if let value {
              // Simply replace the data at this index.
              self = PrecedenceGroupDeclSyntax(data.replacingChild(at: index, with: value.data, arena: SyntaxArena()))
              return
            } else {
              self = PrecedenceGroupDeclSyntax(data.replacingChild(at: index, with: nil, andShiftingItTo: 14, arena: SyntaxArena()))
            }
          }
        }
      }
    }
  }

Another possibility is to statically feature the 4 optional child attributes in PrecedenceGroupAttributeListSyntax, as they would have been in PredenceGroupDeclSyntax, perhaps retaining the conformance to SyntaxCollection. This way no changes to PredenceGroupDeclSyntax are necessary.

Copy link
Member

Choose a reason for hiding this comment

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

An optional child for each attribute kind as originally suggested, with accessors similar to these.

The issue I see with this design is that a client of SwiftSyntax might create a PrecedenceGroupDeclSyntax that has two higherThan relations in the groupAttributes and the higherThanRelation getter would only return the first.

Similarly, if you set a higherThan relation using this setter, you can’t control where it is being inserted if no higherThan relation exists yet. So

let pg = PrecedenceGroupDeclSyntax()
pg.higherThanRelation = // …
pg.assignment = // ...

would give you a different node than

let pg = PrecedenceGroupDeclSyntax()
pg.assignment = // ...
pg.higherThanRelation = // …

and that’s quite surprising IMO.

func visit(_ node: PrecedenceGroupDeclSyntax) -> ASTNode {
let (name, nameLoc) = self.bridgedIdentifierAndSourceLoc(for: node.identifier)

struct PrecedenceGroupBody {
Copy link
Member

Choose a reason for hiding this comment

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

For precedence groups and operators, I wonder if we should instead build on top of the SwiftOperators library, which has a mapping from the syntax tree into a more semantic representation (Operator and PrecedenceGroup).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Apparently the inits from SwiftOperators assume the nodes are valid, so we would have to do most of the same work in ASTGen to prevalidate the nodes and diagnose issues before calling these inits, like switching on the fixity of an operator or iterating over the attributes of a precedence group. Do you believe there is something to gain from building on top of SwiftOperators regardless?

lib/ASTGen/Sources/ASTGen/Decls.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Exprs.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Literals.swift Outdated Show resolved Hide resolved
@ahoppen
Copy link
Member

ahoppen commented Jul 7, 2023

Just a quick comment: I’d like to review the PR but won’t get to it until Monday/Tuesday. I’d appreciate if you could leave it open until then.

@AnthonyLatsis
Copy link
Collaborator Author

I’d like to review the PR but won’t get to it until Monday/Tuesday. I’d appreciate if you could leave it open until then.

No problem! I would hate to land this without a review from you.

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Here’s my first round of review. You’re getting a bit of my complaints with the current ASTGen design but I think that I managed to keep my comments somewhat focused so that I don’t require you to do a massive refactoring first.

lib/AST/CASTBridging.cpp Outdated Show resolved Hide resolved
lib/AST/CASTBridging.cpp Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Misc.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Decls.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Decls.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Decls.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/Exprs.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/ParameterClause.swift Outdated Show resolved Hide resolved
lib/ASTGen/Sources/ASTGen/ParameterClause.swift Outdated Show resolved Hide resolved
Comment on lines 249 to 287
Name: name,
NameLoc: nameLoc,
SecondName: secondName,
SecondNameLoc: secondNameLoc,
UnderscoreLoc: nil, /*N.B. Only important for SIL*/
ColonLoc: self.bridgedSourceLoc(for: element.colon),
Type: type,
TrailingCommaLoc: self.bridgedSourceLoc(for: element.trailingComma)
Copy link
Member

Choose a reason for hiding this comment

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

Just me complaining, no action necessary for you in this PR: I know that you didn’t introduce BridgedTupleTypeElement but the members of it should really be lowercase so I don’t need to look at uppercase parameter labels in Swift.

@xedin xedin removed their request for review July 10, 2023 17:06
@AnthonyLatsis AnthonyLatsis force-pushed the astgen-decls branch 2 times, most recently from b0741a2 to 942f06c Compare September 5, 2023 16:09
@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test macOS

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please smoke test

@ahoppen
Copy link
Member

ahoppen commented Sep 14, 2023

@swift-ci Please test Windows

@AnthonyLatsis
Copy link
Collaborator Author

@ahoppen We have a problem with swift_name on Windows. I thought we were using the just-built clang to compile the Swift compiler, but apparently not.

@AnthonyLatsis
Copy link
Collaborator Author

@swift-ci please test Windows

@AnthonyLatsis
Copy link
Collaborator Author

I take it the conditional def works because we skip the early swift-syntax on Windows. Is this an OK workaround?

@bnbarham
Copy link
Contributor

I take it the conditional def works because we skip the early swift-syntax on Windows. Is this an OK workaround?

That's not going to be true very soon, but regardless the conditional def seems fine to me - it isn't needed when compiling C++, only when importing into Swift (in which case, it will be defined). Unless I'm misunderstanding something crucial here?

@ahoppen
Copy link
Member

ahoppen commented Sep 16, 2023

@swift-ci Please test

@AnthonyLatsis
Copy link
Collaborator Author

AnthonyLatsis commented Sep 16, 2023

it isn't needed when compiling C++, only when importing into Swift (in which case, it will be defined). Unless I'm misunderstanding something crucial here?

Nah, actually, thank you, I had a brain fart. I just realized the importer invokes clang to parse headers (so of course the attribute will be defined when importing into Swift). For some reason I had this silly impression that it would be reading a normalized MSVC AST on Windows by default, even though I knew the importer reads clang ASTs 🙃

@ahoppen ahoppen merged commit 44a4d69 into swiftlang:main Sep 16, 2023
@AnthonyLatsis AnthonyLatsis deleted the astgen-decls branch September 16, 2023 21:27
@AnthonyLatsis
Copy link
Collaborator Author

@ahoppen @DougGregor @bnbarham @hamishknight Thank you so much for weighing in!

@bnbarham
Copy link
Contributor

Woo, it's in! Thanks for all the work here @AnthonyLatsis!

@ahoppen
Copy link
Member

ahoppen commented Sep 18, 2023

Thank you for doing all this work! 🙏🏽

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.

7 participants