Case 89: Inline accessor references renamed pimpl member¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux, macOS |
| Flags | ABI break |
Detected ChangeKinds |
inline_body_references_renamed_member |
| Source files | browse on GitHub |
Category: Pimpl ABI | Verdict: BREAKING
What breaks¶
The public class exposes inline getters that reach into the pimpl implementation by member name:
class descriptor {
public:
inline int get_class_count() const { return impl_->class_count_; }
private:
detail::pimpl<detail::descriptor_impl> impl_;
};
In v2, detail::descriptor_impl::class_count_ is renamed to n_classes_
during a "modernize naming" cleanup. The maintainer updates the inline
accessor body in the header in lockstep, so:
- Rebuilding the library succeeds.
- Rebuilding new consumers against v2 headers succeeds.
- Existing consumer binaries โ compiled against v1.h with the old
inline body baked in โ continue to reference
class_count_at the old offset. Linked against v2'sdescriptor_impllayout, the inline access reads the wrong field (or garbage if the layout shifted).
There is no symbol-level evidence of the break: descriptor::get_class_count
is inline and has no exported symbol. There is no public-type layout change:
descriptor still holds one pimpl pointer. The break lives entirely in the
gap between what the consumer's inline body assumes and what the new
detail layout actually is.
Why distinct from case35 + case47¶
- case35 (
field_rename) covers field renames in isolation. It does not exercise the inline-consumer-baked-in-body trap. - case47 (
inline_to_outlined) is the COMPATIBLE counter-case (moving an inline to outlined is safe). - case89 is the interaction: field rename in a detail:: type, accessed by an inline public method whose body is shipped into consumers.
How abicheck catches it¶
The new inline_body_references_renamed_member detector correlates:
- A
field_renamed(ortype_field_removed+type_field_added) on a record type that lives in an internal namespace (reusesinternal_leak.py::is_internal_type). - An inline public method (presence of body in DWARF, no exported symbol) on a non-internal type whose member-access expression references the old field name.
When both halves match, emit a single
INLINE_BODY_REFERENCES_RENAMED_MEMBER finding describing the chain
descriptor::get_class_count() (inline) โ impl_->class_count_ (renamed).
Real-world reference¶
oneDAL's pimpl idiom plus inline header accessors creates this exact
risk surface for every detail::*_impl field. The class member-rename
is invisible to users at source level but propagates into every
consumer binary already compiled against the previous header.
Source files¶
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.