Case 30 โ Field Qualifier Changes (const, volatile)¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux, macOS, Windows |
| Flags | ABI break, API break |
Detected ChangeKinds |
โ |
| Source files | browse on GitHub |
Category: Type Qualifiers | Verdict: ๐ด BREAKING (policy escalated source break)
Compatibility classification¶
- Binary ABI impact: Usually layout-compatible (no size/offset change), but stale optimization assumptions can still break behavior.
- Source compatibility impact: BREAKING (
constwrite errors,volatilecontract changes). - Runtime behavior impact: Semantic divergence (stale reads / UB writes) without linker errors.
- Policy severity: BREAKING in
ground_truth.json(source_breakcategory escalated by policy).
What changes¶
| Field | v1 | v2 | Effect |
|---|---|---|---|
sample_rate |
int sample_rate |
const int sample_rate |
Writing becomes UB |
raw_value |
int raw_value |
volatile int raw_value |
Compiler must not cache reads |
cache_hits |
int cache_hits |
int cache_hits |
Unchanged |
Why this IS a (semantic) ABI break¶
The binary layout of struct SensorConfig is unchanged โ const and volatile
do not affect size, alignment, or field offsets. An existing binary will link and
run against the v2 library without error.
However, the API contract has changed:
-
const int sample_rate: Code compiled against v1 freely writes tosample_rate. The v2 header declares this fieldconst, meaning the library now considers it immutable after initialization. Writing to aconst-qualified field through a non-constpointer is undefined behavior in C. Compilers recompiling against v2 will reject the write at compile time. -
volatile int raw_value: Code compiled against v1 may have the compiler optimize away redundant reads ofraw_value. The v2 header marks itvolatile, indicating it may change asynchronously (e.g., hardware-mapped). Binaries compiled withoutvolatilemay return stale cached values.
Code diff¶
struct SensorConfig {
- int sample_rate;
- int raw_value;
+ const int sample_rate;
+ volatile int raw_value;
int cache_hits;
};
Real Failure Demo¶
Severity: MODERATE (semantic break, not crash)
Scenario: Compile app against v1 headers, swap in v2 .so.
# Build v1 library + app
gcc -shared -fPIC -g v1.c -o libfoo.so
gcc -g app.c -I. -L. -lfoo -Wl,-rpath,. -o app
./app
# โ Field qualifier change demo (compiled against v1.h):
# โ
# โ Initial state:
# โ sample_rate = 1000
# โ raw_value = 42
# โ cache_hits = 0
# โ
# โ sensor_read(&cfg) = 42
# โ
# โ After setting sample_rate = 2000:
# โ sample_rate = 2000
# โ
# โ raw_value read twice: r1=99 r2=99 (should be equal)
# โ ...
# โ sensor_read(&cfg) after modifications = 99
# Swap in v2 (no recompile)
gcc -shared -fPIC -g v2.c -o libfoo.so
./app
# โ Output is identical โ binary layout unchanged.
# โ But the semantic contract is now violated: the app writes
# โ to sample_rate which v2 declares const.
Source break verification (recompilation against v2 will warn/error):
# Create a temporary source that includes v2.h instead of v1.h
sed 's/#include "v1.h"/#include "v2.h"/' app.c > /tmp/app_v2_test.c
gcc -g /tmp/app_v2_test.c -I. -L. -lfoo -Wl,-rpath,. -o app_v2 2>&1
# โ error: assignment of read-only member 'sample_rate'
# (because sample_rate is const in v2.h)
rm -f /tmp/app_v2_test.c
Reproduce with abicheck¶
gcc -shared -fPIC -g v1.c -o libfoo_v1.so
gcc -shared -fPIC -g v2.c -o libfoo_v2.so
abidw --out-file v1.xml libfoo_v1.so
abidw --out-file v2.xml libfoo_v2.so
abidiff v1.xml v2.xml
echo "exit: $?"
How to fix¶
- Do not add
constto fields of public structs unless the field was always documented as read-only. - If a field must become immutable, provide setter/getter functions instead of direct field access, and hide the struct behind an opaque pointer.
- Adding
volatileshould be done only in a new struct or with a major version bump.
References¶
- C type qualifiers (
const) - C type qualifiers (
volatile) - C volatile semantics in systems code (WG14 N2148 discussion)
Source files¶
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.