Skip to content

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

abidw --out-file v1.xml libcolor_v1.so
abidw --out-file v2.xml libcolor_v2.so
abidiff v1.xml v2.xml

References


Source files

  • CMakeLists.txt
  • app.c

See also: Examples overview ยท All COMPATIBLE cases ยท Category: Addition (Compatible).