From 8e27f3da0e82d141fe452d5c9c5db84016e87e53 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Thu, 15 Jun 2023 13:15:18 +0200 Subject: [PATCH 1/8] Update inference of closures with no context. --- resources/type-system/inference.md | 56 ++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index f33b439e78..05cfb1ff5d 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -6,6 +6,11 @@ Status: Draft ## CHANGELOG +2023.06.15 + - Adjust function literal return type inference to avoid spurious application + of `flatten`, and make sure return statements do not affect generator + functions. + 2022.05.12 - Define the notions of "constraint solution for a set of type variables" and "Grounded constraint solution for a set of type variables". These @@ -286,9 +291,9 @@ type schema `S` as follows: `Stream` for some `S1`, then the context type is `S1`. - If the function expression is declared `sync*` and `S` is of the form `Iterable` for some `S1`, then the context type is `S1`. - - Otherwise, without null safety, the context type is `FutureOr` + - Otherwise, without null safety, the context type is `flatten(T)` where `T` is the imposed return type schema; with null safety, the context - type is `FutureOr`. + type is `futureValueTypeSchema(S)`. The function **futureValueTypeSchema** is defined as follows: @@ -299,7 +304,7 @@ The function **futureValueTypeSchema** is defined as follows: - **futureValueTypeSchema**(`void`) = `void`. - **futureValueTypeSchema**(`dynamic`) = `dynamic`. - **futureValueTypeSchema**(`_`) = `_`. -- Otherwise, for all `S`, **futureValueTypeSchema**(`S`) = `Object?`. +- Otherwise, for all other `S`, **futureValueTypeSchema**(`S`) = `Object?`. _Note that it is a compile-time error unless the return type of an asynchronous non-generator function is a supertype of `Future`, which means that @@ -315,29 +320,44 @@ described below with a typing context as computed above. The actual returned type of a function literal with a block body is computed as follows. Let `T` be `Never` if every control path through the block exits the block without reaching the end of the block, as computed by the **definite -completion** analysis specified elsewhere. Let `T` be `Null` if any control +completion** analysis specified elsewhere, or if the function is a generator +function. +Let `T` be `Null` if the function is a non-generator function and any control path reaches the end of the block without exiting the block, as computed by the **definite completion** analysis specified elsewhere. Let `K` be the typing context for the function body as computed above from the imposed return type schema. - - For each `return e;` statement in the block, let `S` be the inferred type of - `e`, using the local type inference algorithm described below with typing - context `K`, and update `T` to be `UP(flatten(S), T)` if the enclosing - function is `async`, or `UP(S, T)` otherwise. - - For each `return;` statement in the block, update `T` to be `UP(Null, T)`. + - If the enclosing function is a non-`async` non-generator function, + for each `return e;` statement in the block, let `S` be the inferred type + of `e`, using the local type inference algorithm described below with typing + context `K`, and update `T` to be `UP(S, T)`. + - If the enclosing function is marekd `async`, for each `return e;` statement + in the block, let `S` be the inferred type of `e`, using the local type + inference algorithm described below with typing context `FutureOr`, + and update `T` to be `UP(flatten(S), T)`. + - If the enclosing function is a non-generator function, for each `return;` + statement in the block, update `T` to be `UP(Null, T)`. - For each `yield e;` statement in the block, let `S` be the inferred type of `e`, using the local type inference algorithm described below with typing context `K`, and update `T` to be `UP(S, T)`. - If the enclosing function is marked `sync*`, then for each `yield* e;` statement in the block, let `S` be the inferred type of `e`, using the local type inference algorithm described below with a typing context of - `Iterable`; let `E` be the type such that `Iterable` is a - super-interface of `S`; and update `T` to be `UP(E, T)`. + `Iterable`. If there exists a type `E` such that `Iterable` is a + super-interface of `S`, update `T` to be `UP(E, T)`. Otherwise update + `T` to be `UP(S, T)`. + _It is a compile-time error if *S* is not a assignable to + `Iterable`, so either *S* implements `Iterable`, or it is one of + `dynamic` or `Never`._ - If the enclosing function is marked `async*`, then for each `yield* e;` statement in the block, let `S` be the inferred type of `e`, using the local type inference algorithm described below with a typing context of - `Stream`; let `E` be the type such that `Stream` is a super-interface - of `S`; and update `T` to be `UP(E, T)`. + `Stream`. If there exists a type `E` such that `Stream` is a + super-interface of `S`, update `T` to be `UP(E, T)`. Otherwise update + `T` to be `UP(S, T)`. + _It is a compile-time error if *S* is not a assignable to + `Stream`, so either *S* implements `Iterable`, or it is one of + `dynamic` or `Never`._ The **actual returned type** of the function literal is the value of `T` after all `return` and `yield` statements in the block body have been considered. @@ -345,15 +365,15 @@ all `return` and `yield` statements in the block body have been considered. Let `T` be the **actual returned type** of a function literal as computed above. Let `R` be the greatest closure of the typing context `K` as computed above. -With null safety: if `R` is `void`, or the function literal is marked `async` -and `R` is `FutureOr`, let `S` be `void` (without null-safety: no special -treatment is applicable to `void`). +With null safety, if `R` is `void`, let `S` be `void` +_(without null-safety: no special treatment is applicable to `void`)_. -Otherwise, if `T <: R` then let `S` be `T`. Otherwise, let `S` be `R`. The +Otherwise (_without null safety or if `R` is not `void`_), +if `T <: R` then let `S` be `T`. Otherwise, let `S` be `R`. The inferred return type of the function literal is then defined as follows: - If the function literal is marked `async` then the inferred return type is - `Future`. + `Future`. - If the function literal is marked `async*` then the inferred return type is `Stream`. - If the function literal is marked `sync*` then the inferred return type is From 16190b78440e3ed9bd9e7042a2504209b3515f50 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 26 Jun 2023 14:19:22 +0200 Subject: [PATCH 2/8] Address comments. More details on Stream/Iterable. --- resources/type-system/inference.md | 196 +++++++++++++++++++---------- 1 file changed, 127 insertions(+), 69 deletions(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index 05cfb1ff5d..1f78169f1e 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -6,10 +6,12 @@ Status: Draft ## CHANGELOG -2023.06.15 +2023.06.24 - Adjust function literal return type inference to avoid spurious application of `flatten`, and make sure return statements do not affect generator functions. + - Specify the value context to use for generator functions for more + non-trivial return types (not just `Iterable` for some `X`). 2022.05.12 - Define the notions of "constraint solution for a set of type variables" and @@ -265,11 +267,11 @@ unlike normal fields, the initializer for a `late` field may reference `this`. Function literals which are inferred in an empty typing context (see below) are inferred using the declared type for all of their parameters. If a parameter has no declared type, it is treated as if it was declared with type `dynamic`. -Inference for each returned expression in the body of the function literal is -done in an empty typing context (see below). +Inference for each returned or yielded expression in the body of the function +literal is done with an empty _value context_ scheme (see below). Function literals which are inferred in an non-empty typing context where the -context type is a function type are inferred as described below. +context type is a function type are inferred as described here. Each parameter is assumed to have its declared type if present. If no type is declared for a parameter and there is a corresponding parameter in the context @@ -281,19 +283,21 @@ corresponding parameter in the context type schema, the variable is treated as having type `dynamic`. The return type of the context function type is used at several points during -inference. We refer to this type as the **imposed return type -schema**. Inference for each returned or yielded expression in the body of the -function literal is done using a context type derived from the imposed return -type schema `S` as follows: +inference. We refer to this type as the **imposed return type schema**. + +Inference for each returned or yielded expression in the body of the +function literal is done using a **value context** type scheme derived from the +imposed return type schema `S` as follows: + - If function is declared `async`, then without null safety, + the value context scheme is **flatten**(`S`); + with null safety the value context scheme is + **futureValueTypeSchema**(`S`), as defined below. + - If the function expression is declared `async*` then the value context + scheme is **streamElementTypeSchema**(`S`), as defined below. + - If the function expression is declared `sync*` then the value context + scheme is **iterableElementTypeSchema**(`S`), as defined below. - If the function expression is neither `async` nor a generator, then the - context type is `S`. - - If the function expression is declared `async*` and `S` is of the form - `Stream` for some `S1`, then the context type is `S1`. - - If the function expression is declared `sync*` and `S` is of the form - `Iterable` for some `S1`, then the context type is `S1`. - - Otherwise, without null safety, the context type is `flatten(T)` - where `T` is the imposed return type schema; with null safety, the context - type is `futureValueTypeSchema(S)`. + value context scheme is `S`. The function **futureValueTypeSchema** is defined as follows: @@ -306,70 +310,124 @@ The function **futureValueTypeSchema** is defined as follows: - **futureValueTypeSchema**(`_`) = `_`. - Otherwise, for all other `S`, **futureValueTypeSchema**(`S`) = `Object?`. -_Note that it is a compile-time error unless the return type of an asynchronous -non-generator function is a supertype of `Future`, which means that -the last case will only be applied when `S` is `Object` or a top type._ +The function **streamElementTypeSchema** is defined as follow: + +- **streamElementTypeSchema**(`S?`) = **streamElementTypeSchema**(`S`), + for all `S`. +- **streamElementTypeSchema**(`S*`) = **streamElementTypeSchema**(`S`), + for all `S`. +- **streamElementTypeSchema**(`FutureOr`) = **streamElementTypeSchema**(`S`), + for all `S`. +- **streamElementTypeSchema**(`Stream`) = `S`, for all `S`. +- **streamElementTypeSchema**(`void`) = `void` +- **streamElementTypeSchema**(`dynamic`) = `dynamic` +- **streamElementTypeSchema**(`_`) = `_` +- Otherwise, for all other `S`, **streamElementTypeSchema**(`S`) = `Object?`. + +The function **iterableElementTypeSchema** is defined as follow: + +- **iterableElementTypeSchema**(`S?`) = **iterableElementTypeSchema**(`S`), + for all `S`. +- **iterableElementTypeSchema**(`S*`) = **iterableElementTypeSchema**(`S`), + for all `S`. +- **iterableElementTypeSchema**(`FutureOr`) = + **iterableElementTypeSchema**(`S`), for all `S`. +- **iterableElementTypeSchema**(`Iterable`) = `S`, for all `S`. +- **iterableElementTypeSchema**(`void`) = `void` +- **iterableElementTypeSchema**(`dynamic`) = `dynamic` +- **iterableElementTypeSchema**(`_`) = `_` +- Otherwise, for all other `S`, **iterableElementTypeSchema**(`S`) = `Object?`. + +_Note that it is a compile-time error unless the return type of an +asynchronous non-generator function is a supertype of `Future`, +which means that the last case of **futureValueTypeScheme** will only +be applied when `S` is `Object`. +Similarly for `async*` and `sync*` functions whose return types +must be a supertypes of `Stream` and `Iterable`._ In order to infer the return type of a function literal, we first infer the -**actual returned type** of the function literal. - -The actual returned type of a function literal with an expression body is the -inferred type of the expression body, using the local type inference algorithm -described below with a typing context as computed above. - -The actual returned type of a function literal with a block body is computed as -follows. Let `T` be `Never` if every control path through the block exits the -block without reaching the end of the block, as computed by the **definite -completion** analysis specified elsewhere, or if the function is a generator -function. -Let `T` be `Null` if the function is a non-generator function and any control -path reaches the end of the block without exiting the block, as computed by the -**definite completion** analysis specified elsewhere. Let `K` be the typing -context for the function body as computed above from the imposed return type -schema. +**actual value type** of the function literal. _The actual value type +represents the types of the actual values returned by non-generator functions, +and the values emitted by generator functions._ + +Let `K` be the value context schema for the function body as computed above +from the imposed return type schema. +_When we refer to the _inferred type_ of an expression with a typing context, +it is the type inferred using the local type inference algorithm +described below._ + +The actual value type of a function literal with an expression body, `=> e`, +_(which cannot be a generator function)_ is computed as follows: + - If the enclosing function is marked `async`, + let `T` be the inferred type of the returned expession with `FutureOr` + as typing context. + The actual value type is **flatten**(`T`). + - If the enclosing function is not marked `async`, let `T` be the inferred + type of the returned expression with typing context `K`. + The actually value type is `T`. + +The actual value type of a function literal with a block body is computed as +follows. +Let `T` be `Never` if the function is a generator function, +or if every control path through the block exits the block without +reaching the end of the block, as computed by the **definite completion** +analysis specified elsewhere. +Otherwise _(the function is a non-generator function and at least +one control path reaches the end of the block)_ let `T` be `null`. + +Then process relevant statements of the block, one by one in source order, +to find a value type `V` for that statement. + - If the enclosing function is a non-`async` non-generator function, - for each `return e;` statement in the block, let `S` be the inferred type - of `e`, using the local type inference algorithm described below with typing - context `K`, and update `T` to be `UP(S, T)`. - - If the enclosing function is marekd `async`, for each `return e;` statement - in the block, let `S` be the inferred type of `e`, using the local type - inference algorithm described below with typing context `FutureOr`, - and update `T` to be `UP(flatten(S), T)`. - - If the enclosing function is a non-generator function, for each `return;` - statement in the block, update `T` to be `UP(Null, T)`. - - For each `yield e;` statement in the block, let `S` be the inferred type of - `e`, using the local type inference algorithm described below with typing - context `K`, and update `T` to be `UP(S, T)`. - - If the enclosing function is marked `sync*`, then for each `yield* e;` - statement in the block, let `S` be the inferred type of `e`, using the - local type inference algorithm described below with a typing context of - `Iterable`. If there exists a type `E` such that `Iterable` is a - super-interface of `S`, update `T` to be `UP(E, T)`. Otherwise update - `T` to be `UP(S, T)`. - _It is a compile-time error if *S* is not a assignable to - `Iterable`, so either *S* implements `Iterable`, or it is one of - `dynamic` or `Never`._ - - If the enclosing function is marked `async*`, then for each `yield* e;` - statement in the block, let `S` be the inferred type of `e`, using the - local type inference algorithm described below with a typing context of - `Stream`. If there exists a type `E` such that `Stream` is a - super-interface of `S`, update `T` to be `UP(E, T)`. Otherwise update - `T` to be `UP(S, T)`. - _It is a compile-time error if *S* is not a assignable to - `Stream`, so either *S* implements `Iterable`, or it is one of - `dynamic` or `Never`._ - -The **actual returned type** of the function literal is the value of `T` after + the relevant statements are `return;` or `return e;` statements. + - For a `return;` statement, let `V` be `Null`. + - For a `return e;` statement, let `V` be the inferred type of `e` with + `K` as context type scheme, using the local type inference algorithm + described below. + + - If the enclosing function is marked `async`, the relevant statements + are `return;` and `return e;` statements. + * For a `return;` statement, let `V` be `Null`. + * For a `return e;` statement, let `S` be the inferred type of `e` + with typing context `FutureOr`. Let `V` be **flatten**(`S`). + + - If the enclosing function is marked `sync*`, the relevant statements + are `yield e;` or `yield* e;` statement. + * For a `yield e;` statement, let `V` be the inferred type of `e` with + typing context `K`. + * For a `yield* e;` statement, let `S` be the inferred type of `e` with + typing context `Iterable`. + If `S` implements `Iterable` for some `R`, let `V` be `R`. + Otherwise let `V` be `S`. + _It is a compile-time error if `S` is not a assignable to + `Iterable`, so either `S` implements `Iterable`, + or it is one of `dynamic` or `Never`._ + + - If the enclosing function is marked `async*`, the relevant statements are + `yield e;` or `yield* e;` statements. + * For a `yield e;` statement, let `V` be the inferred type of `e` with + typing context `K`. + * For a `yield* e;` statement, let `S` be the inferred type of `e` with + typing context `Stream`. + If `S` implements `Stream` for some `R`, let `V` be `R`. + Otherwise let `V` be `S`. + _It is a compile-time error if `S` is not a assignable to + `Stream`, so either `S` implements `Stream`, + or it is one of `dynamic` or `Never`._ + +After processing each relevant statement, update `T` to be **UP**(`T`, `V`). + +The **actual value type** of the function literal is the value of `T` after all `return` and `yield` statements in the block body have been considered. -Let `T` be the **actual returned type** of a function literal as computed above. +Let `T` be the **actual value type** of a function literal as computed above. Let `R` be the greatest closure of the typing context `K` as computed above. With null safety, if `R` is `void`, let `S` be `void` _(without null-safety: no special treatment is applicable to `void`)_. Otherwise (_without null safety or if `R` is not `void`_), -if `T <: R` then let `S` be `T`. Otherwise, let `S` be `R`. The +if `T <: R` then let `S` be `T`, else let `S` be `R`. The inferred return type of the function literal is then defined as follows: - If the function literal is marked `async` then the inferred return type is From 87b09042b155748ecbcac718e766bb062c3b47bb Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 26 Jun 2023 15:50:43 +0200 Subject: [PATCH 3/8] Tweak phrasing --- resources/type-system/inference.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index 1f78169f1e..ccd081ae4d 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -418,7 +418,8 @@ to find a value type `V` for that statement. After processing each relevant statement, update `T` to be **UP**(`T`, `V`). The **actual value type** of the function literal is the value of `T` after -all `return` and `yield` statements in the block body have been considered. +all relevant `return` and `yield` statements in the block body have been +processed. Let `T` be the **actual value type** of a function literal as computed above. Let `R` be the greatest closure of the typing context `K` as computed above. From a23e4388ad9bb4f0d028bf0ab1b7025f86779568 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 26 Jun 2023 16:02:56 +0200 Subject: [PATCH 4/8] Use `_` as context type for async return instead of `FutureOr<_>` --- resources/type-system/inference.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index ccd081ae4d..cd5420e213 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -389,7 +389,9 @@ to find a value type `V` for that statement. are `return;` and `return e;` statements. * For a `return;` statement, let `V` be `Null`. * For a `return e;` statement, let `S` be the inferred type of `e` - with typing context `FutureOr`. Let `V` be **flatten**(`S`). + with typing context `_` if `K` is `_`, + and with typing context `FutureOr` if `K` is not `_`, + Let `V` be **flatten**(`S`). - If the enclosing function is marked `sync*`, the relevant statements are `yield e;` or `yield* e;` statement. From e34900914fa318b7459655cf30c483c97b3cb35f Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 21 Jul 2023 16:42:27 +0200 Subject: [PATCH 5/8] Addressed comments. --- resources/type-system/inference.md | 38 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index cd5420e213..6683771823 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -352,15 +352,15 @@ and the values emitted by generator functions._ Let `K` be the value context schema for the function body as computed above from the imposed return type schema. -_When we refer to the _inferred type_ of an expression with a typing context, -it is the type inferred using the local type inference algorithm -described below._ +_When we refer to the inferred type of an expression with a typing context, +it is the static type of the expression inferred using the local type inference +algorithm described below._ The actual value type of a function literal with an expression body, `=> e`, _(which cannot be a generator function)_ is computed as follows: - If the enclosing function is marked `async`, - let `T` be the inferred type of the returned expession with `FutureOr` - as typing context. + then let `T` be the inferred type of the returned expession with + `FutureOr` as typing context. The actual value type is **flatten**(`T`). - If the enclosing function is not marked `async`, let `T` be the inferred type of the returned expression with typing context `K`. @@ -380,17 +380,16 @@ to find a value type `V` for that statement. - If the enclosing function is a non-`async` non-generator function, the relevant statements are `return;` or `return e;` statements. - - For a `return;` statement, let `V` be `Null`. - - For a `return e;` statement, let `V` be the inferred type of `e` with - `K` as context type scheme, using the local type inference algorithm - described below. + * For a `return;` statement, let `V` be `Null`. + * For a `return e;` statement, let `V` be the inferred type of `e` with + `K` as context type scheme. - If the enclosing function is marked `async`, the relevant statements - are `return;` and `return e;` statements. + are `return;` and `return e;` statements. * For a `return;` statement, let `V` be `Null`. - * For a `return e;` statement, let `S` be the inferred type of `e` - with typing context `_` if `K` is `_`, - and with typing context `FutureOr` if `K` is not `_`, + * For a `return e;` statement, + then let `S` be the inferred type of `e` with typing context + `FutureOr`. Let `V` be **flatten**(`S`). - If the enclosing function is marked `sync*`, the relevant statements @@ -417,7 +416,7 @@ to find a value type `V` for that statement. `Stream`, so either `S` implements `Stream`, or it is one of `dynamic` or `Never`._ -After processing each relevant statement, update `T` to be **UP**(`T`, `V`). +After processing each relevant statement, update `T` to be **UP**(`V`, `T`). The **actual value type** of the function literal is the value of `T` after all relevant `return` and `yield` statements in the block body have been @@ -426,12 +425,17 @@ processed. Let `T` be the **actual value type** of a function literal as computed above. Let `R` be the greatest closure of the typing context `K` as computed above. +_Then compute the actual value/element type, `S`, to use in the inferred return +type of the function literal, based on the _actual value type_, but bounded +by the _typing context_, if any._ + With null safety, if `R` is `void`, let `S` be `void` _(without null-safety: no special treatment is applicable to `void`)_. -Otherwise (_without null safety or if `R` is not `void`_), -if `T <: R` then let `S` be `T`, else let `S` be `R`. The -inferred return type of the function literal is then defined as follows: +Otherwise (_if `R` is not `void`, or without null safety_), +if `T <: R` then let `S` be `T`, else let `S` be `R`. + +The inferred return type of the function literal is then defined as follows: - If the function literal is marked `async` then the inferred return type is `Future`. From 90b9b73d56ee269e088d186e8d21035e3782920f Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 7 Aug 2023 12:17:06 +0200 Subject: [PATCH 6/8] Replace "enclosing function" with "function literal". --- resources/type-system/inference.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index 6683771823..ee48236dfb 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -343,7 +343,8 @@ asynchronous non-generator function is a supertype of `Future`, which means that the last case of **futureValueTypeScheme** will only be applied when `S` is `Object`. Similarly for `async*` and `sync*` functions whose return types -must be a supertypes of `Stream` and `Iterable`._ +must be a supertypes of `Stream` and `Iterable`. +These requirements also prevent the return type from being a type variable._ In order to infer the return type of a function literal, we first infer the **actual value type** of the function literal. _The actual value type @@ -358,11 +359,11 @@ algorithm described below._ The actual value type of a function literal with an expression body, `=> e`, _(which cannot be a generator function)_ is computed as follows: - - If the enclosing function is marked `async`, + - If the function literal is marked `async`, then let `T` be the inferred type of the returned expession with `FutureOr` as typing context. The actual value type is **flatten**(`T`). - - If the enclosing function is not marked `async`, let `T` be the inferred + - If the function literal is not marked `async`, let `T` be the inferred type of the returned expression with typing context `K`. The actually value type is `T`. @@ -378,13 +379,13 @@ one control path reaches the end of the block)_ let `T` be `null`. Then process relevant statements of the block, one by one in source order, to find a value type `V` for that statement. - - If the enclosing function is a non-`async` non-generator function, + - If the funtion literal is a non-`async` non-generator function, the relevant statements are `return;` or `return e;` statements. * For a `return;` statement, let `V` be `Null`. * For a `return e;` statement, let `V` be the inferred type of `e` with - `K` as context type scheme. + typing context `K`. - - If the enclosing function is marked `async`, the relevant statements + - If the function literal is marked `async`, the relevant statements are `return;` and `return e;` statements. * For a `return;` statement, let `V` be `Null`. * For a `return e;` statement, @@ -392,7 +393,7 @@ to find a value type `V` for that statement. `FutureOr`. Let `V` be **flatten**(`S`). - - If the enclosing function is marked `sync*`, the relevant statements + - If the function literal is marked `sync*`, the relevant statements are `yield e;` or `yield* e;` statement. * For a `yield e;` statement, let `V` be the inferred type of `e` with typing context `K`. @@ -404,7 +405,7 @@ to find a value type `V` for that statement. `Iterable`, so either `S` implements `Iterable`, or it is one of `dynamic` or `Never`._ - - If the enclosing function is marked `async*`, the relevant statements are + - If the function literal is marked `async*`, the relevant statements are `yield e;` or `yield* e;` statements. * For a `yield e;` statement, let `V` be the inferred type of `e` with typing context `K`. @@ -419,15 +420,14 @@ to find a value type `V` for that statement. After processing each relevant statement, update `T` to be **UP**(`V`, `T`). The **actual value type** of the function literal is the value of `T` after -all relevant `return` and `yield` statements in the block body have been -processed. +all relevant statements in the block body have been processed. Let `T` be the **actual value type** of a function literal as computed above. Let `R` be the greatest closure of the typing context `K` as computed above. _Then compute the actual value/element type, `S`, to use in the inferred return -type of the function literal, based on the _actual value type_, but bounded -by the _typing context_, if any._ +type of the function literal, based on the actual value type, but bounded +by the typing context, if any._ With null safety, if `R` is `void`, let `S` be `void` _(without null-safety: no special treatment is applicable to `void`)_. @@ -443,7 +443,8 @@ The inferred return type of the function literal is then defined as follows: `Stream`. - If the function literal is marked `sync*` then the inferred return type is `Iterable`. - - Otherwise, the inferred return type is `S`. + - Otherwise, _a synchronous non-generator function_, the inferred return + type is `S`. ## Local return type inference. From 42dd5cf08228b3b9df3d9daa27d2df91f0616f03 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 19 Mar 2024 23:17:09 +0100 Subject: [PATCH 7/8] Update inference.md Typo. --- resources/type-system/inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index ee48236dfb..6159e990a6 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -360,7 +360,7 @@ algorithm described below._ The actual value type of a function literal with an expression body, `=> e`, _(which cannot be a generator function)_ is computed as follows: - If the function literal is marked `async`, - then let `T` be the inferred type of the returned expession with + then let `T` be the inferred type of the returned expression with `FutureOr` as typing context. The actual value type is **flatten**(`T`). - If the function literal is not marked `async`, let `T` be the inferred From fe4b6b12546357ff23f1cc543f13cdf36c4c4168 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 19 Mar 2024 23:19:44 +0100 Subject: [PATCH 8/8] Update inference.md Typo. --- resources/type-system/inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/type-system/inference.md b/resources/type-system/inference.md index 6159e990a6..2f6b93180e 100644 --- a/resources/type-system/inference.md +++ b/resources/type-system/inference.md @@ -379,7 +379,7 @@ one control path reaches the end of the block)_ let `T` be `null`. Then process relevant statements of the block, one by one in source order, to find a value type `V` for that statement. - - If the funtion literal is a non-`async` non-generator function, + - If the function literal is a non-`async` non-generator function, the relevant statements are `return;` or `return e;` statements. * For a `return;` statement, let `V` be `Null`. * For a `return e;` statement, let `V` be the inferred type of `e` with