Skip to content

Commit 7d07f8d

Browse files
eernstgcommit-bot@chromium.org
authored andcommitted
Add new allowed placements of void expressions for void-to-void flow.
As an example of a void-to-void data flow, consider `void x = foo();`, where `foo` has return type `void`. In general, a void-to-void data flow is a computation which is trivial (we may select one of two branches in a conditional expression and otherwise only pass the value on without any computation) where the expression being evaluated has type void, and the target that receives the value is also of type void. This CL makes adjustments to generalized-void.md such that void-to-void data flows are allowed. Change-Id: Ia1722cd399c77c57cc5c61e9c10b7a84a18fe107 Reviewed-on: https://dart-review.googlesource.com/38060 Reviewed-by: Lasse R.H. Nielsen <[email protected]> Reviewed-by: Leaf Petersen <[email protected]> Commit-Queue: Erik Ernst <[email protected]>
1 parent 846d184 commit 7d07f8d

File tree

1 file changed

+107
-70
lines changed

1 file changed

+107
-70
lines changed

docs/language/informal/generalized-void.md

Lines changed: 107 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@
22

33
**Author**: eernst@
44

5+
**Version**: 0.9 (2018-02-22)
6+
57
**Status**: Under implementation.
68

7-
**This document** is an informal specification of the generalized support
8-
in Dart 1.x for the type `void`. Dart 2 will have a very similar kind of
9-
generalized support for `void`, without the function type subtype exception
10-
that this feature includes for backward compatibility in Dart 1.x. This
11-
document specifies the feature for Dart 1.x and indicates how Dart 2
12-
differs at relevant points.
9+
**This document** is a feature specification of the generalized support
10+
in Dart 2 for the type `void`.
1311

14-
**The feature** described here, *generalized void*, allows for using the
15-
type `void` as a type annotation and as a type argument.
12+
**The feature** described here, *generalized void*, allows for using
13+
`void` as a type annotation and as a type argument.
1614

1715
The **motivation** for allowing the extended usage is that it helps
1816
developers state the intent that a particular **value should be
@@ -30,34 +28,34 @@ covariantly. For instance, the class `Future<T>` uses return types
3028
like `Future<T>` and `Stream<T>`, and it uses `T` as a parameter type of a
3129
callback in the method `then`.
3230

33-
Note that using the value of an expression of type `void` is not
31+
Note that using the value of an expression of type void is not
3432
technically dangerous, doing so does not violate any constraints at the
35-
level of the language semantics. By using the type `void`, developers
33+
level of the language semantics. By using the type void, developers
3634
indicate that the value of the corresponding expression evaluation is
3735
meaningless. Hence, there is **no requirement** for the generalized void
3836
mechanism to be strict and **sound**. However, it is the intention that the
3937
mechanism should be sufficiently sound to make the mechanism helpful and
4038
non-frustrating in practice.
4139

42-
No constraints are imposed on which values may be given type `void`, so in
40+
No constraints are imposed on which values may be given type void, so in
4341
that sense `void` can be considered to be just another name for the type
4442
`Object`, flagged as useless. Note that this is an approximate rule in
4543
Dart 1.x, it fails to hold for function types; it does hold in Dart 2.
4644

4745
The mechanisms helping developers to avoid using the value of an expression
48-
of type `void` are divided into **two phases**. This document specifies the
46+
of type void are divided into **two phases**. This document specifies the
4947
first phase.
5048

5149
The **first phase** uses restrictions which are based on syntactic criteria
5250
in order to ensure that direct usage of the value of an expression of type
53-
`void` is a static warning (in Dart 2: an error). A few exceptions are
51+
void is a compile-time error. A few exceptions are
5452
allowed, e.g., type casts, such that developers can explicitly make the
5553
choice to use such a value. The general rule is that for every expression
56-
of type `void`, its value must be ignored.
54+
of type void, its value must be ignored.
5755

5856
The **second phase** will deal with casts and preservation of
5957
voidness. Some casts will cause derived expressions to switch from having
60-
type `void` to having some other type, and hence those casts introduce the
58+
type void to having some other type, and hence those casts introduce the
6159
possibility that "a void value" will get passed and used. Here is an
6260
example:
6361

