Preparing Swift compiler stage reentrancy in preparation for "constexpr"

[I’m not sure which list should cover this.]

I once thought of having a “#protocols(SomeTypeOrProtocol)” that was a type alias to a composition of all protocols the given type/protocol conforms to (or “Any” if none). It seems simple, but all current uses of protocols requires explicit mentions by the user, while this new facility would require looking back at the table of existing protocols.

It seems that implementing something like C++’s “constexpr” would have the same problem. The compiler stages can’t be one way anymore; a non-literal that’s needed for a compile-time context has to be deferred until its components can be evaluated in compile-time contexts (down to literals). So we would partially evaluate down to SIL, run the SIL in the compiler’s environment to determine all the compile-time data (not a big deal for discrete data, but we’ll have to ignore differences between the compiler’s environment and the target platform’s environment w.r.t. floating-point processing), then go back to the semantic phase to fill in the “constexpr” constants and evaluate the SIL again, possibly taking several cycles depending how deep “constexpr” is needed.

As an example, take a mythical function that converts a mythical one-dimensional fixed-size array to a tuple:

func tuple<T, let N: Int>(from: [N; T]) -> ( #dup(N ; T) )

We would have to defer the return type until each call of “tuple(from:)”, where we look at the input’s type’s shape to determine N, then expand the #dup, then run the semantic phase again with the final type. What if N itself was based off a “constexpr” function? Then we would need (at least) two passes.

Should we start preparing the compiler stages for loopback now? Or do we have to figure out what exactly we want “Swift constexpr” to mean first?

···


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

[I’m not sure which list should cover this.]

I once thought of having a “#protocols(SomeTypeOrProtocol)” that was a type alias to a composition of all protocols the given type/protocol conforms to (or “Any” if none). It seems simple, but all current uses of protocols requires explicit mentions by the user, while this new facility would require looking back at the table of existing protocols.

This is plausible to implement at run-time by inspecting the table of protocol conformances. It won’t be a static result, though, because one could dlopen() a shared library that introduces new conformances for existing types.

It seems that implementing something like C++’s “constexpr” would have the same problem. The compiler stages can’t be one way anymore; a non-literal that’s needed for a compile-time context has to be deferred until its components can be evaluated in compile-time contexts (down to literals). So we would partially evaluate down to SIL, run the SIL in the compiler’s environment to determine all the compile-time data (not a big deal for discrete data, but we’ll have to ignore differences between the compiler’s environment and the target platform’s environment w.r.t. floating-point processing), then go back to the semantic phase to fill in the “constexpr” constants and evaluate the SIL again, possibly taking several cycles depending how deep “constexpr” is needed.

You’re thinking in C++ terms. In Swift, it might be more reasonable to think of “constexpr” as something that will be guaranteed to become a constant at some point in the SIL pipeline (as part of the “guaranteed” transformations we perform for diagnostic generation from SIL), but that the actual constexpr value would be kept as an abstract expression in earlier stages of the pipeline. John McCall made this point better than I in the earlier discussions of constexpr (a few weeks ago).

As an example, take a mythical function that converts a mythical one-dimensional fixed-size array to a tuple:

func tuple<T, let N: Int>(from: [N; T]) -> ( #dup(N ; T) )

We would have to defer the return type until each call of “tuple(from:)”, where we look at the input’s type’s shape to determine N, then expand the #dup, then run the semantic phase again with the final type. What if N itself was based off a “constexpr” function? Then we would need (at least) two passes.

Should we start preparing the compiler stages for loopback now? Or do we have to figure out what exactly we want “Swift constexpr” to mean first?

The latter. We should consider that it might be absolutely fine for Swift to effectively be dependently-typed up until some point in the pipeline, and that might fit better in the language model than introducing circularity.

  - Doug

···

On Aug 19, 2017, at 12:27 PM, Daryle Walker via swift-evolution <swift-evolution@swift.org> wrote: