Skip to content

Add AST representation for coroutines#85372

Open
asl wants to merge 12 commits intomainfrom
coro-ast-no-diff
Open

Add AST representation for coroutines#85372
asl wants to merge 12 commits intomainfrom
coro-ast-no-diff

Conversation

@asl
Copy link
Contributor

@asl asl commented Nov 6, 2025

This is a proof-of-concept PR adding AST representation for coroutines

Further background at https://forums.swift.org/t/pitch-yield-once-functions-first-class-coroutines/77081

@asl
Copy link
Contributor Author

asl commented Nov 6, 2025

@tkremenek @rjmccall Following discussions on LLVM Dev Meeting, this is a stripped-down version of #78508:

  • No autodiff-related parts
  • No silgen (besides the "standard" special path used for coroutine accessors)
  • Some other fixes for SIL optimizer is omitted

Essentially the PR contains AST-related bits for coroutine declarations and coroutine function types as well as required boilerplate for AST mangling, dumping, serialization, etc.

@xedin comments from the original PR were addressed as well.

@asl
Copy link
Contributor Author

asl commented Nov 6, 2025

Please test with following pull request: swiftlang/llvm-project#11396
@swift-ci please test

@asl asl force-pushed the coro-ast-no-diff branch from 8805213 to 29ab221 Compare November 13, 2025 07:01
@asl
Copy link
Contributor Author

asl commented Nov 13, 2025

Please test with following pull request: swiftlang/llvm-project#11396
@swift-ci please test

@asl asl force-pushed the coro-ast-no-diff branch from 29ab221 to 28dadc4 Compare November 13, 2025 20:34
@asl
Copy link
Contributor Author

asl commented Nov 13, 2025

Please test with following pull request: swiftlang/llvm-project#11396
@swift-ci please test

@asl
Copy link
Contributor Author

asl commented Nov 14, 2025

Please test with following pull request: swiftlang/llvm-project#11396
@swift-ci please test windows platform

@asl
Copy link
Contributor Author

asl commented Jan 5, 2026

swiftlang/swift-syntax#3225
@swift-ci please test

@asl
Copy link
Contributor Author

asl commented Jan 5, 2026

@slavapestov @rjmccall

The updated PR contains the support for yields w/o dedicated yield result type (I added them on top of the previous attempts to show the approach).

Things to note:

  1. There are no silgen changes here (as we discussed), though I have ported them separately and verified that removing special path for coroutine accessors and treating them as "ordinary coroutines" does not lead to any test breakage
  2. Function types support multiple yields (there was some initial boilerplate for this there), but I decided to postpone decision how we'd represent multiple yields for declarations (similar to error types), so declarations only support a single yield for now.
  3. It turned out that swift syntax parser treats function result arrow as a part of result clause, so to minimize amount of changes I decided to use for yields the syntax similar to the throws, so the yielded type uses contextual yields keyword that (if present) should be after after specifiers like throws or async
  4. Function type construction always requires yield types to be passed (even if empty), so they won't forgotten by accident. Though, the default implementation w/o yields could be added (I left it commented out for now)
  5. Everything is hidden behind dedicated experimental flag

Please let me know if there is something that should be changed / done differently in this approach.

@asl asl requested a review from rjmccall January 5, 2026 22:25
@asl asl force-pushed the coro-ast-no-diff branch from d5b8b73 to e336b6a Compare January 5, 2026 22:37
@asl
Copy link
Contributor Author

asl commented Jan 5, 2026

swiftlang/swift-syntax#3225
@swift-ci please test

@asl
Copy link
Contributor Author

asl commented Jan 6, 2026

@asl asl force-pushed the coro-ast-no-diff branch from e336b6a to 1c28062 Compare January 6, 2026 20:28
@asl
Copy link
Contributor Author

asl commented Jan 6, 2026

@asl
Copy link
Contributor Author

asl commented Jan 7, 2026

swiftlang/swift-syntax#3225 swiftlang/llvm-project#12064
@swift-ci please test macos platform

@asl
Copy link
Contributor Author

asl commented Jan 8, 2026

Looks like main is broken:

[2026-01-08T05:03:21.576Z] /Users/ec2-user/jenkins/workspace/swift-PR-macos/branch-main/swift/stdlib/public/core/StaticPrint.swift:866:29: error: expression uses unsafe constructs but is not marked with 'unsafe' [#]8;;https://docs.swift.org/compiler/documentation/diagnostics/strict-memory-safety\StrictMemorySafety]8;;\]
[2026-01-08T05:03:21.576Z] 864 |   let argumentClosures = message.interpolation.arguments.argumentClosures
[2026-01-08T05:03:21.576Z] 865 |   if Bool(_builtinBooleanLiteral: Builtin.ifdef_SWIFT_STDLIB_PRINT_DISABLED()) { return }
[2026-01-08T05:03:21.576Z] 866 |   let formatStringPointer = _getGlobalStringTablePointer(formatString)
[2026-01-08T05:03:21.576Z]     |                             |- error: expression uses unsafe constructs but is not marked with 'unsafe' [#]8;;https://docs.swift.org/compiler/documentation/diagnostics/strict-memory-safety\StrictMemorySafety]8;;\]
[2026-01-08T05:03:21.576Z]     |                             `- note: reference to global function '_getGlobalStringTablePointer' involves unsafe type 'UnsafePointer<CChar>' (aka 'UnsafePointer<Int8>')
[2026-01-08T05:03:21.576Z] 867 |   unsafe constant_vprintf_backend(
[2026-01-08T05:03:21.576Z] 868 |     fmt: formatStringPointer,
[2026-01-08T05:03:21.576Z] 
[2026-01-08T05:03:21.576Z] [#StrictMemorySafety]: <https://docs.swift.org/compiler/documentation/diagnostics/strict-memory-safety>
[2026-01-08T05:03:21.576Z] ninja: build stopped: subcommand failed.
[2026-01-08T05:03:21.576Z] ERROR: command `['env', '/usr/local/bin/cmake', '--build', '/Users/ec2-user/jenkins/workspace/swift-PR-macos/branch-main/build/buildbot_incremental/minimalstdlib-macosx-x86_64', '--config', 

@asl
Copy link
Contributor Author

asl commented Jan 8, 2026

swiftlang/swift-syntax#3225 swiftlang/llvm-project#12064
@swift-ci please test macos platform

Copy link
Contributor

@rjmccall rjmccall left a comment

Choose a reason for hiding this comment

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

I think just worrying about the AST part of this is fine for now, yeah. This seems like good progress.

HasSendingResult = 0x00000010U,

// Values if we have any yields
IsCoroutine = 0x00000020U,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you really need runtime metadata support? I don't think there's anything wrong with the code you're adding here, but I don't know if I'm happy about baking ABI support into the runtime for a feature we haven't actually added to the language.

Here (and many other places), I would like to avoid baking in that "coroutine" means this specific kind of coroutine. We do have other coroutine-like features, and I think we will probably add more. "YieldOnceCoroutine" is unambiguous.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, just to clarify – in other places you'd also want to have, say FunctionType::isYieldOnceCoroutine()? This looks different from e.g. SIL level when SILFunctionType::isCoroutine() means any coroutine (being it yield_once or yield_many).

Copy link
Contributor

Choose a reason for hiding this comment

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

That's true. On the other hand, on SILFunctionType we do actually store a coroutine kind, and the calls to isCoroutine() really are all asking for any kind of coroutine. Maybe we just don't need to model that for AST coroutine kinds, though.


public:
/// 'Constructor' Factory Function
#if 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor residue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, no. As I mentioned, I have not decided whether to keep or not yield-less ctors. As one one hand they are less verbose, but on other hand make it easy to forget about yields leading to subtle unintended differences

Copy link
Contributor

@rjmccall rjmccall Jan 28, 2026

Choose a reason for hiding this comment

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

Yeah, it's tricky. I wonder if we can make most of the synthesis call sites call some subsystem-specific helper function instead of directly calling the main constructor. If we did that refactor, we'd be a lot freer to make these generalizations without worrying about making it absolutely awful to write and maintain AST synthesis code.

}

CanFunctionType substGenericArgs(SubstitutionMap subs) const;
#if 0
Copy link
Contributor

Choose a reason for hiding this comment

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

?

Out << "\n";
abort();
}
if (FT->hasExtInfo() && FT->isCoroutine()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (FT->hasExtInfo() && FT->isCoroutine()) {
if (FT->isCoroutine()) {

_label("operation",
_sending(_function(_async(_throws(_thick)), _typeparam(0),
_parameters())))),
_parameters(), _yields())))),
Copy link
Contributor

Choose a reason for hiding this comment

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

We should be able to figure out a way to not need a builder for the yields when we don't have any.

FunctionType::ExtInfo info;
auto innerFunction =
FunctionType::get(params, linearFnResultGen.build(builder), info);
FunctionType::get(params, {}, linearFnResultGen.build(builder), info);
Copy link
Contributor

Choose a reason for hiding this comment

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

It's unfortunate that we need this change in a million places. It was probably really nice for writing this patch that you had to fix a bunch of generic places to propagate and handle yields, but now that that's done, I wonder if we shouldn't just have a factory that defaults to not having any yields.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's exactly what I mentioned above. I do not know :) I'm worrying that the caller might forget about yields and it might lead to dropping yields should they present during various things here and there

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was also thinking to have, say FunctionType::getWithoutYields that would just pass empty yields, however, it looks like as verbose as just passing empty array ref...

@asl asl force-pushed the coro-ast-no-diff branch from 1c28062 to 4cdca9e Compare January 14, 2026 20:51
@asl
Copy link
Contributor Author

asl commented Jan 15, 2026

@asl
Copy link
Contributor Author

asl commented Jan 16, 2026

swiftlang/swift-syntax#3225 swiftlang/llvm-project#12064
@swift-ci please test macos platform

@asl asl requested a review from rjmccall January 19, 2026 19:36
@rjmccall
Copy link
Contributor

Hi, Anton. I'll try to get back to this in a week; I've been getting over a case of the flu and needing to budget my energy.

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