OLD | NEW |
(Empty) | |
| 1 # Asserts in Initializer List |
| 2 [lrn@google.com](mailto:lrn@google.com) |
| 3 Version 1.1 (2017-06-08) |
| 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 ## Syntax |
| 15 |
| 16 The syntax is changed to allow an assert statement without trailing semicolon (j
ust the `assert(condition[, message])`) to appear as an item in the initializer
list. |
| 17 Example: |
| 18 |
| 19 ```dart |
| 20 C(x, y) : this.foo = x, assert(x < y), this.bar = y; |
| 21 ``` |
| 22 |
| 23 The assert can occur anywhere in the list where an initializing assignment can. |
| 24 |
| 25 That is, the grammar changes so that *superCallOrFieldIntitializer* can also pro
duce an assert. |
| 26 |
| 27 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*. |
| 28 |
| 29 > *superCallOrFieldInitializer*: |
| 30 > **super** arguments |
| 31 > | **super** ‘.’ identifier arguments |
| 32 > | fieldInitializer |
| 33 > | assertion |
| 34 > ; |
| 35 > |
| 36 > assertion: **assert** ‘(' expression (‘,' expression)? ‘)' ; |
| 37 > |
| 38 > assertStatement: assertion ‘;' ; |
| 39 |
| 40 The *superCallOrFieldInitializer* production will probably change name too, perh
aps to *initializerListEntry*, but that's not important for the behavior. |
| 41 |
| 42 ## Semantics |
| 43 |
| 44 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: |
| 45 |
| 46 1. evaluate the condition expression (in the initializer list scope) to a resul
t, `o`. |
| 47 1. If `o` implements `Function`, call it with zero arguments and let `r` be the
return value, |
| 48 1. otherwise let `r` = `o`. |
| 49 1. Perform boolean conversion on `r`. This throws if `r` is not an instance of
`bool`. |
| 50 1. if `r` isn't `true`, |
| 51 a. if there is a message expression, evaluate that to a value `m` |
| 52 b. otherwise let `m` be `null` |
| 53 c. then throw an `AssertionError` with `m` as message. |
| 54 |
| 55 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()`
. |
| 56 |
| 57 Here step 2, 4 and 5a may throw before reaching step 5c, in which case that is t
he effect of the assert. |
| 58 |
| 59 |
| 60 The assert statement is evaluated at its position in the initializer list, relat
ive to the left-to-right evaluation of initializer list entries. |
| 61 |
| 62 As usual, assert statements have no effect unless asserts are enabled (e.g., by
running in checked mode). |
| 63 |
| 64 |
| 65 ## Const Semantics |
| 66 |
| 67 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. |
| 68 |
| 69 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: |
| 70 |
| 71 *Step 2 above is dropped for an assert in a const constructor initializer list.* |
| 72 |
| 73 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`. |
| 74 This change from the current specification is needed because asserts previously
couldn't occur in a (potentially) const context[^1]. |
| 75 |
| 76 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. |
| 77 |
| 78 |
| 79 ## Revisions |
| 80 |
| 81 1.0 (2016-06-23) Initial specification. |
| 82 |
| 83 1.1 (2017-06-08) Handle second expression in asserts as well, add grammar rules. |
| 84 |
| 85 |
| 86 ## Notes |
| 87 |
| 88 [^1]: |
| 89 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 |