Skip to content

Add Sendable annotations to swift-syntax #2425

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

Merged
merged 6 commits into from
Jan 24, 2024

Conversation

ahoppen
Copy link
Member

@ahoppen ahoppen commented Jan 9, 2024

Audit all modules in swift-syntax, add Sendable annotations to the types that are sendable.

The audit uncovered that SyntaxArena was sent across actor boundaries even though SyntaxArena is not thread safe. I fixed these issues by:

  • Changing the semantics of ParsingSyntaxArena slightly: If you modify a parsed token and the new arena is a different one than the old arena (and thus might not be a ParsingSyntaxArena), we materialize it and its trivia. That way we no longer need to search through all child arenas to find a ParsingSyntaxArena and their parseTrivia function. It also fixes a potential correctness issue if the modified node is allocated in a ParsingSyntaxArena that has a different parseTrivia function than the original ParsingSyntaxArena.
  • Introducing RetainedSyntaxArena: In many cases where we were passing around a SyntaxArena, we just needed to ensure that the syntax arena stayed alive but never accessed any properties of it. RetainedSyntaxArena is an opaque wrapper around SyntaxArena and even though SyntaxArena is not sendable, we can send this type across actor boundaries because it doesn’t allow access to the internal state of SyntaxArena.
  • Introducing SyntaxArenaAllocated(Buffer)Pointer: This is a wrapper around Unsafe(Buffer)Pointer that indicates that the buffer is allocated in a syntax arena. Because the syntax arena will always outlive any syntax nodes that reference its contents, we know that the buffer's contents won't get deallocated while being accessed and thus we can add an unchecked Sendable conformance to it. Unsafe(Buffer)Pointer is not sendable by default.
  • There is one concurrency issue remaining when setting/accessing SyntaxArena.hasParent, which already had a FIXME associated with it. This PR won’t make the situation any worse than it is today, so I just moved the FIXME but we should fix it in a follow-up PR.

rdar://117813694

@ahoppen ahoppen changed the title WIP: Add Sendable annotations to swift-syntax Add Sendable annotations to swift-syntax Jan 11, 2024
@ahoppen ahoppen force-pushed the ahoppen/sendable-conformances branch from 4fb95c4 to aa91cfc Compare January 11, 2024 21:16
@ahoppen
Copy link
Member Author

ahoppen commented Jan 11, 2024

@swift-ci Please test

@ahoppen ahoppen force-pushed the ahoppen/sendable-conformances branch from aa91cfc to 485f166 Compare January 12, 2024 23:34
grynspan added a commit to swiftlang/swift-testing that referenced this pull request Jan 17, 2024
We are getting a new warning in CI:

```
.../swift-testing/Sources/TestingMacros/ConditionMacro.swift:46:13: warning: let '_sourceLocationLabel' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is an error in Swift 6
private let _sourceLocationLabel = TokenSyntax.identifier("sourceLocation")
            ^
```

Work around it by using `private var ... {}` instead of `private let`. I'm assuming that creating a `TokenSyntax` is not prohibitively expensive, so the change should be safe.

@ahoppen confirms that this operation is reasonably cheap and that syntax nodes are not safely sendable in the 5.9 tag of swift-syntax, so the warning is technically valid. Although our code does not access these constants across concurrency domains, the compiler is (in theory) free to invoke our macro expansion code on multiple tasks concurrently.

Concurrency safety in swift-syntax is tracked with swiftlang/swift-syntax#2425.
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.

Other than the silly nitpick, LGTM. Would be good to have @rintaro take a look too though.

@ahoppen ahoppen force-pushed the ahoppen/sendable-conformances branch from 485f166 to 1388b09 Compare January 20, 2024 23:42
@ahoppen
Copy link
Member Author

ahoppen commented Jan 20, 2024

@swift-ci Please test

@ahoppen
Copy link
Member Author

ahoppen commented Jan 20, 2024

@swift-ci Please test Windows

Copy link
Member

@rintaro rintaro left a comment

Choose a reason for hiding this comment

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

I didn't check what types should be Sendable, but other than that, SyntaxArena/RawSyntax related changes looks good to me.

Comment on lines -207 to +219
payload.presence = newValue
return RawSyntax(arena: arena, payload: .parsedToken(payload))
if arena == self.raw.arenaReference {
payload.presence = newValue
return RawSyntax(arena: arena, payload: .parsedToken(payload))
}
// If the modified token is allocated in a different arena, it might have
// a different or no `parseTrivia` function. We thus cannot use a
// `parsedToken` anymore.
return .makeMaterializedToken(
kind: formKind(),
leadingTrivia: formLeadingTrivia(),
trailingTrivia: formTrailingTrivia(),
presence: newValue,
tokenDiagnostic: tokenDiagnostic,
arena: arena
)
Copy link
Member

Choose a reason for hiding this comment

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

Just to make sure I understand correctly, this should be reverted when SwiftParser is merged, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

I’d prefer to keep it even if/when we merge SwiftSyntax + SwiftParser. Technically, the old implementation is not correct because the new arena could have a different parseTriviaFunction than the arena the node is allocated in and thus the trivia pieces could change here if we don’t materialize them.

@ahoppen ahoppen merged commit 861c6e0 into swiftlang:main Jan 24, 2024
@ahoppen ahoppen deleted the ahoppen/sendable-conformances branch January 24, 2024 05:49
ahoppen added a commit to ahoppen/swift-syntax that referenced this pull request Jan 26, 2024
…le-conformances"

This reverts commit 861c6e0, reversing
changes made to 911284b.

The PR seems to have had a ~2x performance impact on swift-syntax parsing time.
shahmishal added a commit that referenced this pull request Jan 26, 2024
…ances

Revert "Merge pull request #2425 from ahoppen/ahoppen/sendable-conformances"
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.

3 participants