Skip to content

Adjust the constant expression rules for lists, maps, and conditional expressions#4313

Merged
eernstg merged 7 commits into
mainfrom
specify_constant_fix_apr25
Apr 8, 2025
Merged

Adjust the constant expression rules for lists, maps, and conditional expressions#4313
eernstg merged 7 commits into
mainfrom
specify_constant_fix_apr25

Conversation

@eernstg
Copy link
Copy Markdown
Member

@eernstg eernstg commented Apr 2, 2025

See #4311 for background information.

This PR changes the rule about constant expressions of the form b ? e1 : e2 such that it is required that every constant expression of this form is also a potentially constant expression. The point is that this maintains the subset relationship "every constant expression is also a potentially constant expression", which is otherwise maintained in all cases. Also, this implies that the checks that are applied to conditional expressions are slightly stronger than they have been so far.

The PR also changes the rules about constant collection literals and constant object expressions (const C()): They used to have a special part about being a potentially constant expression, plus some constraints on elements/arguments. However, this is already specified in the sections about collection literals / constant object expressions, so there's no need to repeat that (incompletely) in the 'Constants' section. So this PR simplifies those rules to eliminate the redundancy.

Based on the discussion in issue 4311 we might want to add a rule along the lines of "an expression that contains a formal parameter of an enclosing constructor declaration is not constant". This PR hasn't done it, but it could be added.

We need to consider breakage:

The rule about conditional expressions is in principle breaking, because it adds an extra constraint on some constant expressions. However, I suspect that no code is actually broken because the requirements for being a constant expression would also imply the requirements for being a potentially constant expression.

The simplification of the rules about collection literals are non-breaking because everything which has been deleted is already specified as requirements in other sections.

Comment thread specification/dartLangSpec.tex Outdated
Comment thread specification/dartLangSpec.tex
@eernstg
Copy link
Copy Markdown
Member Author

eernstg commented Apr 3, 2025

Responded to the comments, PTAL.

@eernstg
Copy link
Copy Markdown
Member Author

eernstg commented Apr 3, 2025

I made the change I suggested, such that collection literals follow the same approach as all other constructs.

Copy link
Copy Markdown
Member

@lrhn lrhn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add entry in changelog saying why this change was made.

@eernstg
Copy link
Copy Markdown
Member Author

eernstg commented Apr 3, 2025

Just discussed this with @leafpetersen who noticed that the rules are redundant in certain ways: Constant object expressions and constant collection literals already have a set of rules in the sections specific to those constructs, and everything in the 'Constants' section was redundant. So they now just say, essentially, "a constant object expression is a potentially constant and constant expression" and similarly for the other forms.

This preserves the property that the set of constant expressions is a subset of the set of potentially constant expressions.

We can (and probably should) add a rule that if e is an expression that contains v is a subexpression and v denotes a formal parameter of an enclosing constant constructor declaration then e is not constant, but this PR still doesn't do that (because that's clearly breaking).

@eernstg
Copy link
Copy Markdown
Member Author

eernstg commented Apr 3, 2025

@lrhn, PTAL, the latest changes are non-trivial.

Comment thread specification/dartLangSpec.tex
Copy link
Copy Markdown
Member

@lrhn lrhn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, ship it!

@eernstg
Copy link
Copy Markdown
Member Author

eernstg commented Apr 8, 2025

The updated specification of what it takes for a conditional expression to be constant is the already implemented behavior.

An example: The current implementation of the analyzer as well as the CFE (Dart SDK 3.8.0-250.0.dev and Flutter SDK 3.31.0-1.0.pre.363) reject the following as an error:

var v = 'World';

void main() {
  const c = 2 > 1 ? 'Hello' : v; // Error!
}

This clearly shows that the current wording of the rule about constant conditional expressions is not implemented, the actual implementation is more strict (it requires the unused branch to be a constant expression, and the spec does not).

Checking visitConditionalExpression of the relevant visitor, we can see that the implementation in the analyzer checks exactly the condition which is added by this PR (that is, the new behavior is already the implemented behavior in the analyzer).

@chloestefantsova found the reason why the same error is reported by the CFE, and it turns out to be implemented in a very early phase of the compilation (in the BodyBuilder) where it is checked that every expression in a constant context is potentially constant.

In both cases, this shows that the tools implement the approach which is specified in this PR (namely: a conditional expression is not constant if it is not potentially constant), and hence there is no breaking change, and not even an implementation effort.

@eernstg eernstg merged commit f80aceb into main Apr 8, 2025
4 checks passed
@eernstg eernstg deleted the specify_constant_fix_apr25 branch April 8, 2025 09:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants