diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md index b2cc928257f..e57f88a88c2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md @@ -1184,3 +1184,154 @@ or `yieldAll`. Furthermore, we do not want to intercept their continuations. We If we look at `sequence`, the `SequenceScope` interface is annotated with the annotation. Since we do not want to intercept the continuations, their `context`s cannot be other than `EmptyCoroutineContext`. + +### Coroutine Superclasses + +Below is the diagram of all continuation classes, defined in the standard library and used by the compiler: +```text + +------------+ + |Continuation| + +------+-----+ + ^ + | ++---------+----------+ +|BaseContinuationImpl+<---------------+ ++---------+----------+ | + ^ | + | | + +-------+--------+ +-------------+------------+ + |ContinuationImpl| |RestrictedContinuationImpl| + +-------+--------+ +-------------+------------+ + ^ ^ + | | + +-----+-------+ +-----------+-----------+ + |SuspendLambda| |RestrictedSuspendLambda| + +-------------+ +-----------------------+ +``` + +The main superinterface of all coroutines is `Continuation`: +```kotlin +public interface Continuation { + public val context: CoroutineContext + public fun resumeWith(result: Result) +} +``` +which is the only interface accessible by users. It is, essentially, the core of the coroutines' machinery. With the continuation-passing +style, every suspending function and lambda accept additional continuation parameter. + +Every compiler generated continuation extends `BaseContinuationImpl`: +```kotlin +abstract class BaseContinuationImpl( + public val completion: Continuation? +) : Continuation, CoroutineStackFrame, Serializable { + public final override fun resumeWith(result: Result) + protected abstract fun invokeSuspend(result: Result): Any? + protected open fun releaseIntercepted() + public open fun create(completion: Continuation<*>): Continuation + public open fun create(value: Any?, completion: Continuation<*>): Continuation + public override fun toString(): String + public override val callerFrame: CoroutineStackFrame? + public override fun getStackTraceElement(): StackTraceElement? +} +``` +Note, its `resumeWith` function is final, but it introduces the `invokeSuspend` function. The `resumeWith` function does +the following: +1. When the user calls its `resumeWith`, it calls `invokeSuspend` with the argument, resuming the suspended coroutine with passed result or +exception. +2. It calls `completion` continuation's `resumeWith` when the coroutine completes so that execution returns to the caller. +3. It catches exceptions and resumes the `completion` with it, wrapped in `Result`, thus propagating the exception to the caller. + +Additionally, `resumeWith` calls `releaseIntercepted` upon coroutine's completion to clear the interceptor up. + +The compiler generates `create` overrides for suspending lambdas with zero or one parameter, since `createCoroutineUnintercepted` calls +them. + +The rest (`callerFrame` and `getStackTraceElement`) come from the `CoroutineStackFrame` interface, and the debugger and kotlinx.coroutines +library use the interface to generate async stack traces. + +The next class is `ContinuationImpl`. Every continuation of a suspending function, generated by the compiler, extends this class. Note that +the compiler does not generate restricted suspend functions (yet). +```kotlin +abstract class ContinuationImpl( + completion: Continuation?, + private val _context: CoroutineContext? +) : BaseContinuationImpl(completion) { + public override val context: CoroutineContext + protected override fun releaseIntercepted() + + private var intercepted: Continuation? + public fun intercepted(): Continuation +} +``` +It adds the `intercepted` field and `intercepted()` function covered in the corresponding section. + +For restricted suspend function, there is`RestrictedContinuationImpl` class, and thus their context can only be `EmptyCoroutineContext`. It +allows us to save several bytes when one calls `startCoroutine` on a suspending functional type, which does not inherit +`BaseContinuationImpl`. For example, when +the receiver is a callable reference to suspend functions +the context of root continuation, passed to `startCoroutine` is `EmptyCoroutineContext` +```kotlin +abstract class RestrictedContinuationImpl( + completion: Continuation? +) : BaseContinuationImpl(completion) { + public override val context: CoroutineContext +} +``` + +All non-restricted generated suspend lambdas extend `SuspendLambda`: +```kotlin +internal abstract class SuspendLambda( + public override val arity: Int, + completion: Continuation? +) : ContinuationImpl(completion), FunctionBase, SuspendFunction +``` +since all suspend lambdas are functional types, they implement the `FunctionBase` interface. `SuspendFunction` is a marker interface used in +type checks and type conversions (see the next subsection). + +All restricted generated suspend lambdas extend `RestrictedSuspendLambda`: +```kotlin +abstract class RestrictedSuspendLambda( + public override val arity: Int, + completion: Continuation? +) : RestrictedContinuationImpl(completion), FunctionBase, SuspendFunction +``` +the only difference from `SuspendLambda` is superclass. `SuspendLambda` inherits `ContinuationImpl`, while +`RestrictedSuspendLambda` inherits `RestrictedSuspendLambda`. + +#### SuspendFunction{N} + +Every suspending lambda has a special suspend functional type: `SuspendFunction{N}`, where `{N}` is the number of lambda parameters. They +only exist during compile-time and are changed to `Function{N+1}` and `SuspendFunction`. Because `SuspendFunction{N}` is not present at +runtime, there would be no way to distinguish ordinary functional type +from suspend functional type if we would not use `SuspendFunction` marker interface. To be more specific, it is used in +`is SuspendFunction{N}` and `as SuspendFunction{N}` expressions. For example, if we have code like +```kotlin +fun main() { + val lambda: suspend () -> Unit = {} + val a: Any = lambda + print(a is (suspend () -> Unit)) +} +``` +for `is` expression we generate something like +```kotlin +a is SuspendFunction and TypeIntrinsics.isFunctionOfArity(a, 1) +``` +That, by the way, is the reason why `SuspendLambda`'s constructor accepts arity. + +Of course, all generated suspend lambdas implement `SuspendFunction` through `SuspendLambda` and `RestrcitedSuspendLambda`. Callable +references to suspend functions implement the interface directly. + +#### 1.2: CoroutineImpl + +In experimental coroutines, the diagram is much simpler. +```text ++------------+ +|Continuation| ++------+-----+ + ^ + | ++------+------+ +|CoroutineImpl| ++-------------+ +``` +Every suspending function's continuation or suspend lambda extends `CoroutineImpl`.