Skip to main content

rucc_pp/
predef.rs

1//! The predefined macro set, generated from the target description.
2//!
3//! Design: `spec/04-driver-and-cli.md` section 4.5.
4//!
5//! The set is built as text and then read by the directive engine, which is what GCC does and
6//! is not laziness. Constructing a few hundred `MacroDef` values by hand would need its own
7//! parser for macro bodies, would not exercise the one that already exists, and could not be
8//! read by a person checking a limit against the psABI. A file of `#define` lines can be
9//! printed by `-dM`, diffed against GCC's output, and understood at a glance.
10//!
11//! Two synthetic files come out of this, and they are the two GCC names in a diagnostic:
12//! `<built-in>` for the generated set and `<command-line>` for `-D` and `-U`. Keeping them
13//! apart is what lets "`FOO` redefined" point at the command line rather than at a line
14//! nobody wrote.
15//!
16//! The decision that everything else follows from is in section 4.5: we define `__GNUC__`,
17//! which means glibc's headers, the kernel's headers and every autoconf probe take the GNU
18//! path. The version claimed is deliberately conservative and is a knob, because claiming too
19//! high a version means headers use extensions we do not have, and the matrix in `rucc-gnu`
20//! is the list of promises the claim makes.
21
22use rucc_base::float::Format;
23use rucc_session::{GnucVersion, OptLevel, Options, Std};
24use rucc_target::{Arch, Env, Os, TargetInfo};
25
26/// The name a diagnostic about the generated set points at.
27pub const BUILT_IN: &str = "<built-in>";
28
29/// The name a diagnostic about `-D` or `-U` points at.
30pub const COMMAND_LINE: &str = "<command-line>";
31
32/// The translation date, as `__DATE__` and `__TIME__` spell it.
33///
34/// Fixed for the whole translation unit, which is what the standard requires and what makes
35/// the two macros ordinary object-like macros rather than something the expander has to know
36/// about.
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Timestamp {
39    /// `Mmm dd yyyy`, with the day space padded, which is the format the standard fixes.
40    pub date: String,
41    /// `hh:mm:ss`.
42    pub time: String,
43}
44
45impl Timestamp {
46    /// The current time, or `SOURCE_DATE_EPOCH` when the build asked for a reproducible one.
47    ///
48    /// Reading the environment here rather than in the driver is what GCC does, and it keeps
49    /// the variable working for an embedder who never goes through a command line.
50    pub fn now() -> Timestamp {
51        let seconds = match std::env::var("SOURCE_DATE_EPOCH").ok().and_then(|v| v.parse().ok()) {
52            Some(fixed) => fixed,
53            None => std::time::SystemTime::now()
54                .duration_since(std::time::UNIX_EPOCH)
55                .map_or(0, |d| d.as_secs() as i64),
56        };
57        Timestamp::from_unix(seconds)
58    }
59
60    /// The time `seconds` after the epoch, in UTC.
61    ///
62    /// UTC rather than local time, because a compiler whose output depends on the machine's
63    /// time zone is a compiler whose output is not reproducible.
64    pub fn from_unix(seconds: i64) -> Timestamp {
65        let days = seconds.div_euclid(86_400);
66        let rest = seconds.rem_euclid(86_400);
67        let (year, month, day) = civil_from_days(days);
68        const MONTHS: [&str; 12] =
69            ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
70        let name = MONTHS[(month - 1) as usize];
71        Timestamp {
72            date: format!("{name} {day:2} {year}"),
73            time: format!("{:02}:{:02}:{:02}", rest / 3600, (rest / 60) % 60, rest % 60),
74        }
75    }
76}
77
78/// The year, month and day `days` after 1970-01-01.
79///
80/// Howard Hinnant's civil calendar algorithm, which is a handful of divisions and no table.
81/// It is here rather than in a dependency because the whole workspace has no dependencies,
82/// and a date conversion is not a good reason to acquire the first one.
83fn civil_from_days(days: i64) -> (i64, u32, u32) {
84    // Shift the epoch to 0000-03-01, so that a leap day is the last day of the year and the
85    // month lengths become a repeating pattern that one division can invert.
86    let shifted = days + 719_468;
87    let era = shifted.div_euclid(146_097);
88    let day_of_era = shifted.rem_euclid(146_097);
89    let year_of_era =
90        (day_of_era - day_of_era / 1460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
91    let year = year_of_era + era * 400;
92    let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
93    let marched = (5 * day_of_year + 2) / 153;
94    let day = (day_of_year - (153 * marched + 2) / 5 + 1) as u32;
95    let month = if marched < 10 { marched + 3 } else { marched - 9 } as u32;
96    (year + i64::from(month <= 2), month, day)
97}
98
99/// Everything the predefined set is built from that is not the target.
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct Predef {
102    /// The dialect, which decides `__STDC_VERSION__`.
103    pub std: Std,
104    /// Whether the GNU extensions are on, which is `-std=gnu23` rather than `-std=c23`. It
105    /// decides `__STRICT_ANSI__` and the unarmoured `linux` and `unix` macros.
106    pub gnu_extensions: bool,
107    /// The GCC release claimed.
108    pub gnuc: GnucVersion,
109    /// Decides `__OPTIMIZE__`, `__OPTIMIZE_SIZE__` and `__NO_INLINE__`.
110    pub opt_level: OptLevel,
111    /// Whether there is a standard library, which is `-ffreestanding` turned around.
112    pub hosted: bool,
113    /// `__DATE__` and `__TIME__`.
114    pub timestamp: Timestamp,
115    /// `-D` in command line order. `FOO` means `FOO=1`, as GCC has it.
116    pub defines: Vec<String>,
117    /// `-U` in command line order, applied after the defines.
118    pub undefines: Vec<String>,
119}
120
121impl Predef {
122    /// The default dialect, `gnu23`, at `-O0`.
123    pub fn new() -> Predef {
124        Predef {
125            std: Std::default(),
126            gnu_extensions: true,
127            gnuc: GnucVersion::default(),
128            opt_level: OptLevel::O0,
129            hosted: true,
130            timestamp: Timestamp::now(),
131            defines: Vec::new(),
132            undefines: Vec::new(),
133        }
134    }
135}
136
137impl Predef {
138    /// The set the command line asked for.
139    ///
140    /// The mapping lives here rather than in the driver because it is the definition of what
141    /// each flag means to the macro set, and the driver's job is to parse a command line, not
142    /// to know that `-ffreestanding` is `__STDC_HOSTED__` being zero.
143    pub fn for_options(opts: &Options) -> Predef {
144        Predef {
145            std: opts.std,
146            gnu_extensions: opts.gnu_extensions,
147            gnuc: opts.gnuc,
148            opt_level: opts.opt_level,
149            hosted: opts.hosted,
150            timestamp: Timestamp::now(),
151            defines: opts.defines.clone(),
152            undefines: opts.undefines.clone(),
153        }
154    }
155}
156
157impl Default for Predef {
158    fn default() -> Predef {
159        Predef::new()
160    }
161}
162
163/// A file of `#define` lines being built up.
164struct Defs {
165    text: String,
166}
167
168impl Defs {
169    fn new() -> Defs {
170        Defs { text: String::new() }
171    }
172
173    /// `#define name value`.
174    fn set(&mut self, name: &str, value: &str) {
175        self.text.push_str("#define ");
176        self.text.push_str(name);
177        self.text.push(' ');
178        self.text.push_str(value);
179        self.text.push('\n');
180    }
181
182    /// `#define name 1`, which is what a macro that is only ever tested for needs.
183    fn flag(&mut self, name: &str) {
184        self.set(name, "1");
185    }
186
187    fn set_if(&mut self, when: bool, name: &str, value: &str) {
188        if when {
189            self.set(name, value);
190        }
191    }
192
193    fn flag_if(&mut self, when: bool, name: &str) {
194        if when {
195            self.flag(name);
196        }
197    }
198}
199
200/// The whole predefined set for a target, as the text of a file.
201pub(crate) fn built_in(target: &TargetInfo, opts: &Predef) -> String {
202    let mut d = Defs::new();
203    identity(&mut d, opts);
204    // `__DATE__` and `__TIME__` are fixed for the whole translation unit, which is what the
205    // standard asks for, so they are ordinary object-like macros and the expander needs to
206    // know nothing about them.
207    d.set("__DATE__", &format!("\"{}\"", opts.timestamp.date));
208    d.set("__TIME__", &format!("\"{}\"", opts.timestamp.time));
209    dialect(&mut d, opts);
210    optimization(&mut d, opts);
211    platform(&mut d, target, opts);
212    sizes(&mut d, target);
213    integers(&mut d, target);
214    floats(&mut d, target);
215    atomics(&mut d, target);
216    d.text
217}
218
219/// `-D` and `-U`, as the text of a file.
220///
221/// Empty when there are none, so that the caller can skip adding a file that would say
222/// nothing. The undefines come last whatever order they were written in, because `-U` beats
223/// `-D` in GCC no matter which side of it the `-D` was on.
224pub(crate) fn command_line(opts: &Predef) -> String {
225    let mut d = Defs::new();
226    for define in &opts.defines {
227        match define.split_once('=') {
228            Some((name, value)) => d.set(name, value),
229            // `-DFOO` is `-DFOO=1`. A macro nobody gave a value to is one that is only ever
230            // tested for, and giving it an empty body would break `#if FOO`.
231            None => d.flag(define),
232        }
233    }
234    for name in &opts.undefines {
235        d.text.push_str("#undef ");
236        d.text.push_str(name);
237        d.text.push('\n');
238    }
239    d.text
240}
241
242/// Who the compiler says it is.
243fn identity(d: &mut Defs, opts: &Predef) {
244    d.flag("__rucc__");
245    d.set("__rucc_version__", "\"0.1.0\"");
246    d.set("__rucc_major__", "0");
247    d.set("__rucc_minor__", "1");
248    d.set("__rucc_patchlevel__", "0");
249    // The promise from section 4.5. Everything in the matrix hangs off this line.
250    d.set("__GNUC__", &opts.gnuc.major.to_string());
251    d.set("__GNUC_MINOR__", &opts.gnuc.minor.to_string());
252    d.set("__GNUC_PATCHLEVEL__", &opts.gnuc.patch.to_string());
253    d.set("__VERSION__", "\"rucc 0.1.0\"");
254    // Not `__clang__`, deliberately. Section 4.5 says so, and a header that takes the Clang
255    // path expects Clang's extension surface rather than GCC's.
256    d.flag("__GNUC_STDC_INLINE__");
257}
258
259/// What the dialect flags say.
260fn dialect(d: &mut Defs, opts: &Predef) {
261    d.flag("__STDC__");
262    d.set_if(opts.hosted, "__STDC_HOSTED__", "1");
263    d.set_if(!opts.hosted, "__STDC_HOSTED__", "0");
264    if let Some(version) = opts.std.stdc_version() {
265        d.set("__STDC_VERSION__", version);
266    }
267    // Defined exactly when the extensions are off, which is the whole difference between
268    // `-std=c23` and `-std=gnu23` as far as the preprocessor is concerned.
269    d.flag_if(!opts.gnu_extensions, "__STRICT_ANSI__");
270    d.flag("__STDC_UTF_16__");
271    d.flag("__STDC_UTF_32__");
272    d.flag("__STDC_IEC_559__");
273    d.flag("__STDC_IEC_559_COMPLEX__");
274    d.set_if(opts.std == Std::C23, "__STDC_IEC_60559_BFP__", "202311L");
275    d.set("__STDC_ISO_10646__", "201706L");
276    // C11 made these conditional features, and a header that sees `__STDC_VERSION__` at
277    // 201112 with no `__STDC_NO_ATOMICS__` next to it will use `_Atomic`.
278    if opts.std.has_c11() {
279        d.flag("__STDC_NO_ATOMICS__");
280        d.flag("__STDC_NO_THREADS__");
281        d.flag("__STDC_NO_COMPLEX__");
282        d.flag("__STDC_NO_VLA__");
283    }
284    // What `__has_embed` answers with. They are defined in every dialect and not only in C23,
285    // because the operator is answerable in every dialect and a header that writes
286    // `#if __has_embed(...) == __STDC_EMBED_FOUND__` under `-std=gnu17` would otherwise be
287    // comparing against zero and taking the not found branch on a resource that is there.
288    d.set("__STDC_EMBED_NOT_FOUND__", "0");
289    d.set("__STDC_EMBED_FOUND__", "1");
290    d.set("__STDC_EMBED_EMPTY__", "2");
291}
292
293/// The memory orders and the lock free answers.
294///
295/// These are here whether or not `_Atomic` is, and `__STDC_NO_ATOMICS__` does not turn them
296/// off, because they are the numbering the `__atomic` builtins take rather than a promise
297/// about the language. musl's `stdatomic.h` writes `memory_order_relaxed = __ATOMIC_RELAXED`
298/// with no test around it at all, so a compiler without them prints an enumerator whose value
299/// is an identifier.
300///
301/// Two means always lock free, and every integer type gets a two on all three targets, which
302/// are all sixty four bit machines. `long long` is the one that would change on a thirty two
303/// bit target, where a double word load is an instruction the machine may or may not have.
304fn atomics(d: &mut Defs, target: &TargetInfo) {
305    d.set("__ATOMIC_RELAXED", "0");
306    d.set("__ATOMIC_CONSUME", "1");
307    d.set("__ATOMIC_ACQUIRE", "2");
308    d.set("__ATOMIC_RELEASE", "3");
309    d.set("__ATOMIC_ACQ_REL", "4");
310    d.set("__ATOMIC_SEQ_CST", "5");
311    // The gate is the machine word rather than `long`, because Windows has a thirty two bit
312    // `long` on a sixty four bit machine and its `long long` is still one instruction.
313    let llong = if target.pointer_width == 64 { "2" } else { "1" };
314    for name in [
315        "BOOL", "CHAR", "CHAR8_T", "CHAR16_T", "CHAR32_T", "WCHAR_T", "SHORT", "INT", "LONG",
316        "POINTER",
317    ] {
318        d.set(&format!("__GCC_ATOMIC_{name}_LOCK_FREE"), "2");
319    }
320    // The one that is not always two: a target whose word is thirty two bits wide can only
321    // promise `long long` is lock free if it has a double word instruction, and the honest
322    // answer there is sometimes rather than always.
323    d.set("__GCC_ATOMIC_LLONG_LOCK_FREE", llong);
324    d.set("__GCC_ATOMIC_TEST_AND_SET_TRUEVAL", "1");
325}
326
327/// What the optimizer level says.
328fn optimization(d: &mut Defs, opts: &Predef) {
329    d.flag_if(opts.opt_level.runs_optimizer(), "__OPTIMIZE__");
330    d.flag_if(opts.opt_level.is_size(), "__OPTIMIZE_SIZE__");
331    // glibc's headers test this before deciding whether to define a function as an inline
332    // wrapper, so getting it wrong changes what a program links against.
333    d.flag_if(!opts.opt_level.runs_optimizer(), "__NO_INLINE__");
334}
335
336/// The architecture, the operating system and the object format.
337fn platform(d: &mut Defs, target: &TargetInfo, opts: &Predef) {
338    let triple = target.triple;
339    match triple.arch {
340        Arch::X86_64 => {
341            d.flag("__x86_64__");
342            d.flag("__x86_64");
343            d.flag("__amd64__");
344            d.flag("__amd64");
345            d.flag("__SSE__");
346            d.flag("__SSE2__");
347            d.flag("__MMX__");
348            d.flag("__SSE_MATH__");
349            d.flag("__SSE2_MATH__");
350            d.flag("__k8");
351            d.flag("__k8__");
352        }
353        Arch::Aarch64 => {
354            d.flag("__aarch64__");
355            d.flag("__AARCH64EL__");
356            d.set("__ARM_ARCH", "8");
357            d.set("__ARM_ARCH_PROFILE", "'A'");
358            d.set("__ARM_64BIT_STATE", "1");
359            d.set("__ARM_ALIGN_MAX_PWR", "28");
360            d.set("__ARM_FP", "0xe");
361            d.set("__ARM_NEON", "1");
362            d.set("__ARM_FEATURE_UNALIGNED", "1");
363            d.set("__ARM_PCS_AAPCS64", "1");
364        }
365        Arch::Riscv64 => {
366            d.flag("__riscv");
367            d.set("__riscv_xlen", "64");
368            d.set("__riscv_flen", "64");
369            d.flag("__riscv_float_abi_double");
370            d.flag("__riscv_muldiv");
371            d.flag("__riscv_atomic");
372            d.flag("__riscv_compressed");
373            d.set("__riscv_cmodel_medlow", "1");
374        }
375    }
376    match triple.os {
377        Os::Linux => {
378            d.flag("__linux__");
379            d.flag("__linux");
380            d.flag("__unix__");
381            d.flag("__unix");
382            d.flag("__gnu_linux__");
383            d.flag("__ELF__");
384            // The unarmoured spellings are not reserved identifiers, so a strict mode may not
385            // define them. Autoconf still tests for `linux`, which is why they exist at all.
386            if opts.gnu_extensions {
387                d.flag("linux");
388                d.flag("unix");
389            }
390        }
391        Os::Darwin => {
392            d.flag("__APPLE__");
393            d.flag("__MACH__");
394            d.flag("__unix__");
395            d.flag("__unix");
396            d.set("__APPLE_CC__", "6000");
397            d.set("__DYNAMIC__", "1");
398            if triple.arch == Arch::Aarch64 {
399                // Apple's own spelling of the architecture, which its headers use rather than
400                // __aarch64__. sys/cdefs.h tests for it by name and reaches an #error called
401                // "Unsupported architecture" without it, so every system header on this
402                // platform fails on the first include until these two are here.
403                d.flag("__arm64__");
404                d.flag("__arm64");
405            }
406            if opts.gnu_extensions {
407                d.flag("unix");
408            }
409        }
410        Os::Windows => {
411            d.flag("_WIN32");
412            d.flag("__WIN32__");
413            d.flag("_WIN64");
414            d.flag("__WIN64__");
415            d.flag("__MINGW32__");
416        }
417        Os::None => {
418            // Freestanding. `__ELF__` still holds, because the object format is a property of
419            // the target rather than of having an operating system under it.
420            d.flag("__ELF__");
421        }
422    }
423    match triple.env {
424        Env::Musl => d.flag("__musl__"),
425        Env::Gnu | Env::None | Env::Msvc => {}
426    }
427    // LP64 is the model everywhere except Windows, and a great deal of code tests for it
428    // rather than testing pointer and long widths separately.
429    if target.long_width == 64 && target.pointer_width == 64 {
430        d.flag("__LP64__");
431        d.flag("_LP64");
432    }
433    // What the assembler prepends to a C name to get the symbol. Mach-O keeps the leading
434    // underscore that every a.out toolchain had and ELF dropped it. It has to be defined even
435    // where it is empty, because of how it is used: glibc writes `__asm__ (__ASMNAME (name))`
436    // and that stringifies `__USER_LABEL_PREFIX__`, so a compiler that leaves it undefined
437    // does not get an error, it gets the name of the macro as the string and renames the
438    // function.
439    d.set("__USER_LABEL_PREFIX__", if triple.os == Os::Darwin { "_" } else { "" });
440
441    // Position independent code is the default on the ELF targets and on Apple's, which is
442    // what a distribution build expects. The value 2 is GCC's for `-fPIC` rather than `-fpic`.
443    if !matches!(triple.os, Os::Windows) {
444        d.set("__PIC__", "2");
445        d.set("__pic__", "2");
446    }
447}
448
449/// `__CHAR_BIT__`, the `__SIZEOF_*__` family and the alignment macros.
450fn sizes(d: &mut Defs, target: &TargetInfo) {
451    let pointer = target.pointer_width / 8;
452    let long = target.long_width / 8;
453    let long_double = target.long_double_width / 8;
454    d.set("__CHAR_BIT__", "8");
455    d.set("__SIZEOF_SHORT__", "2");
456    d.set("__SIZEOF_INT__", "4");
457    d.set("__SIZEOF_LONG__", &long.to_string());
458    d.set("__SIZEOF_LONG_LONG__", "8");
459    d.set("__SIZEOF_INT128__", "16");
460    d.set("__SIZEOF_FLOAT__", "4");
461    d.set("__SIZEOF_DOUBLE__", "8");
462    d.set("__SIZEOF_LONG_DOUBLE__", &long_double.to_string());
463    d.set("__SIZEOF_POINTER__", &pointer.to_string());
464    d.set("__SIZEOF_SIZE_T__", &pointer.to_string());
465    d.set("__SIZEOF_PTRDIFF_T__", &pointer.to_string());
466    d.set("__SIZEOF_WCHAR_T__", &wchar(target).size.to_string());
467    d.set("__SIZEOF_WINT_T__", "4");
468    d.set("__BIGGEST_ALIGNMENT__", "16");
469    // The `__BYTE_ORDER__` family, which the kernel and every serialisation library read.
470    // The names of the orders are defined whichever one is in force, because code compares
471    // against both.
472    d.set("__ORDER_LITTLE_ENDIAN__", "1234");
473    d.set("__ORDER_BIG_ENDIAN__", "4321");
474    d.set("__ORDER_PDP_ENDIAN__", "3412");
475    let order =
476        if target.little_endian { "__ORDER_LITTLE_ENDIAN__" } else { "__ORDER_BIG_ENDIAN__" };
477    d.set("__BYTE_ORDER__", order);
478    d.set("__FLOAT_WORD_ORDER__", order);
479    d.flag_if(!target.char_is_signed, "__CHAR_UNSIGNED__");
480}
481
482/// How `wchar_t` is spelled on a target, and what it holds.
483struct Wchar {
484    /// The C type it is a name for.
485    spelling: &'static str,
486    /// Its width in bytes.
487    size: u32,
488    /// `__WCHAR_MAX__`.
489    max: &'static str,
490    /// `__WCHAR_MIN__`.
491    min: &'static str,
492}
493
494/// `wchar_t` is the type that divides the targets most and is written down least.
495///
496/// Windows makes it 16 bits so that a wide string is UTF-16. AArch64 Linux makes it unsigned,
497/// following the psABI's rule for plain `char`, while x86-64 Linux makes it signed. Code that
498/// compares a `wchar_t` against a negative value is correct on one and not on the other.
499///
500/// The width and the signedness come from the target description rather than from another match
501/// on the triple, because the lexer needs the same two facts to convert a wide literal and the
502/// two answers have to be the same one.
503fn wchar(target: &TargetInfo) -> Wchar {
504    match (target.wchar_width, target.wchar_is_signed) {
505        (16, false) => Wchar { spelling: "short unsigned int", size: 2, max: "0xffff", min: "0" },
506        (16, true) => Wchar { spelling: "short int", size: 2, max: "0x7fff", min: "(-32767 - 1)" },
507        (_, false) => Wchar { spelling: "unsigned int", size: 4, max: "0xffffffffU", min: "0U" },
508        (_, true) => {
509            Wchar { spelling: "int", size: 4, max: "0x7fffffff", min: "(-__WCHAR_MAX__ - 1)" }
510        }
511    }
512}
513
514/// How `wint_t` is spelled on a target, and what it holds.
515struct Wint {
516    /// The C type it is a name for.
517    spelling: &'static str,
518    /// `__WINT_MAX__`.
519    max: &'static str,
520    /// `__WINT_MIN__`.
521    min: &'static str,
522    /// `__WINT_WIDTH__`, which follows the spelling rather than `__SIZEOF_WINT_T__`.
523    width: u32,
524}
525
526/// `wint_t` does not follow `wchar_t`, and Darwin is where that shows.
527///
528/// Apple makes it a signed `int`, so that `WEOF` is negative the way `EOF` is, while Linux
529/// makes it `unsigned int` and gives `WEOF` the value `0xffffffff`. The SDK's `arm/_types.h`
530/// spells `__darwin_wint_t` as `__WINT_TYPE__` and nothing else, so getting this wrong changes
531/// the signedness of every wide character function's argument on that platform.
532fn wint(target: &TargetInfo) -> Wint {
533    match target.triple.os {
534        Os::Windows => Wint { spelling: "short unsigned int", max: "0xffff", min: "0", width: 16 },
535        Os::Darwin => {
536            Wint { spelling: "int", max: "2147483647", min: "(-__WINT_MAX__ - 1)", width: 32 }
537        }
538        _ => Wint { spelling: "unsigned int", max: "4294967295U", min: "0U", width: 32 },
539    }
540}
541
542/// The integer type names, their limits, and the exact width family.
543fn integers(d: &mut Defs, target: &TargetInfo) {
544    // The one fact everything below turns on: which type is 64 bits wide. On LP64 it is
545    // `long`, and on Windows LLP64 it is `long long`, and every `size_t`, `intmax_t` and
546    // `int64_t` spelling follows from that.
547    let lp64 = target.long_width == 64;
548    let wide = if lp64 { "long int" } else { "long long int" };
549    let wide_unsigned = if lp64 { "long unsigned int" } else { "long long unsigned int" };
550    let wide_suffix = if lp64 { "L" } else { "LL" };
551    let wide_max = format!("9223372036854775807{wide_suffix}");
552    let wide_umax = format!("18446744073709551615U{wide_suffix}");
553
554    d.set("__SCHAR_MAX__", "127");
555    d.set("__SHRT_MAX__", "32767");
556    d.set("__INT_MAX__", "2147483647");
557    d.set("__LONG_MAX__", if lp64 { "9223372036854775807L" } else { "2147483647L" });
558    d.set("__LONG_LONG_MAX__", "9223372036854775807LL");
559    d.set("__INTMAX_MAX__", &wide_max);
560    d.set("__UINTMAX_MAX__", &wide_umax);
561    d.set("__SIZE_MAX__", &wide_umax);
562    d.set("__PTRDIFF_MAX__", &wide_max);
563    d.set("__INTPTR_MAX__", &wide_max);
564    d.set("__UINTPTR_MAX__", &wide_umax);
565    d.set("__SIG_ATOMIC_MAX__", "2147483647");
566    d.set("__SIG_ATOMIC_MIN__", "(-__SIG_ATOMIC_MAX__ - 1)");
567    // The widest `_BitInt` this compiler builds, which is narrower than gcc 16's sixty five
568    // thousand five hundred and thirty five because a folded constant here is a hundred and
569    // twenty eight bits wide. A program that reads this macro to decide what to write gets an
570    // answer it can rely on, which is the point of saying a number smaller than gcc's rather
571    // than saying gcc's and refusing what it asked for. `MAX_BIT_INT_WIDTH` in `rucc-sema` is
572    // the same number and has to be changed with it.
573    d.set("__BITINT_MAXWIDTH__", "128");
574
575    let wchar = wchar(target);
576    d.set("__WCHAR_TYPE__", wchar.spelling);
577    d.set("__WCHAR_MAX__", wchar.max);
578    d.set("__WCHAR_MIN__", wchar.min);
579    let wint = wint(target);
580    d.set("__WINT_TYPE__", wint.spelling);
581    d.set("__WINT_MAX__", wint.max);
582    d.set("__WINT_MIN__", wint.min);
583    d.set("__SIZE_TYPE__", wide_unsigned);
584    d.set("__PTRDIFF_TYPE__", wide);
585    d.set("__INTMAX_TYPE__", wide);
586    d.set("__UINTMAX_TYPE__", wide_unsigned);
587    d.set("__INTPTR_TYPE__", wide);
588    d.set("__UINTPTR_TYPE__", wide_unsigned);
589    d.set("__SIG_ATOMIC_TYPE__", "int");
590    d.set("__CHAR16_TYPE__", "short unsigned int");
591    d.set("__CHAR32_TYPE__", "unsigned int");
592    d.set("__INTMAX_C(c)", &format!("c ## {wide_suffix}"));
593    d.set("__UINTMAX_C(c)", &format!("c ## U{wide_suffix}"));
594
595    // The exact width family, which is what a freestanding `stdint.h` is written out of.
596    exact(d, 8, "signed char", "unsigned char", "127", "255", "");
597    exact(d, 16, "short int", "short unsigned int", "32767", "65535", "");
598    // No suffix. An `int` needs none, and the `U` on the unsigned side is added by `exact`
599    // rather than being part of the width.
600    exact(d, 32, "int", "unsigned int", "2147483647", "4294967295U", "");
601    exact(d, 64, wide, wide_unsigned, &wide_max, &wide_umax, wide_suffix);
602
603    // The fast types. GCC makes the 16 and 32 bit ones `long` on x86-64 glibc and `int`
604    // everywhere else, and a header that computes a printf format from the type name notices
605    // the difference.
606    //
607    // musl is the reason this is not simply a question of the architecture. musl defines
608    // `int_fast16_t` and `int_fast32_t` as `int32_t` on every target it supports, GCC built
609    // for a musl target agrees with it, and GCC built for glibc on the same processor does
610    // not. The place it shows is `stdatomic.h`, which GCC ships and writes directly out of
611    // these macros: `typedef _Atomic __INT_FAST16_TYPE__ atomic_int_fast16_t;`. Get this wrong
612    // and every atomic fast type in the program is the wrong width.
613    let fast_is_wide = target.triple.arch == Arch::X86_64 && lp64 && target.triple.env != Env::Musl;
614    let fast_middle = if fast_is_wide { wide } else { "int" };
615    d.set("__INT_FAST8_TYPE__", "signed char");
616    d.set("__UINT_FAST8_TYPE__", "unsigned char");
617    d.set("__INT_FAST8_MAX__", "127");
618    d.set("__UINT_FAST8_MAX__", "255");
619    for width in [16, 32] {
620        let unsigned = if fast_middle == "int" { "unsigned int" } else { wide_unsigned };
621        let max = if fast_middle == "int" { "2147483647" } else { wide_max.as_str() };
622        let umax = if fast_middle == "int" { "4294967295U" } else { wide_umax.as_str() };
623        d.set(&format!("__INT_FAST{width}_TYPE__"), fast_middle);
624        d.set(&format!("__UINT_FAST{width}_TYPE__"), unsigned);
625        d.set(&format!("__INT_FAST{width}_MAX__"), max);
626        d.set(&format!("__UINT_FAST{width}_MAX__"), umax);
627    }
628    d.set("__INT_FAST64_TYPE__", wide);
629    d.set("__UINT_FAST64_TYPE__", wide_unsigned);
630    d.set("__INT_FAST64_MAX__", &wide_max);
631    d.set("__UINT_FAST64_MAX__", &wide_umax);
632
633    widths(d, target, &wchar, &wint, if fast_is_wide { 64 } else { 32 });
634}
635
636/// The widths, which C23's `limits.h` and `stdint.h` are written out of.
637///
638/// Twenty macros and not a few more: there is no `__INT8_WIDTH__`, because the width of an
639/// exact width type is in its name and gcc does not define one, and there is no unsigned member
640/// of any of these pairs, because a signed type and its unsigned counterpart have the same
641/// width and `UINTMAX_WIDTH` is written `__INTMAX_WIDTH__` in every header that needs it.
642///
643/// Each of these says how many value bits and sign bits the type has, which is not the same as
644/// how many bits it occupies. They agree for every type on every target here, and the day one of
645/// them does not, this is the family that has to say the smaller number.
646fn widths(d: &mut Defs, target: &TargetInfo, wchar: &Wchar, wint: &Wint, fast_middle: u32) {
647    let pointer = target.pointer_width;
648    d.set("__SCHAR_WIDTH__", "8");
649    d.set("__SHRT_WIDTH__", "16");
650    d.set("__INT_WIDTH__", "32");
651    d.set("__LONG_WIDTH__", &target.long_width.to_string());
652    d.set("__LONG_LONG_WIDTH__", "64");
653    d.set("__INTMAX_WIDTH__", "64");
654    d.set("__INTPTR_WIDTH__", &pointer.to_string());
655    d.set("__PTRDIFF_WIDTH__", &pointer.to_string());
656    d.set("__SIZE_WIDTH__", &pointer.to_string());
657    d.set("__SIG_ATOMIC_WIDTH__", "32");
658    d.set("__WCHAR_WIDTH__", &(wchar.size * 8).to_string());
659    d.set("__WINT_WIDTH__", &wint.width.to_string());
660    for width in [8, 16, 32, 64] {
661        d.set(&format!("__INT_LEAST{width}_WIDTH__"), &width.to_string());
662    }
663    d.set("__INT_FAST8_WIDTH__", "8");
664    d.set("__INT_FAST16_WIDTH__", &fast_middle.to_string());
665    d.set("__INT_FAST32_WIDTH__", &fast_middle.to_string());
666    d.set("__INT_FAST64_WIDTH__", "64");
667}
668
669/// One width of the exact and least families, which are the same types.
670fn exact(
671    d: &mut Defs,
672    width: u32,
673    signed: &str,
674    unsigned: &str,
675    max: &str,
676    umax: &str,
677    // The suffix the width needs and nothing more, so `""`, `"L"` or `"LL"`. The `U` that
678    // makes a constant unsigned is added below and is not part of this, because a caller that
679    // wrote it here would produce `UU` on the unsigned macro and a stray `U` on the signed one.
680    width_suffix: &str,
681) {
682    d.set(&format!("__INT{width}_TYPE__"), signed);
683    d.set(&format!("__UINT{width}_TYPE__"), unsigned);
684    d.set(&format!("__INT{width}_MAX__"), max);
685    d.set(&format!("__UINT{width}_MAX__"), umax);
686    d.set(&format!("__INT_LEAST{width}_TYPE__"), signed);
687    d.set(&format!("__UINT_LEAST{width}_TYPE__"), unsigned);
688    d.set(&format!("__INT_LEAST{width}_MAX__"), max);
689    d.set(&format!("__UINT_LEAST{width}_MAX__"), umax);
690    // The constant makers. `__INT8_C(1)` is `1` and not `1 ## `, because a paste with nothing
691    // on the right is not a token the expander should have to think about.
692    if width_suffix.is_empty() {
693        d.set(&format!("__INT{width}_C(c)"), "c");
694        d.set(&format!("__UINT{width}_C(c)"), "c ## U");
695    } else {
696        d.set(&format!("__INT{width}_C(c)"), &format!("c ## {width_suffix}"));
697        d.set(&format!("__UINT{width}_C(c)"), &format!("c ## U{width_suffix}"));
698    }
699}
700
701/// What a header needs to know about one floating format, as the text the macros expand to.
702///
703/// The four values are written to the digit gcc writes them to rather than rounded to something
704/// tidier, because a header carrying its own copy of a limit compares the two spellings and a
705/// difference in the last place is a difference.
706struct Characteristics {
707    mant_dig: &'static str,
708    dig: &'static str,
709    min_exp: &'static str,
710    min_10_exp: &'static str,
711    max_exp: &'static str,
712    max_10_exp: &'static str,
713    decimal_dig: &'static str,
714    max: &'static str,
715    min: &'static str,
716    epsilon: &'static str,
717    denorm_min: &'static str,
718    /// Whether the format is one IEC 60559 describes, which every one of them is but the brain
719    /// float, whose significand is a `float`'s with sixteen bits cut off the end of it.
720    is_iec_60559: &'static str,
721}
722
723/// IEEE binary16, which is `_Float16`.
724const HALF: Characteristics = Characteristics {
725    mant_dig: "11",
726    dig: "3",
727    min_exp: "(-13)",
728    min_10_exp: "(-4)",
729    max_exp: "16",
730    max_10_exp: "4",
731    decimal_dig: "5",
732    max: "6.55040000000000000000000000000000000e+4",
733    min: "6.10351562500000000000000000000000000e-5",
734    epsilon: "9.76562500000000000000000000000000000e-4",
735    denorm_min: "5.96046447753906250000000000000000000e-8",
736    is_iec_60559: "1",
737};
738
739/// The brain float, which nothing here names yet and which every format table has a row for.
740const BFLOAT16: Characteristics = Characteristics {
741    mant_dig: "8",
742    dig: "2",
743    min_exp: "(-125)",
744    min_10_exp: "(-37)",
745    max_exp: "128",
746    max_10_exp: "38",
747    decimal_dig: "4",
748    max: "3.38953138925153547590470800371487867e+38",
749    min: "1.17549435082228750796873653722224568e-38",
750    epsilon: "7.81250000000000000000000000000000000e-3",
751    denorm_min: "9.18354961579912115600575419704879436e-41",
752    is_iec_60559: "0",
753};
754
755/// IEEE binary32, which is `float` and `_Float32`.
756const SINGLE: Characteristics = Characteristics {
757    mant_dig: "24",
758    dig: "6",
759    min_exp: "(-125)",
760    min_10_exp: "(-37)",
761    max_exp: "128",
762    max_10_exp: "38",
763    decimal_dig: "9",
764    max: "3.40282346638528859811704183484516925e+38",
765    min: "1.17549435082228750796873653722224568e-38",
766    epsilon: "1.19209289550781250000000000000000000e-7",
767    denorm_min: "1.40129846432481707092372958328991613e-45",
768    is_iec_60559: "1",
769};
770
771/// IEEE binary64, which is `double`, `_Float64`, `_Float32x` and `long double` on Apple and
772/// on Windows.
773const DOUBLE: Characteristics = Characteristics {
774    mant_dig: "53",
775    dig: "15",
776    min_exp: "(-1021)",
777    min_10_exp: "(-307)",
778    max_exp: "1024",
779    max_10_exp: "308",
780    decimal_dig: "17",
781    max: "1.79769313486231570814527423731704357e+308",
782    min: "2.22507385850720138309023271733240406e-308",
783    epsilon: "2.22044604925031308084726333618164062e-16",
784    denorm_min: "4.94065645841246544176568792868221372e-324",
785    is_iec_60559: "1",
786};
787
788/// The x87 eighty bit format, which on x86-64 is both `long double` and `_Float64x`.
789const X87: Characteristics = Characteristics {
790    mant_dig: "64",
791    dig: "18",
792    min_exp: "(-16381)",
793    min_10_exp: "(-4931)",
794    max_exp: "16384",
795    max_10_exp: "4932",
796    decimal_dig: "21",
797    max: "1.18973149535723176502126385303097021e+4932",
798    min: "3.36210314311209350626267781732175260e-4932",
799    epsilon: "1.08420217248550443400745280086994171e-19",
800    denorm_min: "3.64519953188247460252840593361941982e-4951",
801    is_iec_60559: "1",
802};
803
804/// IEEE binary128, which is `_Float128`, `_Float64x` off x86 and `long double` on AArch64 and
805/// RISC-V Linux.
806const QUAD: Characteristics = Characteristics {
807    mant_dig: "113",
808    dig: "33",
809    min_exp: "(-16381)",
810    min_10_exp: "(-4931)",
811    max_exp: "16384",
812    max_10_exp: "4932",
813    decimal_dig: "36",
814    max: "1.18973149535723176508575932662800702e+4932",
815    min: "3.36210314311209350626267781732175260e-4932",
816    epsilon: "1.92592994438723585305597794258492732e-34",
817    denorm_min: "6.47517511943802511092443895822764655e-4966",
818    is_iec_60559: "1",
819};
820
821/// The row of the table a format has, so that a type the target chooses the format of can look
822/// its own limits up rather than have them written out again per architecture.
823const fn characteristics(format: Format) -> &'static Characteristics {
824    match format {
825        Format::Half => &HALF,
826        Format::BFloat16 => &BFLOAT16,
827        Format::Single => &SINGLE,
828        Format::Double => &DOUBLE,
829        Format::X87Extended => &X87,
830        Format::Quad => &QUAD,
831    }
832}
833
834/// The `float.h` characteristics.
835///
836/// Nine families of them, which is `float`, `double` and `long double` and the six C23 named
837/// them after. Only two of the nine depend on the target, and they are the two whose format is
838/// a target property: `long double`, which is x87 on x86-64 Linux, quad on AArch64 and RISC-V
839/// Linux and a `double` on Apple and on Windows, and `_Float64x`, which is the widest format the
840/// processor has and so does not follow `long double` down on the targets that shrink it.
841///
842/// `__FLT128X_*__` is deliberately missing. `_Float128x` is a type no target gcc supports has,
843/// so gcc defines nothing for it and neither does this.
844fn floats(d: &mut Defs, target: &TargetInfo) {
845    d.set("__FLT_RADIX__", "2");
846    // Every operation is done in the type of its operands, which is what SSE2 and the AArch64
847    // and RISC-V floating units all do. The other two names are the same answer asked under the
848    // rules of C99 and of TS 18661-3, which are the same rules for a target with no excess
849    // precision to have, and glibc's `<math.h>` reads the last of the three.
850    d.set("__FLT_EVAL_METHOD__", "0");
851    d.set("__FLT_EVAL_METHOD_C99__", "0");
852    d.set("__FLT_EVAL_METHOD_TS_18661_3__", "0");
853
854    family(d, "FLT", &SINGLE, |value| format!("{value}F"));
855    // gcc writes the `double` values as `long double` constants cast back down, which is exact
856    // in every format `long double` has and is the one family whose values are not a suffix.
857    family(d, "DBL", &DOUBLE, |value| format!("((double){value}L)"));
858    family(d, "LDBL", characteristics(target.long_double_format), |value| format!("{value}L"));
859
860    family(d, "FLT16", &HALF, |value| format!("{value}F16"));
861    family(d, "FLT32", &SINGLE, |value| format!("{value}F32"));
862    family(d, "FLT64", &DOUBLE, |value| format!("{value}F64"));
863    family(d, "FLT128", &QUAD, |value| format!("{value}F128"));
864    family(d, "FLT32X", &DOUBLE, |value| format!("{value}F32x"));
865    family(d, "FLT64X", characteristics(target.float64x_format), |value| format!("{value}F64x"));
866
867    d.set("__DECIMAL_DIG__", "__LDBL_DECIMAL_DIG__");
868}
869
870/// One family of `float.h` macros, named `__{prefix}_*__`.
871///
872/// `write` turns a value into the constant its macro expands to, which is a suffix for every
873/// family but `double`. `NORM_MAX` is `MAX` for all six formats, since the two differ only
874/// where a format holds values above its largest normal one and none of these do.
875fn family(d: &mut Defs, prefix: &str, c: &Characteristics, write: impl Fn(&str) -> String) {
876    d.set(&format!("__{prefix}_MANT_DIG__"), c.mant_dig);
877    d.set(&format!("__{prefix}_DIG__"), c.dig);
878    d.set(&format!("__{prefix}_MIN_EXP__"), c.min_exp);
879    d.set(&format!("__{prefix}_MIN_10_EXP__"), c.min_10_exp);
880    d.set(&format!("__{prefix}_MAX_EXP__"), c.max_exp);
881    d.set(&format!("__{prefix}_MAX_10_EXP__"), c.max_10_exp);
882    d.set(&format!("__{prefix}_DECIMAL_DIG__"), c.decimal_dig);
883    d.set(&format!("__{prefix}_MAX__"), &write(c.max));
884    d.set(&format!("__{prefix}_NORM_MAX__"), &write(c.max));
885    d.set(&format!("__{prefix}_MIN__"), &write(c.min));
886    d.set(&format!("__{prefix}_EPSILON__"), &write(c.epsilon));
887    d.set(&format!("__{prefix}_DENORM_MIN__"), &write(c.denorm_min));
888    d.set(&format!("__{prefix}_IS_IEC_60559__"), c.is_iec_60559);
889    d.set(&format!("__{prefix}_HAS_DENORM__"), "1");
890    d.set(&format!("__{prefix}_HAS_INFINITY__"), "1");
891    d.set(&format!("__{prefix}_HAS_QUIET_NAN__"), "1");
892}
893
894#[cfg(test)]
895mod tests {
896    use rucc_target::Triple;
897
898    use super::*;
899
900    fn set_for(triple: &str) -> String {
901        let triple: Triple = triple.parse().expect("a triple the compiler supports");
902        built_in(&TargetInfo::new(triple), &Predef::new())
903    }
904
905    fn has(text: &str, line: &str) -> bool {
906        text.lines().any(|l| l == line)
907    }
908
909    #[test]
910    fn the_set_is_driven_by_the_target_rather_than_by_the_host() {
911        let x86 = set_for("x86_64-unknown-linux-gnu");
912        let arm = set_for("aarch64-unknown-linux-gnu");
913        assert!(has(&x86, "#define __x86_64__ 1"));
914        assert!(!has(&x86, "#define __aarch64__ 1"));
915        assert!(has(&arm, "#define __aarch64__ 1"));
916        assert!(!has(&arm, "#define __x86_64__ 1"));
917        assert!(has(&x86, "#define __linux__ 1") && has(&arm, "#define __linux__ 1"));
918    }
919
920    #[test]
921    fn windows_is_the_target_that_makes_long_thirty_two_bits() {
922        let windows = set_for("x86_64-pc-windows-msvc");
923        let linux = set_for("x86_64-unknown-linux-gnu");
924        assert!(has(&windows, "#define __SIZEOF_LONG__ 4"));
925        assert!(has(&windows, "#define __SIZE_TYPE__ long long unsigned int"));
926        assert!(has(&windows, "#define __INT64_TYPE__ long long int"));
927        assert!(!has(&windows, "#define __LP64__ 1"));
928        assert!(has(&linux, "#define __SIZEOF_LONG__ 8"));
929        assert!(has(&linux, "#define __SIZE_TYPE__ long unsigned int"));
930        assert!(has(&linux, "#define __INT64_TYPE__ long int"));
931        assert!(has(&linux, "#define __LP64__ 1"));
932    }
933
934    #[test]
935    fn wchar_t_is_the_type_that_divides_the_targets() {
936        // Signed on x86-64 Linux, unsigned on AArch64 Linux, and sixteen bits on Windows.
937        assert!(has(&set_for("x86_64-unknown-linux-gnu"), "#define __WCHAR_TYPE__ int"));
938        assert!(has(&set_for("aarch64-unknown-linux-gnu"), "#define __WCHAR_TYPE__ unsigned int"));
939        let windows = set_for("x86_64-pc-windows-msvc");
940        assert!(has(&windows, "#define __WCHAR_TYPE__ short unsigned int"));
941        assert!(has(&windows, "#define __SIZEOF_WCHAR_T__ 2"));
942    }
943
944    #[test]
945    fn apple_spells_the_architecture_its_own_way_and_its_headers_only_know_that_spelling() {
946        // sys/cdefs.h reaches #error "Unsupported architecture" without these, which is the
947        // first line of the first header of every program on the platform.
948        let darwin = set_for("aarch64-apple-darwin");
949        assert!(has(&darwin, "#define __arm64__ 1"));
950        assert!(has(&darwin, "#define __arm64 1"));
951        assert!(has(&darwin, "#define __aarch64__ 1"), "the portable spelling stays too");
952        let linux = set_for("aarch64-unknown-linux-gnu");
953        assert!(!has(&linux, "#define __arm64__ 1"), "Apple's spelling is Apple's alone");
954        assert!(!has(&set_for("x86_64-apple-darwin"), "#define __arm64__ 1"));
955    }
956
957    #[test]
958    fn wint_t_does_not_follow_wchar_t() {
959        // Apple makes it signed so that WEOF is negative the way EOF is. Linux does not.
960        let darwin = set_for("aarch64-apple-darwin");
961        assert!(has(&darwin, "#define __WINT_TYPE__ int"));
962        assert!(has(&darwin, "#define __WINT_MAX__ 2147483647"));
963        assert!(has(&darwin, "#define __WCHAR_TYPE__ int"));
964        let linux = set_for("aarch64-unknown-linux-gnu");
965        assert!(has(&linux, "#define __WINT_TYPE__ unsigned int"));
966        assert!(has(&linux, "#define __WINT_MAX__ 4294967295U"));
967        assert!(has(&linux, "#define __WCHAR_TYPE__ unsigned int"), "and wchar_t is its own");
968        assert!(has(
969            &set_for("x86_64-pc-windows-msvc"),
970            "#define __WINT_TYPE__ short unsigned int"
971        ));
972    }
973
974    #[test]
975    fn the_widths_say_what_the_type_holds_and_follow_the_target_that_changes_it() {
976        // Twenty of them, which is gcc's set: no exact width member, since the width of an
977        // `int32_t` is in its name, and no unsigned member, since a header that wants
978        // `UINTMAX_WIDTH` writes `__INTMAX_WIDTH__`.
979        let linux = set_for("x86_64-unknown-linux-gnu");
980        assert_eq!(linux.lines().filter(|line| line.contains("_WIDTH__")).count(), 20);
981        assert!(has(&linux, "#define __LONG_WIDTH__ 64"));
982        assert!(has(&linux, "#define __SIZE_WIDTH__ 64"));
983        assert!(has(&linux, "#define __WCHAR_WIDTH__ 32"));
984        assert!(has(&linux, "#define __INT_LEAST16_WIDTH__ 16"));
985        // x86-64 glibc is where `int_fast16_t` is a `long`, and the width has to say so or a
986        // program that switches on it picks the wrong branch.
987        assert!(has(&linux, "#define __INT_FAST16_WIDTH__ 64"));
988        assert!(has(&set_for("x86_64-unknown-linux-musl"), "#define __INT_FAST16_WIDTH__ 32"));
989        // Windows has a thirty two bit `long` and a sixteen bit `wint_t`, and the pointer
990        // sized types stay sixty four bits wide whatever `long` does.
991        let windows = set_for("x86_64-pc-windows-msvc");
992        assert!(has(&windows, "#define __LONG_WIDTH__ 32"));
993        assert!(has(&windows, "#define __WINT_WIDTH__ 16"));
994        assert!(has(&windows, "#define __SIZE_WIDTH__ 64"));
995        assert!(has(&windows, "#define __INTMAX_WIDTH__ 64"));
996    }
997
998    #[test]
999    fn a_constant_maker_gets_the_suffix_its_width_needs_and_no_other() {
1000        // Found by diffing `-dM` against the system compiler. The 32 bit row was passing `U`
1001        // as its width suffix, which put a `U` on the signed macro and two on the unsigned
1002        // one, and `UINT32_C(1)` expanded to `1UU`, which is not a token.
1003        let linux = set_for("x86_64-unknown-linux-gnu");
1004        assert!(has(&linux, "#define __INT32_C(c) c"));
1005        assert!(has(&linux, "#define __UINT32_C(c) c ## U"));
1006        assert!(has(&linux, "#define __INT16_C(c) c"));
1007        assert!(has(&linux, "#define __UINT16_C(c) c ## U"));
1008        // The wide ones do take a suffix, and the `U` goes in front of it.
1009        assert!(has(&linux, "#define __INT64_C(c) c ## L"));
1010        assert!(has(&linux, "#define __UINT64_C(c) c ## UL"));
1011        // Windows has a thirty two bit `long`, so its sixty four bit constants are `long long`.
1012        let windows = set_for("x86_64-pc-windows-msvc");
1013        assert!(has(&windows, "#define __INT64_C(c) c ## LL"));
1014        assert!(has(&windows, "#define __UINT64_C(c) c ## ULL"));
1015    }
1016
1017    #[test]
1018    fn the_symbol_prefix_is_defined_everywhere_including_where_it_is_empty() {
1019        // Empty is not the same as absent, because glibc stringifies it. Leaving it undefined
1020        // turns `__asm__ (__ASMNAME ("__xpg_strerror_r"))` into an asm name of
1021        // "__USER_LABEL_PREFIX__" "__xpg_strerror_r", which renames the function instead of
1022        // failing, and that is a bug found at link time or later.
1023        for triple in
1024            ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
1025        {
1026            assert!(has(&set_for(triple), "#define __USER_LABEL_PREFIX__ "), "{triple}");
1027        }
1028        // Mach-O keeps the underscore that ELF dropped.
1029        assert!(has(&set_for("aarch64-apple-darwin"), "#define __USER_LABEL_PREFIX__ _"));
1030    }
1031
1032    #[test]
1033    fn the_memory_orders_are_there_even_without_atomics() {
1034        // musl's stdatomic.h writes `memory_order_relaxed = __ATOMIC_RELAXED` with no test
1035        // around it, so these are not a promise about `_Atomic`, they are the numbering the
1036        // builtins take, and a compiler without them prints an enumerator whose value is an
1037        // identifier.
1038        let linux = set_for("x86_64-unknown-linux-gnu");
1039        assert!(has(&linux, "#define __ATOMIC_RELAXED 0"));
1040        assert!(has(&linux, "#define __ATOMIC_SEQ_CST 5"));
1041        assert!(has(&linux, "#define __STDC_NO_ATOMICS__ 1"), "and we still have no _Atomic");
1042        assert!(has(&linux, "#define __GCC_ATOMIC_INT_LOCK_FREE 2"));
1043        assert!(has(&linux, "#define __GCC_ATOMIC_LLONG_LOCK_FREE 2"));
1044        assert!(has(&set_for("x86_64-pc-windows-msvc"), "#define __GCC_ATOMIC_LLONG_LOCK_FREE 2"));
1045    }
1046
1047    #[test]
1048    fn long_double_is_three_types_and_the_macros_say_which() {
1049        assert!(has(&set_for("x86_64-unknown-linux-gnu"), "#define __LDBL_MANT_DIG__ 64"));
1050        assert!(has(&set_for("aarch64-unknown-linux-gnu"), "#define __LDBL_MANT_DIG__ 113"));
1051        assert!(has(&set_for("aarch64-apple-darwin"), "#define __LDBL_MANT_DIG__ 53"));
1052    }
1053
1054    #[test]
1055    fn the_extended_floating_types_have_the_limits_their_formats_have() {
1056        // Every one of these but `_Float64x` is the same format on every target, which is the
1057        // point of the interchange types, so the limits are the same everywhere too.
1058        let linux = set_for("x86_64-unknown-linux-gnu");
1059        assert!(has(&linux, "#define __FLT16_MANT_DIG__ 11"));
1060        assert!(has(&linux, "#define __FLT32_MANT_DIG__ 24"));
1061        assert!(has(&linux, "#define __FLT64_MANT_DIG__ 53"));
1062        assert!(has(&linux, "#define __FLT128_MANT_DIG__ 113"));
1063        assert!(has(&linux, "#define __FLT32X_MANT_DIG__ 53"));
1064        // Each family writes its values with its own suffix, so a header that assigns one to an
1065        // object of the type gets the type back rather than a conversion.
1066        assert!(has(&linux, "#define __FLT16_MAX__ 6.55040000000000000000000000000000000e+4F16"));
1067        assert!(has(
1068            &linux,
1069            "#define __FLT32X_MIN__ 2.22507385850720138309023271733240406e-308F32x"
1070        ));
1071        // `_Float128x` is a type no target has, so gcc defines nothing for it and neither
1072        // does this.
1073        assert!(!linux.contains("__FLT128X_"));
1074    }
1075
1076    #[test]
1077    fn float64x_keeps_the_width_that_long_double_loses_on_apple() {
1078        // The two are the same eighty bit x87 format on x86-64 and part company everywhere
1079        // else, because `_Float64x` follows the processor and `long double` follows the ABI.
1080        let linux = set_for("x86_64-unknown-linux-gnu");
1081        assert!(has(&linux, "#define __FLT64X_MANT_DIG__ 64"));
1082        assert!(has(&linux, "#define __LDBL_MANT_DIG__ 64"));
1083        let mac = set_for("aarch64-apple-darwin");
1084        assert!(has(&mac, "#define __FLT64X_MANT_DIG__ 113"));
1085        assert!(has(&mac, "#define __LDBL_MANT_DIG__ 53"));
1086        let windows = set_for("x86_64-pc-windows-msvc");
1087        assert!(has(&windows, "#define __FLT64X_MANT_DIG__ 64"));
1088        assert!(has(&windows, "#define __LDBL_MANT_DIG__ 53"));
1089    }
1090
1091    #[test]
1092    fn the_largest_value_of_a_binary_format_is_also_its_largest_normal_one() {
1093        // `NORM_MAX` is only ever smaller than `MAX` for a format that holds values above its
1094        // largest normal one, and none of the six here does.
1095        let linux = set_for("x86_64-unknown-linux-gnu");
1096        for prefix in ["FLT", "DBL", "LDBL", "FLT16", "FLT32", "FLT64", "FLT128", "FLT32X"] {
1097            let value = |suffix: &str| {
1098                let name = format!("#define __{prefix}_{suffix}__ ");
1099                let line = linux
1100                    .lines()
1101                    .find(|line| line.starts_with(&name))
1102                    .unwrap_or_else(|| panic!("__{prefix}_{suffix}__ is defined"));
1103                line[name.len()..].to_owned()
1104            };
1105            assert_eq!(value("MAX"), value("NORM_MAX"), "__{prefix}_NORM_MAX__");
1106        }
1107    }
1108
1109    #[test]
1110    fn the_widest_bit_int_is_said_in_every_dialect() {
1111        // gcc defines it under `-std=c17` as well as `-std=c23`, and a header that reaches for
1112        // `_BitInt` tests the macro rather than the version, so an absent one reads as a
1113        // compiler without the type at all.
1114        let mut opts = Predef::new();
1115        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1116        assert!(has(&built_in(&target, &opts), "#define __BITINT_MAXWIDTH__ 128"));
1117        opts.std = Std::C17;
1118        assert!(has(&built_in(&target, &opts), "#define __BITINT_MAXWIDTH__ 128"));
1119    }
1120
1121    #[test]
1122    fn char_signedness_is_recorded_only_when_it_is_unsigned() {
1123        // Which is how GCC does it: the macro exists to mark the unusual case.
1124        assert!(has(&set_for("aarch64-unknown-linux-gnu"), "#define __CHAR_UNSIGNED__ 1"));
1125        assert!(!has(&set_for("x86_64-unknown-linux-gnu"), "#define __CHAR_UNSIGNED__ 1"));
1126    }
1127
1128    #[test]
1129    fn the_dialect_decides_the_standard_macros() {
1130        let mut opts = Predef::new();
1131        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1132        assert!(has(&built_in(&target, &opts), "#define __STDC_VERSION__ 202311L"));
1133        assert!(!has(&built_in(&target, &opts), "#define __STRICT_ANSI__ 1"));
1134        assert!(has(&built_in(&target, &opts), "#define linux 1"));
1135
1136        opts.gnu_extensions = false;
1137        assert!(has(&built_in(&target, &opts), "#define __STRICT_ANSI__ 1"));
1138        assert!(!has(&built_in(&target, &opts), "#define linux 1"), "not a reserved name");
1139
1140        opts.std = Std::C89;
1141        let c89 = built_in(&target, &opts);
1142        assert!(!c89.contains("__STDC_VERSION__"), "C89 does not define it at all");
1143        assert!(has(&c89, "#define __STDC__ 1"));
1144    }
1145
1146    #[test]
1147    fn the_optimizer_level_is_visible_to_the_preprocessor() {
1148        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1149        let mut opts = Predef::new();
1150        assert!(has(&built_in(&target, &opts), "#define __NO_INLINE__ 1"));
1151        assert!(!built_in(&target, &opts).contains("__OPTIMIZE__"));
1152
1153        opts.opt_level = OptLevel::O2;
1154        assert!(has(&built_in(&target, &opts), "#define __OPTIMIZE__ 1"));
1155        assert!(!built_in(&target, &opts).contains("__OPTIMIZE_SIZE__"));
1156
1157        opts.opt_level = OptLevel::Os;
1158        assert!(has(&built_in(&target, &opts), "#define __OPTIMIZE_SIZE__ 1"));
1159    }
1160
1161    #[test]
1162    fn a_command_line_define_with_no_value_is_one() {
1163        let mut opts = Predef::new();
1164        opts.defines = vec!["FOO".to_owned(), "BAR=2".to_owned(), "F(x)=x + 1".to_owned()];
1165        opts.undefines = vec!["__linux__".to_owned()];
1166        let text = command_line(&opts);
1167        assert!(has(&text, "#define FOO 1"));
1168        assert!(has(&text, "#define BAR 2"));
1169        assert!(has(&text, "#define F(x) x + 1"));
1170        // The undefine comes last, because `-U` beats `-D` whichever side of it it was on.
1171        assert!(text.trim_end().ends_with("#undef __linux__"));
1172    }
1173
1174    #[test]
1175    fn no_command_line_macros_is_no_file_at_all() {
1176        assert!(command_line(&Predef::new()).is_empty());
1177    }
1178
1179    #[test]
1180    fn a_date_is_spelled_the_way_the_standard_fixes() {
1181        // The epoch itself, and a day that needs the space padding the format asks for.
1182        let epoch = Timestamp::from_unix(0);
1183        assert_eq!(epoch.date, "Jan  1 1970");
1184        assert_eq!(epoch.time, "00:00:00");
1185        let leap = Timestamp::from_unix(1_709_164_800);
1186        assert_eq!(leap.date, "Feb 29 2024", "2024 is a leap year");
1187        let late = Timestamp::from_unix(1_735_689_599);
1188        assert_eq!(late.date, "Dec 31 2024");
1189        assert_eq!(late.time, "23:59:59");
1190    }
1191
1192    #[test]
1193    fn a_date_before_the_epoch_still_comes_out_right() {
1194        // Not because anyone compiles in 1969, but because the arithmetic that gets this
1195        // wrong is the same arithmetic that gets a time zone offset wrong.
1196        assert_eq!(Timestamp::from_unix(-1).date, "Dec 31 1969");
1197        assert_eq!(Timestamp::from_unix(-1).time, "23:59:59");
1198    }
1199
1200    #[test]
1201    fn the_gnuc_version_is_a_knob_rather_than_a_constant() {
1202        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
1203        let mut opts = Predef::new();
1204        assert!(has(&built_in(&target, &opts), "#define __GNUC__ 7"));
1205        opts.gnuc = GnucVersion { major: 15, minor: 1, patch: 0 };
1206        assert!(has(&built_in(&target, &opts), "#define __GNUC__ 15"));
1207        assert!(has(&built_in(&target, &opts), "#define __GNUC_MINOR__ 1"));
1208    }
1209
1210    #[test]
1211    fn musl_and_glibc_disagree_about_the_fast_types_on_the_same_processor() {
1212        // The same x86-64 machine, two libcs, two answers. GCC built for glibc says `long int`
1213        // and GCC built for musl says `int`, because musl defines `int_fast16_t` as `int32_t`
1214        // everywhere. It shows in `stdatomic.h`, which GCC writes out of these macros, so
1215        // getting it wrong makes every atomic fast type the wrong width.
1216        let gnu = set_for("x86_64-unknown-linux-gnu");
1217        let musl = set_for("x86_64-unknown-linux-musl");
1218        assert!(has(&gnu, "#define __INT_FAST16_TYPE__ long int"));
1219        assert!(has(&gnu, "#define __INT_FAST32_TYPE__ long int"));
1220        assert!(has(&gnu, "#define __UINT_FAST16_TYPE__ long unsigned int"));
1221        assert!(has(&musl, "#define __INT_FAST16_TYPE__ int"));
1222        assert!(has(&musl, "#define __INT_FAST32_TYPE__ int"));
1223        assert!(has(&musl, "#define __UINT_FAST16_TYPE__ unsigned int"));
1224        // The limits have to move with the types or a header that checks them stops agreeing
1225        // with the header that uses them.
1226        assert!(has(&gnu, "#define __INT_FAST16_MAX__ 9223372036854775807L"));
1227        assert!(has(&musl, "#define __INT_FAST16_MAX__ 2147483647"));
1228        assert!(has(&musl, "#define __UINT_FAST16_MAX__ 4294967295U"));
1229    }
1230
1231    #[test]
1232    fn the_libc_only_moves_the_two_fast_types_it_is_allowed_to_move() {
1233        // 8 and 64 are the same on both, and so is everything outside the fast family. A libc
1234        // is not a processor and this is the whole of what it is permitted to change here.
1235        let gnu = set_for("x86_64-unknown-linux-gnu");
1236        let musl = set_for("x86_64-unknown-linux-musl");
1237        for line in [
1238            "#define __INT_FAST8_TYPE__ signed char",
1239            "#define __INT_FAST64_TYPE__ long int",
1240            "#define __INT64_TYPE__ long int",
1241            "#define __SIZE_TYPE__ long unsigned int",
1242            "#define __SIZEOF_LONG__ 8",
1243            "#define __LP64__ 1",
1244        ] {
1245            assert!(has(&gnu, line), "glibc lost {line}");
1246            assert!(has(&musl, line), "musl lost {line}");
1247        }
1248    }
1249
1250    #[test]
1251    fn a_non_x86_target_has_int_sized_fast_types_whatever_the_libc() {
1252        // The `long` answer was always specific to x86-64. aarch64 glibc says `int` too, so
1253        // adding the libc axis must not have turned into a second way to say x86-64.
1254        let arm_gnu = set_for("aarch64-unknown-linux-gnu");
1255        let arm_musl = set_for("aarch64-unknown-linux-musl");
1256        assert!(has(&arm_gnu, "#define __INT_FAST16_TYPE__ int"));
1257        assert!(has(&arm_musl, "#define __INT_FAST16_TYPE__ int"));
1258    }
1259}