Skip to content

Commit 590708d

Browse files
committed
Address review comments
1 parent c9db873 commit 590708d

File tree

1 file changed

+118
-51
lines changed

1 file changed

+118
-51
lines changed

resources/type-system/inference.md

+118-51
Original file line numberDiff line numberDiff line change
@@ -1134,25 +1134,58 @@ succintly, the syntax of Dart is extended to allow the following forms:
11341134
any loss of functionality, provided they are not trying to construct a proof
11351135
of soundness._
11361136

1137-
In addition, the following forms are added to allow constructor invocations,
1138-
dynamic method invocations, function object invocations, instance method
1139-
invocations, and static/toplevel method invocations to be distinguished:
1137+
In addition, forms are added to allow constructor invocations, dynamic method
1138+
invocations, function object invocations, instance method invocations, and
1139+
static/toplevel method invocations to be distinguished. In these forms, `n_i:
1140+
m_i` (where `i` is an integer) is used as a convenient meta-syntax to refer to
1141+
an invocation argument `m_i` (an elaborated expression), possibly preceded by a
1142+
name selector `n_i:` (where `n_i` is a string). In this document, we use the
1143+
string `` to represent a name selector which is absent (meaning the
1144+
corresponding `m_i` is a positional argument rather than a named argument).
1145+
1146+
The new invocation forms are as follows:
11401147

11411148
- `@DYNAMIC_INVOKE(m_0.id<T_1, T_2, ...>(n_1: m_1, n_2: m_2, ...))`
11421149

1150+
_This covers invocations of user-definable binary and unary operators, method
1151+
invocations, and implicit uses of the `call` method, where the target has
1152+
static type `dynamic` or `Function`. For example, if `d` has static type
1153+
`dynamic`, the following expressions will be elaborated using
1154+
`@DYNAMIC_INVOKE`: `d.f()`, `-d`, `d + 0`, and `d()`._
1155+
11431156
- `@FUNCTION_INVOKE(m_0.call<T_1, T_2, ...>(n_1: m_1, n_2: m_2, ...))`
11441157

1158+
_This covers invocations of expressions whose type is a function type (but not
1159+
`Function`). For example, if `l` has type `List<void Function()>`, then
1160+
`l.first()` will be elaborated using `@FUNCTION_INVOKE`._
1161+
11451162
- `@INSTANCE_INVOKE(m_0.id<T_1, T_2, ...>(n_1: m_1, n_2: m_2, ...))`
11461163

1164+
_This covers invocations of user-definable binary and unary operators, method
1165+
invocations, and implicit uses of the `call` method, where the static type of
1166+
the target is the interface type of a class, mixin, enum, or extension
1167+
type. For example, if `i` has static type `int`, and the static type of `x` is
1168+
a class containing a `call` method, then the following expressions will be
1169+
elaborated using `@INSTANCE_INVOKE`: `i.abs()`, `-i`, `i + 1`, and `x()`._
1170+
11471171
- `@STATIC_INVOKE(f<T_1, T_2, ...>(n_1: m_1, n_2: m_2, ...))`
11481172

1173+
_This covers invocations of static methods and top level methods. For example,
1174+
the following expressions will be elaborated using `@STATIC_INVOKE`:
1175+
`print(s)` and `int.tryParse(s)`._
1176+
11491177
In each of these forms, each `m_i` represents an elaborated expression, each
11501178
`T_i` represents a type, and each `n_i` represents an optional argument name
11511179
identifier. When present, `id` represents an identifier or operator name, and
11521180
`f` represents a static method or top level function.
11531181

11541182
The semantics of each of these forms is to evaluate the `m_i` in sequence, then
1155-
perform the appropriate kind of method or function call.
1183+
perform the appropriate kind of method or function call. _The precise runtime
1184+
semantics are not specified here, however it should be noted that in the case of
1185+
`@DYNAMIC_INVOKE`, the name `id` may not be found in the target's runtime type
1186+
information at all (in which case `noSuchMethod` will be invoked), or `id` may
1187+
resolve to a getter (in which case the getter will be invoked, and then a
1188+
dynamic invocation of `call` will be attempted)._
11561189

11571190
## Additional properties satisfied by elaborated expressions
11581191

@@ -1263,8 +1296,8 @@ non-generic function or method, or when type arguments need to be inferred._
12631296
The output of argument part inference is a sequence of elaborated expressions
12641297
`{m_1, m_2, ...}` (of the same length as the sequence of input expressions), a
12651298
sequence of zero or more elaborated type arguments `{U_1, U_2, ...}`, and a
1266-
result type known as `R`. _(The sequence of optional names is unchanged by
1267-
argument part inference.)_
1299+
result type `R`. _(The sequence of optional names is unchanged by argument part
1300+
inference.)_
12681301

12691302
To emphasize the relationship between argument part inference and the syntax of
12701303
Dart source code, the inputs and outputs of argument part inference are
@@ -1281,8 +1314,8 @@ The procedure for argument part inference is as follows:
12811314
- Let `{X_1, X_2, ...}` be the list of formal type parameters in `F`, or an
12821315
empty list if `F` is ``.
12831316

1284-
- Let `{P_1, P_2, ...}` be the list of formal parameter types corresponding to
1285-
`{e_1, e_2, ...}`, determined as follows:
1317+
- Let `{P_1, P_2, ...}` be the list of formal parameter type schemas
1318+
corresponding to `{e_1, e_2, ...}`, determined as follows:
12861319

12871320
- If `F` is ``, then let each `P_i` be `_`.
12881321

@@ -1330,17 +1363,17 @@ The procedure for argument part inference is as follows:
13301363
- Initialize `{U_1, U_2, ...}` to a list of type schemas, of the same length
13311364
as `{X_1, X_2, ...}`, each of which is `_`.
13321365

1333-
- Using subtype constraint generation, attempt to match `R_F` as a subtype
1334-
of `K` with respect to the list of type variables `{X_1, X_2, ...}`.
1366+
- Check whether `R_F` is a subtype match for `K` with respect to the list of
1367+
type variables `{X_1, X_2, ...}`.
13351368

13361369
- If this succeeds, then accumulate the resulting list of type constraints
13371370
into `C`. Then, let `{V_1, V_2, ...}` be the constraint solution for the
1338-
set of type variables `{X_1, X_2, ...}` with respect to the constaint
1339-
set `C`, with partial solution `{U_1, U_2, ...}`. Replace `{U_1, U_2,
1340-
...}` with `{V_1, V_2, ...}`. _This step is known as "downwards
1341-
inference", since it has the effect of passing type information __down__
1342-
the syntax tree from the context of the invocation to the context of the
1343-
arguments._
1371+
set of type variables `{X_1, X_2, ...}` with respect to the constraint
1372+
set `C`, with partial solution `{U_1, U_2, ...}`. Let the new values of
1373+
`{U_1, U_2, ...}` be `{V_1, V_2, ...}`. _This step is known as
1374+
"downwards inference", since it has the effect of passing type
1375+
information __down__ the syntax tree from the context of the invocation
1376+
to the context of the arguments._
13441377

13451378
- Otherwise, leave `C` and `{U_1, U_2, ...}` unchanged.
13461379

@@ -1369,18 +1402,27 @@ The procedure for argument part inference is as follows:
13691402
has the effect that all arguments that are not function expressions are type
13701403
inferred first, in the order in which they appear in source code, followed
13711404
by all arguments that __are__ function expressions, possibly in a staged
1372-
fashion._
1405+
fashion. The reason for traversing the non-function expressions in stage
1406+
zero before the function expressions is to allow any type promotions that
1407+
occur in the non-function expressions to carry over into the function
1408+
expressions, regardless of the order of the arguments. For example, `f(int?
1409+
i) => g(() { print(i+1); }, i!);` is accepted by type inference, because the
1410+
null check `i!` is guaranteed to execute before the function expression `()
1411+
{ print(i+1); }` can ever be invoked. See
1412+
https://github.com/dart-lang/language/blob/main/accepted/2.18/horizontal-inference/feature-specification.md#why-do-we-defer-all-function-literals
1413+
for more information._
13731414

13741415
- If `F` has at least one formal type parameter, and there are zero type
13751416
arguments `T` (_in other words, generic type inference is being performed_),
13761417
then:
13771418

1378-
- For each `e_i` in stage _k_:
1419+
- For each `e_i` in stage _k_, in the order in which expression inference
1420+
was performed:
13791421

13801422
- Let `S_i` be the static type of `m_i_preliminary`.
13811423

1382-
- Using subtype constraint generation, attempt to match `S_i` as a subtype
1383-
of `K_i` with respect to the list of type variables `{X_1, X_2, ...}`.
1424+
- Check whether `S_i` is a subtype match for `P_i` with respect to the
1425+
list of type variables `{X_1, X_2, ...}`.
13841426

13851427
- If this succeeds, then accumulate the resulting list of type
13861428
constraints into `C`.
@@ -1391,21 +1433,21 @@ The procedure for argument part inference is as follows:
13911433

13921434
- If _k_ is not the last stage in the argument partitioning, then let
13931435
`{V_1, V_2, ...}` be the constraint solution for the set of type
1394-
variables `{X_1, X_2, ...}` with respect to the constaint set `C`, with
1395-
partial solution `{U_1, U_2, ...}`. Replace `{U_1, U_2, ...}` with
1396-
`{V_1, V_2, ...}`. _This step is known as "horizontal inference", since
1397-
it has the effect of passing type information __across__ the syntax tree
1398-
from the static type of some arguments to the context of other
1399-
arguments._
1436+
variables `{X_1, X_2, ...}` with respect to the constraint set `C`, with
1437+
partial solution `{U_1, U_2, ...}`. Let the new values of `{U_1, U_2,
1438+
...}` be `{V_1, V_2, ...}`. _This step is known as "horizontal
1439+
inference", since it has the effect of passing type information
1440+
__across__ the syntax tree from the static type of some arguments to the
1441+
context of other arguments._
14001442

14011443
- Otherwise (_k_ __is__ the last stage in the argument partitioning), let
14021444
`{V_1, V_2, ...}` be the _grounded_ constraint solution for the set of
1403-
type variables `{X_1, X_2, ...}` with respect to the constaint set `C`,
1404-
with partial solution `{U_1, U_2, ...}`. Replace `{U_1, U_2, ...}` with
1405-
`{V_1, V_2, ...}`. _This step is known as "upwards inference", since it
1406-
has the effect of passing type information __up__ the syntax tree from
1407-
the static type of arguments to the inferred type arguments of the
1408-
invocation._
1445+
type variables `{X_1, X_2, ...}` with respect to the constraint set `C`,
1446+
with partial solution `{U_1, U_2, ...}`. Let the new values of `{U_1,
1447+
U_2, ...}` be `{V_1, V_2, ...}`. _This step is known as "upwards
1448+
inference", since it has the effect of passing type information __up__
1449+
the syntax tree from the static type of arguments to the inferred type
1450+
arguments of the invocation._
14091451

14101452
- _Note that at this point, all entries in `{U_1, U_2, ...}` have been obtained
14111453
either from a _grounded_ constraint solution or from explicit types in the
@@ -1465,7 +1507,10 @@ _Note that if `F` is `∅`, then the resulting graph has no edges._
14651507
_So, for example, if the invocation in question is this:_
14661508

14671509
```dart
1468-
f((t, u) { ... } /* A */, () { ... } /* B */, (v) { ... } /* C */, (u) { ... } /* D */)
1510+
f((t, u) { ... }, // A
1511+
() { ... }, // B
1512+
(v) { ... }, // C
1513+
(u) { ... }) // D
14691514
```
14701515

14711516
_And the target of the invocation is declared like this:_
@@ -1537,6 +1582,16 @@ name identifiers `{n_1, n_2, ...}`, a sequence of zero or more type arguments
15371582
`{T_1, T_2, ...}`, and a type schema `K`. As with [argument part
15381583
inference](#Argument-part-inference), the symbol `` denotes a missing name.
15391584

1585+
_Method invocation inference is used for the following constructs:_
1586+
1587+
- _Explicit method invocations (e.g. `[].add(x)`), in which case `id` is the name of the method (`add` in this example)._
1588+
1589+
- _Call invocations (e.g. `f()`, where `f` is a local variable), in which case
1590+
`id` is `call`._
1591+
1592+
- _Invocations of user-definable operators (e.g. `1 + 2`), in which case `id` is
1593+
the name of the operator._
1594+
15401595
The output of method invocation inference is an elaborated expression `m`, with
15411596
static type `T`, where `m` and `T` are determined as follows:
15421597

@@ -1550,9 +1605,9 @@ static type `T`, where `m` and `T` are determined as follows:
15501605
then:
15511606

15521607
- Invoke [argument part inference](#Argument-part-inference) on `<T_1, T_2,
1553-
...>(n_1: e_1, n_2: e_2, ...)`, using a target function type of `∅` and a
1554-
type schema `K`. Denote the resulting elaborated expressions by `{m_1, m_2,
1555-
...}`.
1608+
...>(n_1: e_1, n_2: e_2, ...)`, using `∅` as the target function type and
1609+
`K` as the context. Denote the resulting elaborated expressions by `{m_1,
1610+
m_2, ...}`.
15561611

15571612
- Let `m` be `@DYNAMIC_INVOKE(m_0.id<U_1, U_2, ...>(n_1: m_1, n_2: m_2,
15581613
...))`.
@@ -1583,10 +1638,10 @@ static type `T`, where `m` and `T` are determined as follows:
15831638
then:
15841639

15851640
- Invoke [argument part inference](#Argument-part-inference) on `<T_1, T_2,
1586-
...>(n_1: e_1, n_2: e_2, ...)`, using a target function type of `U_0` and a
1587-
type schema `K`. Denote the resulting elaborated expressions by `{m_1, m_2,
1588-
...}`, the resulting elaborated type arguments by `{U_1, U_2, ...}`, and the
1589-
result type by `R`.
1641+
...>(n_1: e_1, n_2: e_2, ...)`, using `U_0` as the target function type and
1642+
`K` as the context. Denote the resulting elaborated expressions by `{m_1,
1643+
m_2, ...}`, the resulting elaborated type arguments by `{U_1, U_2, ...}`,
1644+
and the result type by `R`.
15901645

15911646
- Let `m` be `@FUNCTION_INVOKE(m_0.call<U_1, U_2, ...>(n_1: m_1, n_2: m_2,
15921647
...))`, and let `T` be `R`. _Soundness follows from the fact that the static
@@ -1598,10 +1653,10 @@ static type `T`, where `m` and `T` are determined as follows:
15981653
- Let `F` be the return type of `U_0`'s accessible instance getter named `id`.
15991654

16001655
- Invoke [argument part inference](#Argument-part-inference) on `<T_1, T_2,
1601-
...>(n_1: e_1, n_2: e_2, ...)`, using a target function type of `F` and a
1602-
type schema `K`. Denote the resulting elaborated expressions by `{m_1, m_2,
1603-
...}`, the resulting elaborated type arguments by `{U_1, U_2, ...}`, and the
1604-
result type by `R`.
1656+
...>(n_1: e_1, n_2: e_2, ...)`, using `F` as the target function type and
1657+
`K` as the context. Denote the resulting elaborated expressions by `{m_1,
1658+
m_2, ...}`, the resulting elaborated type arguments by `{U_1, U_2, ...}`,
1659+
and the result type by `R`.
16051660

16061661
- Let `m` be `@FUNCTION_INVOKE(m_0.id.call<U_1, U_2, ...>(n_1: m_1, n_2: m_2,
16071662
...))`, and let `T` be `R`. _Soundness follows from the fact that the static
@@ -1614,10 +1669,18 @@ static type `T`, where `m` and `T` are determined as follows:
16141669
- Let `F` be the type of `U_0`'s accessible instance method named `id`.
16151670

16161671
- Invoke [argument part inference](#Argument-part-inference) on `<T_1, T_2,
1617-
...>(n_1: e_1, n_2: e_2, ...)`, using a target function type of `F` and a
1618-
type schema `K`. Denote the resulting elaborated expressions by `{m_1, m_2,
1619-
...}`, the resulting elaborated type arguments by `{U_1, U_2, ...}`, and the
1620-
result type by `R`.
1672+
...>(n_1: e_1, n_2: e_2, ...)`, using `F` as the target function type and
1673+
`K` as the context. Denote the resulting elaborated expressions by `{m_1,
1674+
m_2, ...}`, the resulting elaborated type arguments by `{U_1, U_2, ...}`,
1675+
and the result type by `R`.
1676+
1677+
_TODO(paulberry): handle the possibility that `F` is not a function type (it
1678+
might even be `dynamic`). Note that the analyzer and CFE currently behave
1679+
slightly differently if `F` is an interface type that declares a getter
1680+
called `call`: the analyzer rejects this (`call` must be a method), but the
1681+
CFE accepts it, recursively desugaring `.call` invocations to arbitrary
1682+
depth. See https://github.com/dart-lang/language/issues/3482 for more
1683+
context._
16211684

16221685
- Let `m` be `@INSTANCE_INVOKE(m_0.id<U_1, U_2, ...>(n_1: m_1, n_2: m_2,
16231686
...))`, and let `T` be `R`. _Soundness follows from the fact that the static
@@ -1702,11 +1765,15 @@ _TODO(paulberry): specify this._
17021765
### Implicit this invocation
17031766

17041767
If the selector chain consists of _&lt;identifier&gt; &lt;argumentPart&gt;_, and
1705-
_&lt;identifier&gt;_ __cannot__ be resolved to the name of a local variable,
1768+
_&lt;identifier&gt;_ __cannot__ be resolved using local scope resolution rules,
17061769
then the result of selector chain type inference in context `K` is the
17071770
elaborated expression `m`, with static type `T`, where `m` and `T` are
17081771
determined as follows:
17091772

1773+
_TODO(paulberry): also handle the situation where &lt;identifier&gt; __can__ be
1774+
resolved using local scope resolution rules, but it resolves to a member of the
1775+
current class, mixin, enum, extension, or extension type._
1776+
17101777
- Let `m_0` be `this`, with static type `T_0`, where `T_0` is the interface type
17111778
of the immediately enclosing class, enum, mixin, or extension type, or the "on"
17121779
type of the immediately enclosing extension. _The runtime behavior of `this` is
@@ -1853,8 +1920,8 @@ _TODO(paulberry): specify this._
18531920
### Implicit this method tearoff with type arguments
18541921

18551922
If the selector chain consists of _&lt;identifier&gt; &lt;typeArguments&gt;_,
1856-
and _&lt;identifier&gt;_ __cannot__ be resolved to the name of a local variable,
1857-
then the result of selector chain type inference in context `K` is the
1923+
and _&lt;identifier&gt;_ __cannot__ be resolved using local scope resolution
1924+
rules, then the result of selector chain type inference in context `K` is the
18581925
elaborated expression `m`, with static type `T`, where `m` and `T` are
18591926
determined as follows:
18601927

0 commit comments

Comments
 (0)