Case 204: Class Gained a Non-Virtual Method¶
| Field | Value |
|---|---|
| Verdict | 🟢 COMPATIBLE |
| Category | Addition (Compatible) |
| Classification | Rule |
| Platforms | Linux |
| Flags | — |
Detected ChangeKinds |
func_added |
| Source files | catalog/cases/case204_class_gained_non_virtual_method/ |
| Rule family | class-gained-non-virtual-method |
| Subject | Safe changes correctly not flagged |
Category: Addition | Verdict: ✅ COMPATIBLE
Verdict and consumer impact¶
Handle gains an is_open() member function that is not virtual. The
class stays non-polymorphic, so no vtable pointer is inserted:
sizeof(Handle), its alignment and id_'s offset are all unchanged. The
release adds one exported symbol and nothing else. Consumers embedding a
Handle by value, deriving from it, or allocating one keep working
unmodified.
This is the negative control paired with
case203_class_gained_vtable_pointer,
which makes the nearest breaking version of the same edit by marking the
new member virtual. The pair proves two things about these two fixtures:
adding the first virtual function is flagged, and adding an ordinary member
function is not.
Old/new diff¶
| v1.h | v2.h |
|---|---|
(no is_open) |
bool is_open() const; |
abicheck command¶
g++ -shared -fPIC -g v1.cpp -o libv1.so
g++ -shared -fPIC -g v2.cpp -o libv2.so
abicheck compare libv1.so libv2.so --header old=v1.h --header new=v2.h
Expected abicheck finding¶
vptr_introduced, type_size_changed and type_field_offset_changed are
expected not to fire. Those absences are the point of the case.
Minimum evidence¶
min_evidence: L0 — the added member function is a new mangled export
visible in the dynamic symbol table alone. abicheck still reads the headers
here so the absence of the layout findings is established on the same
evidence tier case203's presence is.
Why abicheck catches it¶
A non-virtual member function is not part of the object's layout, so
diff_types.py finds Handle's size, alignment and member offsets
unchanged and reports nothing about the record. diff_symbols.py reports
the new mangled export as func_added, a COMPATIBLE kind.
Runtime failure demonstration¶
Severity: none — verified no observable effect.
g++ -shared -fPIC -g v1.cpp -o libv1.so
g++ -g app.cpp -L. -lv1 -Wl,-rpath,. -o app
./app
# → via factory: id() = 7 / local: id() = 7
g++ -shared -fPIC -g v2.cpp -o libv1.so # swap in v2, no recompile
./app
# → identical output
Safe redesign¶
None needed — this is the safe pattern. Adding non-virtual member functions to a released class is the standard way to extend it without touching its layout.
Cross-tool comparison¶
abidiff and ABICC both report only the added function. The case exists so
that the vtable-introduction detector has an adjacent fixture it must stay
quiet on, not to separate the tools.
Source files¶
CMakeLists.txtapp.cppv1.cppv1.hv2.cppv2.h
See also: Compatibility Catalog · All COMPATIBLE cases · Category: Addition (Compatible) · Rule: Class gained a non-virtual method · Subject: Safe changes correctly not flagged.