rucc-sema 0.10.21

Type checking, conversions, initialization, constant evaluation, and the typed AST.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
//! Typed expressions.
//!
//! Design: `spec/07-types-and-semantics.md` sections 7.2 and 7.14.
//!
//! Every node here has a type and a value category, and every conversion the language performs
//! without being asked is a [`Conversion`] node written into the tree. That is the whole point
//! of the typed tree: nothing downstream is allowed to work out that an `int` and a `long` must
//! have met somewhere, because if the two operands of an addition do not already have the same
//! type then semantic analysis has a bug and the verifier is entitled to say so.
//!
//! The operators are [`rucc_ast::UnaryOp`] and [`rucc_ast::BinaryOp`], the same ones the parser
//! read, rather than a second set with the same names. What the typed tree adds is not different
//! operators, it is knowing what they are applied to.

use rucc_ast::{BinaryOp, UnaryOp};
use rucc_base::{Idx, IdxRange};
use rucc_types::TypeId;

use crate::decl::DeclId;
use crate::stmt::StmtId;
use crate::tast::{ConstId, LabelId, StrId};

/// One typed expression in the arena.
pub type ExprId = Idx<Expr>;

/// The table of references to expressions, which is what a call's arguments are a run of.
#[derive(Debug)]
pub struct ExprRef;

/// A run of expressions.
pub type ExprList = IdxRange<ExprRef>;

/// An expression, its type, and what may be done with it.
///
/// Twenty four bytes: the kind, the type it has, and the category it is in. The type is in the
/// node rather than in a table beside it, which is the opposite of what the untyped tree does
/// with spans, because everything that walks this tree reads the type at every node and almost
/// nothing reads the span at any node.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Expr {
    /// What the expression is.
    pub kind: ExprKind,
    /// The type it has, after every conversion that applies to it.
    pub ty: TypeId,
    /// What may be done with it.
    pub category: Category,
}

impl Expr {
    /// An expression of the given kind, type and category.
    #[must_use]
    pub const fn new(kind: ExprKind, ty: TypeId, category: Category) -> Expr {
        Expr { kind, ty, category }
    }
}

/// What may be done with an expression, which C decides rather than the programmer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Category {
    /// A value. It has no address and nothing may be assigned to it.
    Rvalue,
    /// An object. It has an address, it may be assigned to when it is not `const`, and reading
    /// it is a [`Conversion::Lvalue`] rather than something a reader has to remember.
    Lvalue,
    /// A bit-field, which is an lvalue whose address cannot be taken and whose assignment
    /// truncates to the declared width. Kept apart from an ordinary lvalue because the two
    /// rules above are the ones a compiler forgets.
    Bitfield,
    /// A function designator, which is not an lvalue and which decays to a pointer everywhere
    /// except under `sizeof` and `&`.
    Function,
}

