Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ SpacesInCStyleCastParentheses: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
Standard: c++17
Standard: c++20
TabWidth: '2'
UseTab: Never
---
Expand Down
41 changes: 41 additions & 0 deletions regression/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,47 @@ This folder contains the CProver regression test-suite.
- `thorough-smt-backend`:
These tests are too slow to be run in CI with the SMT backend.

### Standard library headers (`cbmc-cpp`)

- `libcxx`:
These tests need the libc++ headers (`--stdlib libc++`); they are run only
where libc++ is installed.
- `libstdcxx`:
These tests pass only with the libstdc++ headers: they check details of
those headers (internal names, or the header shapes the test was reduced
from), or with libc++ or the MSVC STL they fail for a known bug, named in
the desc (with its KNOWNBUG where it could be reduced).
`regression/cbmc-cpp`'s CMake and Makefile exclude them, and the
`libstdcxxNN` tags, when cbmc does not preprocess with GNU gcc: with
MSVC, or where `gcc` is clang (macOS).
- `libstdcxx12`, `libstdcxx13`, `libstdcxx16`:
These tests need the libstdc++ headers of GCC 12, 13 or 16 (respectively)
or later.
cbmc preprocesses with the host `gcc`, so `regression/cbmc-cpp`'s CMake and
Makefile exclude the tags whose version the host `gcc -dumpversion` does not
meet (`-X libstdcxx16`): with older headers the test would not exercise the
header shape it was written for. A test for such a shape belongs here as
`CORE gcc-only libstdcxx16`, not as KNOWNBUG.
- `broken-libstdcxx16`:
A CORE test known to fail with the libstdc++ 16 headers; excluded (like
`libstdcxx16`, by the host `gcc -dumpversion`) when the host gcc is 16 or
later. This is temporary: each such test has a KNOWNBUG, reduced to its
root cause and named in the test's desc, and the tag is removed when that
KNOWNBUG is fixed.
- `gcc16`:
Tests motivated by the libstdc++ 16 header shapes; a grouping tag only (the
test may pass with older headers too). Combine with `libstdcxx16` when the
newer headers are required.
- `gnu-extensions`:
Tests of GNU extensions (`__attribute__`, `__alignof__`, GNU builtins,
`__complex__`) that both gcc and clang accept; excluded with MSVC.
- `itanium-abi`:
Tests of class layouts that the Itanium C++ ABI (gcc and clang) defines and
the MSVC ABI defines differently, such as bit-field allocation; excluded
with MSVC.
- `gcc-only`:
Tests that need `goto-cc` in gcc mode (excluded with MSVC or a non-GNU
build compiler).
### Solver

- `broken-cprover-smt-backend`:
Expand Down
98 changes: 94 additions & 4 deletions regression/cbmc-cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,99 @@
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR
NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(gcc_only -X gcc-only)
else()
set(gcc_only "")
endif()
# GNU extensions (__attribute__, __alignof__, GNU builtins), which g++ and
# clang++ accept and cbmc's Visual Studio mode does not
# and layouts of the Itanium C++ ABI (bit fields), which MSVC's ABI does not
# share
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
list(APPEND gcc_only -X gnu-extensions -X itanium-abi)
else()
# tests of the MSVC STL, which needs cl's preprocessing
list(APPEND gcc_only -X msvc-stl)
endif()

