kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit da7fa2922ee93c3891d072432008bfe713ff84cd
parent d51c0ff8fbdc514827366d5bed17e1352c39cbb5
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu,  7 May 2026 09:05:51 -0700

cfree assumptions

Diffstat:
Mbuiltins.md | 177+++++++++++++++++++++++++++++++------------------------------------------------
Minclude/float.h | 103++++++++++++++++++++++++++++++++++++-------------------------------------------
Minclude/limits.h | 74++++++++++++++++++++++++++++++++++++++++----------------------------------
Minclude/stdint.h | 103+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mtest/smoke.c | 12++++++++++++
5 files changed, 227 insertions(+), 242 deletions(-)

diff --git a/builtins.md b/builtins.md @@ -1,135 +1,96 @@ # Compiler builtins used by cfree -Every value in cfree's freestanding headers is delegated to the compiler. -This file lists what we rely on, grouped by header. All names below are -provided by both GCC (≥ 4.x) and Clang. They are predefined macros unless -marked **(builtin)**, in which case they are intrinsic identifiers usable -in expressions. +cfree's freestanding headers hardcode every value that's invariant under +its target assumptions, and delegate to compiler builtins for everything +that genuinely varies across targets. This file is the contract: if a +target violates an "assumption" below, the headers (and `test/smoke.c`) +will be wrong. + +## Target assumptions (hardcoded) + +- `CHAR_BIT == 8` +- `short == 16` bits, `int == 32` bits, `long long == 64` bits +- Two's complement integer representation +- `float` is IEEE 754 binary32 +- `double` is IEEE 754 binary64 + +## What genuinely varies (delegated) + +| Quantity | Why it varies | +| ------------------------- | -------------------------------------------------- | +| `char` signedness | ARM defaults unsigned, x86 signed; flippable with `-funsigned-char`. Not changeable from a header. | +| `long` width | LP64 (Unix 64-bit) makes it 64; LLP64 (Win64) and ILP32 keep it 32 | +| `long double` format | x86 80-bit, AArch64 binary128 *or* binary64, PowerPC double-double, MSVC binary64 | +| `FLT_ROUNDS` | Runtime rounding mode (function call required) | +| `FLT_EVAL_METHOD` | x87 vs SSE vs embedded toolchains differ | +| `intptr_t` width | 32 vs 64 bits | +| `size_t`, `ptrdiff_t` | Track pointer width | +| `wchar_t` | 16-bit on Windows, 32-bit on Unix; signedness varies | +| `intmax_t` literal type | `long` on LP64, `long long` on LLP64 | +| `int_fast{N}_t` widths | Each target picks its own "fast" width | +| `va_list` and varargs ABI | Call convention is target-defined | +| `max_align_t` | Track widest scalar alignment | --- -## `<float.h>` - -Mostly a 1-to-1 passthrough of `__*__` macros to their C11 spellings. - -| C11 macro | Compiler source | -| ------------------ | ------------------------------------- | -| `FLT_ROUNDS` | `__builtin_flt_rounds()` **(builtin)** — runtime rounding mode | -| `FLT_EVAL_METHOD` | `__FLT_EVAL_METHOD__` | -| `FLT_RADIX` | `__FLT_RADIX__` | -| `*_HAS_SUBNORM` | `__{FLT,DBL,LDBL}_HAS_DENORM__` | -| `*_MANT_DIG` | `__{FLT,DBL,LDBL}_MANT_DIG__` | -| `*_DECIMAL_DIG` | `__{FLT,DBL,LDBL}_DECIMAL_DIG__` | -| `DECIMAL_DIG` | `__DECIMAL_DIG__` | -| `*_DIG` | `__{FLT,DBL,LDBL}_DIG__` | -| `*_MIN_EXP` | `__{FLT,DBL,LDBL}_MIN_EXP__` | -| `*_MIN_10_EXP` | `__{FLT,DBL,LDBL}_MIN_10_EXP__` | -| `*_MAX_EXP` | `__{FLT,DBL,LDBL}_MAX_EXP__` | -| `*_MAX_10_EXP` | `__{FLT,DBL,LDBL}_MAX_10_EXP__` | -| `*_MAX` | `__{FLT,DBL,LDBL}_MAX__` | -| `*_EPSILON` | `__{FLT,DBL,LDBL}_EPSILON__` | -| `*_MIN` | `__{FLT,DBL,LDBL}_MIN__` | -| `*_TRUE_MIN` | `__{FLT,DBL,LDBL}_DENORM_MIN__` | - -Note the rename: C11 uses `SUBNORM`/`TRUE_MIN`; GCC/Clang use `DENORM`/`DENORM_MIN`. - ---- - -## `<limits.h>` - -| C11 macro | Compiler source | -| ------------ | --------------------------------------------------------- | -| `CHAR_BIT` | `__CHAR_BIT__` | -| `SCHAR_MAX` | `__SCHAR_MAX__` | -| `CHAR_*` | derived from `__CHAR_UNSIGNED__` (defined ⇒ char is unsigned) | -| `SHRT_MAX` | `__SHRT_MAX__` | -| `INT_MAX` | `__INT_MAX__` | -| `LONG_MAX` | `__LONG_MAX__` | -| `LLONG_MAX` | `__LONG_LONG_MAX__` | - -`*_MIN` / unsigned `*_MAX` are derived arithmetically from the signed `*_MAX` -(C11 guarantees two's-complement-or-equivalent representations make this -exact). `MB_LEN_MAX` is *not* compiler-provided — we hardcode `1`. - ---- - -## `<stddef.h>` - -| C11 name | Compiler source | -| ------------ | ------------------------------------- | -| `ptrdiff_t` | `__PTRDIFF_TYPE__` | -| `size_t` | `__SIZE_TYPE__` | -| `wchar_t` | `__WCHAR_TYPE__` (C only; keyword in C++) | -| `offsetof` | `__builtin_offsetof(t, m)` **(builtin)** | -| `NULL` | `((void*)0)` — not compiler-provided | -| `max_align_t`| no builtin — a struct of `long long` + `long double` is the conventional definition | - ---- - -## `<stdint.h>` - -For each width N ∈ {8, 16, 32, 64} the compiler provides a *type*, a *max*, -and a *constant-suffix* macro. We pass them through unchanged. - -### Types -- `__INT{N}_TYPE__`, `__UINT{N}_TYPE__` +## Builtins + +Grouped by header, every `__builtin_*` or `__*__` we still depend on. + +### `<float.h>` +- `__builtin_flt_rounds()` — runtime rounding mode → `FLT_ROUNDS` +- `__FLT_EVAL_METHOD__` +- `__DECIMAL_DIG__` +- `__LDBL_HAS_DENORM__` → `LDBL_HAS_SUBNORM` +- `__LDBL_MANT_DIG__`, `__LDBL_DECIMAL_DIG__`, `__LDBL_DIG__` +- `__LDBL_MIN_EXP__`, `__LDBL_MIN_10_EXP__` +- `__LDBL_MAX_EXP__`, `__LDBL_MAX_10_EXP__` +- `__LDBL_MAX__`, `__LDBL_MIN__`, `__LDBL_EPSILON__` +- `__LDBL_DENORM_MIN__` → `LDBL_TRUE_MIN` + +### `<limits.h>` +- `__LONG_MAX__` → `LONG_MAX` (and derived `LONG_MIN`, `ULONG_MAX`) +- `__CHAR_UNSIGNED__` — defined ⇔ plain `char` is unsigned + +### `<stddef.h>` +- `__PTRDIFF_TYPE__` → `ptrdiff_t` +- `__SIZE_TYPE__` → `size_t` +- `__WCHAR_TYPE__` → `wchar_t` (C only; in C++ it's a keyword) +- `__builtin_offsetof(t, m)` → `offsetof` + +### `<stdint.h>` +Types (aliases vary by data model even when limits don't): +- `__INT{8,16,32,64}_TYPE__`, `__UINT{N}_TYPE__` - `__INT_LEAST{N}_TYPE__`, `__UINT_LEAST{N}_TYPE__` - `__INT_FAST{N}_TYPE__`, `__UINT_FAST{N}_TYPE__` - `__INTPTR_TYPE__`, `__UINTPTR_TYPE__` - `__INTMAX_TYPE__`, `__UINTMAX_TYPE__` -### Limits -- `__INT{N}_MAX__`, `__UINT{N}_MAX__` -- `__INT_LEAST{N}_MAX__`, `__UINT_LEAST{N}_MAX__` +Limits that are not pinned by the target assumptions: - `__INT_FAST{N}_MAX__`, `__UINT_FAST{N}_MAX__` - `__INTPTR_MAX__`, `__UINTPTR_MAX__` - `__INTMAX_MAX__`, `__UINTMAX_MAX__` -- `__PTRDIFF_MAX__`, `__SIZE_MAX__` +- `__PTRDIFF_MAX__` +- `__SIZE_MAX__` - `__WCHAR_MAX__`, `__WCHAR_MIN__` - `__WINT_MAX__`, `__WINT_MIN__` - `__SIG_ATOMIC_MAX__`, `__SIG_ATOMIC_MIN__` -`*_MIN` for signed types is derived as `(-MAX - 1)`. - -### Constant macros (function-like) -- `__INT{N}_C(c)`, `__UINT{N}_C(c)` — append the literal suffix appropriate - for the corresponding `int_least{N}_t` (e.g. `__INT64_C(1)` → `1L` or `1LL` - depending on target). -- `__INTMAX_C(c)`, `__UINTMAX_C(c)` — same for `intmax_t`. - ---- - -## `<stdarg.h>` +64-bit and intmax constant macros (literal suffix tracks the alias): +- `__INT64_C(c)`, `__UINT64_C(c)` +- `__INTMAX_C(c)`, `__UINTMAX_C(c)` -Entirely compiler-supplied — varargs ABI is too target-specific to express -in portable C. - -| C11 name | Builtin | -| ---------- | ----------------------------- | -| `va_list` | `__builtin_va_list` (type) | -| `va_start` | `__builtin_va_start(ap, last)`| -| `va_arg` | `__builtin_va_arg(ap, type)` | -| `va_end` | `__builtin_va_end(ap)` | -| `va_copy` | `__builtin_va_copy(dst, src)` | - ---- - -## `<stdalign.h>`, `<stdbool.h>`, `<stdnoreturn.h>`, `<iso646.h>` - -No builtins. These headers are pure preprocessor aliases for C11 keywords -(`_Alignas`, `_Alignof`, `_Bool`, `_Noreturn`) and operator spellings. +### `<stdarg.h>` +Entirely compiler-supplied — varargs ABI is target-defined: +- `__builtin_va_list` (type) +- `__builtin_va_start`, `__builtin_va_arg`, `__builtin_va_end`, `__builtin_va_copy` --- ## Discovery -To enumerate every macro a particular compiler predefines on the current -target: +To enumerate what a compiler predefines for the current target: ```sh cc -dM -E -x c /dev/null | sort ``` - -The full set is target- and version-dependent; the macros above are the -ones cfree relies on, and all are present on every GCC/Clang target we -target. diff --git a/include/float.h b/include/float.h @@ -1,76 +1,67 @@ -/* float.h -- C11 7.7 -- Characteristics of floating types */ +/* float.h -- C11 7.7 -- Characteristics of floating types + * + * cfree assumes the target's `float` is IEEE 754 binary32 and `double` is + * IEEE 754 binary64. Every value below for FLT_* / DBL_* follows directly + * from those formats and is hardcoded. + * + * `long double` is *not* fixed: x86 uses 80-bit extended, AArch64 uses + * binary128 (or binary64), PowerPC uses double-double, MSVC aliases it to + * binary64, etc. Same story for FLT_ROUNDS (runtime) and FLT_EVAL_METHOD + * (x87 vs SSE vs embedded). Those stay compiler-supplied. + */ #ifndef CFREE_FLOAT_H #define CFREE_FLOAT_H -/* Rounding mode, FLT_EVAL_METHOD, and the radix are target-/runtime-dependent - and predefined by the compiler. */ +/* ---- target-varying ---------------------------------------------- */ #define FLT_ROUNDS (__builtin_flt_rounds()) #define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ -#define FLT_RADIX __FLT_RADIX__ -/* Subnormal support (C11 added). 1 = supports subnormals, 0 = does not, - -1 = indeterminable. */ -#define FLT_HAS_SUBNORM __FLT_HAS_DENORM__ -#define DBL_HAS_SUBNORM __DBL_HAS_DENORM__ #define LDBL_HAS_SUBNORM __LDBL_HAS_DENORM__ - -/* Number of base-FLT_RADIX mantissa digits. */ -#define FLT_MANT_DIG __FLT_MANT_DIG__ -#define DBL_MANT_DIG __DBL_MANT_DIG__ #define LDBL_MANT_DIG __LDBL_MANT_DIG__ - -/* Number of decimal digits, n, such that any floating value with p radix-b - digits can be rounded into a decimal with n digits and back without loss. */ -#define FLT_DECIMAL_DIG __FLT_DECIMAL_DIG__ -#define DBL_DECIMAL_DIG __DBL_DECIMAL_DIG__ #define LDBL_DECIMAL_DIG __LDBL_DECIMAL_DIG__ -#define DECIMAL_DIG __DECIMAL_DIG__ - -/* Number of decimal digits q such that any number with q decimals can be - rounded into the type and back unchanged. */ -#define FLT_DIG __FLT_DIG__ -#define DBL_DIG __DBL_DIG__ #define LDBL_DIG __LDBL_DIG__ - -/* Minimum negative integer e such that FLT_RADIX**(e-1) is a normalized - value. */ -#define FLT_MIN_EXP __FLT_MIN_EXP__ -#define DBL_MIN_EXP __DBL_MIN_EXP__ #define LDBL_MIN_EXP __LDBL_MIN_EXP__ - -/* Minimum negative integer such that 10 raised to it is a normalized value. */ -#define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ -#define DBL_MIN_10_EXP __DBL_MIN_10_EXP__ #define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__ - -/* Maximum integer e such that FLT_RADIX**(e-1) is representable. */ -#define FLT_MAX_EXP __FLT_MAX_EXP__ -#define DBL_MAX_EXP __DBL_MAX_EXP__ #define LDBL_MAX_EXP __LDBL_MAX_EXP__ - -/* Maximum integer such that 10 raised to it is representable. */ -#define FLT_MAX_10_EXP __FLT_MAX_10_EXP__ -#define DBL_MAX_10_EXP __DBL_MAX_10_EXP__ #define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__ - -/* Maximum representable finite value. */ -#define FLT_MAX __FLT_MAX__ -#define DBL_MAX __DBL_MAX__ #define LDBL_MAX __LDBL_MAX__ - -/* Difference between 1 and the least value greater than 1. */ -#define FLT_EPSILON __FLT_EPSILON__ -#define DBL_EPSILON __DBL_EPSILON__ #define LDBL_EPSILON __LDBL_EPSILON__ - -/* Minimum normalized positive value. */ -#define FLT_MIN __FLT_MIN__ -#define DBL_MIN __DBL_MIN__ #define LDBL_MIN __LDBL_MIN__ - -/* Minimum positive value (may be subnormal). C11 added. */ -#define FLT_TRUE_MIN __FLT_DENORM_MIN__ -#define DBL_TRUE_MIN __DBL_DENORM_MIN__ #define LDBL_TRUE_MIN __LDBL_DENORM_MIN__ +/* DECIMAL_DIG = max of FLT/DBL/LDBL_DECIMAL_DIG. Since LDBL varies, this + does too -- compiler-provided. */ +#define DECIMAL_DIG __DECIMAL_DIG__ + +/* ---- IEEE 754 invariants ----------------------------------------- */ +#define FLT_RADIX 2 + +/* binary32 (float) */ +#define FLT_HAS_SUBNORM 1 +#define FLT_MANT_DIG 24 +#define FLT_DECIMAL_DIG 9 +#define FLT_DIG 6 +#define FLT_MIN_EXP (-125) +#define FLT_MIN_10_EXP (-37) +#define FLT_MAX_EXP 128 +#define FLT_MAX_10_EXP 38 +#define FLT_MAX 0x1.fffffep+127f +#define FLT_EPSILON 0x1p-23f +#define FLT_MIN 0x1p-126f +#define FLT_TRUE_MIN 0x1p-149f + +/* binary64 (double) */ +#define DBL_HAS_SUBNORM 1 +#define DBL_MANT_DIG 53 +#define DBL_DECIMAL_DIG 17 +#define DBL_DIG 15 +#define DBL_MIN_EXP (-1021) +#define DBL_MIN_10_EXP (-307) +#define DBL_MAX_EXP 1024 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX 0x1.fffffffffffffp+1023 +#define DBL_EPSILON 0x1p-52 +#define DBL_MIN 0x1p-1022 +#define DBL_TRUE_MIN 0x1p-1074 + #endif diff --git a/include/limits.h b/include/limits.h @@ -1,50 +1,56 @@ -/* limits.h -- C11 7.10 -- Sizes of integer types */ +/* limits.h -- C11 7.10 -- Sizes of integer types + * + * cfree assumes a target where: + * - CHAR_BIT == 8 + * - short == 16 bits + * - int == 32 bits + * - long long == 64 bits + * - two's complement representation + * These hold on every modern C11 target we care about. The signedness of + * plain `char` and the width of `long` genuinely differ across targets and + * are still derived from compiler-predefined macros. + */ #ifndef CFREE_LIMITS_H #define CFREE_LIMITS_H -/* Number of bits in a byte. C11 mandates value >= 8; on every supported - GCC/Clang target this is 8. */ -#define CHAR_BIT __CHAR_BIT__ +#define CHAR_BIT 8 +#define MB_LEN_MAX 1 /* freestanding has no locale; smallest legal value */ -/* Maximum length of a multibyte character. C11 requires this to be >= 1. - The value depends on the locale; freestanding code has no locale facility, - so 1 is the safest portable answer. (Compilers do not predefine this.) */ -#define MB_LEN_MAX 1 +/* signed / unsigned char */ +#define SCHAR_MAX 127 +#define SCHAR_MIN (-128) +#define UCHAR_MAX 255 -/* signed char */ -#define SCHAR_MAX __SCHAR_MAX__ -#define SCHAR_MIN (-SCHAR_MAX - 1) - -/* unsigned char */ -#define UCHAR_MAX (SCHAR_MAX * 2 + 1) - -/* plain char -- signedness is implementation-defined */ +/* plain char -- signedness is implementation-defined and set by the + compiler/ABI; a header cannot change it. We report what the compiler + chose. -funsigned-char / -fsigned-char flip __CHAR_UNSIGNED__. */ #ifdef __CHAR_UNSIGNED__ # define CHAR_MIN 0 -# define CHAR_MAX UCHAR_MAX +# define CHAR_MAX 255 #else -# define CHAR_MIN SCHAR_MIN -# define CHAR_MAX SCHAR_MAX +# define CHAR_MIN (-128) +# define CHAR_MAX 127 #endif -/* short */ -#define SHRT_MAX __SHRT_MAX__ -#define SHRT_MIN (-SHRT_MAX - 1) -#define USHRT_MAX (SHRT_MAX * 2 + 1) +/* short / unsigned short */ +#define SHRT_MAX 32767 +#define SHRT_MIN (-32768) +#define USHRT_MAX 65535 -/* int */ -#define INT_MAX __INT_MAX__ -#define INT_MIN (-INT_MAX - 1) -#define UINT_MAX (INT_MAX * 2U + 1U) +/* int / unsigned int */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX 4294967295U -/* long */ -#define LONG_MAX __LONG_MAX__ -#define LONG_MIN (-LONG_MAX - 1L) -#define ULONG_MAX (LONG_MAX * 2UL + 1UL) +/* long / unsigned long -- LP64 (Unix 64-bit) makes long 64-bit, while + LLP64 (Windows 64-bit) and ILP32 keep it 32-bit. Genuinely varying. */ +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN (-LONG_MAX - 1L) +#define ULONG_MAX (LONG_MAX * 2UL + 1UL) -/* long long */ -#define LLONG_MAX __LONG_LONG_MAX__ +/* long long / unsigned long long */ +#define LLONG_MAX 9223372036854775807LL #define LLONG_MIN (-LLONG_MAX - 1LL) -#define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +#define ULLONG_MAX 18446744073709551615ULL #endif diff --git a/include/stdint.h b/include/stdint.h @@ -1,9 +1,19 @@ -/* stdint.h -- C11 7.20 -- Integer types of specified widths */ +/* stdint.h -- C11 7.20 -- Integer types of specified widths + * + * Given cfree's assumptions (CHAR_BIT == 8, two's complement, int == 32-bit, + * long long == 64-bit), every exact-width and minimum-width *limit* is + * fixed and hardcoded below. The underlying *type aliases* still vary + * (e.g. int32_t may be `int` or `long` depending on the data model), + * so types remain compiler-supplied. + * + * `intptr_t`, `intmax_t`, and the FAST family genuinely vary across + * targets and stay delegated. + */ #ifndef CFREE_STDINT_H #define CFREE_STDINT_H /* ------------------------------------------------------------------ */ -/* 7.20.1.1 Exact-width integer types */ +/* 7.20.1.1 Exact-width integer types (aliases vary by data model) */ /* ------------------------------------------------------------------ */ typedef __INT8_TYPE__ int8_t; typedef __INT16_TYPE__ int16_t; @@ -29,7 +39,7 @@ typedef __UINT_LEAST32_TYPE__ uint_least32_t; typedef __UINT_LEAST64_TYPE__ uint_least64_t; /* ------------------------------------------------------------------ */ -/* 7.20.1.3 Fastest minimum-width integer types */ +/* 7.20.1.3 Fastest minimum-width integer types (vary by target) */ /* ------------------------------------------------------------------ */ typedef __INT_FAST8_TYPE__ int_fast8_t; typedef __INT_FAST16_TYPE__ int_fast16_t; @@ -42,7 +52,7 @@ typedef __UINT_FAST32_TYPE__ uint_fast32_t; typedef __UINT_FAST64_TYPE__ uint_fast64_t; /* ------------------------------------------------------------------ */ -/* 7.20.1.4 Integer types capable of holding object pointers */ +/* 7.20.1.4 Pointer-holding integer types (32 or 64 bit) */ /* ------------------------------------------------------------------ */ typedef __INTPTR_TYPE__ intptr_t; typedef __UINTPTR_TYPE__ uintptr_t; @@ -57,35 +67,37 @@ typedef __UINTMAX_TYPE__ uintmax_t; /* 7.20.2 Limits of specified-width integer types */ /* ------------------------------------------------------------------ */ -/* exact-width */ -#define INT8_MAX __INT8_MAX__ -#define INT16_MAX __INT16_MAX__ -#define INT32_MAX __INT32_MAX__ -#define INT64_MAX __INT64_MAX__ -#define INT8_MIN (-INT8_MAX - 1) -#define INT16_MIN (-INT16_MAX - 1) +/* exact-width -- two's complement, 8-bit byte */ +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 +#define INT64_MAX 9223372036854775807LL +#define INT8_MIN (-128) +#define INT16_MIN (-32768) #define INT32_MIN (-INT32_MAX - 1) -#define INT64_MIN (-INT64_MAX - 1) -#define UINT8_MAX __UINT8_MAX__ -#define UINT16_MAX __UINT16_MAX__ -#define UINT32_MAX __UINT32_MAX__ -#define UINT64_MAX __UINT64_MAX__ - -/* minimum-width */ -#define INT_LEAST8_MAX __INT_LEAST8_MAX__ -#define INT_LEAST16_MAX __INT_LEAST16_MAX__ -#define INT_LEAST32_MAX __INT_LEAST32_MAX__ -#define INT_LEAST64_MAX __INT_LEAST64_MAX__ -#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1) -#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1) -#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1) -#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1) -#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ -#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ -#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ -#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ - -/* fastest minimum-width */ +#define INT64_MIN (-INT64_MAX - 1LL) +#define UINT8_MAX 255 +#define UINT16_MAX 65535 +#define UINT32_MAX 4294967295U +#define UINT64_MAX 18446744073709551615ULL + +/* minimum-width -- equal to exact-width on every modern target */ +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +/* fastest minimum-width -- the underlying type is target-dependent + (often `int` for FAST{8,16}_t on 32/64-bit machines), so the limits + stay compiler-supplied. */ #define INT_FAST8_MAX __INT_FAST8_MAX__ #define INT_FAST16_MAX __INT_FAST16_MAX__ #define INT_FAST32_MAX __INT_FAST32_MAX__ @@ -104,13 +116,15 @@ typedef __UINTMAX_TYPE__ uintmax_t; #define INTPTR_MIN (-INTPTR_MAX - 1) #define UINTPTR_MAX __UINTPTR_MAX__ -/* greatest-width */ +/* greatest-width -- intmax_t is `long` on LP64 and `long long` on LLP64, + so the value is fixed at 2^63-1 but the literal type is not. Keep the + compiler's macro to preserve the right type. */ #define INTMAX_MAX __INTMAX_MAX__ #define INTMAX_MIN (-INTMAX_MAX - 1) #define UINTMAX_MAX __UINTMAX_MAX__ /* ------------------------------------------------------------------ */ -/* 7.20.3 Limits of other integer types defined in <stddef.h> etc. */ +/* 7.20.3 Limits of other integer types */ /* ------------------------------------------------------------------ */ #define PTRDIFF_MAX __PTRDIFF_MAX__ #define PTRDIFF_MIN (-PTRDIFF_MAX - 1) @@ -123,24 +137,25 @@ typedef __UINTMAX_TYPE__ uintmax_t; #define WINT_MAX __WINT_MAX__ #define WINT_MIN __WINT_MIN__ -/* sig_atomic_t bounds: defined in <signal.h>, which is not freestanding, - but the macros must still be exposed by <stdint.h>. */ #define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__ #define SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__ /* ------------------------------------------------------------------ */ /* 7.20.4 Macros for integer constants */ /* ------------------------------------------------------------------ */ -#define INT8_C(c) __INT8_C(c) -#define INT16_C(c) __INT16_C(c) -#define INT32_C(c) __INT32_C(c) -#define INT64_C(c) __INT64_C(c) -#define UINT8_C(c) __UINT8_C(c) -#define UINT16_C(c) __UINT16_C(c) -#define UINT32_C(c) __UINT32_C(c) -#define UINT64_C(c) __UINT64_C(c) +/* Widths up to 32 fit in `int`, so a bare literal has the right type. */ +#define INT8_C(c) (c) +#define INT16_C(c) (c) +#define INT32_C(c) (c) +#define UINT8_C(c) (c) +#define UINT16_C(c) (c) +#define UINT32_C(c) (c ## U) +/* 64-bit literal suffix is L on LP64 and LL on LLP64; intmax_t same. + Keep the compiler's macro so the constant has the right type. */ +#define INT64_C(c) __INT64_C(c) +#define UINT64_C(c) __UINT64_C(c) #define INTMAX_C(c) __INTMAX_C(c) #define UINTMAX_C(c) __UINTMAX_C(c) diff --git a/test/smoke.c b/test/smoke.c @@ -16,6 +16,18 @@ /* ---- compile-time assertions over the contracts ------------------ */ +/* hardcoded contract -- cfree assumes 8-bit byte, 32-bit int, 64-bit long long, + IEEE 754 binary32/64. Pin the values so a compiler/target violating these + fails loudly here. */ +_Static_assert(CHAR_BIT == 8, "cfree assumption"); +_Static_assert(SHRT_MAX == 32767, "cfree assumption"); +_Static_assert(INT_MAX == 2147483647, "cfree assumption"); +_Static_assert(UINT_MAX == 4294967295U, "cfree assumption"); +_Static_assert(LLONG_MAX == 9223372036854775807LL,"cfree assumption"); +_Static_assert(FLT_RADIX == 2, "cfree assumption"); +_Static_assert(FLT_MANT_DIG == 24, "binary32"); +_Static_assert(DBL_MANT_DIG == 53, "binary64"); + _Static_assert(CHAR_BIT >= 8, "C11 5.2.4.2.1"); _Static_assert(SCHAR_MIN <= -127, "C11 5.2.4.2.1"); _Static_assert(SCHAR_MAX >= 127, "C11 5.2.4.2.1");