Case 21 — Method Became Static¶
| Field | Value |
|---|---|
| Verdict | 🔴 BREAKING |
| Category | Breaking |
| Platforms | Linux |
| Flags | ABI break, API break |
Detected ChangeKinds |
func_static_changed |
| Source files | browse on GitHub |
Verdict: 🔴 BREAKING (calling convention contract)
What changes¶
| Version | Declaration |
|---|---|
| v1 | class Widget { int value; int bar(); }; |
| v2 | class Widget { static int bar(); }; |
Why this is ABI-breaking¶
For Itanium C++ ABI, static-ness is not encoded in symbol mangling for this case,
so the symbol still resolves (_ZN6Widget3barEv).
But call contract changes:
- v1 call site passes implicit this
- v2 static function expects no this
So old binaries can still link, yet execute with a mismatched calling convention.
Real Failure Demo¶
Severity: CRITICAL
This demo shows a deterministic wrong result (not only “possible UB”):
- v1 behavior: returns value + 1
- app sets value=41, expects 42
- v2 static method returns fixed 7
# Build v1 + app (compiled against old header)
g++ -shared -fPIC -g old/lib.cpp -Iold -o libwidget.so
g++ -g app.cpp -Iold -L. -lwidget -Wl,-rpath,. -o app
./app
# expected:
# bar() called (instance method), value=41
# got=42 expected=42
# Swap in v2 (method became static, same symbol still links)
g++ -shared -fPIC -g new/lib.cpp -Inew -o libwidget.so
./app
# expected:
# bar() called (static method), returning fixed value
# got=7 expected=42
# WRONG RESULT: method call contract changed (instance -> static)
Why this case matters¶
This is a silent binary-compat trap: loader/linker are happy, but runtime semantics are broken. No crash is required to prove ABI break — wrong results are enough.
Source files¶
See also: Examples overview · All BREAKING cases · Category: Breaking.