Continuing the discussion from Compile-Time Constant Expressions for Swift:
Since code has already been written, I hope my concerns don't end anyone's career. I'm hoping they can be incorporated with what's been done already. I came up with these questions after writing my latest fixed-size array manifesto.
In a previous constexpr
thread, someone said that the MemoryLayout
properties couldn't be constexpr
because the implementation worked at the SIL level and types aren't laid out until after that. I reasoned that since type layout was delayed until the IR generation phase, I could move FSA bounds to that phase too. For example:
func byteCopy<T>(of t: T) -> [MemoryLayout<T>.stride ; UInt8] { /*...*/ }
This function converts a given object to its raw bytes, but statically sized and scoped. Obviously, since the MemoryLayout
property can't be determined until IR-Gen, the array's shape is also delayed until IR-Gen. But what about an object like:
let sample: [delayed ; Int] = [1, 2, 3, 4, 5, 7]
where "delayed
" is a (bike-shedded) keyword indicating that the given bound will be specified later. Here, "later" is pretty quick since we can see that the literal has six terms. The array's shape can be determined as soon as the AST phase. What difference does that make? Imagine we made a function like:
func makeTuple<T: [_ ; some Any]>(from a: T) -> ( #dup(T.staticCount ; T.Element) )
where "#dup
" duplicates the given token a given number of times. You couldn't use this with a result from byteCopy
, but using sample
should be perfectly reasonable.
Even if you respond with "#dup
" shouldn't be supported, the idea still applies to conditional compilation. The "#if
" and related constructs should test and work as early as possible to exclude banned code from ever being compiled (by SIL and later; it still needs to be parsed).
This shows we need at least two kinds of constexpr
, one that works at the AST level and another that works at the IR-Gen level. We could have ones in-between for SIL constant expressions, like others are working on now. There is a sub-typing relationship; anything that takes a IR-Gen constexpr
should be able to accept AST and SIL ones, and SIL constexpr
usages can accept AST ones.
What about @compilerEvaluable
functions? What level should they have? They should probably work like rethrows
; at IR-Gen level if at least one operand is an IR-Gen constepxr
, otherwise at SIL- or AST-level depending on how early the last constexpr
is determined. However, we need to mark a function requiring and/or returning a fixed level when necessary. For example, makeTuple
needs to be marked that T.staticCount
doesn't just need to be constexpr
, but an AST-only constexpr
.
With the different levels, we should had worked on either AST constexpr
first, then generalized, or IR-Gen constexpr
then refined. Now, we're in the middle with SIL constepxr
and we need to expand both ways, and hope the SIL-to-AST refinement is compatible with the SIL-to-IRGen generalization.
Actually, what use cases are there for SIL-level constexpr
? I showed examples for the AST and IR-Gen levels. Is there any reason for any constexpr
that can't be done during AST to not be delayed until IR-Gen?