/// What an expression is.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExprKind {
    /// A node that was already the subject of a diagnostic.
    ///
    /// Poisoned, in the sense of `spec/06-lexer-and-parser.md` section 6.8: nothing is reported
    /// about one of these, which is what stops one bad declaration becoming forty bad uses.
    Error,
    /// A constant, in the value table. Every constant that could be folded already has been.
    Const(ConstId),
    /// A string literal, which is an array of characters with static storage duration.
    Str(StrId),
    /// A use of a declared object or function.
    Decl(DeclId),
    /// `base.field` or, after the pointer has been dereferenced, `base->field`.
    Member {
        /// The object the field is in.
        base: ExprId,
        /// Which field, as an index into the record's field list rather than as a name, since
        /// the lookup happened here and nothing after this should repeat it.
        field: u32,
    },
    /// `base[index]`, with the pointer operand first however it was written.
    ///
    /// Kept as a subscript rather than rewritten into `*(base + index)` because the rewriting
    /// has exactly one home, which is the walk to the IR, and because a diagnostic about a
    /// subscript should talk about a subscript.
    Subscript {
        /// The pointer, which has already decayed if it was an array.
        base: ExprId,
        /// The integer.
        index: ExprId,
    },
    /// `callee(args)`, with the arguments already converted to the parameter types.
    Call {
        /// The function, which is a pointer to a function after its decay.
        callee: ExprId,
        /// The arguments, in order, each converted to what the prototype asks for and each
        /// promoted where the prototype does not say.
        args: ExprList,
    },
    /// A prefix or postfix operator on one operand.
    Unary {
        /// Which operator.
        op: UnaryOp,
        /// What it applies to.
        operand: ExprId,
    },
    /// A binary operator on two operands of the same type, except for the shifts and the
    /// pointer arithmetic, where the two sides legitimately differ.
    Binary {
        /// Which operator.
        op: BinaryOp,
        /// The left side.
        lhs: ExprId,
        /// The right side.
        rhs: ExprId,
    },
    /// `lhs = rhs`, or a compound assignment with the operator kept as written.
    Assign {
        /// The operator of a compound assignment, absent for a plain one.
        op: Option<BinaryOp>,
        /// The type the operation is performed in, which is the node's own type for a plain
        /// assignment and for most compound ones.
        ///
        /// It is here because `a op= b` is not `a = a op b` with the conversions left out, and
        /// the difference is not academic: in `int i = 5; i /= 0.5;` the division happens in
        /// `double` and the answer is ten, and a compiler that converts the right side to `int`
        /// first divides by zero. The left side is an lvalue and cannot carry a conversion node
        /// of its own, so the type it is read into is written here instead, which is what clang
        /// calls the computation type and for the same reason.
        computation: TypeId,
        /// What is assigned to, which is an lvalue.
        lhs: ExprId,
        /// What is assigned.
        rhs: ExprId,
    },
    /// `cond ? then : otherwise`, with both arms already converted to the common type.
    Cond {
        /// The condition, converted to `bool`.
        cond: ExprId,
        /// The arm taken when it is true. GNU's `cond ?: otherwise` has this equal to the
        /// condition before its conversion, so the value is computed once.
        then: ExprId,
        /// The arm taken when it is false.
        otherwise: ExprId,
    },
    /// `lhs, rhs`, whose value is the right side and whose left side is evaluated and dropped.
    Comma {
        /// Evaluated first, for its effects.
        lhs: ExprId,
        /// The value.
        rhs: ExprId,
    },
    /// A cast the program wrote. The type is the node's type.
    Cast(ExprId),
    /// A conversion the language performed. The type is the node's type.
    Convert {
        /// Which conversion, so that a reader and the verifier can both tell what happened
        /// rather than comparing the two types and guessing.
        kind: Conversion,
        /// What was converted.
        operand: ExprId,
    },
    /// `(T){ ... }`, which is an unnamed object with an initializer and not a conversion.
    CompoundLiteral(DeclId),
    /// `({ ... })`, GNU's statement expression, whose value is its last expression statement.
    StmtExpr(StmtId),
    /// `&&label`, GNU's label address.
    LabelAddr(LabelId),
    /// `va_arg(list, T)`, which reads the next argument and moves the list on.
    ///
    /// The type it fetches is the node's own type, so there is nothing else to hold. It is a
    /// node rather than a call because what it becomes is the target's own sequence of loads
    /// and not a function anything links against.
    VaArg {
        /// The address of the list, which is what this reads through and moves on.
        list: ExprId,
    },
    /// `va_start(list, last)`, which sets a list to the first argument past the named ones.
    ///
    /// What the source wrote as the second argument is not here. It names where the named
    /// arguments stopped, which the enclosing function's own type already says, and it is not
    /// evaluated: gcc rewrites `va_start(ap, last)` to a call with a zero in that place and C23
    /// lets the program leave it out altogether.
    VaStart {
        /// The address of the list, which this writes.
        list: ExprId,
    },
    /// `va_end(list)`, which is the end of the reading and is nothing at all on most targets.
    VaEnd {
        /// The address of the list.
        list: ExprId,
    },
    /// `va_copy(dst, src)`, which makes a second list standing where the first one stands.
    VaCopy {
        /// The address of the list being written.
        dst: ExprId,
        /// The address of the list being read, which stays where it is.
        src: ExprId,
    },
    /// One of the floating point classification builtins, which asks about a value rather than
    /// computing one.
    ///
    /// A node rather than a call because there is nothing to call: `isnan` and the rest are
    /// macros in `math.h` that expand to exactly these, so the name has no function under it on
    /// any platform. What each becomes is a comparison, and the four of the family that C
    /// already has an operator for are [`ExprKind::Binary`] instead. See
    /// `check/builtin/classify.rs` for which are here and why.
    Classify {
        /// Which question is being asked.
        op: Classify,
        /// The value asked about, converted to the type the question is asked in.
        lhs: ExprId,
        /// The value it is asked against, for the two questions that are about a pair of them.
        rhs: Option<ExprId>,
    },
    /// `__builtin_fpclassify(nan, inf, normal, subnormal, zero, x)`, which answers with whichever
    /// of the five the value is.
    ///
    /// A node of its own rather than one of [`ExprKind::Classify`] because it has five operands
    /// besides the value, and a node rather than the chain of conditionals it turns into because
    /// the value is asked about four times and a program that writes `__builtin_fpclassify(..,
    /// f())` calls `f` once.
    FpClassify {
        /// The value asked about.
        value: ExprId,
        /// The five answers, in the order the call writes them: a NaN, an infinity, a normal
        /// number, a subnormal and a zero. gcc requires each to be an integer constant
        /// expression and so does this.
        answers: ExprList,
    },
    /// `__builtin_fabs` or `__builtin_copysign`, which set the sign bit of a value from somewhere
    /// and leave every other bit of it alone.
    ///
    /// A node rather than a call because the call would be to the math library, which is not on
    /// the link line of a program that never asked for it, and because neither one needs anything
    /// the library has: both are a mask and an or over the bits. See `check/builtin/sign.rs`.
    Sign {
        /// Where the sign of the answer comes from.
        op: Sign,
        /// The value whose magnitude the answer has.
        lhs: ExprId,
        /// The value whose sign the answer has, for `copysign`, which is the only one that reads
        /// a sign from anywhere other than nowhere.
        rhs: Option<ExprId>,
    },
    /// `abs`, `labs` and `llabs`, which are the magnitude of an integer.
    ///
    /// A node rather than a call because the names are the C library's and the compiler is allowed
    /// to know what they do, which is what lets a program define one of them and still get the
    /// magnitude. See `check/builtin/abs.rs` for when a call becomes one of these and when it
    /// stays a call.
    ///
    /// The operand has already been converted to the type of the answer, which is the type the
    /// declaration gave the parameter, so nothing downstream has to widen it.
    Abs {
        /// The value whose magnitude this is.
        operand: ExprId,
    },
    /// `__builtin_bswap16`, `__builtin_bswap32` and `__builtin_bswap64`, which are the bytes of a
    /// value in the other order.
    ///
    /// A node rather than a call because no object file defines one of these, and because a byte
    /// order swap is arithmetic: every machine can do it and most have an instruction for it. See
    /// `check/builtin/bswap.rs`.
    ///
    /// The operand has already been converted to the unsigned type the declaration gave the
    /// parameter, which is also the type of the answer, so the width the bytes are reversed in is
    /// the width of the node and nothing downstream has to work it out.
    ByteSwap {
        /// The value whose bytes these are.
        operand: ExprId,
    },
    /// The bit counting builtins, which are five questions about which bits of a value are set.
    ///
    /// `__builtin_clz`, `__builtin_ctz`, `__builtin_popcount`, `__builtin_parity` and
    /// `__builtin_ffs`, each in the plain, `l` and `ll` widths. Nodes rather than calls for the
    /// reason [`ExprKind::ByteSwap`] is one: no object file defines any of them, and every machine
    /// can answer them with instructions it already has. See `check/builtin/count.rs`.
    ///
    /// The operand keeps the width its declaration gave it, because that width is the question. The
    /// type of the whole node is `int` whatever that width is, which is the one place this differs
    /// from the byte swaps, so the walk to the IR counts at the operand's width and then narrows
    /// the answer.
    BitCount {
        /// The value whose bits are being counted.
        operand: ExprId,
        /// Which of the five questions this asks.
        count: BitCount,
    },
    /// The overflow checking builtins, which do the arithmetic exactly and say whether it fit.
    ///
    /// `__builtin_add_overflow`, `__builtin_sub_overflow` and `__builtin_mul_overflow`. A node
    /// rather than a call for the reason [`ExprKind::ByteSwap`] is one, and for a second reason
    /// besides: the answer is two things, a value and a bit, and a call in C can only give back
    /// one. See `check/builtin/overflow.rs`.
    ///
    /// The operands keep the types they were written with, because the arithmetic is defined as
    /// happening in infinite precision and then being put somewhere. What stands in for infinite
    /// precision is `at`, a type wide enough to hold every value all three of the written types
    /// can hold, and the walk to the IR converts both operands to it before doing anything.
    ///
    /// The three operands are a run rather than three fields, because three of them and a type
    /// would be the widest variant here and every expression in the program is the size of the
    /// widest one. See [`Tast`](crate::Tast) for the same trade made about a declaration.
    Overflow {
        /// Which of the three operations this is.
        op: OverflowOp,
        /// The type the arithmetic is done at, which represents every value of both operand types
        /// and of what the third operand points at. Working it out is the whole of the type
        /// checking here.
        at: TypeId,
        /// The two operands in the types they were written with, and then the pointer the exact
        /// result is written through whether or not it fit. Always exactly three.
        args: ExprList,
    },
    /// The atomic accesses and the barriers, which carry a memory ordering.
    ///
    /// `__atomic_load_n`, `__atomic_store_n`, `__atomic_thread_fence`, `__atomic_signal_fence` and
    /// `__sync_synchronize`. A node rather than a call because none of them is a function anywhere:
    /// what they are is an access with an ordering on it, and an ordering is a thing the IR says
    /// about an access rather than an argument something is passed. See `check/builtin/atomic.rs`.
    ///
    /// The order is a value here rather than an operand, because it was a constant in the source
    /// and the ordering of an access has to be known when the access is built. A call that wrote a
    /// value the compiler cannot fold gets the strongest ordering, which is what gcc does and is
    /// the only safe reading of a question that has to be answered before the program runs.
    Atomic {
        /// Which of the three shapes this is.
        op: AtomicOp,
        /// How strongly it is ordered, after the source's number has been read and checked.
        order: Ordering,
        /// The address for a load, the address and then the value for a store, and nothing at all
        /// for a barrier.
        args: ExprList,
    },
    /// `__builtin_unreachable()`, which is the program promising control does not get here.
    ///
    /// It has no operands and no value, and it is a node rather than a call for the reason
    /// [`ExprKind::VaArg`] is one: there is no function of the name for a call to reach. What it
    /// carries is the promise itself, which the optimizer is where it will pay, and until then
    /// what it costs to honour is nothing at all. See `check/builtin/unreachable.rs`.
    Unreachable,
}

/// Which question one of the bit counting builtins asks.
///
/// Three of these are an instruction on most machines and the other two are one of those and a
/// little arithmetic, which is why they are one node with a question rather than five nodes.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BitCount {
    /// `__builtin_clz`, the number of zero bits above the highest set one. Undefined for a zero
    /// argument, which is gcc's rule and not an accident of any machine: `bsr` leaves its
    /// destination alone for a zero input rather than writing an answer to it.
    Leading,
    /// `__builtin_ctz`, the number of zero bits below the lowest set one. Undefined for a zero
    /// argument for the same reason.
    Trailing,
    /// `__builtin_popcount`, how many bits are set. Defined everywhere, including at zero.
    Ones,
    /// `__builtin_parity`, whether the number of set bits is odd. Defined everywhere. Not the
    /// machine's parity flag, which on x86-64 is over the low byte of the result and so answers a
    /// different question.
    Parity,
    /// `__builtin_ffs`, the position of the lowest set bit counting from one, and zero for a zero
    /// argument. The one in the family that is defined at zero, and the one whose operand is
    /// signed, because that is the signature the C library's `ffs` has.
    FirstSet,
}

impl BitCount {
    /// How the question is written in the typed tree's textual form.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            BitCount::Leading => "leading-zeroes",
            BitCount::Trailing => "trailing-zeroes",
            BitCount::Ones => "set-bits",
            BitCount::Parity => "parity",
            BitCount::FirstSet => "first-set",
        }
    }
}

