-
Notifications
You must be signed in to change notification settings - Fork 214
Should constant expressions be potentially constant? #4311
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
Comments
Likely also applies to the other conditionally branching expressions, Adding "further" won't be enough, because it still won't evaluate the expression, and being a potentially constant expression also depends on being a constant expression. class C {
static const wtf = bool.fromEnvironment("break-me");
List<int> list;
const C(int? v, bool useV) : list = v != null ? const [wtf ? v : 0] : [];
}
} Here But now we're back to the original problem, that we'd like to say that (By all means add the "further", it will reduce the problem, it just won't remove it entirely.) |
What would a real solution to this problem be. (And while we're there, maybe add more obvious static errors to constant expressions). Modes and contextsDefine: Mode of evaluation. Dart has two modes of evaluation: Non-Constant ("normal"), and Constant. When evaluating an expression, it is evaluated as one of those. Define: Constantness Context. Every expression occurs syntactically in a context which either implies, allows or requires one of those evaluation modes: a non-constant, potentially-constant, constant (aka constant-implies) or constant-required context.) The default is non-constant, we'll only specify which constructs introduce the other contexts. That is, every expression has an associated "constantness-context" which it must satisfy. Different expressions propagate their context to their sub-expressions. Collection elements: Like expressions, they have a constantness context and are evaluated in a mode of evaluation. Define: Constant availability. Dart constant values exist in two variants: Eager and Late, where late constants arise from using Some non-consistent evaluations create (inherently) constant values, if they're built bottom-up from constant operations on constant values. That's entirely to allow canonicalization of strings in non-constant contexts. We'll then define the expressions that are valid in different contexts. Constant expressionsAn expression is only allowed in a potentially-constant, constant or constant-required context if it's specified below. First define a constant reference as a name, possibly qualified, that denotes some declaration: (The important part is that it denotes some declaration, and it doesn't go through a deferred import.) Then the following expressions are not automatically compile-time errors if they occur in a context other than a non-constant context.
... etc. ... Default value expressions and instance variable initializer expressions of a class with a Generative const constructor initializer lists entries/redirections are potentially constant contexts. The usual places introduce const contexts. A Then rather than saying anything about how an expression must evaluate, we say that it's in a constant context, and then only some expressions are allowed there. And among those which are, some may still cause compile-time errors because of types or, if evaluted, concrete values. Or failed downcasts from I think something like this could be both well-defined, implementable, and possible to reason about. |
FYI @bwilkerson, for that revision on const errors, you want to do. |
@lrhn wrote:
All of those already have the word 'further' as proposed here.
This is working as intended, and I do not wish to change anything such that those expressions will be evaluated during constant evaluation, or during a determination of whether or not a given expression is constant. class A {
final int i;
const A(Object o): i = o is bool && o ? 1 : 0;
}
void main() {
const c = [A("Hello!"), A(true)];
}
Looking at the rules about constant expressions, I can see that we have lots of cases where an expression A special exception (a mistake, I'd say) is that potentially constant collection literals require subexpressions to be constant (not just potentially constant, as with lists and sets), or leaves them completely underspecified (as with maps). We should fix that. Good catch! class C {
static const wtf = bool.fromEnvironment("break-me");
final List<int> list;
const C(int? v) : list = v != null ? const [wtf ? v : 0] : const [];
} (I made a couple of changes to avoid errors, and removed Now, I don't think we can hope to report an error for constant expressions based on criteria like "if some subexpressions in this constant expression were to have a different value then the expression wouldn't be constant". For example, we probably shouldn't emit a warning in the situation where More realistically, we could introduce a rule along the lines of "an expression that contains a formal parameter of an enclosing constructor declaration is not constant." This would turn
Next comment:
That would be an interesting adventure, and I can see that you have taken a number of steps. Also, it's potentially a non-trivial change. In a similar vein, we have #1296 where I've suggested that we should improve on the static analysis of potentially constant expressions. However, right here I think we can get a reasonable improvement simply by (1) adding 'further' to the For instance, #4313 (where I haven't done (3)). |
I just noticed the same thing reviewing your change. If people did more constant The example I wrote in that review is: void main() {
const v1 = bool.fromEnvironment("maybe") ? "A string" : 42; // is 42.
const v2 = v1 is String ? <int>[v1.length] : <int>[v1 as int];
print(v2.first); // 42
} This runs, but the analyzer reports an error because That would be the consequence of requiring the non-taken branch to be a potentially constant expression. Which also means that it's already the case today for: const dynamic v1 = bool.fromEnvironment("maybe") ? "A string" : 42; // is 42.
class C {
final List<int> l;
C() : l = v1 is String ? const <int>[v1.length] : const <int>[v1 as int];
} because that puts the conditional expression in a position where it must be potentially constant, The "easiest" way to a better static-constant-checking seems (IMO) to be adding a notion of "statically constant expression", which does not do evaluation, but which does check that the expression doesn't contain non-constant variables or type variables. (That's what the propagated constant context above was about. If a parameter occurs in a constant context, it's an error, whether it's evaluated or not. Something like this.) Then the only thing that actually evaluates is evaluation. Something can be invalid as a constant or potentially constant expression when not evaluated, and it can fail when evaluated, but we never need to know what evaluation does to a branch that isn't taken. |
Great point, and a great analysis overall! |
Proposed spec change: #4313, updated recently. |
#4313 has landed. |
Uh oh!
There was an error while loading. Please reload this page.
The language specification implies in commentary that every constant expression is also a potentially constant expression:
However, this is not expressed in the case of conditional expressions (
e1 ? e2 : e3
):For example,
true ? 1 : v
is a constant expression which is not potentially constant in the following example:We could add the word 'further' to make it 'It is further constant', like many other cases in the same itemized list, which would cause
true ? 1 : v
to be non-constant because it isn't potentially constant (becausev
isn't a constant variable, and it isn't a formal parameter of a constant constructor).Alternatively, we could decide that it is not a problem that some constant expressions aren't potentially constant, and we could then adjust the commentary in the language specification.
I'd recommend the former. @dart-lang/language-team, WDYT?
Note that this would be a mildly breaking change. For instance, the CFE follows the specification and does not report the error on line 48 and 52 of LanguageFeatures/Static-access-shorthand/constant_expression_A10_t17.dart, but the analyzer does report these errors.
The text was updated successfully, but these errors were encountered: