OLD | NEW |
---|---|
(Empty) | |
1 # Asserts in Initializer List | |
2 [lrn@google.com](mailto:lrn@google.com) | |
3 Version 1.1 | |
4 Status: Accepted, Informally specified | |
5 | |
6 (See: http://dartbug.com/24841, http://dartbug.com/27141) | |
7 | |
8 In some cases, you want to validate your inputs before creating an instance, eve n in a const constructor. To allow that, we have tested the possibility of allow ing assert statements in the initializer list of a generative constructor. | |
9 | |
10 We started by implementing the feature in the VM behind a flag, with at syntax s upport from the analyzer and the formatter. | |
11 | |
12 This was as successful experiment, and the feature is actively being used by the Flutter project, so now we promote the experimental feature to a language featu re. | |
13 | |
14 The feature should now be implemented by all Dart tools and be formally specifie d in the Dart language specification. | |
floitsch
2017/06/08 11:44:59
Remove paragraph.
| |
15 | |
16 | |
17 ## Syntax | |
18 | |
19 The syntax will be changed to allow an assert statement without trailing semicol on (just the `assert(condition[, message])`) to appear as an item in the initial izer list. | |
floitsch
2017/06/08 11:44:59
will be -> is
| |
20 Example: | |
21 | |
22 ```dart | |
23 C(x, y) : this.foo = x, assert(x < y), this.bar = y; | |
24 ``` | |
25 | |
26 The assert can occur anywhere in the list where an initializing assignment can. | |
27 | |
28 That is, the grammar changes so that *superCallOrFieldIntitializer* can also pro duce an assert. | |
29 | |
30 For simplicity, we add a new production for the assert-without-the-semicolon, an d reuse that in both the initializer list and the *assertStatement*. | |
31 | |
32 > *superCallOrFieldInitializer*: | |
33 > **super** arguments | |
34 > | **super** ‘.’ identifier arguments | |
35 > | fieldInitializer | |
36 > | assertion | |
37 > ; | |
38 > | |
39 > assertion: **assert** ‘(' expression (‘,' expression)? ‘)' ; | |
40 > | |
41 > assertStatement: assertion ‘;' ; | |
42 | |
43 The *superCallOrFieldInitializer* production will probably change name too, perh aps to *initializerListEntry*, but that's not important for the behavior. | |
44 | |
45 ## Semantics | |
46 | |
47 The initializer list assert works the same way as an assert statement in a funct ion body (with special treatment for asserts in a const constructor's initialize r list, see next section). The assert expressions are evaluated in the initializ er list scope, which does not have access to `this`, exactly the same way that a n assert statement would be evaluated in the same scope. The runtime behavior is effectively: | |
48 | |
49 1. evaluate the condition expression (in the initializer list scope) to a resul t, `o`. | |
50 1. If `o` implements `Function`, call it with zero arguments and let `r` be the return value, | |
51 1. otherwise let `r` = `o`. | |
52 1. Perform boolean conversion on `r`. This throws if `r` is not an instance of `bool`. | |
53 1. if `r` isn't `true`, | |
54 a. if there is a message expression, evaluate that to a value `m` | |
55 b. otherwise let `m` be `null` | |
56 c. then throw an `AssertionError` with `m` as message. | |
57 | |
58 Statically, like in an assertion statement, it's a warning if the static type of the condition expression isn't assignable to either `bool` or `bool Function()` . | |
59 | |
60 Here step 2, 4 and 5a may throw before reaching step 5c, in which case that is t he effect of the assert. | |
61 | |
62 | |
63 The assert statement is evaluated at its position in the initializer list, relat ive to the left-to-right evaluation of initializer list entries. | |
64 | |
65 As usual, assert statements have no effect unless asserts are enabled (e.g., by running in checked mode). | |
66 | |
67 | |
68 ## Const Semantics | |
69 | |
70 If the constructor is a const constructor, the condition and message expressions in the assert must be potentially compile-time constant expressions. If any of them aren't, it is a compile-time error, the same way a non-potentially compile- time constant initializer expression in the initializer list is. | |
71 | |
72 Further, the condition expression should not evaluate to a function, since we ca n't call functions at compile time. We can't prevent it from evaluating to a fun ction, but the function cannot not be called. To account for this, the behavior above is changed for const constructor initializer list asserts: | |
73 | |
74 *Step 2 above is dropped for an assert in a const constructor initializer list.* | |
75 | |
76 The change is entirely syntax driven - an assert inside a const constructor init ializer list does not test whether the expression is a function, not even when t he constructor is invoked using `new`. | |
77 This change from the current specification is needed because asserts previously couldn't occur in a (potentially) const context[^1]. | |
78 | |
79 During a const constructor invocation (that is, when the const constructor is in voked using the `const` prefix), if the assert fails, either due to boolean conv ersion when `r` is not a boolean value or due to assertion failure when `r` is ` false`, it is treated like any other compile-time throw in a compile-time consta nt expression, and it causes a compile-time error. | |
80 | |
81 | |
82 ## Revisions | |
83 | |
84 1.0 Initial specification. | |
85 | |
86 1.1 Handle second expression in asserts as well, add grammar rules. | |
87 | |
88 | |
89 ## Notes | |
90 | |
91 [^1]: | |
92 If we ever add "const functions" which can be "called" in a const context, then we may allow them here, but other functions are still compile time errors. | |
OLD | NEW |