/// Which arithmetic one of the overflow checking builtins does.
///
/// One node with an operation rather than three nodes, because everything around the arithmetic
/// itself is the same for all three: the same rule picks the type it happens at, the same narrowing
/// decides whether the answer fit, and the same store puts it where it was asked for.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OverflowOp {
    /// `__builtin_add_overflow`.
    Add,
    /// `__builtin_sub_overflow`.
    Sub,
    /// `__builtin_mul_overflow`.
    Mul,
}

impl OverflowOp {
    /// How the operation is written in the typed tree's textual form.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            OverflowOp::Add => "add",
            OverflowOp::Sub => "sub",
            OverflowOp::Mul => "mul",
        }
    }
}

/// Which of the atomic shapes a call is, once the family it came from stops mattering.
///
/// Fewer of these than there are names, because `__sync_synchronize()` and
/// `__atomic_thread_fence(__ATOMIC_SEQ_CST)` are the same node with the same ordering, and the
/// only difference between the two families is which orderings a name can be written with.
///
/// The three compare and exchange shapes are three rather than one because they differ in what
/// they answer and in where they were handed the value to compare against, and those are the two
/// things the walk to the IR has to know. What they do to the object is the same in all three.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AtomicOp {
    /// A read of the object the first operand points at.
    Load,
    /// The same read, written into the object the second operand points at rather than answered.
    ///
    /// The unsuffixed `__atomic_load`, which is the form for an object too big to come back in a
    /// register. A separate shape rather than the same one with an operand on the end, because what
    /// the walk to the IR does with it differs: this one answers nothing and writes twice.
    LoadInto,
    /// A write of the second operand into the object the first points at.
    Store,
    /// A barrier, which touches no object and is the ordering by itself.
    Fence,
    /// A compare and exchange whose second operand is a pointer to the value expected, which is
    /// where what was found is written back when the two did not match. Answers whether they did.
    CompareExchange,
    /// A compare and exchange whose second operand is the expected value itself, answering whether
    /// it matched. The older family's `__sync_bool_compare_and_swap`.
    SwapBool,
    /// The same, answering what was found rather than whether it matched.
    SwapValue,
    /// The second operand goes into the object and what was there comes back.
    ///
    /// One shape rather than two, because an exchange is the one read modify write whose answer
    /// afterwards is a value the caller already has, so no name in the family asks for it.
    Exchange,
    /// The same exchange, with what was there written into the object the third operand points at
    /// rather than answered. The unsuffixed `__atomic_exchange`.
    ExchangeInto,
    /// An exchange of a one into the byte the first operand points at, answering whether that byte
    /// held anything before.
    ///
    /// `__atomic_test_and_set`, which is the one name in the family whose object is a byte whatever
    /// the pointer it was handed points at. It is a shape of its own rather than an exchange with
    /// the comparison written around it because the value that goes in is the implementation's to
    /// choose, and choosing it in one place is what keeps it the same value `__atomic_clear` puts
    /// back.
    TestAndSet,
    /// A read, an operation on what was read, and a write back, answering what was there before.
    Fetch(Rmw),
    /// The same, answering what is there afterwards.
    ///
    /// A separate shape rather than the arithmetic written around a [`AtomicOp::Fetch`] by whatever
    /// checked the call, because the two are one instruction on most machines and the one that
    /// answers afterwards is the one that is a subtraction away from it. Which of the two a machine
    /// has is the back end's business, and this is where the difference is written down until it
    /// gets there.
    Update(Rmw),
}

impl AtomicOp {
    /// How the operation is written in the typed tree's textual form.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            AtomicOp::Load => "load",
            AtomicOp::LoadInto => "load_into",
            AtomicOp::Store => "store",
            AtomicOp::Fence => "fence",
            AtomicOp::CompareExchange => "compare_exchange",
            AtomicOp::SwapBool => "swap_bool",
            AtomicOp::SwapValue => "swap_value",
            AtomicOp::Exchange => "exchange",
            AtomicOp::ExchangeInto => "exchange_into",
            AtomicOp::TestAndSet => "test_and_set",
            AtomicOp::Fetch(Rmw::Add) => "fetch_add",
            AtomicOp::Fetch(Rmw::Sub) => "fetch_sub",
            AtomicOp::Fetch(Rmw::And) => "fetch_and",
            AtomicOp::Fetch(Rmw::Nand) => "fetch_nand",
            AtomicOp::Fetch(Rmw::Or) => "fetch_or",
            AtomicOp::Fetch(Rmw::Xor) => "fetch_xor",
            AtomicOp::Update(Rmw::Add) => "add_fetch",
            AtomicOp::Update(Rmw::Sub) => "sub_fetch",
            AtomicOp::Update(Rmw::And) => "and_fetch",
            AtomicOp::Update(Rmw::Nand) => "nand_fetch",
            AtomicOp::Update(Rmw::Or) => "or_fetch",
            AtomicOp::Update(Rmw::Xor) => "xor_fetch",
        }
    }
}

