Skip to content

[Syntax] Introduce SyntaxLayout - #2824

Draft
rintaro wants to merge 5 commits into
swiftlang:mainfrom
rintaro:syntaxlayout
Draft

rintaro wants to merge 5 commits into
swiftlang:mainfrom
rintaro:syntaxlayout

Conversation

@rintaro

@rintaro rintaro commented Aug 26, 2024 •

Copy link
Copy Markdown
Member

Types:

  • SyntaxLayout<T>: represents the layout of the T syntax layout type
  • SyntaxLayoutProperty: represents an element in SyntaxLayout. like AnyKeyPath
  • ConcreteSyntaxProperty<Base, Value>: represent a typed property. like KeyPath<Root, Value>

API:

  • _LeafSyntaxNodeProtocol.syntaxKind to get the syntax kind from the meta type.
  • _LayoutSyntaxNodeProtocol protocol for non-collection leaf syntax nodes
  • _LayoutSyntaxNodeProtocol.layout to get the layout (SyntaxLayout<Self>)

Usage:

func usage(node: some SyntaxProtocol) {
  node.propertyInParent == FuncDeclSyntax.layout[.funcKeyword]
}

This is faster than keyPathInParent and comparing KeyPath. Combined with #2842, BasicFormat performs 30% faster

@rintaro
rintaro force-pushed the syntaxlayout branch 2 times, most recently from a8678db to 0ae17dc Compare September 10, 2024 13:46
@rintaro

rintaro commented Sep 10, 2024

Copy link
Copy Markdown
Member Author

@swift-ci Please test

@rintaro
rintaro marked this pull request as ready for review September 10, 2024 14:38
@rintaro

rintaro commented Sep 10, 2024

Copy link
Copy Markdown
Member Author

@swift-ci Please test

@rintaro
rintaro force-pushed the syntaxlayout branch 7 times, most recently from 341f740 to bcf256e Compare January 21, 2025 17:20
This replaces most use case of `keyPathInParent` and
`SyntaxNodeStructure.layout`. Since getting and comparing `KeyPath` is
not trivial, `keyPathInParent` were suffered from performance issue,

Newly introduced `SyntaxProtocol.propertyInParent` is trivial, and
comparing it with a SyntaxLayoutProperty is also trivial.
'debugInitCall' handles collections by opening 'any SyntaxCollection', which
yields elements typed as the collection's 'Element' and so no longer needs the
element metatype from '.collection'. For layout nodes it walks the children and
identifies each slot with 'propertyInParent'. Only a child declared as a
'SyntaxChildChoices' type has to be wrapped in that type to be passed to the
parent's initializer; a child declared as a base type is taken as
'some XSyntaxProtocol' and is now emitted unwrapped. The generated
'childChoicesTypeName' names the types that do need wrapping.

'swift-parser-cli' enumerated the parseable node types by walking the
'.choices' of 'Syntax.structure'. It now uses 'syntaxParseableTypes', which
additionally covers the base types and the parseable collections.
…ition

Building with '-DSWIFTSYNTAX_NO_STRUCTURE_REFLECTION' drops
'SyntaxProtocol.structure', 'SyntaxProtocol.keyPathInParent' and
'SyntaxNodeStructure'. Clients that don't reflect over the static shape of a
node no longer pay for the key path literals and the metatype arrays that back
them: SwiftSyntax's object code shrinks by 1.87 MB (9.5%) and a binary linking
SwiftSyntax, SwiftParser, SwiftBasicFormat, SwiftIDEUtils and
SwiftParserDiagnostics loses 311 KB of '__TEXT' (6.8%).
'Syntax.parent' builds a 'Syntax', which retains and releases the arena.
'-O' pairs that up and removes it, but '-Onone' does not: reading
'dataRef.pointee.parent' instead takes the getter from 172 instructions and 5
'swift_release' calls down to 101 and 3, and leaves the '-O' code byte-identical.

Reading through 'dataRef' also avoids the 'Syntax.data' accessor, whose
'mark_dependence' on a copy of 'Syntax' fails SIL verification in
'OSSACompleteLifetime' at '-O'.

The one remaining 'swift_release' per call is the arena reference that the
'_syntaxNode' witness returns owned, on each of the two exit paths.
… ARC

'_syntaxNode' is a property requirement returning 'Syntax', so its witness hands
back an owned value and every caller balances a retain with a release. The new
requirement returns 'SyntaxDataRef', which is trivial and therefore has no
ownership convention at all, so no witness can imply ARC. Each node type
satisfies it by borrowing the trivial reference out of its stored '_syntaxNode'.

'propertyInParent' reads it instead of building a 'Syntax': 33 instructions and
one 'swift_release' per call become 28 and none.

Conforming types must now be able to see 'SyntaxDataRef', so a module that
conforms to 'SyntaxProtocol' has to import SwiftSyntax with '@_spi(RawSyntax)'.
The default implementation covers conformances that don't provide the property
themselves, but only once that import is in place.
@rintaro
rintaro marked this pull request as draft October 23, 2026 13:34
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.

1 participant