-
Notifications
You must be signed in to change notification settings - Fork 208
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
Spurious reference to function literals in 'constant context' #3619
Comments
I think it's not wrong, and it's deliberately to ensure that a constant context does not cross into a nested function. A I'm not sure being minimal here is desirable. One could argue that we should add something like "immediate subexpressions of an expression in a constant context which is itself a constant expression", but that gets us into cyclical issues, because for |
Makes sense. However, we don't usually specify that a source code construct with an error has any particular properties, we'd usually say/imply that it isn't relevant whether or not I'd recommend that we change the normative text 'where |
I'd like to introduce the notion of "constant evaluation", as a variant of evaluation that happens in a constant context. If we do that, an expression in a constant context is evauated using constant evaluation semantics, which is like normal semantics, except that it can end in either a value or a compile-time error. A lot of expression shapes directly evaluate to a compile-time error. Actually, I think I'd want four modes:
In a required constant context (optional parameter default values, final field initializer of class with constant constructor), collection literals and object creation expressions are not automatically made constant, like in a constant context. Then it's not so much that an expression is in a "constant context", but that the expression is evaluated using constant evaluation, which automatically evaluates subexpressions using constant evaluation as well, if the subexpression can be a constant expression. That evaluation will fail when reaching a function literal, and therefore it will never try to evaluate the body in any context. In a normal evaluation context, obvious constants should still be canonicalized (simple literals, type literals with constant type arguments, static/top-level tear-offs, constant instantiations of those). That would mean that an expression like (I think it's only really strings where we might not canonicalize all possible constant strings today, which is acceptable because the spec still doesn't explicitly require constant strings to be canonicalized. It just requires constant |
@lrhn, I really appreciate the urge to dig deeper and find some fundamental structures, that's great! However, I'm a little worried about a future where we have 4 (or, over time, perhaps even more) modes of evaluation. The language specification tries very hard to reduce two modes of operation to one: It is a special property of constant expression evaluation that the results are globally canonicalized (that seems to imply that we have two modes). However, constant expression evaluation is explicitly specified to be no different from non-constant evaluation, it's just at the very end that the resulting object is checked against prior results of constant evaluation for a existing result which is "the same", in which case the older one is used ("sameness" is a bit tricky to define, but this is working today). The canonicalization step is specified separately for specific expressions, including collection literals and constant object expressions ( The fact that constant expressions can be evaluated at compile time, and run-time evaluation can just be "return this pre-computed result" is described as an option, not a requirement. So we probably have to have two modes of evaluation, but they only need to differ as follows: (1) Normal expression evaluation is given (this is most of the language specification). Constant evaluation differs from normal evaluation by canonicalizing each expression evaluation step with certain syntactic forms (collection literals, constant object expressions, etc.). And (2) exceptions are handled differently, as described below. This is not quite "just one mode", but it is as close as we can get. The point is that canonicalization is described as a different overall semantics, and everything else (of which we currently have just one thing, exception handling) is treated as "the normal semantics" respectively "the constant evaluation semantics" in the specification of each construct. Hence, the dynamic semantics of throw expressions would need to be specified for normal evaluation and for constant evaluation separately. Then we won't need to say "if it would have thrown", anywhere, we just need a new primitive behavior which is only used during constant expression evaluation at compile time: "Take note that the following throw expression was evaluated: ...., and then terminate the current constant expression evaluation, reporting that error". With maybe some weasel words to say that there can be more than one error. In addition to that, we probably need to mention that the runtime which is evaluating the constant expression at compile time will have the same behavior in case of runtime errors that aren't initiated by a throw expression evaluation (say, "divide by zero", "failed type cast", ..., and in the future maybe "out of memory" etc., depending on whether/how the constant expression sublanguage is generalized). We probably have to specify that constant expression evaluation must be used for specific constant expressions (e.g., with This is still only two modes rather than 4, and they differ in only two very specific ways. I'd prefer if we can keep it minimal like that.
I guess this would apply when an expression isn't in a constant context, and otherwise isn't required to be constant, but the expression is a constant expression (e.g., We could require such expressions to be subject to constant evaluation (hence canonicalization), but I'd prefer to hesitate a bit and make it optional. How much work would it take to be able to promise that we find all of those "fuzzy constants", and how helpful is it (for performance, for program correctness) to require that they are subject to constant evaluation? Is it a breaking change? I proposed in #985 that we should clarify the rules about canonicalization of strings. The fact that an |
Thanks to @sgrekhov for noticing this!
The definition of what it means to be in a constant context includes a case involving a function literal, here.
However, the situation which is described there can never occur: It is always a compile-time error for a function literal to occur in a constant context, because a function literal is never a constant expression.
This part of the rule is only relevant if we introduce a notion of constant function literals (see #1048 for a proposal ... from 2012 ... with about 250 supporting emojis).
We may wish to eliminate this part of the 'constant context' rule (or we may wish to add constant function literals). In any case, the current situation is somewhat confusing.
The text was updated successfully, but these errors were encountered: