RFC: AST representation for coroutine types

SIL now has some minimal representational support for coroutines, and while there's significantly more to do there, I'd like to simultaneously kick off a conversation about how best to represent them at the AST level.

Only special kinds of declarations can be coroutines. In the first stage, that will exclusively be read/modify accessors; eventually we will add generators, but maybe not in Swift 5. These declarations are not referenced directly in the AST. In fact, accessor coroutine types never need to be first-class in the function type system, and generator types... well, that depends on the generator language design. Therefore, we could make this purely a special case in both SIL constant-type lowering and various places in Sema. However, I think it would be better to represent it more faithfully in the AST, in the interest of not creating unnecessary technical debt. Since coroutine types still wouldn't be first-class, we could still safely ignore them in systems like the constraint solver and runtime type metadata.

The basic structure of an accessor coroutine type is:
  - It can still have arbitrary parameters as normal (because of subscripts).
  - The result type is always ().
  - There is one yield.
  - The yield can be inout, owned, or shared.

It is reasonable to anticipate adding generators. Generators should be able to yield multiple values independently, each of them with its own ownership. (For example, an "enumerated" mutating generator could reasonably yield a shared index value and an inout element.) My preference, therefore, would be to structure the yields more like a parameter list, except hopefully without the unfortunate legacy paren/tuple representation. But we could also wait on generalizing the representation this way until we decide it's important; collection generators, after all, will only have one semantic yield.

John.

SIL now has some minimal representational support for coroutines, and while there's significantly more to do there, I'd like to simultaneously kick off a conversation about how best to represent them at the AST level.

Only special kinds of declarations can be coroutines. In the first stage, that will exclusively be read/modify accessors; eventually we will add generators, but maybe not in Swift 5. These declarations are not referenced directly in the AST. In fact, accessor coroutine types never need to be first-class in the function type system, and generator types... well, that depends on the generator language design. Therefore, we could make this purely a special case in both SIL constant-type lowering and various places in Sema. However, I think it would be better to represent it more faithfully in the AST, in the interest of not creating unnecessary technical debt. Since coroutine types still wouldn't be first-class, we could still safely ignore them in systems like the constraint solver and runtime type metadata.

I don't have a full sense of the tradeoffs yet, and you've probably thought about the tradeoffs a lot more, but my initial reaction to this is a bit hesitant. The other places we have not-really-first-class types in the type system, like InoutType and LValueType, have been sources of tech debt in their own way when they occasionally manage to leak out of their intended corners. Maybe that's not a concern so much since you can't ever directly reference an accessor decl in expressions.

The basic structure of an accessor coroutine type is:
  - It can still have arbitrary parameters as normal (because of subscripts).
  - The result type is always ().
  - There is one yield.
  - The yield can be inout, owned, or shared.

It is reasonable to anticipate adding generators. Generators should be able to yield multiple values independently, each of them with its own ownership. (For example, an "enumerated" mutating generator could reasonably yield a shared index value and an inout element.) My preference, therefore, would be to structure the yields more like a parameter list, except hopefully without the unfortunate legacy paren/tuple representation. But we could also wait on generalizing the representation this way until we decide it's important; collection generators, after all, will only have one semantic yield.

Independent of the question of "do we want to represent their types in the AST", if the answer is "yes", this sounds like a reasonable design.

-Joe

···

On Nov 13, 2017, at 9:52 PM, John McCall via swift-dev <swift-dev@swift.org> wrote:

SIL now has some minimal representational support for coroutines, and while there's significantly more to do there, I'd like to simultaneously kick off a conversation about how best to represent them at the AST level.

Only special kinds of declarations can be coroutines. In the first stage, that will exclusively be read/modify accessors; eventually we will add generators, but maybe not in Swift 5. These declarations are not referenced directly in the AST. In fact, accessor coroutine types never need to be first-class in the function type system, and generator types... well, that depends on the generator language design. Therefore, we could make this purely a special case in both SIL constant-type lowering and various places in Sema. However, I think it would be better to represent it more faithfully in the AST, in the interest of not creating unnecessary technical debt. Since coroutine types still wouldn't be first-class, we could still safely ignore them in systems like the constraint solver and runtime type metadata.

I don't have a full sense of the tradeoffs yet, and you've probably thought about the tradeoffs a lot more, but my initial reaction to this is a bit hesitant. The other places we have not-really-first-class types in the type system, like InoutType and LValueType, have been sources of tech debt in their own way when they occasionally manage to leak out of their intended corners. Maybe that's not a concern so much since you can't ever directly reference an accessor decl in expressions.

Yeah, that's what I was thinking. Also, I don't think we're really relying on the types not being first-class. There's nothing conceptually wrong about making them first-class in the same way that there'd be something wrong with making InoutType first-class. We're just taking advantage of the fact that they aren't to avoid doing some work until we need to. My intuition is that the language design of generators will make generator coroutines first-class types, but that's not mandatory.

John.

···

On Nov 14, 2017, at 2:50 PM, Joe Groff <jgroff@apple.com> wrote:

On Nov 13, 2017, at 9:52 PM, John McCall via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

The basic structure of an accessor coroutine type is:
  - It can still have arbitrary parameters as normal (because of subscripts).
  - The result type is always ().
  - There is one yield.
  - The yield can be inout, owned, or shared.

It is reasonable to anticipate adding generators. Generators should be able to yield multiple values independently, each of them with its own ownership. (For example, an "enumerated" mutating generator could reasonably yield a shared index value and an inout element.) My preference, therefore, would be to structure the yields more like a parameter list, except hopefully without the unfortunate legacy paren/tuple representation. But we could also wait on generalizing the representation this way until we decide it's important; collection generators, after all, will only have one semantic yield.

Independent of the question of "do we want to represent their types in the AST", if the answer is "yes", this sounds like a reasonable design.

-Joe