Skip to content

Commit 3a0da49

Browse files
committed
Address comments. More details on Stream/Iterable.
1 parent 7c533bb commit 3a0da49

File tree

1 file changed

+127
-69
lines changed

1 file changed

+127
-69
lines changed

resources/type-system/inference.md

Lines changed: 127 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ Status: Draft
66

77
## CHANGELOG
88

9-
2023.06.15
9+
2023.06.24
1010
- Adjust function literal return type inference to avoid spurious application
1111
of `flatten`, and make sure return statements do not affect generator
1212
functions.
13+
- Specify the value context to use for generator functions for more
14+
non-trivial return types (not just `Iterable<X>` for some `X`).
1315

1416
2022.05.12
1517
- 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`.
265267
Function literals which are inferred in an empty typing context (see below) are
266268
inferred using the declared type for all of their parameters. If a parameter
267269
has no declared type, it is treated as if it was declared with type `dynamic`.
268-
Inference for each returned expression in the body of the function literal is
269-
done in an empty typing context (see below).
270+
Inference for each returned or yielded expression in the body of the function
271+
literal is done with an empty _value context_ scheme (see below).
270272

271273
Function literals which are inferred in an non-empty typing context where the
272-
context type is a function type are inferred as described below.
274+
context type is a function type are inferred as described here.
273275

274276
Each parameter is assumed to have its declared type if present. If no type is
275277
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
281283
having type `dynamic`.
282284

283285
The return type of the context function type is used at several points during
284-
inference. We refer to this type as the **imposed return type
285-
schema**. Inference for each returned or yielded expression in the body of the
286-
function literal is done using a context type derived from the imposed return
287-
type schema `S` as follows:
286+
inference. We refer to this type as the **imposed return type schema**.
287+
288+
Inference for each returned or yielded expression in the body of the
289+
function literal is done using a **value context** type scheme derived from the
290+
imposed return type schema `S` as follows:
291+
- If function is declared `async`, then without null safety,
292+
the value context scheme is **flatten**(`S`);
293+
with null safety the value context scheme is
294+
**futureValueTypeSchema**(`S`), as defined below.
295+
- If the function expression is declared `async*` then the value context
296+
scheme is **streamElementTypeSchema**(`S`), as defined below.
297+
- If the function expression is declared `sync*` then the value context
298+
scheme is **iterableElementTypeSchema**(`S`), as defined below.
288299
- If the function expression is neither `async` nor a generator, then the
289-
context type is `S`.
290-
- If the function expression is declared `async*` and `S` is of the form
291-
`Stream<S1>` for some `S1`, then the context type is `S1`.
292-
- If the function expression is declared `sync*` and `S` is of the form
293-
`Iterable<S1>` for some `S1`, then the context type is `S1`.
294-
- Otherwise, without null safety, the context type is `flatten(T)`
295-
where `T` is the imposed return type schema; with null safety, the context
296-
type is `futureValueTypeSchema(S)`.
300+
value context scheme is `S`.
297301

298302
The function **futureValueTypeSchema** is defined as follows:
299303

@@ -306,70 +310,124 @@ The function **futureValueTypeSchema** is defined as follows:
306310
- **futureValueTypeSchema**(`_`) = `_`.
307311
- Otherwise, for all other `S`, **futureValueTypeSchema**(`S`) = `Object?`.
308312

309-
_Note that it is a compile-time error unless the return type of an asynchronous
310-
non-generator function is a supertype of `Future<Never>`, which means that
311-
the last case will only be applied when `S` is `Object` or a top type._
313+
The function **streamElementTypeSchema** is defined as follow:
314+
315+
- **streamElementTypeSchema**(`S?`) = **streamElementTypeSchema**(`S`),
316+
for all `S`.
317+
- **streamElementTypeSchema**(`S*`) = **streamElementTypeSchema**(`S`),
318+
for all `S`.
319+
- **streamElementTypeSchema**(`FutureOr<S>`) = **streamElementTypeSchema**(`S`),
320+
for all `S`.
321+
- **streamElementTypeSchema**(`Stream<S>`) = `S`, for all `S`.
322+
- **streamElementTypeSchema**(`void`) = `void`
323+
- **streamElementTypeSchema**(`dynamic`) = `dynamic`
324+
- **streamElementTypeSchema**(`_`) = `_`
325+
- Otherwise, for all other `S`, **streamElementTypeSchema**(`S`) = `Object?`.
326+
327+
The function **iterableElementTypeSchema** is defined as follow:
328+
329+
- **iterableElementTypeSchema**(`S?`) = **iterableElementTypeSchema**(`S`),
330+
for all `S`.
331+
- **iterableElementTypeSchema**(`S*`) = **iterableElementTypeSchema**(`S`),
332+
for all `S`.
333+
- **iterableElementTypeSchema**(`FutureOr<S>`) =
334+
**iterableElementTypeSchema**(`S`), for all `S`.
335+
- **iterableElementTypeSchema**(`Iterable<S>`) = `S`, for all `S`.
336+
- **iterableElementTypeSchema**(`void`) = `void`
337+
- **iterableElementTypeSchema**(`dynamic`) = `dynamic`
338+
- **iterableElementTypeSchema**(`_`) = `_`
339+
- Otherwise, for all other `S`, **iterableElementTypeSchema**(`S`) = `Object?`.
340+
341+
_Note that it is a compile-time error unless the return type of an
342+
asynchronous non-generator function is a supertype of `Future<Never>`,
343+
which means that the last case of **futureValueTypeScheme** will only
344+
be applied when `S` is `Object`.
345+
Similarly for `async*` and `sync*` functions whose return types
346+
must be a supertypes of `Stream<Never>` and `Iterable<Never>`._
312347

313348
In order to infer the return type of a function literal, we first infer the
314-
**actual returned type** of the function literal.
315-
316-
The actual returned type of a function literal with an expression body is the
317-
inferred type of the expression body, using the local type inference algorithm
318-
described below with a typing context as computed above.
319-
320-
The actual returned type of a function literal with a block body is computed as
321-
follows. Let `T` be `Never` if every control path through the block exits the
322-
block without reaching the end of the block, as computed by the **definite
323-
completion** analysis specified elsewhere, or if the function is a generator
324-
function.
325-
Let `T` be `Null` if the function is a non-generator function and any control
326-
path reaches the end of the block without exiting the block, as computed by the
327-
**definite completion** analysis specified elsewhere. Let `K` be the typing
328-
context for the function body as computed above from the imposed return type
329-
schema.
349+
**actual value type** of the function literal. _The actual value type
350+
represents the types of the actual values returned by non-generator functions,
351+
and the values emitted by generator functions._
352+
353+
Let `K` be the value context schema for the function body as computed above
354+
from the imposed return type schema.
355+
_When we refer to the _inferred type_ of an expression with a typing context,
356+
it is the type inferred using the local type inference algorithm
357+
described below._
358+
359+
The actual value type of a function literal with an expression body, `=> e`,
360+
_(which cannot be a generator function)_ is computed as follows:
361+
- If the enclosing function is marked `async`,
362+
let `T` be the inferred type of the returned expession with `FutureOr<K>`
363+
as typing context.
364+
The actual value type is **flatten**(`T`).
365+
- If the enclosing function is not marked `async`, let `T` be the inferred
366+
type of the returned expression with typing context `K`.
367+
The actually value type is `T`.
368+
369+
The actual value type of a function literal with a block body is computed as
370+
follows.
371+
Let `T` be `Never` if the function is a generator function,
372+
or if every control path through the block exits the block without
373+
reaching the end of the block, as computed by the **definite completion**
374+
analysis specified elsewhere.
375+
Otherwise _(the function is a non-generator function and at least
376+
one control path reaches the end of the block)_ let `T` be `null`.
377+
378+
Then process relevant statements of the block, one by one in source order,
379+
to find a value type `V` for that statement.
380+
330381
- If the enclosing function is a non-`async` non-generator function,
331-
for each `return e;` statement in the block, let `S` be the inferred type
332-
of `e`, using the local type inference algorithm described below with typing
333-
context `K`, and update `T` to be `UP(S, T)`.
334-
- If the enclosing function is marekd `async`, for each `return e;` statement
335-
in the block, let `S` be the inferred type of `e`, using the local type
336-
inference algorithm described below with typing context `FutureOr<K>`,
337-
and update `T` to be `UP(flatten(S), T)`.
338-
- If the enclosing function is a non-generator function, for each `return;`
339-
statement in the block, update `T` to be `UP(Null, T)`.
340-
- For each `yield e;` statement in the block, let `S` be the inferred type of
341-
`e`, using the local type inference algorithm described below with typing
342-
context `K`, and update `T` to be `UP(S, T)`.
343-
- If the enclosing function is marked `sync*`, then for each `yield* e;`
344-
statement in the block, let `S` be the inferred type of `e`, using the
345-
local type inference algorithm described below with a typing context of
346-
`Iterable<K>`. If there exists a type `E` such that `Iterable<E>` is a
347-
super-interface of `S`, update `T` to be `UP(E, T)`. Otherwise update
348-
`T` to be `UP(S, T)`.
349-
_It is a compile-time error if *S* is not a assignable to
350-
`Iterable<Object?>`, so either *S* implements `Iterable`, or it is one of
351-
`dynamic` or `Never`._
352-
- If the enclosing function is marked `async*`, then for each `yield* e;`
353-
statement in the block, let `S` be the inferred type of `e`, using the
354-
local type inference algorithm described below with a typing context of
355-
`Stream<K>`. If there exists a type `E` such that `Stream<E>` is a
356-
super-interface of `S`, update `T` to be `UP(E, T)`. Otherwise update
357-
`T` to be `UP(S, T)`.
358-
_It is a compile-time error if *S* is not a assignable to
359-
`Stream<Object?>`, so either *S* implements `Iterable`, or it is one of
360-
`dynamic` or `Never`._
361-
362-
The **actual returned type** of the function literal is the value of `T` after
382+
the relevant statements are `return;` or `return e;` statements.
383+
- For a `return;` statement, let `V` be `Null`.
384+
- For a `return e;` statement, let `V` be the inferred type of `e` with
385+
`K` as context type scheme, using the local type inference algorithm
386+
described below.
387+
388+
- If the enclosing function is marked `async`, the relevant statements
389+
are `return;` and `return e;` statements.
390+
* For a `return;` statement, let `V` be `Null`.
391+
* For a `return e;` statement, let `S` be the inferred type of `e`
392+
with typing context `FutureOr<K>`. Let `V` be **flatten**(`S`).
393+
394+
- If the enclosing function is marked `sync*`, the relevant statements
395+
are `yield e;` or `yield* e;` statement.
396+
* For a `yield e;` statement, let `V` be the inferred type of `e` with
397+
typing context `K`.
398+
* For a `yield* e;` statement, let `S` be the inferred type of `e` with
399+
typing context `Iterable<K>`.
400+
If `S` implements `Iterable<R>` for some `R`, let `V` be `R`.
401+
Otherwise let `V` be `S`.
402+
_It is a compile-time error if `S` is not a assignable to
403+
`Iterable<Object?>`, so either `S` implements `Iterable`,
404+
or it is one of `dynamic` or `Never`._
405+
406+
- If the enclosing function is marked `async*`, the relevant statements are
407+
`yield e;` or `yield* e;` statements.
408+
* For a `yield e;` statement, let `V` be the inferred type of `e` with
409+
typing context `K`.
410+
* For a `yield* e;` statement, let `S` be the inferred type of `e` with
411+
typing context `Stream<K>`.
412+
If `S` implements `Stream<R>` for some `R`, let `V` be `R`.
413+
Otherwise let `V` be `S`.
414+
_It is a compile-time error if `S` is not a assignable to
415+
`Stream<Object?>`, so either `S` implements `Stream`,
416+
or it is one of `dynamic` or `Never`._
417+
418+
After processing each relevant statement, update `T` to be **UP**(`T`, `V`).
419+
420+
The **actual value type** of the function literal is the value of `T` after
363421
all `return` and `yield` statements in the block body have been considered.
364422

365-
Let `T` be the **actual returned type** of a function literal as computed above.
423+
Let `T` be the **actual value type** of a function literal as computed above.
366424
Let `R` be the greatest closure of the typing context `K` as computed above.
367425

368426
With null safety, if `R` is `void`, let `S` be `void`
369427
_(without null-safety: no special treatment is applicable to `void`)_.
370428

371429
Otherwise (_without null safety or if `R` is not `void`_),
372-
if `T <: R` then let `S` be `T`. Otherwise, let `S` be `R`. The
430+
if `T <: R` then let `S` be `T`, else let `S` be `R`. The
373431
inferred return type of the function literal is then defined as follows:
374432

375433
- If the function literal is marked `async` then the inferred return type is

0 commit comments

Comments
 (0)