-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specify that constant assertions are always evaluated #1271
base: main
Are you sure you want to change the base?
Conversation
See #447 for context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I put the question about going ahead and doing this on the language team meeting agenda for tomorrow.
|
||
Currently assertions are enabled only when the compiler is passed an `enable-asserts` flag. | ||
With null safety, when an assert is evaluated as part of a constant invocation of a `const` | ||
constructor, any `assert`s in the initializer list are evaluated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could say are enabled
, to keep the number of concepts about assertions lower, and allowing implementations to compile non-enabled assertions away entirely, or keep them and skip them, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that sentence is garbled. That said, an assertion "being enabled" is a concept more than simply saying what its semantics are in different cases. Implementations can still completely remove run-time assertions from the compiled output when assertions are disabled, because they have the "null-semantics": completes normally with no side effects.
### Constant assertions | ||
|
||
Currently assertions are enabled only when the compiler is passed an `enable-asserts` flag. | ||
With null safety, when an assert is evaluated as part of a constant invocation of a `const` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could call them an assertion
as a rule, and only use assert
when referring to actual code. The language specification has assert statement
which is a statement that is an assertion, but otherwise it refers to assertions as assertion
s. Might as well do that here, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. I think I did that deliberately when I introduced assertions into initializer lists (where they are not statements).
More precisely, a assert _s_ of the form <code>assert(_e_<sub>1</sub>, _e_<sub>2</sub>)</code> | ||
is executed as follows: | ||
* If the execution of _s_ happens during evaluation of a const invocation of the | ||
surrounding `const` constructor, then _e_<sub>1</sub> is evaluated to a constant value _c_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about then it is a compile-time error if _e_<sub>1</sub> is not a constant expression with the value
true``?
Then we don't have to mention 'completes normally' as if it's part of the constant evaluation semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. We already do the "compile-time error if expression would throw" for constant evaluation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could rely more on the existing specification text, and just have a very short section in this document.
@@ -1008,6 +1011,33 @@ void main() { | |||
} | |||
``` | |||
|
|||
### Constant assertions | |||
|
|||
Currently assertions are enabled only when the compiler is passed an `enable-asserts` flag. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The text in section 'Assert' of the language specification already says almost everything which is said here.
Wouldn't it suffice to have a very short section here? We would then rely on the existing specification text for all the details:
Currently assertions are enabled only when the compiler is passed an `enable-asserts` flag. | |
Assertions are enabled during the evaluation of a constant object expression. | |
*This means that properties checked by assertions will always be enforced with constant expressions. The implementation specific mechanism to enable assertions is used to enable the remaining assertions, namely the ones that are executed at run time.* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's tricky, because we don't actually separate compile-time and run-time in the specification, we just specify the meaning of constants such that they can be pre-computed, but we don't actually say that you have to do so.
We still need a way to specify that something is happening as part of constant evaluation because it matters in some situations. For example const x = 1 ~/ 0;
is a compile-time error instead of throwing a DivisionByNullError
... and assert(false, e2)
shouldn't need to evaluate e2
, so we don't need to require that e2
is a potentially constant expression.
Which actually does mean that we can't reasonably delay evaluation of constant expressions until run-time unless we allow compile-time errors at run-time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, we specify that evaluation of a constant object expression occurs at run time and then it is (recursively, bottom-up) canonicalized. It makes no difference whether that constant object expression is in a constant context, whether it is used in the initialization of a const
variable, etc., it is a rule for <constObjectExpression>
anywhere. Of course, if it is in a constant context then the const
keyword may be absent, it is still statically known to be there.
This rule is compatible with an evaluation at compile time. However, it doesn't require an implementation to perform the evaluation at compile time and reuse that result every time it is needed at run time. For instance, the expression could be evaluated at compile time and the result could be discarded, and then evaluation and canonicalization could be done at run time. Also, because the constant sublanguage is small and Turing-incomplete, execution is guaranteed to be possible at compile time. So when we want to determine whether an error should be emitted, we can evaluate the constant object expression.
So if we do say 'Assertions are enabled during the evaluation of a constant object expression' then it is allowed for an implementation to raise the compile-time error early ("at compile time"). That's probably exactly what we want.
This also means that if a tool gives rise to an execution where a constant object expression is actually evaluated at run time then there will be a run-time error, even though we set out to say that these errors should occur at compile-time. But tools don't have to do this, so in practice we can maintain that these errors occur early.
If an execution uses hot reload then compilation will occur at run time anyway.
Don't you think that would be OK for tool teams to work with?
See #447 for context.