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_session::{GnucVersion, OptLevel, Options, Std};
23use rucc_target::{Arch, Env, Os, TargetInfo};
24
25/// The name a diagnostic about the generated set points at.
26pub const BUILT_IN: &str = "<built-in>";
27
28/// The name a diagnostic about `-D` or `-U` points at.
29pub const COMMAND_LINE: &str = "<command-line>";
30
31/// The translation date, as `__DATE__` and `__TIME__` spell it.
32///
33/// Fixed for the whole translation unit, which is what the standard requires and what makes
34/// the two macros ordinary object-like macros rather than something the expander has to know
35/// about.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Timestamp {
38    /// `Mmm dd yyyy`, with the day space padded, which is the format the standard fixes.
39    pub date: String,
40    /// `hh:mm:ss`.
41    pub time: String,
42}
43
44impl Timestamp {
45    /// The current time, or `SOURCE_DATE_EPOCH` when the build asked for a reproducible one.
46    ///
47    /// Reading the environment here rather than in the driver is what GCC does, and it keeps
48    /// the variable working for an embedder who never goes through a command line.
49    pub fn now() -> Timestamp {
50        let seconds = match std::env::var("SOURCE_DATE_EPOCH").ok().and_then(|v| v.parse().ok()) {
51            Some(fixed) => fixed,
52            None => std::time::SystemTime::now()
53                .duration_since(std::time::UNIX_EPOCH)
54                .map_or(0, |d| d.as_secs() as i64),
55        };
56        Timestamp::from_unix(seconds)
57    }
58
59    /// The time `seconds` after the epoch, in UTC.
60    ///
61    /// UTC rather than local time, because a compiler whose output depends on the machine's
62    /// time zone is a compiler whose output is not reproducible.
63    pub fn from_unix(seconds: i64) -> Timestamp {
64        let days = seconds.div_euclid(86_400);
65        let rest = seconds.rem_euclid(86_400);
66        let (year, month, day) = civil_from_days(days);
67        const MONTHS: [&str; 12] =
68            ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
69        let name = MONTHS[(month - 1) as usize];
70        Timestamp {
71            date: format!("{name} {day:2} {year}"),
72            time: format!("{:02}:{:02}:{:02}", rest / 3600, (rest / 60) % 60, rest % 60),
73        }
74    }
75}
76
77/// The year, month and day `days` after 1970-01-01.
78///
79/// Howard Hinnant's civil calendar algorithm, which is a handful of divisions and no table.
80/// It is here rather than in a dependency because the whole workspace has no dependencies,
81/// and a date conversion is not a good reason to acquire the first one.
82fn civil_from_days(days: i64) -> (i64, u32, u32) {
83    // Shift the epoch to 0000-03-01, so that a leap day is the last day of the year and the
84    // month lengths become a repeating pattern that one division can invert.
85    let shifted = days + 719_468;
86    let era = shifted.div_euclid(146_097);
87    let day_of_era = shifted.rem_euclid(146_097);
88    let year_of_era =
89        (day_of_era - day_of_era / 1460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
90    let year = year_of_era + era * 400;
91    let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
92    let marched = (5 * day_of_year + 2) / 153;
93    let day = (day_of_year - (153 * marched + 2) / 5 + 1) as u32;
94    let month = if marched < 10 { marched + 3 } else { marched - 9 } as u32;
95    (year + i64::from(month <= 2), month, day)
96}
97
98/// Everything the predefined set is built from that is not the target.
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct Predef {
101    /// The dialect, which decides `__STDC_VERSION__`.
102    pub std: Std,
103    /// Whether the GNU extensions are on, which is `-std=gnu23` rather than `-std=c23`. It
104    /// decides `__STRICT_ANSI__` and the unarmoured `linux` and `unix` macros.
105    pub gnu_extensions: bool,
106    /// The GCC release claimed.
107    pub gnuc: GnucVersion,
108    /// Decides `__OPTIMIZE__`, `__OPTIMIZE_SIZE__` and `__NO_INLINE__`.
109    pub opt_level: OptLevel,
110    /// Whether there is a standard library, which is `-ffreestanding` turned around.
111    pub hosted: bool,
112    /// `__DATE__` and `__TIME__`.
113    pub timestamp: Timestamp,
114    /// `-D` in command line order. `FOO` means `FOO=1`, as GCC has it.
115    pub defines: Vec<String>,
116    /// `-U` in command line order, applied after the defines.
117    pub undefines: Vec<String>,
118}
119
120impl Predef {
121    /// The default dialect, `gnu23`, at `-O0`.
122    pub fn new() -> Predef {
123        Predef {
124            std: Std::default(),
125            gnu_extensions: true,
126            gnuc: GnucVersion::default(),
127            opt_level: OptLevel::O0,
128            hosted: true,
129            timestamp: Timestamp::now(),
130            defines: Vec::new(),
131            undefines: Vec::new(),
132        }
133    }
134}
135
136impl Predef {
137    /// The set the command line asked for.
138    ///
139    /// The mapping lives here rather than in the driver because it is the definition of what
140    /// each flag means to the macro set, and the driver's job is to parse a command line, not
141    /// to know that `-ffreestanding` is `__STDC_HOSTED__` being zero.
142    pub fn for_options(opts: &Options) -> Predef {
143        Predef {
144            std: opts.std,
145            gnu_extensions: opts.gnu_extensions,
146            gnuc: opts.gnuc,
147            opt_level: opts.opt_level,
148            hosted: opts.hosted,
149            timestamp: Timestamp::now(),
150            defines: opts.defines.clone(),
151            undefines: opts.undefines.clone(),
152        }
153    }
154}
155
156impl Default for Predef {
157    fn default() -> Predef {
158        Predef::new()
159    }
160}
161
162/// A file of `#define` lines being built up.
163struct Defs {
164    text: String,
165}
166
167impl Defs {
168    fn new() -> Defs {
169        Defs { text: String::new() }
170    }
171
172    /// `#define name value`.
173    fn set(&mut self, name: &str, value: &str) {
174        self.text.push_str("#define ");
175        self.text.push_str(name);
176        self.text.push(' ');
177        self.text.push_str(value);
178        self.text.push('\n');
179    }
180
181    /// `#define name 1`, which is what a macro that is only ever tested for needs.
182    fn flag(&mut self, name: &str) {
183        self.set(name, "1");
184    }
185
186    fn set_if(&mut self, when: bool, name: &str, value: &str) {
187        if when {
188            self.set(name, value);
189        }
190    }
191
192    fn flag_if(&mut self, when: bool, name: &str) {
193        if when {
194            self.flag(name);
195        }
196    }
197}
198
199/// The whole predefined set for a target, as the text of a file.
200pub(crate) fn built_in(target: &TargetInfo, opts: &Predef) -> String {
201    let mut d = Defs::new();
202    identity(&mut d, opts);
203    // `__DATE__` and `__TIME__` are fixed for the whole translation unit, which is what the
204    // standard asks for, so they are ordinary object-like macros and the expander needs to
205    // know nothing about them.
206    d.set("__DATE__", &format!("\"{}\"", opts.timestamp.date));
207    d.set("__TIME__", &format!("\"{}\"", opts.timestamp.time));
208    dialect(&mut d, opts);
209    optimization(&mut d, opts);
210    platform(&mut d, target, opts);
211    sizes(&mut d, target);
212    integers(&mut d, target);
213    floats(&mut d, target);
214    atomics(&mut d, target);
215    d.text
216}
217
218/// `-D` and `-U`, as the text of a file.
219///
220/// Empty when there are none, so that the caller can skip adding a file that would say
221/// nothing. The undefines come last whatever order they were written in, because `-U` beats
222/// `-D` in GCC no matter which side of it the `-D` was on.
223pub(crate) fn command_line(opts: &Predef) -> String {
224    let mut d = Defs::new();
225    for define in &opts.defines {
226        match define.split_once('=') {
227            Some((name, value)) => d.set(name, value),
228            // `-DFOO` is `-DFOO=1`. A macro nobody gave a value to is one that is only ever
229            // tested for, and giving it an empty body would break `#if FOO`.
230            None => d.flag(define),
231        }
232    }
233    for name in &opts.undefines {
234        d.text.push_str("#undef ");
235        d.text.push_str(name);
236        d.text.push('\n');
237    }
238    d.text
239}
240
241/// Who the compiler says it is.
242fn identity(d: &mut Defs, opts: &Predef) {
243    d.flag("__rucc__");
244    d.set("__rucc_version__", "\"0.1.0\"");
245    d.set("__rucc_major__", "0");
246    d.set("__rucc_minor__", "1");
247    d.set("__rucc_patchlevel__", "0");
248    // The promise from section 4.5. Everything in the matrix hangs off this line.
249    d.set("__GNUC__", &opts.gnuc.major.to_string());
250    d.set("__GNUC_MINOR__", &opts.gnuc.minor.to_string());
251    d.set("__GNUC_PATCHLEVEL__", &opts.gnuc.patch.to_string());
252    d.set("__VERSION__", "\"rucc 0.1.0\"");
253    // Not `__clang__`, deliberately. Section 4.5 says so, and a header that takes the Clang
254    // path expects Clang's extension surface rather than GCC's.
255    d.flag("__GNUC_STDC_INLINE__");
256}
257
258/// What the dialect flags say.
259fn dialect(d: &mut Defs, opts: &Predef) {
260    d.flag("__STDC__");
261    d.set_if(opts.hosted, "__STDC_HOSTED__", "1");
262    d.set_if(!opts.hosted, "__STDC_HOSTED__", "0");
263    if let Some(version) = opts.std.stdc_version() {
264        d.set("__STDC_VERSION__", version);
265    }
266    // Defined exactly when the extensions are off, which is the whole difference between
267    // `-std=c23` and `-std=gnu23` as far as the preprocessor is concerned.
268    d.flag_if(!opts.gnu_extensions, "__STRICT_ANSI__");
269    d.flag("__STDC_UTF_16__");
270    d.flag("__STDC_UTF_32__");
271    d.flag("__STDC_IEC_559__");
272    d.flag("__STDC_IEC_559_COMPLEX__");
273    d.set_if(opts.std == Std::C23, "__STDC_IEC_60559_BFP__", "202311L");
274    d.set("__STDC_ISO_10646__", "201706L");
275    // C11 made these conditional features, and a header that sees `__STDC_VERSION__` at
276    // 201112 with no `__STDC_NO_ATOMICS__` next to it will use `_Atomic`.
277    if opts.std.has_c11() {
278        d.flag("__STDC_NO_ATOMICS__");
279        d.flag("__STDC_NO_THREADS__");
280        d.flag("__STDC_NO_COMPLEX__");
281        d.flag("__STDC_NO_VLA__");
282    }
283    // What `__has_embed` answers with. They are defined in every dialect and not only in C23,
284    // because the operator is answerable in every dialect and a header that writes
285    // `#if __has_embed(...) == __STDC_EMBED_FOUND__` under `-std=gnu17` would otherwise be
286    // comparing against zero and taking the not found branch on a resource that is there.
287    d.set("__STDC_EMBED_NOT_FOUND__", "0");
288    d.set("__STDC_EMBED_FOUND__", "1");
289    d.set("__STDC_EMBED_EMPTY__", "2");
290}
291
292/// The memory orders and the lock free answers.
293///
294/// These are here whether or not `_Atomic` is, and `__STDC_NO_ATOMICS__` does not turn them
295/// off, because they are the numbering the `__atomic` builtins take rather than a promise
296/// about the language. musl's `stdatomic.h` writes `memory_order_relaxed = __ATOMIC_RELAXED`
297/// with no test around it at all, so a compiler without them prints an enumerator whose value
298/// is an identifier.
299///
300/// Two means always lock free, and every integer type gets a two on all three targets, which
301/// are all sixty four bit machines. `long long` is the one that would change on a thirty two
302/// bit target, where a double word load is an instruction the machine may or may not have.
303fn atomics(d: &mut Defs, target: &TargetInfo) {
304    d.set("__ATOMIC_RELAXED", "0");
305    d.set("__ATOMIC_CONSUME", "1");
306    d.set("__ATOMIC_ACQUIRE", "2");
307    d.set("__ATOMIC_RELEASE", "3");
308    d.set("__ATOMIC_ACQ_REL", "4");
309    d.set("__ATOMIC_SEQ_CST", "5");
310    // The gate is the machine word rather than `long`, because Windows has a thirty two bit
311    // `long` on a sixty four bit machine and its `long long` is still one instruction.
312    let llong = if target.pointer_width == 64 { "2" } else { "1" };
313    for name in [
314        "BOOL", "CHAR", "CHAR8_T", "CHAR16_T", "CHAR32_T", "WCHAR_T", "SHORT", "INT", "LONG",
315        "POINTER",
316    ] {
317        d.set(&format!("__GCC_ATOMIC_{name}_LOCK_FREE"), "2");
318    }
319    // The one that is not always two: a target whose word is thirty two bits wide can only
320    // promise `long long` is lock free if it has a double word instruction, and the honest
321    // answer there is sometimes rather than always.
322    d.set("__GCC_ATOMIC_LLONG_LOCK_FREE", llong);
323    d.set("__GCC_ATOMIC_TEST_AND_SET_TRUEVAL", "1");
324}
325
326/// What the optimizer level says.
327fn optimization(d: &mut Defs, opts: &Predef) {
328    d.flag_if(opts.opt_level.runs_optimizer(), "__OPTIMIZE__");
329    d.flag_if(opts.opt_level.is_size(), "__OPTIMIZE_SIZE__");
330    // glibc's headers test this before deciding whether to define a function as an inline
331    // wrapper, so getting it wrong changes what a program links against.
332    d.flag_if(!opts.opt_level.runs_optimizer(), "__NO_INLINE__");
333}
334
335/// The architecture, the operating system and the object format.
336fn platform(d: &mut Defs, target: &TargetInfo, opts: &Predef) {
337    let triple = target.triple;
338    match triple.arch {
339        Arch::X86_64 => {
340            d.flag("__x86_64__");
341            d.flag("__x86_64");
342            d.flag("__amd64__");
343            d.flag("__amd64");
344            d.flag("__SSE__");
345            d.flag("__SSE2__");
346            d.flag("__MMX__");
347            d.flag("__SSE_MATH__");
348            d.flag("__SSE2_MATH__");
349            d.flag("__k8");
350            d.flag("__k8__");
351        }
352        Arch::Aarch64 => {
353            d.flag("__aarch64__");
354            d.flag("__AARCH64EL__");
355            d.set("__ARM_ARCH", "8");
356            d.set("__ARM_ARCH_PROFILE", "'A'");
357            d.set("__ARM_64BIT_STATE", "1");
358            d.set("__ARM_ALIGN_MAX_PWR", "28");
359            d.set("__ARM_FP", "0xe");
360            d.set("__ARM_NEON", "1");
361            d.set("__ARM_FEATURE_UNALIGNED", "1");
362            d.set("__ARM_PCS_AAPCS64", "1");
363        }
364        Arch::Riscv64 => {
365            d.flag("__riscv");
366            d.set("__riscv_xlen", "64");
367            d.set("__riscv_flen", "64");
368            d.flag("__riscv_float_abi_double");
369            d.flag("__riscv_muldiv");
370            d.flag("__riscv_atomic");
371            d.flag("__riscv_compressed");
372            d.set("__riscv_cmodel_medlow", "1");
373        }
374    }
375    match triple.os {
376        Os::Linux => {
377            d.flag("__linux__");
378            d.flag("__linux");
379            d.flag("__unix__");
380            d.flag("__unix");
381            d.flag("__gnu_linux__");
382            d.flag("__ELF__");
383            // The unarmoured spellings are not reserved identifiers, so a strict mode may not
384            // define them. Autoconf still tests for `linux`, which is why they exist at all.
385            if opts.gnu_extensions {
386                d.flag("linux");
387                d.flag("unix");
388            }
389        }
390        Os::Darwin => {
391            d.flag("__APPLE__");
392            d.flag("__MACH__");
393            d.flag("__unix__");
394            d.flag("__unix");
395            d.set("__APPLE_CC__", "6000");
396            d.set("__DYNAMIC__", "1");
397            if triple.arch == Arch::Aarch64 {
398                // Apple's own spelling of the architecture, which its headers use rather than
399                // __aarch64__. sys/cdefs.h tests for it by name and reaches an #error called
400                // "Unsupported architecture" without it, so every system header on this
401                // platform fails on the first include until these two are here.
402                d.flag("__arm64__");
403                d.flag("__arm64");
404            }
405            if opts.gnu_extensions {
406                d.flag("unix");
407            }
408        }
409        Os::Windows => {
410            d.flag("_WIN32");
411            d.flag("__WIN32__");
412            d.flag("_WIN64");
413            d.flag("__WIN64__");
414            d.flag("__MINGW32__");
415        }
416        Os::None => {
417            // Freestanding. `__ELF__` still holds, because the object format is a property of
418            // the target rather than of having an operating system under it.
419            d.flag("__ELF__");
420        }
421    }
422    match triple.env {
423        Env::Musl => d.flag("__musl__"),
424        Env::Gnu | Env::None | Env::Msvc => {}
425    }
426    // LP64 is the model everywhere except Windows, and a great deal of code tests for it
427    // rather than testing pointer and long widths separately.
428    if target.long_width == 64 && target.pointer_width == 64 {
429        d.flag("__LP64__");
430        d.flag("_LP64");
431    }
432    // What the assembler prepends to a C name to get the symbol. Mach-O keeps the leading
433    // underscore that every a.out toolchain had and ELF dropped it. It has to be defined even
434    // where it is empty, because of how it is used: glibc writes `__asm__ (__ASMNAME (name))`
435    // and that stringifies `__USER_LABEL_PREFIX__`, so a compiler that leaves it undefined
436    // does not get an error, it gets the name of the macro as the string and renames the
437    // function.
438    d.set("__USER_LABEL_PREFIX__", if triple.os == Os::Darwin { "_" } else { "" });
439
440    // Position independent code is the default on the ELF targets and on Apple's, which is
441    // what a distribution build expects. The value 2 is GCC's for `-fPIC` rather than `-fpic`.
442    if !matches!(triple.os, Os::Windows) {
443        d.set("__PIC__", "2");
444        d.set("__pic__", "2");
445    }
446}
447
448/// `__CHAR_BIT__`, the `__SIZEOF_*__` family and the alignment macros.
449fn sizes(d: &mut Defs, target: &TargetInfo) {
450    let pointer = target.pointer_width / 8;
451    let long = target.long_width / 8;
452    let long_double = target.long_double_width / 8;
453    d.set("__CHAR_BIT__", "8");
454    d.set("__SIZEOF_SHORT__", "2");
455    d.set("__SIZEOF_INT__", "4");
456    d.set("__SIZEOF_LONG__", &long.to_string());
457    d.set("__SIZEOF_LONG_LONG__", "8");
458    d.set("__SIZEOF_INT128__", "16");
459    d.set("__SIZEOF_FLOAT__", "4");
460    d.set("__SIZEOF_DOUBLE__", "8");
461    d.set("__SIZEOF_LONG_DOUBLE__", &long_double.to_string());
462    d.set("__SIZEOF_POINTER__", &pointer.to_string());
463    d.set("__SIZEOF_SIZE_T__", &pointer.to_string());
464    d.set("__SIZEOF_PTRDIFF_T__", &pointer.to_string());
465    d.set("__SIZEOF_WCHAR_T__", &wchar(target).size.to_string());
466    d.set("__SIZEOF_WINT_T__", "4");
467    d.set("__BIGGEST_ALIGNMENT__", "16");
468    // The `__BYTE_ORDER__` family, which the kernel and every serialisation library read.
469    // The names of the orders are defined whichever one is in force, because code compares
470    // against both.
471    d.set("__ORDER_LITTLE_ENDIAN__", "1234");
472    d.set("__ORDER_BIG_ENDIAN__", "4321");
473    d.set("__ORDER_PDP_ENDIAN__", "3412");
474    let order =
475        if target.little_endian { "__ORDER_LITTLE_ENDIAN__" } else { "__ORDER_BIG_ENDIAN__" };
476    d.set("__BYTE_ORDER__", order);
477    d.set("__FLOAT_WORD_ORDER__", order);
478    d.flag_if(!target.char_is_signed, "__CHAR_UNSIGNED__");
479}
480
481/// How `wchar_t` is spelled on a target, and what it holds.
482struct Wchar {
483    /// The C type it is a name for.
484    spelling: &'static str,
485    /// Its width in bytes.
486    size: u32,
487    /// `__WCHAR_MAX__`.
488    max: &'static str,
489    /// `__WCHAR_MIN__`.
490    min: &'static str,
491}
492
493/// `wchar_t` is the type that divides the targets most and is written down least.
494///
495/// Windows makes it 16 bits so that a wide string is UTF-16. AArch64 Linux makes it unsigned,
496/// following the psABI's rule for plain `char`, while x86-64 Linux makes it signed. Code that
497/// compares a `wchar_t` against a negative value is correct on one and not on the other.
498fn wchar(target: &TargetInfo) -> Wchar {
499    match (target.triple.arch, target.triple.os) {
500        (_, Os::Windows) => {
501            Wchar { spelling: "short unsigned int", size: 2, max: "0xffff", min: "0" }
502        }
503        (Arch::Aarch64, Os::Linux | Os::None) => {
504            Wchar { spelling: "unsigned int", size: 4, max: "0xffffffffU", min: "0U" }
505        }
506        _ => Wchar { spelling: "int", size: 4, max: "0x7fffffff", min: "(-__WCHAR_MAX__ - 1)" },
507    }
508}
509
510/// How `wint_t` is spelled on a target, and what it holds.
511struct Wint {
512    /// The C type it is a name for.
513    spelling: &'static str,
514    /// `__WINT_MAX__`.
515    max: &'static str,
516    /// `__WINT_MIN__`.
517    min: &'static str,
518}
519
520/// `wint_t` does not follow `wchar_t`, and Darwin is where that shows.
521///
522/// Apple makes it a signed `int`, so that `WEOF` is negative the way `EOF` is, while Linux
523/// makes it `unsigned int` and gives `WEOF` the value `0xffffffff`. The SDK's `arm/_types.h`
524/// spells `__darwin_wint_t` as `__WINT_TYPE__` and nothing else, so getting this wrong changes
525/// the signedness of every wide character function's argument on that platform.
526fn wint(target: &TargetInfo) -> Wint {
527    match target.triple.os {
528        Os::Windows => Wint { spelling: "short unsigned int", max: "0xffff", min: "0" },
529        Os::Darwin => Wint { spelling: "int", max: "2147483647", min: "(-__WINT_MAX__ - 1)" },
530        _ => Wint { spelling: "unsigned int", max: "4294967295U", min: "0U" },
531    }
532}
533
534/// The integer type names, their limits, and the exact width family.
535fn integers(d: &mut Defs, target: &TargetInfo) {
536    // The one fact everything below turns on: which type is 64 bits wide. On LP64 it is
537    // `long`, and on Windows LLP64 it is `long long`, and every `size_t`, `intmax_t` and
538    // `int64_t` spelling follows from that.
539    let lp64 = target.long_width == 64;
540    let wide = if lp64 { "long int" } else { "long long int" };
541    let wide_unsigned = if lp64 { "long unsigned int" } else { "long long unsigned int" };
542    let wide_suffix = if lp64 { "L" } else { "LL" };
543    let wide_max = format!("9223372036854775807{wide_suffix}");
544    let wide_umax = format!("18446744073709551615U{wide_suffix}");
545
546    d.set("__SCHAR_MAX__", "127");
547    d.set("__SHRT_MAX__", "32767");
548    d.set("__INT_MAX__", "2147483647");
549    d.set("__LONG_MAX__", if lp64 { "9223372036854775807L" } else { "2147483647L" });
550    d.set("__LONG_LONG_MAX__", "9223372036854775807LL");
551    d.set("__INTMAX_MAX__", &wide_max);
552    d.set("__UINTMAX_MAX__", &wide_umax);
553    d.set("__SIZE_MAX__", &wide_umax);
554    d.set("__PTRDIFF_MAX__", &wide_max);
555    d.set("__INTPTR_MAX__", &wide_max);
556    d.set("__UINTPTR_MAX__", &wide_umax);
557    d.set("__SIG_ATOMIC_MAX__", "2147483647");
558    d.set("__SIG_ATOMIC_MIN__", "(-__SIG_ATOMIC_MAX__ - 1)");
559
560    let wchar = wchar(target);
561    d.set("__WCHAR_TYPE__", wchar.spelling);
562    d.set("__WCHAR_MAX__", wchar.max);
563    d.set("__WCHAR_MIN__", wchar.min);
564    let wint = wint(target);
565    d.set("__WINT_TYPE__", wint.spelling);
566    d.set("__WINT_MAX__", wint.max);
567    d.set("__WINT_MIN__", wint.min);
568    d.set("__SIZE_TYPE__", wide_unsigned);
569    d.set("__PTRDIFF_TYPE__", wide);
570    d.set("__INTMAX_TYPE__", wide);
571    d.set("__UINTMAX_TYPE__", wide_unsigned);
572    d.set("__INTPTR_TYPE__", wide);
573    d.set("__UINTPTR_TYPE__", wide_unsigned);
574    d.set("__SIG_ATOMIC_TYPE__", "int");
575    d.set("__CHAR16_TYPE__", "short unsigned int");
576    d.set("__CHAR32_TYPE__", "unsigned int");
577    d.set("__INTMAX_C(c)", &format!("c ## {wide_suffix}"));
578    d.set("__UINTMAX_C(c)", &format!("c ## U{wide_suffix}"));
579
580    // The exact width family, which is what a freestanding `stdint.h` is written out of.
581    exact(d, 8, "signed char", "unsigned char", "127", "255", "");
582    exact(d, 16, "short int", "short unsigned int", "32767", "65535", "");
583    // No suffix. An `int` needs none, and the `U` on the unsigned side is added by `exact`
584    // rather than being part of the width.
585    exact(d, 32, "int", "unsigned int", "2147483647", "4294967295U", "");
586    exact(d, 64, wide, wide_unsigned, &wide_max, &wide_umax, wide_suffix);
587
588    // The fast types. GCC makes the 16 and 32 bit ones `long` on x86-64 glibc and `int`
589    // everywhere else, and a header that computes a printf format from the type name notices
590    // the difference.
591    //
592    // musl is the reason this is not simply a question of the architecture. musl defines
593    // `int_fast16_t` and `int_fast32_t` as `int32_t` on every target it supports, GCC built
594    // for a musl target agrees with it, and GCC built for glibc on the same processor does
595    // not. The place it shows is `stdatomic.h`, which GCC ships and writes directly out of
596    // these macros: `typedef _Atomic __INT_FAST16_TYPE__ atomic_int_fast16_t;`. Get this wrong
597    // and every atomic fast type in the program is the wrong width.
598    let fast_is_wide = target.triple.arch == Arch::X86_64 && lp64 && target.triple.env != Env::Musl;
599    let fast_middle = if fast_is_wide { wide } else { "int" };
600    d.set("__INT_FAST8_TYPE__", "signed char");
601    d.set("__UINT_FAST8_TYPE__", "unsigned char");
602    d.set("__INT_FAST8_MAX__", "127");
603    d.set("__UINT_FAST8_MAX__", "255");
604    for width in [16, 32] {
605        let unsigned = if fast_middle == "int" { "unsigned int" } else { wide_unsigned };
606        let max = if fast_middle == "int" { "2147483647" } else { wide_max.as_str() };
607        let umax = if fast_middle == "int" { "4294967295U" } else { wide_umax.as_str() };
608        d.set(&format!("__INT_FAST{width}_TYPE__"), fast_middle);
609        d.set(&format!("__UINT_FAST{width}_TYPE__"), unsigned);
610        d.set(&format!("__INT_FAST{width}_MAX__"), max);
611        d.set(&format!("__UINT_FAST{width}_MAX__"), umax);
612    }
613    d.set("__INT_FAST64_TYPE__", wide);
614    d.set("__UINT_FAST64_TYPE__", wide_unsigned);
615    d.set("__INT_FAST64_MAX__", &wide_max);
616    d.set("__UINT_FAST64_MAX__", &wide_umax);
617}
618
619/// One width of the exact and least families, which are the same types.
620fn exact(
621    d: &mut Defs,
622    width: u32,
623    signed: &str,
624    unsigned: &str,
625    max: &str,
626    umax: &str,
627    // The suffix the width needs and nothing more, so `""`, `"L"` or `"LL"`. The `U` that
628    // makes a constant unsigned is added below and is not part of this, because a caller that
629    // wrote it here would produce `UU` on the unsigned macro and a stray `U` on the signed one.
630    width_suffix: &str,
631) {
632    d.set(&format!("__INT{width}_TYPE__"), signed);
633    d.set(&format!("__UINT{width}_TYPE__"), unsigned);
634    d.set(&format!("__INT{width}_MAX__"), max);
635    d.set(&format!("__UINT{width}_MAX__"), umax);
636    d.set(&format!("__INT_LEAST{width}_TYPE__"), signed);
637    d.set(&format!("__UINT_LEAST{width}_TYPE__"), unsigned);
638    d.set(&format!("__INT_LEAST{width}_MAX__"), max);
639    d.set(&format!("__UINT_LEAST{width}_MAX__"), umax);
640    // The constant makers. `__INT8_C(1)` is `1` and not `1 ## `, because a paste with nothing
641    // on the right is not a token the expander should have to think about.
642    if width_suffix.is_empty() {
643        d.set(&format!("__INT{width}_C(c)"), "c");
644        d.set(&format!("__UINT{width}_C(c)"), "c ## U");
645    } else {
646        d.set(&format!("__INT{width}_C(c)"), &format!("c ## {width_suffix}"));
647        d.set(&format!("__UINT{width}_C(c)"), &format!("c ## U{width_suffix}"));
648    }
649}
650
651/// The `float.h` characteristics.
652fn floats(d: &mut Defs, target: &TargetInfo) {
653    d.set("__FLT_RADIX__", "2");
654    d.set("__FLT_EVAL_METHOD__", "0");
655    d.set("__FLT_MANT_DIG__", "24");
656    d.set("__FLT_DIG__", "6");
657    d.set("__FLT_MIN_EXP__", "(-125)");
658    d.set("__FLT_MIN_10_EXP__", "(-37)");
659    d.set("__FLT_MAX_EXP__", "128");
660    d.set("__FLT_MAX_10_EXP__", "38");
661    d.set("__FLT_DECIMAL_DIG__", "9");
662    d.set("__FLT_MAX__", "3.40282346638528859811704183484516925e+38F");
663    d.set("__FLT_MIN__", "1.17549435082228750796873653722224568e-38F");
664    d.set("__FLT_EPSILON__", "1.19209289550781250000000000000000000e-7F");
665    d.set("__FLT_DENORM_MIN__", "1.40129846432481707092372958328991613e-45F");
666    d.set("__FLT_HAS_DENORM__", "1");
667    d.set("__FLT_HAS_INFINITY__", "1");
668    d.set("__FLT_HAS_QUIET_NAN__", "1");
669
670    d.set("__DBL_MANT_DIG__", "53");
671    d.set("__DBL_DIG__", "15");
672    d.set("__DBL_MIN_EXP__", "(-1021)");
673    d.set("__DBL_MIN_10_EXP__", "(-307)");
674    d.set("__DBL_MAX_EXP__", "1024");
675    d.set("__DBL_MAX_10_EXP__", "308");
676    d.set("__DBL_DECIMAL_DIG__", "17");
677    d.set("__DBL_MAX__", "((double)1.79769313486231570814527423731704357e+308L)");
678    d.set("__DBL_MIN__", "((double)2.22507385850720138309023271733240406e-308L)");
679    d.set("__DBL_EPSILON__", "((double)2.22044604925031308084726333618164062e-16L)");
680    d.set("__DBL_DENORM_MIN__", "((double)4.94065645841246544176568792868221372e-324L)");
681    d.set("__DBL_HAS_DENORM__", "1");
682    d.set("__DBL_HAS_INFINITY__", "1");
683    d.set("__DBL_HAS_QUIET_NAN__", "1");
684
685    long_double(d, target);
686    d.set("__DECIMAL_DIG__", "__LDBL_DECIMAL_DIG__");
687}
688
689/// `long double` is three different types across the targets, and the macros have to say so.
690///
691/// On SysV x86-64 it is 80 bits of x87 stored in 16 bytes. On AArch64 and RISC-V Linux it is
692/// true quad precision. On Apple platforms and Windows it is `double` under another name.
693/// `spec/12-abi-and-runtime.md` section 12.3 has the ABI consequences.
694fn long_double(d: &mut Defs, target: &TargetInfo) {
695    if target.long_double_width == 64 {
696        d.set("__LDBL_MANT_DIG__", "53");
697        d.set("__LDBL_DIG__", "15");
698        d.set("__LDBL_MIN_EXP__", "(-1021)");
699        d.set("__LDBL_MIN_10_EXP__", "(-307)");
700        d.set("__LDBL_MAX_EXP__", "1024");
701        d.set("__LDBL_MAX_10_EXP__", "308");
702        d.set("__LDBL_DECIMAL_DIG__", "17");
703        d.set("__LDBL_MAX__", "1.79769313486231570814527423731704357e+308L");
704        d.set("__LDBL_MIN__", "2.22507385850720138309023271733240406e-308L");
705        d.set("__LDBL_EPSILON__", "2.22044604925031308084726333618164062e-16L");
706        d.set("__LDBL_DENORM_MIN__", "4.94065645841246544176568792868221372e-324L");
707    } else if target.triple.arch == Arch::X86_64 {
708        d.set("__LDBL_MANT_DIG__", "64");
709        d.set("__LDBL_DIG__", "18");
710        d.set("__LDBL_MIN_EXP__", "(-16381)");
711        d.set("__LDBL_MIN_10_EXP__", "(-4931)");
712        d.set("__LDBL_MAX_EXP__", "16384");
713        d.set("__LDBL_MAX_10_EXP__", "4932");
714        d.set("__LDBL_DECIMAL_DIG__", "21");
715        d.set("__LDBL_MAX__", "1.18973149535723176502126385303097021e+4932L");
716        d.set("__LDBL_MIN__", "3.36210314311209350626267781732175260e-4932L");
717        d.set("__LDBL_EPSILON__", "1.08420217248550443400745280086994171e-19L");
718        d.set("__LDBL_DENORM_MIN__", "3.64519953188247460252840593361941982e-4951L");
719    } else {
720        d.set("__LDBL_MANT_DIG__", "113");
721        d.set("__LDBL_DIG__", "33");
722        d.set("__LDBL_MIN_EXP__", "(-16381)");
723        d.set("__LDBL_MIN_10_EXP__", "(-4931)");
724        d.set("__LDBL_MAX_EXP__", "16384");
725        d.set("__LDBL_MAX_10_EXP__", "4932");
726        d.set("__LDBL_DECIMAL_DIG__", "36");
727        d.set("__LDBL_MAX__", "1.18973149535723176508575932662800702e+4932L");
728        d.set("__LDBL_MIN__", "3.36210314311209350626267781732175260e-4932L");
729        d.set("__LDBL_EPSILON__", "1.92592994438723585305597794258492732e-34L");
730        d.set("__LDBL_DENORM_MIN__", "6.47517511943802511092443895822764655e-4966L");
731    }
732    d.set("__LDBL_HAS_DENORM__", "1");
733    d.set("__LDBL_HAS_INFINITY__", "1");
734    d.set("__LDBL_HAS_QUIET_NAN__", "1");
735}
736
737#[cfg(test)]
738mod tests {
739    use rucc_target::Triple;
740
741    use super::*;
742
743    fn set_for(triple: &str) -> String {
744        let triple: Triple = triple.parse().expect("a triple the compiler supports");
745        built_in(&TargetInfo::new(triple), &Predef::new())
746    }
747
748    fn has(text: &str, line: &str) -> bool {
749        text.lines().any(|l| l == line)
750    }
751
752    #[test]
753    fn the_set_is_driven_by_the_target_rather_than_by_the_host() {
754        let x86 = set_for("x86_64-unknown-linux-gnu");
755        let arm = set_for("aarch64-unknown-linux-gnu");
756        assert!(has(&x86, "#define __x86_64__ 1"));
757        assert!(!has(&x86, "#define __aarch64__ 1"));
758        assert!(has(&arm, "#define __aarch64__ 1"));
759        assert!(!has(&arm, "#define __x86_64__ 1"));
760        assert!(has(&x86, "#define __linux__ 1") && has(&arm, "#define __linux__ 1"));
761    }
762
763    #[test]
764    fn windows_is_the_target_that_makes_long_thirty_two_bits() {
765        let windows = set_for("x86_64-pc-windows-msvc");
766        let linux = set_for("x86_64-unknown-linux-gnu");
767        assert!(has(&windows, "#define __SIZEOF_LONG__ 4"));
768        assert!(has(&windows, "#define __SIZE_TYPE__ long long unsigned int"));
769        assert!(has(&windows, "#define __INT64_TYPE__ long long int"));
770        assert!(!has(&windows, "#define __LP64__ 1"));
771        assert!(has(&linux, "#define __SIZEOF_LONG__ 8"));
772        assert!(has(&linux, "#define __SIZE_TYPE__ long unsigned int"));
773        assert!(has(&linux, "#define __INT64_TYPE__ long int"));
774        assert!(has(&linux, "#define __LP64__ 1"));
775    }
776
777    #[test]
778    fn wchar_t_is_the_type_that_divides_the_targets() {
779        // Signed on x86-64 Linux, unsigned on AArch64 Linux, and sixteen bits on Windows.
780        assert!(has(&set_for("x86_64-unknown-linux-gnu"), "#define __WCHAR_TYPE__ int"));
781        assert!(has(&set_for("aarch64-unknown-linux-gnu"), "#define __WCHAR_TYPE__ unsigned int"));
782        let windows = set_for("x86_64-pc-windows-msvc");
783        assert!(has(&windows, "#define __WCHAR_TYPE__ short unsigned int"));
784        assert!(has(&windows, "#define __SIZEOF_WCHAR_T__ 2"));
785    }
786
787    #[test]
788    fn apple_spells_the_architecture_its_own_way_and_its_headers_only_know_that_spelling() {
789        // sys/cdefs.h reaches #error "Unsupported architecture" without these, which is the
790        // first line of the first header of every program on the platform.
791        let darwin = set_for("aarch64-apple-darwin");
792        assert!(has(&darwin, "#define __arm64__ 1"));
793        assert!(has(&darwin, "#define __arm64 1"));
794        assert!(has(&darwin, "#define __aarch64__ 1"), "the portable spelling stays too");
795        let linux = set_for("aarch64-unknown-linux-gnu");
796        assert!(!has(&linux, "#define __arm64__ 1"), "Apple's spelling is Apple's alone");
797        assert!(!has(&set_for("x86_64-apple-darwin"), "#define __arm64__ 1"));
798    }
799
800    #[test]
801    fn wint_t_does_not_follow_wchar_t() {
802        // Apple makes it signed so that WEOF is negative the way EOF is. Linux does not.
803        let darwin = set_for("aarch64-apple-darwin");
804        assert!(has(&darwin, "#define __WINT_TYPE__ int"));
805        assert!(has(&darwin, "#define __WINT_MAX__ 2147483647"));
806        assert!(has(&darwin, "#define __WCHAR_TYPE__ int"));
807        let linux = set_for("aarch64-unknown-linux-gnu");
808        assert!(has(&linux, "#define __WINT_TYPE__ unsigned int"));
809        assert!(has(&linux, "#define __WINT_MAX__ 4294967295U"));
810        assert!(has(&linux, "#define __WCHAR_TYPE__ unsigned int"), "and wchar_t is its own");
811        assert!(has(
812            &set_for("x86_64-pc-windows-msvc"),
813            "#define __WINT_TYPE__ short unsigned int"
814        ));
815    }
816
817    #[test]
818    fn a_constant_maker_gets_the_suffix_its_width_needs_and_no_other() {
819        // Found by diffing `-dM` against the system compiler. The 32 bit row was passing `U`
820        // as its width suffix, which put a `U` on the signed macro and two on the unsigned
821        // one, and `UINT32_C(1)` expanded to `1UU`, which is not a token.
822        let linux = set_for("x86_64-unknown-linux-gnu");
823        assert!(has(&linux, "#define __INT32_C(c) c"));
824        assert!(has(&linux, "#define __UINT32_C(c) c ## U"));
825        assert!(has(&linux, "#define __INT16_C(c) c"));
826        assert!(has(&linux, "#define __UINT16_C(c) c ## U"));
827        // The wide ones do take a suffix, and the `U` goes in front of it.
828        assert!(has(&linux, "#define __INT64_C(c) c ## L"));
829        assert!(has(&linux, "#define __UINT64_C(c) c ## UL"));
830        // Windows has a thirty two bit `long`, so its sixty four bit constants are `long long`.
831        let windows = set_for("x86_64-pc-windows-msvc");
832        assert!(has(&windows, "#define __INT64_C(c) c ## LL"));
833        assert!(has(&windows, "#define __UINT64_C(c) c ## ULL"));
834    }
835
836    #[test]
837    fn the_symbol_prefix_is_defined_everywhere_including_where_it_is_empty() {
838        // Empty is not the same as absent, because glibc stringifies it. Leaving it undefined
839        // turns `__asm__ (__ASMNAME ("__xpg_strerror_r"))` into an asm name of
840        // "__USER_LABEL_PREFIX__" "__xpg_strerror_r", which renames the function instead of
841        // failing, and that is a bug found at link time or later.
842        for triple in
843            ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
844        {
845            assert!(has(&set_for(triple), "#define __USER_LABEL_PREFIX__ "), "{triple}");
846        }
847        // Mach-O keeps the underscore that ELF dropped.
848        assert!(has(&set_for("aarch64-apple-darwin"), "#define __USER_LABEL_PREFIX__ _"));
849    }
850
851    #[test]
852    fn the_memory_orders_are_there_even_without_atomics() {
853        // musl's stdatomic.h writes `memory_order_relaxed = __ATOMIC_RELAXED` with no test
854        // around it, so these are not a promise about `_Atomic`, they are the numbering the
855        // builtins take, and a compiler without them prints an enumerator whose value is an
856        // identifier.
857        let linux = set_for("x86_64-unknown-linux-gnu");
858        assert!(has(&linux, "#define __ATOMIC_RELAXED 0"));
859        assert!(has(&linux, "#define __ATOMIC_SEQ_CST 5"));
860        assert!(has(&linux, "#define __STDC_NO_ATOMICS__ 1"), "and we still have no _Atomic");
861        assert!(has(&linux, "#define __GCC_ATOMIC_INT_LOCK_FREE 2"));
862        assert!(has(&linux, "#define __GCC_ATOMIC_LLONG_LOCK_FREE 2"));
863        assert!(has(&set_for("x86_64-pc-windows-msvc"), "#define __GCC_ATOMIC_LLONG_LOCK_FREE 2"));
864    }
865
866    #[test]
867    fn long_double_is_three_types_and_the_macros_say_which() {
868        assert!(has(&set_for("x86_64-unknown-linux-gnu"), "#define __LDBL_MANT_DIG__ 64"));
869        assert!(has(&set_for("aarch64-unknown-linux-gnu"), "#define __LDBL_MANT_DIG__ 113"));
870        assert!(has(&set_for("aarch64-apple-darwin"), "#define __LDBL_MANT_DIG__ 53"));
871    }
872
873    #[test]
874    fn char_signedness_is_recorded_only_when_it_is_unsigned() {
875        // Which is how GCC does it: the macro exists to mark the unusual case.
876        assert!(has(&set_for("aarch64-unknown-linux-gnu"), "#define __CHAR_UNSIGNED__ 1"));
877        assert!(!has(&set_for("x86_64-unknown-linux-gnu"), "#define __CHAR_UNSIGNED__ 1"));
878    }
879
880    #[test]
881    fn the_dialect_decides_the_standard_macros() {
882        let mut opts = Predef::new();
883        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
884        assert!(has(&built_in(&target, &opts), "#define __STDC_VERSION__ 202311L"));
885        assert!(!has(&built_in(&target, &opts), "#define __STRICT_ANSI__ 1"));
886        assert!(has(&built_in(&target, &opts), "#define linux 1"));
887
888        opts.gnu_extensions = false;
889        assert!(has(&built_in(&target, &opts), "#define __STRICT_ANSI__ 1"));
890        assert!(!has(&built_in(&target, &opts), "#define linux 1"), "not a reserved name");
891
892        opts.std = Std::C89;
893        let c89 = built_in(&target, &opts);
894        assert!(!c89.contains("__STDC_VERSION__"), "C89 does not define it at all");
895        assert!(has(&c89, "#define __STDC__ 1"));
896    }
897
898    #[test]
899    fn the_optimizer_level_is_visible_to_the_preprocessor() {
900        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
901        let mut opts = Predef::new();
902        assert!(has(&built_in(&target, &opts), "#define __NO_INLINE__ 1"));
903        assert!(!built_in(&target, &opts).contains("__OPTIMIZE__"));
904
905        opts.opt_level = OptLevel::O2;
906        assert!(has(&built_in(&target, &opts), "#define __OPTIMIZE__ 1"));
907        assert!(!built_in(&target, &opts).contains("__OPTIMIZE_SIZE__"));
908
909        opts.opt_level = OptLevel::Os;
910        assert!(has(&built_in(&target, &opts), "#define __OPTIMIZE_SIZE__ 1"));
911    }
912
913    #[test]
914    fn a_command_line_define_with_no_value_is_one() {
915        let mut opts = Predef::new();
916        opts.defines = vec!["FOO".to_owned(), "BAR=2".to_owned(), "F(x)=x + 1".to_owned()];
917        opts.undefines = vec!["__linux__".to_owned()];
918        let text = command_line(&opts);
919        assert!(has(&text, "#define FOO 1"));
920        assert!(has(&text, "#define BAR 2"));
921        assert!(has(&text, "#define F(x) x + 1"));
922        // The undefine comes last, because `-U` beats `-D` whichever side of it it was on.
923        assert!(text.trim_end().ends_with("#undef __linux__"));
924    }
925
926    #[test]
927    fn no_command_line_macros_is_no_file_at_all() {
928        assert!(command_line(&Predef::new()).is_empty());
929    }
930
931    #[test]
932    fn a_date_is_spelled_the_way_the_standard_fixes() {
933        // The epoch itself, and a day that needs the space padding the format asks for.
934        let epoch = Timestamp::from_unix(0);
935        assert_eq!(epoch.date, "Jan  1 1970");
936        assert_eq!(epoch.time, "00:00:00");
937        let leap = Timestamp::from_unix(1_709_164_800);
938        assert_eq!(leap.date, "Feb 29 2024", "2024 is a leap year");
939        let late = Timestamp::from_unix(1_735_689_599);
940        assert_eq!(late.date, "Dec 31 2024");
941        assert_eq!(late.time, "23:59:59");
942    }
943
944    #[test]
945    fn a_date_before_the_epoch_still_comes_out_right() {
946        // Not because anyone compiles in 1969, but because the arithmetic that gets this
947        // wrong is the same arithmetic that gets a time zone offset wrong.
948        assert_eq!(Timestamp::from_unix(-1).date, "Dec 31 1969");
949        assert_eq!(Timestamp::from_unix(-1).time, "23:59:59");
950    }
951
952    #[test]
953    fn the_gnuc_version_is_a_knob_rather_than_a_constant() {
954        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse().unwrap());
955        let mut opts = Predef::new();
956        assert!(has(&built_in(&target, &opts), "#define __GNUC__ 4"));
957        opts.gnuc = GnucVersion { major: 15, minor: 1, patch: 0 };
958        assert!(has(&built_in(&target, &opts), "#define __GNUC__ 15"));
959        assert!(has(&built_in(&target, &opts), "#define __GNUC_MINOR__ 1"));
960    }
961
962    #[test]
963    fn musl_and_glibc_disagree_about_the_fast_types_on_the_same_processor() {
964        // The same x86-64 machine, two libcs, two answers. GCC built for glibc says `long int`
965        // and GCC built for musl says `int`, because musl defines `int_fast16_t` as `int32_t`
966        // everywhere. It shows in `stdatomic.h`, which GCC writes out of these macros, so
967        // getting it wrong makes every atomic fast type the wrong width.
968        let gnu = set_for("x86_64-unknown-linux-gnu");
969        let musl = set_for("x86_64-unknown-linux-musl");
970        assert!(has(&gnu, "#define __INT_FAST16_TYPE__ long int"));
971        assert!(has(&gnu, "#define __INT_FAST32_TYPE__ long int"));
972        assert!(has(&gnu, "#define __UINT_FAST16_TYPE__ long unsigned int"));
973        assert!(has(&musl, "#define __INT_FAST16_TYPE__ int"));
974        assert!(has(&musl, "#define __INT_FAST32_TYPE__ int"));
975        assert!(has(&musl, "#define __UINT_FAST16_TYPE__ unsigned int"));
976        // The limits have to move with the types or a header that checks them stops agreeing
977        // with the header that uses them.
978        assert!(has(&gnu, "#define __INT_FAST16_MAX__ 9223372036854775807L"));
979        assert!(has(&musl, "#define __INT_FAST16_MAX__ 2147483647"));
980        assert!(has(&musl, "#define __UINT_FAST16_MAX__ 4294967295U"));
981    }
982
983    #[test]
984    fn the_libc_only_moves_the_two_fast_types_it_is_allowed_to_move() {
985        // 8 and 64 are the same on both, and so is everything outside the fast family. A libc
986        // is not a processor and this is the whole of what it is permitted to change here.
987        let gnu = set_for("x86_64-unknown-linux-gnu");
988        let musl = set_for("x86_64-unknown-linux-musl");
989        for line in [
990            "#define __INT_FAST8_TYPE__ signed char",
991            "#define __INT_FAST64_TYPE__ long int",
992            "#define __INT64_TYPE__ long int",
993            "#define __SIZE_TYPE__ long unsigned int",
994            "#define __SIZEOF_LONG__ 8",
995            "#define __LP64__ 1",
996        ] {
997            assert!(has(&gnu, line), "glibc lost {line}");
998            assert!(has(&musl, line), "musl lost {line}");
999        }
1000    }
1001
1002    #[test]
1003    fn a_non_x86_target_has_int_sized_fast_types_whatever_the_libc() {
1004        // The `long` answer was always specific to x86-64. aarch64 glibc says `int` too, so
1005        // adding the libc axis must not have turned into a second way to say x86-64.
1006        let arm_gnu = set_for("aarch64-unknown-linux-gnu");
1007        let arm_musl = set_for("aarch64-unknown-linux-musl");
1008        assert!(has(&arm_gnu, "#define __INT_FAST16_TYPE__ int"));
1009        assert!(has(&arm_musl, "#define __INT_FAST16_TYPE__ int"));
1010    }
1011}