Skip to content

Case 98: C++ Standard Floor Raised

Field Value
Verdict ๐ŸŸก COMPATIBLE_WITH_RISK
Category Risk
Platforms Linux, macOS, Windows
Flags โ€”
Detected ChangeKinds abi_relevant_build_flag_changed
Source files examples/case98_cxx_standard_floor_raised/

Category: Build context risk | Verdict: โš ๏ธ COMPATIBLE_WITH_RISK

Verdict and consumer impact

v1.h/v2.h declare an identical public surface, and neither uses any post-C++17 construct โ€” a consumer TU including either header compiles unchanged. What changes is the library's own build contract: v1 is built with -std=gnu++17, v2 with -std=c++20. The binary ABI (symbols, layout, vtables) is untouched, so nothing breaks today โ€” but the producer now assumes a newer C++ dialect than before, and that's a deployment risk signal worth surfacing before it turns into a real break (e.g. once the library starts relying on standard-library types whose layout differs across dialects).

Old/new diff

v1.h / v1.cpp v2.h / v2.cpp
Declarations byte-identical to v2 Declarations byte-identical to v1
Built with -std=gnu++17 Built with -std=c++20 (V2_COMPILE_OPTIONS in CMakeLists.txt)

abicheck command

g++ -std=gnu++17 -shared -fPIC -g v1.cpp -o old/liblib_v1.so
g++ -std=c++20  -shared -fPIC -g v2.cpp -o new/liblib_v2.so
# compile_commands.json per side records the -std flag actually used
abicheck compare old/liblib_v1.so new/liblib_v2.so \
  --depth build \
  --build-info old=old/compile_commands.json \
  --build-info new=new/compile_commands.json

Expected abicheck finding

Verdict: COMPATIBLE_WITH_RISK (exit 0)

## Deployment Risk Changes

- abi_relevant_build_flag_changed: Build option 'std:CXX' changed: 'gnu++17' -> 'c++20'

Minimum evidence

min_evidence: L3 โ€” the symbol set, layout, and header-declared surface are all byte-identical between v1 and v2, so an L0โ€“L2 artifact-only scan returns NO_CHANGE: that's a documented false negative from insufficient evidence, not an alternative valid verdict. Only L3 build-context comparison (a compile database recording each side's -std= flag) carries the fact abicheck needs here. L4/L5 source replay is not required.

Why abicheck catches it

With --depth build and a --build-info compile database per side, abicheck's build-context collector reads each compile unit's flags and records std:CXX. Comparing the two sides' recorded standards directly surfaces the gnu++17 โ†’ c++20 change as abi_relevant_build_flag_changed, independent of whether the flag change produced any observable binary difference yet.

Runtime failure demonstration

This case is COMPATIBLE_WITH_RISK, not a hard break โ€” there is no crash to demonstrate. Verified directly: the consumer app compiles unchanged against either header and behaves identically whether linked against liblib_v1.so or hot-swapped to liblib_v2.so (no recompile):

g++ -std=c++17 app.cpp -Iold -Lold -llib_v1 -Wl,-rpath,old -o app
./app          # โ†’ 42
# swap in the v2 .so (no recompile)
cp new/liblib_v2.so old/liblib_v1.so
./app          # โ†’ 42 (identical output)

No observable effect on existing binaries โ€” the risk is in what the raised standard floor licenses the library to do next, not in today's ABI.

Safe redesign

Track and publish the minimum/maximum supported C++ standard as part of the library's documented ABI contract, and treat a floor change as a notable release-note item even when it produces no symbol/layout diff today. If the library exposes any STL types across the ABI boundary (std::string, std::vector, โ€ฆ), pin the standard library's ABI mode explicitly (see case 104) rather than letting the compiler default drift between builds.

Real-world example: projects that bump CMAKE_CXX_STANDARD in a "modernization" PR often don't realize the new floor is itself an ABI-relevant fact for consumers cross-compiling or vendoring the library under a pinned older toolchain โ€” the break shows up later as a confusing toolchain-compatibility bug, not at review time.

Cross-tool comparison

abidiff/abi-compliance-checker compare binary artifacts (with optional DWARF) and have no concept of "build context" outside the binary itself; neither would report anything for this case, since the .so files are layout-identical. Detecting a build-flag drift like this requires consuming the compile database directly, which is what abicheck's L3 build-context layer is for.

References


Source files

  • CMakeLists.txt
  • app.cpp
  • v1.cpp
  • v1.h
  • v2.cpp
  • v2.h

See also: Examples overview ยท All COMPATIBLE_WITH_RISK cases ยท Category: Risk.