[SPEC] Constexpr Specification#97
Conversation
Add spec/constexpr.rst defining a constexpr as a property of a const variable whose initializer is fully evaluable at compile time: the composition rules, the recursive validation requirement, and the aggregate (array and tuple) cases. This is the analysis-derived notion used to size statically-sized arrays and to constrain global initializers. Examples are written against the staging array model (no spread operator).
Add spec/constexpr to the Language Specification toctree, after declarations, so the new page is included in the rendered docs.
Replace the old single-scalar-literal restriction on global initializers with the constexpr rule (see sec:constexpr). Globals may now reference other globals and use arithmetic and constexpr aggregates, provided the initializer is fully evaluable at compile time. Spell out the consequences: no function/procedure/IO in initializers, no dynamically-sized array globals, and all globals are implicitly constexpr. The list of legal global statements is unchanged.
novo52
left a comment
There was a problem hiding this comment.
typedef.rst says an array-alias size may use only "arithmetic operations on scalar literals … not constant propagation." constexpr rule 5 explicitly permits "Other variables that are themselves valid constexprs," and the array example sizes with integer[WIDTH] / integer[ELEMENT]. So typealias integer[WIDTH] foo; is illegal per typedef.rst but should be legal under the constexpr model.
My suggestion: array-alias sizes should be constexpr
array.rst does not speak on statically-sized or dynamically-sized arrays at all. These are different from explicit vs inferred size. Are explicitly sized arrays necessarily statically-sized? See the example on line 121, this is an explicit-and-dynamically-sized array. Clarify either here or in arrays.rst.
| * Functions, procedures, and I/O operations may not appear in a global's | ||
| initializer. | ||
| * A global may not have a dynamically-sized array type (e.g. ``integer[*]``), | ||
| because its size would not be known at compile time. |
There was a problem hiding this comment.
| because its size would not be known at compile time. |
Not true: integer[*] X = [1,2,3]; is known.
Conflation of inferred size and dynamic size; will be confusing without clarification here or elsewhere.
There was a problem hiding this comment.
Apologies, these are artifacts from my vector/array proposals. Updating accordingly, thank
| An expression is a valid ``constexpr`` if it is composed exclusively of: | ||
|
|
||
| 1. Literals of base types (``boolean``, ``integer``, ``real``, ``character``). | ||
| 2. Operators, including ``+``, ``-``, ``*``, ``/``, ``not``, ``and``, ``or``, |
There was a problem hiding this comment.
| 2. Operators, including ``+``, ``-``, ``*``, ``/``, ``not``, ``and``, ``or``, | |
| 2. Operators ``+``, ``-``, ``*``, ``/``, ``not``, ``and``, ``or``, |
"Exclusively" does not work with "including". I assume these are the only valid operations.
There was a problem hiding this comment.
Do you consider an expression like var a <- std_input to have the <- operator? I think this is what I'm gating against. Maybe a better wording is The operators ``+``, ``-``, ...
There was a problem hiding this comment.
Yes, that is good. The problem with "including" is that it implies other operators can also be constexpr, but (I believe) your intent is to communicate that this is an exhaustive list of `constexpr '- compatible operators.
|
|
||
| * Functions, procedures, and I/O operations may not appear in a global's | ||
| initializer. | ||
| * A global may not have a dynamically-sized array type (e.g. ``integer[*]``), |
There was a problem hiding this comment.
| * A global may not have a dynamically-sized array type (e.g. ``integer[*]``), | |
| * A global may not have a inferred-sized array type (e.g. ``integer[*]``). |
There was a problem hiding this comment.
However a global may have an inferred sized array type, we just need to make sure that the array type size is inferrable at compile time (eek that sounds circular). I can't think of a way this does not hold in the current spec.
Array-alias sizes must be valid constexprs (permitting constant propagation), updating typedef.rst away from the literals-only rule. Disentangle inferred size from dynamic size: integer[*] denotes an inferred, compile-time-known size, while the dynamically-sized type is the vector. Explicit array sizes must be constexprs, so a runtime-sized collection requires a vector. Updates globals.rst, constexpr.rst, and array.rst accordingly. Assisted-by: Agent (claude) <ai@blobfish.icu>
|
Note that there is an issue declaring all array lengths as constexpr: What happens to the range operator with a non-constexpr index? Should |
We might consider making all arrays statically sized, and only vectors may have dynamic size. This would achieve this goal. Edit: I see this is your idea as well, lol |
novo52
left a comment
There was a problem hiding this comment.
With the two changes I suggested, this is overall an excellent change. I think that making arrays static in size is a win for student learning. They will have the opportunity to do optimizations that would be complicated by dynamically-sized arrays, and the distinction between vectors and arrays allows student compilers an "easy-out" if they do not wish to do the same optimizations for vectors; they can implement vectors with {cap, len, ptr}, but arrays are an easy optimization win for them.
| aggregates as their size is not known at compile time. | ||
| A ``vector`` (the dynamically-sized type) can never be a ``constexpr`` | ||
| aggregate, since its size is determined at runtime. An inferred-size array | ||
| such as ``integer[*] X = [1, 2, 3]`` *can* be a ``constexpr`` when its |
There was a problem hiding this comment.
I feel "can" is the wrong language--an inferred size array must be constexpr, as all arrays must be. Therefore, for [*], the RHS must be constexpr.
| An expression is a valid ``constexpr`` if it is composed exclusively of: | ||
|
|
||
| 1. Literals of base types (``boolean``, ``integer``, ``real``, ``character``). | ||
| 2. Operators, including ``+``, ``-``, ``*``, ``/``, ``not``, ``and``, ``or``, |
There was a problem hiding this comment.
Yes, that is good. The problem with "including" is that it implies other operators can also be constexpr, but (I believe) your intent is to communicate that this is an exhaustive list of `constexpr '- compatible operators.
I agree. exhaustive it is, shall change wording |
I am thinking about this, would that mean that a range always returns a vector and never an array? And I can't remember if we deleted the iterator for loop, but if not that would mean that we can iterate over the vectors? I think this direction makes sense if we do not want to unify arrays/vectors, but I opine that optimization becomes significantly less interesting in this case. |
|
Taking executive privilege here: arrays are statically sized and vectors are not. Note that statically sized does not mean the size is known at compile time, only that the size can't change. Note also that both arrays and vectors:
|
|
Ron: What does it mean for vectors to be dense? Should, for example, vector[10] or vector<integer[10]> be a single allocation? If we allow nested vectors and arrays and try to make them work together (i.e. as a single multidimensional array with some growable dimensions) we run into issues with the vectors all having different lengths, AKA ragged vectors. I was wondering what your vision of this is. |
No description provided.