Case 102: Frozen Runtime Signature Changed (oneTBB detail::r1 shape)¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux, macOS |
| Flags | ABI break, API break |
Detected ChangeKinds |
func_params_changed, func_return_changed |
| Source files | examples/case102_frozen_runtime_signature_changed/ |
Category: Runtime Contract | Verdict: ๐ด BREAKING
What breaks¶
An extern "C" runtime entry point inside a contractually-frozen namespace
(mylib::detail::r1::dispatch) has its parameter type widened from int to
long in place. The exported symbol name is unchanged, so the binary still
links โ but every consumer compiled against v1 pushes an int argument
into what is now a long parameter slot. On most calling conventions this
is silent corruption (high half-word garbage).
Why this is a breaking change¶
This fixture mirrors a real frozen-namespace contract.
oneTBB's VERSIONING.md
specifies that the runtime-symbol namespace tbb::detail::r1 is
append-only: existing entry points are frozen at their shipped
signature forever. Any incompatible change must introduce a new entry
point โ typically in r2 โ while keeping r1 alive indefinitely.
This case captures the failure mode where a library author bypasses that
contract and edits an r1 signature in place. The policy file declares
the namespace frozen so the existing FUNC_PARAMS_CHANGED finding cannot
be silently downgraded via a policy override.
Code diff¶
| v1 | v2 |
|---|---|
extern "C" int dispatch(int); |
extern "C" long dispatch(long); |
consumer pushes int โ reads int |
consumer pushes int โ reads long (silent corruption) |
How abicheck catches it¶
This case does NOT introduce a new ChangeKind. The existing detectors fire as usual:
FUNC_PARAMS_CHANGEDโ parameter type widened.FUNC_RETURN_CHANGEDโ return type widened.
Both are already BREAKING in strict_abi. The new frozen_namespaces:
policy field adds two things on top:
- Tag: the post-processing step
EscalateFrozenNamespaceViolationssetsChange.frozen_namespace_violationto the matching glob pattern and prefixes the description with[frozen-namespace violation: โฆ]so reviewers see the policy context. - Downgrade guard:
PolicyFile.compute_verdictrefuses anyoverrides:entry that would downgrade a tagged finding. A user cannot accidentally writefunc_params_changed: ignoreand make the r1 contract violation invisible.
A new namespace: selector on Suppression lets users declare narrow
exemptions (e.g. for a deliberate r1โr2 migration) without falling back
to a giant regex.
How to fix the underlying code¶
Don't edit detail::r1::* in place. Either:
- Add to r2: introduce
detail::r2::dispatch(long)as a new exported symbol; keepdetail::r1::dispatch(int)alive forever as a thin shim that calls into the r2 entry. - Wrap, don't rename: provide an overload at the public surface
(
mylib::run(long)) that dispatches to the new entry; the old inlinerun(int)keeps calling the oldr1::dispatch(int).
References¶
- oneTBB VERSIONING.md
Policy file syntax(if available)
Source files¶
CMakeLists.txtapp.cpppolicy.yamlv1.cppv1.hv2.cppv2.h
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.