Proposal: introduce new RLS mechanism to share peers #20627
Labels
proposal
This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone
This is a proposal that's been cooking in the back of my mind for some time, and I think it's finally fully-formed.
Background
In Zig, we refer to a set of expressions as being "peers" if Peer Type Resolution is applied to the set. For example, consider the following code:
Here, the expressions
my_u32
,10
, and20
are peers. This is what allows this code to work correctly at runtime despite10
and20
having typecomptime_int
: Peer Type Resolution resolves that these expressions must all have typeu32
(the peer type ofu32
,comptime_int
,comptime_int
), so the compiler inserts code to coerce them each tou32
.Unfortunately, the peer mechanism is fairly limited. In a sense, peers do not "nest well". For instance, consider this code:
One might expect this code to act identically to the
switch
expression above. However, at runtime, this causes a compile error. This is because chained else-if expressions are not a first-class concept in Zig, but rather nested expressions; so, this expression parses asif (my_u8 == 0) my_u32 else (if (my_u8 == 1) 10 else 20)
. The innerif
expression here has twocomptime_int
peers, so PTR determines the expression as a whole to have typecomptime_int
, meaning a value of a comptime-only type depends on runtime control flow.This isn't a huge issue, but can be unintuitive and frustrating in some cases. Notably, #11957 actually special-cased a few syntax forms with the explicit goal of making sub-expressions peers when they otherwise would not be. This compiler change had other benefits, but this language change was the primary motivator. The problem with this is that it subtly complicates the language specification, and leads to somewhat unintuitive behavior where seemingly-equivalent code (for instance, just adding some redundant parentheses) can affect a program's behavior. Ideally, we would have a more general mechanism to achieve the same result.
Proposal
Introduce a new form of result location, which indicates that an expression's result should be a peer for a specific block. In terms of implementation, it contains the
Zir.Inst.Index
of a ZIR block which the result should be abreak
operand to.In general, in any case where a pointer result location (
std.zig.AstGen.ResultInfo.Loc.ptr
) can be forwarded to sub-expressions, this result location will also be eligible for forwarding. This means that #11957 will no longer have an impact on language semantics (although the corresponding compiler changes are still valuable for optimization purposes).Here's an example of what this allows in practice:
Today, this overall expression performs PTR in several places:
b
andc
are peer resolved to typeT1
d
,e
, andf
and peer resolved to typeT2
a
,T1
,T2
, andg
are peer resolved to the final typeT
ofx
Under this proposal,
a
,b
,c
,d
,e
,f
, andg
would all be peers, so only one instance of peer resolution occurs. This leads to a more intuitive behavior when considering comptime-only types involved in runtime control flow.This also has some minor performance benefits, both in the compiler itself and in generated unoptimized code. In terms of compiler performance,
Sema
now has to do less work overall; the code snippet above currently requires analysis of 9break
instructions and 3 peer resolutions, whereas under this proposal, it only needs analysis of 7break
instructions and 1 peer resolution. Regarding generated code, the AIR here would have a simpler structure, without multiple "layers" ofbr
instructions appearing. It would also avoid any potential intermediate coercions -- for instance, if the final resolved type isu32
, butb
andc
have typesu8
andu16
respectively, today there would be an initial coercion ofb
to au16
followed by the outer coercion to au32
, whereas under this proposal all peers are directly coerced to their final type.The text was updated successfully, but these errors were encountered: