Lasse Nielsen (@lrhn)
Status: Experimental
(See: http://github.com/dart-lang/sdk/issues/24841, http://github.com/dart-lang/sdk/issues/27141)
In some cases, you want to validate your inputs before creating an instance, even in a const constructor. To allow that, we plan to test the possibility of allowing assert statements in the initializer list of a generative constructor.
We will start by implementing it in the VM behind a flag, with at least syntax support from the analyzer and possibly the formatter.
The syntax will be changed to allow an assert statement without trailing semicolon (just the assert(expression)
) to appear as an item in the initializer list.
Example:
C(x, y) : this.foo = x, assert(x < y), this.bar = y;
The assert can occur anywhere in the list where an initializing assignment can.
The initializer list assert works the same way as an assert in a function body (with special treatment for asserts in a const constructor's initializer list, see next section). The assert expression is evaluated in the initializer list scope, which does not have access to this
. The behavior is effectively:
- evaluate the expression (in the initializer list scope) to a result,
o
. - If
o
implementsFunction
, call it with zero arguments and letr
be the return value, - otherwise let
r
=o
. - Perform boolean conversion on
r
. This throws ifr
is not an instance ofbool
. - if
r
isn'ttrue
, the assertion throws anAssertionError
.
Here step 2 and 4 may throw before reaching step 5, in which case that is the effect of the assert.
The assert statement is evaluated at its position in the initializer list, relative to the left-to-right evaluation of initializer list entries.
As usual, assert statements have no effect unless asserts are enabled (e.g., by running in checked mode).
If the constructor is a const constructor, the expression in the assert must be a potentially compile-time constant expression. If it isn't, it is a compile-time error, the same way a non-potentially compile-time constant initializer expression is.
Further, the expression should not evaluate to a function, since we can't call functions at compile time. We can't prevent it from evaluating to a function, but the function cannot not be called. To account for this, the behavior above is changed for const constructor initializer list asserts:
Step 2 above is dropped for an assert in a const constructor initializer list.
The change is entirely syntax driven - an assert inside a const constructor initializer list does not test whether the expression is a function, not even when the constructor is invoked using new
.
This change from the current specification is needed because asserts previously couldn't occur in a (potentially) const context.
During a const constructor invocation (that is, when the const constructor is invoked using the "const" prefix), if the assert fails, either due to boolean conversion 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, and it causes a compile-time error.