# Tests tagged `libstdcxxNN' need the libstdc++ headers of GCC >= NN (13:
# <format>): cbmc preprocesses with the HOST gcc (not necessarily the
# compiler cmake builds with), so ask it, and exclude the tags whose
# requirement it does not meet.
find_program(HOST_GCC gcc)
# `gcc' may be clang (macOS), whose standard library is libc++: then no test
# that needs the libstdc++ headers can run (`libstdcxx', `libstdcxxNN').
# With MSVC, cbmc preprocesses with cl (the MSVC STL) whatever gcc is
# installed (Strawberry Perl ships one).
set(host_gcc_is_gnu FALSE)
if(HOST_GCC AND NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
execute_process(
COMMAND ${HOST_GCC} --version
OUTPUT_VARIABLE HOST_GCC_VERSION_TEXT
ERROR_QUIET)
if(NOT HOST_GCC_VERSION_TEXT MATCHES "clang")
set(host_gcc_is_gnu TRUE)
endif()
endif()
if(host_gcc_is_gnu)
execute_process(
COMMAND ${HOST_GCC} -dumpversion
OUTPUT_VARIABLE HOST_GCC_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
foreach(libstdcxx_version 12 13 16)
if(HOST_GCC_VERSION AND
HOST_GCC_VERSION VERSION_LESS "${libstdcxx_version}")
list(APPEND gcc_only -X libstdcxx${libstdcxx_version})
endif()
endforeach()
# `broken-libstdcxx16': known to fail with the libstdc++ 16 headers (a
# temporary exclusion; each such test has a KNOWNBUG for its root cause)
if(HOST_GCC_VERSION AND NOT HOST_GCC_VERSION VERSION_LESS "16")
list(APPEND gcc_only -X broken-libstdcxx16)
endif()
else()
list(APPEND gcc_only -X libstdcxx -X libstdcxx12 -X libstdcxx13 -X libstdcxx16)
endif()

# On GCC < 13, system header processing leaves more incomplete template
# symbols that cause --validate-goto-model to crash. Run validation
# only on GCC 13+ where headers are well-supported.
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS "13")
set(cbmc_cmd "$<TARGET_FILE:cbmc>")
else()
set(cbmc_cmd "$<TARGET_FILE:cbmc> --validate-goto-model --validate-ssa-equation")
endif()

add_test_pl_tests(
"$<TARGET_FILE:cbmc> --validate-goto-model --validate-ssa-equation" ${gcc_only}
)
# A per-test timeout (-t) so that a single hanging test fails with
# [TIMEOUT] and its name instead of exhausting the whole suite's TIMEOUT
# (the slowest passing test took 339 s on the macOS CI runner).
set(cbmc_cpp_test_timeout -t 900)

# CORE as four shards that ctest runs in parallel: run serially, the suite
# exceeded the 4800 s TIMEOUT on the macOS CI runner (about 2900 s of
# passing tests there), with no single test at fault.
set(cbmc_cpp_flags ${gcc_only} -X libcxx ${cbmc_cpp_test_timeout})
add_sharded_test_pl_profile(
"cbmc-cpp" "${cbmc_cmd}" "-C;${cbmc_cpp_flags}" CORE "cbmc-cpp" 4 4800)
add_test_pl_profile("cbmc-cpp" "${cbmc_cmd}" -T THOROUGH ${cbmc_cpp_flags})
add_test_pl_profile("cbmc-cpp" "${cbmc_cmd}" -F FUTURE ${cbmc_cpp_flags})
add_test_pl_profile("cbmc-cpp" "${cbmc_cmd}" -K KNOWNBUG ${cbmc_cpp_flags})

# libc++ tests run without --validate-goto-model because system header
# processing may leave missing symbols in the goto model.
# Only enable if clang and libc++ headers are available.
find_program(CLANG_FOUND clang)
find_path(LIBCXX_FOUND "vector" PATHS /usr/include/c++/v1 /usr/lib/llvm-18/include/c++/v1 NO_DEFAULT_PATH)
if(CLANG_FOUND AND LIBCXX_FOUND)
add_test_pl_profile(
"cbmc-cpp-libcxx"
"$<TARGET_FILE:cbmc>"
-C CORE
-I libcxx
${gcc_only}
${cbmc_cpp_test_timeout}
)
# ~15 minutes of libc++ header-heavy tests on the CI runners
set_tests_properties("cbmc-cpp-libcxx-CORE" PROPERTIES TIMEOUT 2400)
endif()
49 changes: 46 additions & 3 deletions regression/cbmc-cpp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,58 @@ default: tests.log
include ../../src/config.inc
include ../../src/common

# `msvc-stl': tests of the MSVC STL, which needs cl's preprocessing
ifeq ($(BUILD_ENV_),MSVC)
excluded_tests = -X gcc-only
excluded_tests = -X gcc-only -X gnu-extensions -X itanium-abi
else ifneq ($(shell ../../../src/goto-cc/goto-cc --version 2>&1 | grep -c 'gcc:'),0)
excluded_tests = -X msvc-stl
else
excluded_tests = -X gcc-only -X msvc-stl
endif

# Tests tagged `libstdcxxNN' need the libstdc++ headers of GCC >= NN (cbmc
# preprocesses with the host gcc); exclude those the host cannot provide.
# `gcc' may be clang (macOS), whose standard library is libc++: then no test
# that needs the libstdc++ headers can run (`libstdcxx', `libstdcxxNN').
# With MSVC, cbmc preprocesses with cl (the MSVC STL) whatever gcc is
# installed.
ifeq ($(BUILD_ENV_),MSVC)
host_gcc_major =
else ifeq ($(shell gcc --version 2>/dev/null | grep -c clang),0)
host_gcc_major = $(shell gcc -dumpversion 2>/dev/null | cut -d. -f1)
else
host_gcc_major =
endif
ifeq ($(shell test -n "$(host_gcc_major)" && test $(host_gcc_major) -ge 12 && echo yes),yes)
excluded_libstdcxx =
else
excluded_libstdcxx = -X libstdcxx12
endif
ifeq ($(host_gcc_major),)
excluded_libstdcxx += -X libstdcxx
endif
ifneq ($(shell test -n "$(host_gcc_major)" && test $(host_gcc_major) -ge 13 && echo yes),yes)
excluded_libstdcxx += -X libstdcxx13
endif
ifneq ($(shell test -n "$(host_gcc_major)" && test $(host_gcc_major) -ge 16 && echo yes),yes)
excluded_libstdcxx += -X libstdcxx16
else
# known to fail with the libstdc++ 16 headers (temporary, see README)
excluded_libstdcxx += -X broken-libstdcxx16
endif

# Exclude libc++ tests when libc++ headers are not installed
ifeq ($(wildcard /usr/include/c++/v1/vector),)
ifeq ($(wildcard /Library/Developer/CommandLineTools/SDKs/*/usr/include/c++/v1/vector),)
excluded_libcxx = -X libcxx
endif
endif

test:
@../test.pl -e -p -c "../../../src/cbmc/cbmc --validate-goto-model --validate-ssa-equation" $(excluded_tests)
@../test.pl -e -p -c "../../../src/cbmc/cbmc --validate-goto-model --validate-ssa-equation" $(excluded_tests) $(excluded_libstdcxx) $(excluded_libcxx) -t 1800

tests.log: ../test.pl
@../test.pl -e -p -c "../../../src/cbmc/cbmc --validate-goto-model --validate-ssa-equation" $(excluded_tests)
@../test.pl -e -p -c "../../../src/cbmc/cbmc --validate-goto-model --validate-ssa-equation" $(excluded_tests) $(excluded_libstdcxx) $(excluded_libcxx) -t 1800

clean:
find . -name '*.out' -execdir $(RM) '{}' \;
Expand Down
12 changes: 12 additions & 0 deletions regression/cbmc-cpp/cpp20_abbreviated_fn_tmpl/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// C++20 abbreviated function template
int add(auto a, auto b)
{
return a + b;
}

int main()
{
int r = add(1, 2);
__CPROVER_assert(r == 3, "abbreviated fn tmpl");
return 0;
}
9 changes: 9 additions & 0 deletions regression/cbmc-cpp/cpp20_abbreviated_fn_tmpl/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
KNOWNBUG
main.cpp
--cpp20
^VERIFICATION SUCCESSFUL$
^EXIT=0$
^SIGNAL=0$
--
dropped the body
--
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
extern "C" void __CPROVER_assert(bool, const char *);
template <class It, class Sent = It>
struct sub
{
It b;
Sent e;
// N5008 [dcl.fct]/20: an abbreviated function template constructor;
// {first, last} initializes the parameter sub&& through it
// ([dcl.init.list]/3.7, [over.ics.list])
sub(auto first, Sent last) : b(first), e(last)
{
}
};
template <class V>
struct view
{
V base;
view(V &&v) : base(static_cast<V &&>(v))
{
}
};
int main()
{
const char s[] = "ab";
const char *first = s, *last = s + 2;
view<sub<const char *>> v({first, last});
__CPROVER_assert(v.base.e - v.base.b == 2, "braced argument");
__CPROVER_assert(v.base.e == v.base.b, "WRONG (must FAIL)");
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
KNOWNBUG
main.cpp
--cpp20
^\[main\.assertion\.1\] line \d+ braced argument: SUCCESS$
^\[main\.assertion\.2\] line \d+ WRONG \(must FAIL\): FAILURE$
^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
^CONVERSION ERROR$
--
An abbreviated function template was recognized only at namespace scope;
a member with a placeholder parameter (subrange(auto __first, _Sent
__last) in <ranges>) kept an `auto' parameter type and was no candidate.
GCC 14's <format> _M_parse_fill_and_align (`_Utf32_view<subrange<iterator>>
__uv({__first, __last})') lost its body once __literal_encoding_is_unicode
was evaluated.
43 changes: 43 additions & 0 deletions regression/cbmc-cpp/cpp20_adl_enum_poison_pill/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// N5008 [basic.lookup.argdep]/3.3, 4: the associated entities of an
// ENUMERATION type include its innermost enclosing namespace. CBMC's
// ADL only associated CLASS types, so libc++'s poison-pill pattern --
// using __adl_only::make_error_code; // deleted
// *this = make_error_code(__e); // real overload via ADL
// (std::error_code's converting constructor over io_errc/future_errc)
// found only the deleted pill and the whole <system_error> chain
// failed "found no match for symbol 'make_error_code'".
extern "C" void __CPROVER_assert(bool, const char *);
namespace std
{
namespace __adl_only
{
void make_error_code() = delete;
}
struct error_code
{
int v;
template <class E>
error_code(E e)
{
using __adl_only::make_error_code;
*this = make_error_code(e);
}
error_code(int val, bool) : v(val)
{
}
};
} // namespace std
enum class io_errc
{
stream = 1
};
std::error_code make_error_code(io_errc e)
{
return std::error_code(static_cast<int>(e), true);
}
int main()
{
std::error_code ec(io_errc::stream);
__CPROVER_assert(ec.v == 1, "adl make_error_code");
return 0;
}
12 changes: 12 additions & 0 deletions regression/cbmc-cpp/cpp20_adl_enum_poison_pill/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
KNOWNBUG
main.cpp
--cpp20
^\[main\.assertion\.1\] line \d+ adl make_error_code: SUCCESS$
^VERIFICATION SUCCESSFUL$
^EXIT=0$
^SIGNAL=0$
--
dropped the body
--
Fixed 2026-08-04 (round 12): resolve_with_arguments associates the
innermost enclosing namespace of enum-typed arguments.
16 changes: 16 additions & 0 deletions regression/cbmc-cpp/cpp20_aggregate_base/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// C++20 aggregate initialization with base class
struct Base
{
int x;
};
struct Derived : Base
{
int y;
};
int main()
{
Derived d{{10}, 20};
__CPROVER_assert(d.x == 10, "base member");
__CPROVER_assert(d.y == 20, "derived member");
return 0;
}
10 changes: 10 additions & 0 deletions regression/cbmc-cpp/cpp20_aggregate_base/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
KNOWNBUG
main.cpp
--cpp20
^VERIFICATION SUCCESSFUL$
^EXIT=0$
^SIGNAL=0$
--
dropped the body
--
C++20 aggregate initialization with base class
Loading
Loading