Skip to content

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

Verdict: NO_CHANGE (exit 0)

_No ABI changes detected._

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.txt
  • app.c
  • v1.c
  • v1.h
  • v2.c
  • v2.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.