Case 208: restrict Added to the Definition Only¶
| Field | Value |
|---|---|
| Verdict | ✅ NO_CHANGE |
| Category | No Change |
| Classification | Rule |
| Platforms | Linux |
| Flags | — |
Detected ChangeKinds |
— |
| Source files | catalog/cases/case208_restrict_added_to_definition_only/ |
| Rule family | restrict-added-to-definition-only |
| Subject | Safe changes correctly not flagged |
Category: No Change | Verdict: ✅ NO_CHANGE
Verdict and consumer impact¶
The implementation of blend() gains restrict on both pointer parameters,
but the declaration in the public header is unchanged. Consumers compile
against the header, so the contract they are held to is exactly the one v1
published: overlapping buffers remain permitted. The qualifier is a statement
about how this one translation unit is compiled, not about what callers must
guarantee.
This is the negative control paired with
case207_pointer_parameter_gained_restrict,
where the same qualifier is added to the published declaration. The pair
proves two things about these two fixtures: a restrict change on the
published contract is reported (case207), and one confined to the
implementation is not (this case).
Old/new diff¶
| v1.c | v2.c |
|---|---|
void blend(float *dst, const float *src, int n) |
void blend(float *restrict dst, const float *restrict src, int n) |
v1.h and v2.h are identical.
abicheck command¶
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -shared -fPIC -g v2.c -o libv2.so
abicheck compare libv1.so libv2.so --header old=v1.h --header new=v2.h
Expected abicheck finding¶
param_restrict_changed is expected not to fire. That absence is the point
of the case.
Minimum evidence¶
min_evidence: L2 — the public header AST is what establishes that the
published declaration is unchanged. A comparison that read the qualifier
from the definition instead (or from DWARF's DW_TAG_restrict_type on the
defining subprogram) would report a contract change the consumer's own
compiler never sees.
Why abicheck catches it¶
Nothing is reported because the header AST is the authority for a
declaration's qualifiers, and both sides' headers declare blend()
identically. diff_param_qualifiers.py therefore finds both parameters'
is_restrict_fact determinations equal and emits nothing. The case pins
which artifact abicheck treats as the contract: the header a consumer
compiles against, not the source file the library happens to build from.
Runtime failure demonstration¶
Severity: none — verified no observable effect.
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -g app.c -L. -lv1 -Wl,-rpath,. -o app
./app
# → 11.0 22.0 33.0 44.0
gcc -shared -fPIC -g v2.c -o libv1.so # swap in v2, no recompile
./app
# → 11.0 22.0 33.0 44.0 (identical output)
Callers that pass overlapping buffers remain well-defined, because the contract they compiled against never promised otherwise — the implementation simply may not exploit that promise for those callers.
Safe redesign¶
None needed. If the library wants the optimisation licence for all callers, it has to publish the stronger precondition, which is case207's break — the point of this pair is that those are two different changes, not one.
Cross-tool comparison¶
abidiff, reading DWARF for the definition, can see the qualifier on the
defining subprogram; abicheck deliberately answers the declaration question
from the public header instead, so the two tools are answering different
questions here rather than disagreeing about one.
Source files¶
CMakeLists.txtapp.cv1.cv1.hv2.cv2.h
See also: Compatibility Catalog · All NO_CHANGE cases · Category: No Change · Rule: restrict added to the definition only · Subject: Safe changes correctly not flagged.