Case 25: Enum Member Added¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Addition (Compatible) |
| Platforms | Linux, macOS, Windows |
| Flags | โ |
Detected ChangeKinds |
enum_member_added |
| Source files | examples/case25_enum_member_added/ |
Category: Type Layout | Verdict: ๐ข COMPATIBLE
Verdict and consumer impact¶
enum Color gains a fourth member, YELLOW = 3, appended after BLUE.
Every existing member keeps its old numeric value, so any binary compiled
against v1 that reads RED/GREEN/BLUE from get_color() continues to
work unmodified โ no recompilation required. The only residual risk is
source-level: a switch written against v1 has no case YELLOW, so if the
v2 library ever actually returns YELLOW at runtime, an old binary falls
through to its default branch (or undefined behavior if there is none).
Old/new diff¶
| old/lib.h | new/lib.h |
|---|---|
enum Color { RED = 0, GREEN = 1, BLUE = 2 }; |
enum Color { RED = 0, GREEN = 1, BLUE = 2, YELLOW = 3 }; |
abicheck command¶
gcc -shared -fPIC -g old/lib.c -Iold -o libcolor_v1.so
gcc -shared -fPIC -g new/lib.c -Inew -o libcolor_v2.so
abicheck compare libcolor_v1.so libcolor_v2.so
Expected abicheck finding¶
Verdict: COMPATIBLE (exit 0)
Additions:
- enum_member_added: Enum member added: Color::YELLOW (3)
> New enumerator may shift subsequent values in non-fixed enums; switch
defaults may miss the new case.
Minimum evidence¶
min_evidence: L1 โ DWARF's DW_TAG_enumeration_type carries each
enumerator's name and constant value for both versions; abicheck diffs the
two enumerator lists directly from debug info, no public headers required.
Why abicheck catches it¶
abicheck compares the old and new enumerator lists by name and value: a
name present only in v2 with no existing member's value disturbed is
reported as enum_member_added (compatible); a name whose value changed
between versions would instead be enum_member_value_changed and
classified BREAKING.
Runtime failure demonstration¶
No observable effect on existing binaries โ get_color() still returns
BLUE in both versions, so old callers see the identical value before and
after the swap. The interesting case (an old binary encountering the new
YELLOW value at runtime) requires the library to actually start
returning it, which this pair's get_color() does not do.
# Build old library + app
gcc -shared -fPIC -g old/lib.c -Iold -o libcolor.so
gcc -g app.c -Iold -L. -lcolor -Wl,-rpath,. -o app
./app
# โ BLUE
# Swap in new library (no recompile)
gcc -shared -fPIC -g new/lib.c -Inew -o libcolor.so
./app
# โ BLUE (identical โ YELLOW added at end, existing values unchanged)
Safe redesign¶
This is already the safe way to extend an enum: append new members at the
end with explicit or naturally-incrementing values, never renumber
existing ones. Consumers that need to future-proof switch statements
should always include a default case rather than relying on exhaustive
enumeration.
Real-world example: POSIX signal numbers and errno codes are only ever extended by appending new values at unused numbers โ never by renumbering existing ones โ precisely to preserve this kind of compatibility.
Cross-tool comparison¶
References¶
Source files¶
CMakeLists.txtapp.c
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Addition (Compatible).