commit da7fa2922ee93c3891d072432008bfe713ff84cd
parent d51c0ff8fbdc514827366d5bed17e1352c39cbb5
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 7 May 2026 09:05:51 -0700
cfree assumptions
Diffstat:
| M | builtins.md | | | 177 | +++++++++++++++++++++++++++++++------------------------------------------------ |
| M | include/float.h | | | 103 | ++++++++++++++++++++++++++++++++++++------------------------------------------- |
| M | include/limits.h | | | 74 | ++++++++++++++++++++++++++++++++++++++++---------------------------------- |
| M | include/stdint.h | | | 103 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
| M | test/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");