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
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// N5008 [class.access.base]/5.4 with /4.4: m, named in F, is accessible in
// G: E is a public base of F, D a public base of E, and m is a private member
// of D (A is a private base of D), accessible to D's friend G. And in a
// member of D itself, through an object of the derived F.
extern "C" void __CPROVER_assert(bool, const char *);
struct A
{
protected:
int m = 1;
};
struct D : private A
{
friend struct G;
static int peek(struct F &f);
};
struct E : D
{
};
struct F : E
{
};
int D::peek(F &f)
{
return f.m;
}
struct G
{
int get(F &f)
{
return f.m;
}
};
int main()
{
F f;
G g;
__CPROVER_assert(g.get(f) == 1, "friend of D, two levels down");
__CPROVER_assert(D::peek(f) == 1, "member of D");
__CPROVER_assert(g.get(f) == 2, "WRONG (must FAIL)");
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
KNOWNBUG
main.cpp
--cpp11
^\[main\.assertion\.1\] line \d+ friend of D, two levels down: SUCCESS$
^\[main\.assertion\.2\] line \d+ member of D: SUCCESS$
^\[main\.assertion\.3\] line \d+ WRONG \(must FAIL\): FAILURE$
^\*\* 1 of \d+ failed
^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
^CONVERSION ERROR$
--
check_component_access applied [class.access.base]/5.4 only with the
declaring class A as B; D, an intermediate base, granted nothing.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
extern "C" void __CPROVER_assert(bool, const char *);
struct s
{
int x = 5;
// N5008 [expr.unary.op]/4: only `&C::m' (a qualified-id) forms a pointer
// to member; in a default member initializer `x' is `(*this).x'
// ([expr.prim.id.general]/2), so `&x' is an `int *'
int *p = &x;
};
int main()
{
s v;
__CPROVER_assert(*v.p == 5 && v.p == &v.x, "points to the object's member");
__CPROVER_assert(*v.p == 6, "WRONG (must FAIL)");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
extern "C" void __CPROVER_assert(bool, const char *);
struct s
{
int x = 5;
int *p = &x;
int y = x + 1;
// [expr.unary.op]/4: a parenthesized qualified-id names the member object
int *pp = &(s::x);
bool f()
{
// a qualified-id forms a pointer to member, also where `this' exists
int s::*m = &s::y;
(void)m;
int *q = &x;
return q == &x;
}
};
struct t
{
int a = 1;
int *q = &a;
t()
{
}
};
int main()
{
s v;
t w;
__CPROVER_assert(
v.p == &v.x && v.y == 6 && v.pp == &v.x && w.q == &w.a && v.f(),
"member names in default member initializers");
__CPROVER_assert(v.y == 5, "WRONG (must FAIL)");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
KNOWNBUG
main.cpp
--cpp11
^\[main\.assertion\.1\] line \d+ points to the object's member: SUCCESS$
^\[main\.assertion\.2\] line \d+ WRONG \(must FAIL\): FAILURE$
^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
^CONVERSION ERROR$
dropped the body
--
N5008 [expr.unary.op]/4: the address of a member forms a pointer to member
only for a qualified-id `&C::m'; [expr.prim.id.general]/2: in a default
member initializer an unqualified member name `x' is transformed into
`(*this).x', so `&x' has type `int *'. The front end typed the NSDMI `int
*p = &x;' as a pointer to member ("invalid implicit conversion from
'signed int (struct s:: *)' to 'signed int *'") and rejected the unit: the
initializer of a class without a user-provided constructor was type-
checked in class scope without `this'; the same initializer in a
constructor's mem-initializer works. Found while testing
cpp11_parenthesized_member_dereference_in_member_function. g++ 13 and
clang++ 18 accept the program. Fails on bin53.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
KNOWNBUG
shapes.cpp
--cpp11
^\[main\.assertion\.1\] line \d+ member names in default member initializers: SUCCESS$
^\[main\.assertion\.2\] line \d+ WRONG \(must FAIL\): FAILURE$
^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
^CONVERSION ERROR$
--
N5008 [expr.prim.id.general]/2, [expr.unary.op]/4: in a default member
initializer, of a class with or without a user-provided constructor, `x'
is `(*this).x', so `&x' and the parenthesized `&(s::x)' are `int *'; the
qualified `&s::y' forms a pointer to member, also in a member function
where `this' exists (on bin53 that was `int *': "invalid implicit
conversion from 'signed int *' to 'signed int (struct s:: *)'"). g++ 13
and clang++ 18 accept the program.
Loading
Loading