Since this is along the path to @const, is the idea that the following that this:
let schemaRowSize = 32
let buffer: InlineArray<(2 * schemaRowSize), UInt8>
is equivalent to some future proposed syntax like where it can be explicitly declared:
@const let schemaRowSize = 32
let buffer: InlineArray<(2 * schemaRowSize), UInt8>
If so, "inferred compile-time constant expressions" feels like the right way to capture what's going on here. I only saw the root word "infer" twice in the proposal text; it might be helpful to bubble that right up to the introduction.
Yeah exactly, sorry I should have been more explicit in my original post. The parens aren't really there to disambiguate between types and expressions, they're primarily there to guide generic argument parsing in expression contexts, e.g for cases like:
(x<x && b, c > .d)
Today this is a tuple expression, but if the rule for generic argument parsing were that it consumes everything up to the next , or > then this would become a generic argument list for x.
That being said, I think there are still a couple of source breaking cases that should be noted in the proposal:
(x<(x + 1), c > .d): currently a tuple, would become a generic argument list
If you shadow a type with a value then today type resolution ignores the value, but with the proposed change we would prefer the value (credit to @rintaro for this one):
let Int = 0
Array<(Int)>()
I would expect both to be incredibly rare though.
This one is fine since . is a "generic disambiguation token" and so a < b, c > .d is always parsed as a generic argument list in expression position.
I don’t think that’s correct. The current parser already uses heuristics to determine whether identifier '<' in expression position should be parsed as a generic argument clause, and it already parses this as such today.
I'm concerned this proposal reintroduces a fixed set of compiler-known primitive types and operations for @const expressions which directly undermines Swift's core design that even the "basic" types live in the stdlib as ordinary value types.
As proposed, we're now carving out a special compile-time subset, treating some stdlib types as more equal than others. That breaks the uniformity and extensibility Swift deliberately built in.
It feels like a step backward, re-creating the exact "primitive types" barrier Swift eliminated.
There are, as far as I can tell, two existing forms of constant evaluation in Swift that are semantically meaningful (that is, not just optimizations). First, certain integer expressions that overflow cause a compile-time error instead of a runtime error, such as UInt8(256) and Int.max + 1. Second, certain boolean expressions are known to be true or false at compile time, which affects control flow rules; for example, while true {} and while 1 == 1 {} are understood to never return, so they can be the body of a Never-returning function or the else block of a guard statement.
The existing constant evaluation even seems to understand local immutable variables (but not global immutable variables, for some reason). For example, this function compiles successfully:
func f() -> Never {
let myTrue = true
while myTrue {}
}
It would be nice for the proposal to discuss how these existing forms of constant evaluation will interact with the proposed literal expressions, such as if they will eventually become the same concept.
This isn't really much different than all of the standard library types that use _underscoredFeatures. It's a testing ground for a later public feature. Like _read & _modify accessors, which we're finally standardizing now.
_read and _modify and most other "underscore" features I'm aware of aren't restricted to a set of known types or functions though. _read and _modify while underscore have been fundamental for efficient custom data structures or just simple wrappers even outside of the standard library.
This is proposed as a "public" feature. It is already available for testing with the Experimental Feature Flag LiteralExpressions
Another concern I have is that this only works with lets within a single swift module. This means that I can end up in a situation where I'm not able to split and move my code between modules/frameworks as needed.
It is also a bit too light that I would be able to use it for anything I can currently think of. I would at least need to be able to use MemoryLayout<T>.stride/size/alignment to be able to play around with it so maybe this isn't a problem until this can be used more widely.
This may be counter-productive, but I think they should be considered (and if selected, trigger errors). That way, type checking and operator overloading are consistent between modes; a future @const doesn't cause the evaluation to change; and developers aren't surprised when they see their custom code totally ignored.
I would expect that in practice, few developers are overriding Int.+. I hope.