@@ -88,7 +86,7 @@ typeNotVoid ::= // NEW
8886
type ::= // ENTIRE RULE MODIFIED
8987
typeNotVoid | 'void'
9088
redirectingFactoryConstructorSignature ::=
91-
'const'? 'factory' identifier ('.' identifier)?
89+
'const'? 'factory' identifier ('.' identifier)?
9290
formalParameterList `=' typeNotVoid ('.' identifier)?
9391
superclass ::=
9492
'extends' typeNotVoid
@@ -211,7 +209,7 @@ the type void is treated as being the built-in class `Object`.
211209
are erased to regular function types during compilation. Hence there is no
212210
need to specify the the typing relations for generic function types. In
213211
Dart 2, the subtype relationship for generic function types follows from
214-
the rule that `void` is treated as `Object`.*
212+
the rule that the type void is treated as `Object`.*
215213

216214
Consider a function type _T_ where the return type is the type void. In
217215
Dart 1.x, the dynamic more-specific-than relation, `<<`, and the dynamic
@@ -236,78 +234,108 @@ we were to consistently treat the type void as `Object` at run time (as in
236234
Dart 2) then this assignment would be permitted (but we would then use
237235
voidness preservation to detect and avoid this situation at compile time).*
238236

239-
*The semantics of checked mode checks involving types where the type void
237+
*The semantics of dynamic checks involving types where the type void
240238
occurs is determined by the semantics of subtype tests, so we do not
241239
specify that separately.*
242240

243-
An instantiation of a generic class `G` is malbounded if it contains `void`
244-
as an actual type argument for a formal type parameter, unless that type
245-
parameter does not have a bound, or it has a bound which is the built-in
246-
class `Object`, or `dynamic`.
241+
It is a compile-time error to use `void` as the bound of a type variable.
242+
243+
An instantiation of a generic class `G` is malbounded if it contains the
244+
type void as an actual type argument for a formal type parameter, unless
245+
that type parameter does not have a bound, or it has a bound which is the
246+
built-in class `Object`, or `dynamic`.
247247

248248
*The treatment of malbounded types follows the current specification.*
249249

250+
250251
## Static Analysis
251252

252-
For the static analysis, the more-specific-than relation, `<<`, and the
253-
subtype relation, `<:`, are determined by the same rules as described above
254-
for the dynamic semantics, for both Dart 1.x and Dart 2.
255-
256-
*That is, the type void is considered to be equivalent to the built-in
257-
class `Object` in Dart 1.x, except when used as a return type, in which
258-
case it is effectively considered to be a proper supertype of `Object`. In
259-
Dart 2 subtyping, the type void is consistently considered to be equivalent
260-
to the built-in class `Object`. As mentioned, this document does not
261-
specify voidness preservation; however, when voidness preservation checks
262-
are added we get an effect in Dart 2 which is similar to the special
263-
treatment of void as a return type in Dart 1.x: The function type downcast
264-
which will be rejected in Dart 1.x (at run time, with a static warning at
265-
compile time) will become a voidness preservation violation, i.e., a
266-
compile-time error.*
267-
268-
It is a static warning for an expression to have type void (in Dart 2: a
269-
compile-time error), except for the following situations:
270-
271-
* In an expressionStatement `e;`, e may have type void.
253+
For the static analysis, the subtype relation, `<:`, is determined by the
254+
same rules as described above for the dynamic semantics.
255+
256+
*That is, the type void, for the purposes of subtyping, is considered to be
257+
equivalent to the built-in class `Object`. As mentioned, this document does
258+
not specify voidness preservation. However, when voidness preservation
259+
checks are added we will get (among other things) an effect which is
260+
similar to the special treatment of void as a return type which was used in
261+
Dart 1.x: In Dart 1.x, an implicit downcast from `void Function()` to
262+
`Object Function()` will fail at run time, but with voidness preservation
263+
it will be a compile-time error.*
264+
265+
It is a compile-time error to evaluate an expression of type void, except
266+
for the following situations:
267+
268+
* In an expressionStatement `e;`, `e` may have type void.
272269
* In the initialization and increment expressions of a for-loop,
273270
`for (e1; e2; e3) {..}`, `e1` and `e3` may have type void.
274-
* In a typeCast `e as T`, `e` may have type void.
271+
* In a type cast `e as T`, `e` may have type void.
275272
* In a parenthesized expression `(e)`, `e` may have type void.
273+
* In a conditional expression `e ? e1 : e2`, `e1` and `e2` may have the
274+
type void; the static type of the conditional expression is then the
275+
type void. (*This is true even if one of the branches has a different
276+
type.*)
277+
* If _N1_ and _N2_ are non-terminals in the Dart grammar, and there is a
278+
derivation of the form _N1 --> N2_, and _N2_ can have type void, then
279+
_N1_ can also have type void for such a derivation. *In this derivation
280+
no additional tokens are included, it is only the non-terminal which
281+
changes.*
276282
* In a return statement `return e;`, when the return type of the innermost
277283
enclosing function is the type void, `e` may have type void.
278-
279-
*Note that the parenthesized expression itself has type void, so it is
280-
again subject to the same constraints. Also note that we may not allow
281-
return statements returning an expression of type void in Dart 2, but
282-
it is allowed here for backward compatibility.*
283-
284-
*The value yielded by an expression of type void must be discarded (and
285-
hence ignored), except when explicitly subjected to a type cast. This
284+
* An initializing expression for a variable of type void may have the type
285+
void.
286+
* An actual parameter expression corresponding to a formal parameter whose
287+
statically known type annotation is the type void may have the type void.
288+
* In an expression of the form `e1 = e2` where `e1` is an
289+
assignableExpression denoting a variable or parameter of type void, `e2` may
290+
have the type void.
291+
* Assume that `e` is an expression ending in a `cascadeSection` of the
292+
form `'..' S s = e1` where `S` is of the form `(cascadeSelector
293+
argumentPart*) (assignableSelector argumentPart*)*` and `e1` is an
294+
`expressionWithoutCascade`. If `s` is an `assignableSelector` of the
295+
form `'.' identifier` or `'?.' identifier` where the static type of the
296+
`identifier` is the type void, `e1` may have type void; otherwise, if
297+
`s` is an `assignableSelector` of the form `'[' e0 ']'` where the
298+
static type of the first formal parameter in the statically known
299+
declaration of operator `[]=` is the type void, `e0` may have type
300+
void; also, if the static type of the second formal parameter is the
301+
type void, `e1` may have type void.
302+
303+
*The rule about non-terminals is needed in order to allow, say, `void x = b
304+
? (y) : e2;` where `y` has type void: `y` is an identifier which is derived
305+
from primary, which is derived from postfixExpression, from
306+
unaryExpression, from multiplicativeExpression, etc. Only if we allow such
307+
a (trivial) multiplicativeExpression can we allow the corresponding
308+
(trivial) unaryExpression, etc., all the way down to identifier, and all
309+
the way up to expression, which is needed for the initialization of `x`.*
310+
311+
*The general rule is that the value yielded by an expression of type void
312+
must be discarded (and hence ignored), except when explicitly subjected to
313+
a type cast, or when returned or assigned to a target of type void. This
286314
"makes it hard to use a meaningless value", but leaves a small escape hatch
287315
open for the cases where the developer knows that the typing misrepresents
288316
the actual situation.*
289317

290-
It is a static warning (in Dart 2: a compile-time error) if a return
291-
statement `return e;` occurs such that the innermost enclosing function
292-
has return type `void` and the static type of `e` is not the type void.
318+
It is a compile-time error if a return statement `return e;` occurs such
319+
that the innermost enclosing function has return type `void` and the static
320+
type of `e` is not the type void.
293321

294-
It is a static warning (in Dart 2: a compile-time error) if a function
295-
marked `async*`, or `sync*` has return type `void`.
322+
It is a compile-time error if a function marked `async*`, or `sync*` has
323+
return type `void`.
296324

297325
*Note that it is allowed for an `async` function to have return type
298326
`void`. This serves to indicate that said function performs a
299327
"fire-and-forget" operation, that is, it is not even useful for the caller
300328
to synchronize with the completion of that task.*
301329

302-
It is a static warning (Dart 2: a compile-time error) for a for-in
303-
statement to have an iterator expression of type `T` such that
304-
`Iterator<void>` is the most specific instantiation of `Iterator` that is a
305-
superinterface of `T`, unless the iteration variable has type void.
330+
It is a compile-time error for a for-in statement to have an iterator
331+
expression of type `T` such that `Iterator<void>` is the most specific
332+
instantiation of `Iterator` that is a superinterface of `T`, unless the
333+
iteration variable has type void.
306334

307-
It is a static warning (Dart 2: a compile-time error) for an asynchronous
308-
for-in statement to have a stream expression of type `T` such that
309-
`Stream<void>` is the most specific instantiation of `Stream` that is a
310-
superinterface of `T`, unless the iteration variable has type void.
335+
It is a compile-time error for an asynchronous for-in statement to have a
336+
stream expression of type `T` such that `Stream<void>` is the most specific
337+
instantiation of `Stream` that is a superinterface of `T`, unless the
338+
iteration variable has type void.
311339

312340
*Hence, `for (Object x in <void>[]) {}` and
313341
`await for (int x in new Stream<void>.empty()) {}` are errors, whereas
@@ -320,13 +348,16 @@ parameter of a generic class or function is statically known to be the type
320348
void. In this case, the bound is considered to be the built-in class
321349
`Object`.
322350

323-
In Dart 2, it is a compile-time error when a method declaration _D2_ with
324-
return type void overrides a method declaration _D1_ whose return type is
325-
not void.
351+
It is a compile-time error when a method declaration _D2_ with return type
352+
void overrides a method declaration _D1_ whose return type is not void.
326353

327-
*This rule is a special case of voidness preservation, which is needed in
328-
order to maintain the discipline which arises naturally from the function
329-
type subtype rules in Dart 1.x concerning void as a return type.*
354+
*This rule is a special case of voidness preservation, which maintains the
355+
discipline which arises naturally from the function type subtype rules in
356+
Dart 1.x concerning void as a return type. It also matches the conceptual
357+
interpretation that a value of type void can be anything, but it should be
358+
discarded: This ensures that a subtype can be used where the supertype is
359+
expected (also known as Liskov substitutability), because it is always
360+
considered safe to ignore the value of an expression evaluation.*
330361

331362
## Discussion
332363

@@ -356,6 +387,12 @@ bound as `Object`.
356387

357388
## Updates
358389

390+
* February 22nd 2018, v0.9: Added several new contexts where an
391+
expression with static type void may be evaluated, such that pure data
392+
transfers to a target of type void are allowed. For instance, a void
393+
expression may be passed as an actual argument to a parameter of type
394+
void.
395+
359396
* August 22nd 2017: Reworded specification of reified types to deal with
360397
only such values which may be obtained at run time (previously mentioned
361398
some entities which may not exist). Added one override rule.

0 commit comments

Comments
 (0)