/// What a read modify write does to the value it read.
///
/// The six gcc has, which is every operation either family names. What a machine has a single
/// instruction for is not decided here: the back end reads this and either finds an instruction or
/// writes the loop around a compare and exchange that stands in for one.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rmw {
    /// Addition, which is `lock xadd` on this machine.
    Add,
    /// Subtraction, which is the same instruction over the negated operand.
    Sub,
    /// Bitwise and.
    And,
    /// Bitwise and with every bit of the answer flipped, which is the one of the six that is two
    /// operations rather than one and the one no machine here has anything for.
    Nand,
    /// Bitwise or.
    Or,
    /// Bitwise exclusive or.
    Xor,
}

/// How strongly an atomic access or a barrier is ordered against everything around it.
///
/// The C11 memory model's orderings, which gcc's `__atomic_` family took from the same place. This
/// is spelled here rather than reused from the IR because nothing in this crate knows what an IR
/// is, and the walk that builds one is where the two are put side by side.
///
/// `memory_order_consume` is not here. Every compiler in use today gives it the same code as
/// `acquire`, C++17 discourages it, and a spelling of it that means acquire would be a name whose
/// only effect is to make a reader think something was implemented. The number the source wrote is
/// read and turned into [`Ordering::Acquire`] where it appears, and the fact that it was written is
/// not carried any further.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Ordering {
    /// Atomic, and ordered against nothing.
    Relaxed,
    /// Nothing after this moves before it.
    Acquire,
    /// Nothing before this moves after it.
    Release,
    /// Both, which only a read-modify-write or a barrier can ask for.
    AcqRel,
    /// Both, and one total order over every sequentially consistent operation in the program.
    SeqCst,
}

impl Ordering {
    /// How the ordering is written in the typed tree's textual form.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Ordering::Relaxed => "relaxed",
            Ordering::Acquire => "acquire",
            Ordering::Release => "release",
            Ordering::AcqRel => "acq_rel",
            Ordering::SeqCst => "seq_cst",
        }
    }
}

/// Which question one of the floating point classification builtins asks.
///
/// The four that this does not have are `isgreater`, `isgreaterequal`, `isless` and
/// `islessequal`, which are `>`, `>=`, `<` and `<=` and are those.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Classify {
    /// `isunordered(a, b)`, true when either of the two is a NaN and so the two cannot be put
    /// in an order at all. C has no operator for this one.
    Unordered,
    /// `islessgreater(a, b)`, which is `a < b || a > b` and so is false when either is a NaN.
    /// That is not `a != b`, which is true of a NaN, so C has no operator for this one either.
    LessGreater,
    /// `isnan(x)`, the value that is not in an order with itself.
    Nan,
    /// `isinf(x)`, either infinity.
    Infinite,
    /// `isfinite(x)`, which is neither an infinity nor a NaN.
    Finite,
    /// `isnormal(x)`, which is finite and whose magnitude is at least the smallest normal of its
    /// format, so it is false of a zero and of a subnormal as well as of the two `isfinite`
    /// rules out.
    Normal,
    /// `signbit(x)`, which asks about the sign and not about the value, so it is true of a
    /// negative zero and of a NaN whose sign bit is set.
    SignBit,
    /// `isinf_sign(x)`, which is `isinf` with a sign: one for a positive infinity, minus one for
    /// a negative one and zero for everything else. It is the one question in the family whose
    /// answer is a number rather than a bit.
    InfiniteSign,
}

impl Classify {
    /// How the question is written in the typed tree's textual form.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Classify::Unordered => "unordered",
            Classify::LessGreater => "less-greater",
            Classify::Nan => "nan",
            Classify::Infinite => "infinite",
            Classify::Finite => "finite",
            Classify::Normal => "normal",
            Classify::SignBit => "signbit",
            Classify::InfiniteSign => "infinite-sign",
        }
    }

    /// Whether the question is about a pair of values rather than about one.
    #[must_use]
    pub const fn is_pair(self) -> bool {
        matches!(self, Classify::Unordered | Classify::LessGreater)
    }

    /// Whether the answer is one bit, which is every question here but `isinf_sign`.
    ///
    /// The type of the whole node is `int` either way. What this decides is whether the walk to
    /// the IR has a bit to widen into one or a number that is already one.
    #[must_use]
    pub const fn answers_a_bit(self) -> bool {
        !matches!(self, Classify::InfiniteSign)
    }
}

/// Where the sign of the answer to one of the sign builtins comes from.
///
/// Neither of these is a computation on the value. `fabs` of a NaN is that NaN with its sign bit
/// clear, payload and all, and `copysign` of one is that NaN with the other value's sign bit, so
/// what both do is described entirely in terms of the bits.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Sign {
    /// `fabs(x)`, whose sign is always clear.
    Clear,
    /// `copysign(x, y)`, whose sign is the sign of the second operand.
    Of,
}

impl Sign {
    /// How the operation is written in the typed tree's textual form.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Sign::Clear => "clear",
            Sign::Of => "of",
        }
    }

    /// Whether it reads a sign from a second operand.
    #[must_use]
    pub const fn is_pair(self) -> bool {
        matches!(self, Sign::Of)
    }
}

/// A conversion the language performs without being asked.
///
/// Each of these is a node in the tree rather than a difference between two types that a later
/// pass notices. The IR builder is entitled to assume it never has to insert one, and the
/// verifier in `spec/08-ir.md` checks that assumption on every function.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Conversion {
    /// Reading an object, which drops the qualifiers and turns an lvalue into a value.
    Lvalue,
    /// An array becoming a pointer to its first element.
    ArrayDecay,
    /// A function becoming a pointer to itself.
    FunctionDecay,
    /// One arithmetic type to another. The integer promotions, the usual arithmetic
    /// conversions, and the conversions an assignment or an argument performs are all this.
    Arithmetic,
    /// A pointer to another pointer type, which includes both directions of `void *`.
    Pointer,
    /// A scalar to `bool`, which is a comparison against zero rather than a truncation, and
    /// which is why it is not [`Conversion::Arithmetic`].
    Bool,
    /// A null pointer constant becoming a pointer, which is not the same as converting the
    /// integer zero, because the constant may have any integer type and `(void *)0` is one.
    NullPointer,
    /// A value being discarded, which is what a cast to `void` and an expression statement do.
    Void,
    /// A scalar becoming a vector, by being copied into every lane of it.
    ///
    /// Written where a scalar stands beside a vector in an operator, which GNU C reads as that
    /// scalar in every lane. It is not [`Conversion::Arithmetic`] because the lane type and the
    /// scalar's type are already the same by the time this is reached: the narrowing that a
    /// lane asks for is an arithmetic conversion of its own underneath this one, so that the
    /// two questions are answered where each of them is usually answered.
    Broadcast,
}

impl Conversion {
    /// How the conversion is written in the typed tree's textual form.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Conversion::Lvalue => "lvalue",
            Conversion::ArrayDecay => "array-decay",
            Conversion::FunctionDecay => "function-decay",
            Conversion::Arithmetic => "arithmetic",
            Conversion::Pointer => "pointer",
            Conversion::Bool => "bool",
            Conversion::NullPointer => "null-pointer",
            Conversion::Void => "void",
            Conversion::Broadcast => "broadcast",
        }
    }
}