diff --git a/.circleci/config.yml b/.circleci/config.yml index e20005e1b..f689048f4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -73,11 +73,35 @@ jobs: - run: name: Build and test command: bash ./tools/circleci_bionic_clang6.sh + focal_clang9_msan_00: + docker: + - image: circleci/buildpack-deps:focal + steps: + - checkout + - run: + name: Build and test + command: bash ./tools/circleci_focal_clang9_msan.sh + environment: + TEST_NSPLIT: 2 + SPLIT_TEST_NUM: 0 + focal_clang9_msan_01: + docker: + - image: circleci/buildpack-deps:focal + steps: + - checkout + - run: + name: Build and test + command: bash ./tools/circleci_focal_clang9_msan.sh + environment: + TEST_NSPLIT: 2 + SPLIT_TEST_NUM: 1 workflows: version: 2 all_builds: jobs: + - focal_clang9_msan_00 + - focal_clang9_msan_01 - bionic_clang6 - bionic_gcc7_conda_coverage - bionic_gcc7_conda_release diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5708118e5..8c2e0203e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -80,6 +80,11 @@ endfunction() ADD_MPPP_TESTCASE(concepts) ADD_MPPP_TESTCASE(global_header) +# NOTE: the interop test requires all optional +# deps to be enabled. +if(MPPP_WITH_QUADMATH AND MPPP_WITH_MPFR AND MPPP_WITH_MPC) + ADD_MPPP_TESTCASE(interop_test) +endif() ADD_MPPP_TESTCASE(integer_abs) ADD_MPPP_TESTCASE(integer_addsub_ui_si) diff --git a/test/interop_test.cpp b/test/interop_test.cpp new file mode 100644 index 000000000..34ac47c9d --- /dev/null +++ b/test/interop_test.cpp @@ -0,0 +1,270 @@ +// Copyright 2016-2020 Francesco Biscani (bluescarni@gmail.com) +// +// This file is part of the mp++ library. +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#if defined(__clang__) || defined(__GNUC__) + +// NOTE: range warnings in the interop between +// real128 and __(u)int128_t. +#pragma GCC diagnostic ignored "-Wconversion" + +#endif + +#include +#include +#include + +#include + +#include "catch.hpp" +#include "test_utils.hpp" + +// NOLINTNEXTLINE(google-build-using-namespace) +using namespace mppp; +// NOLINTNEXTLINE(google-build-using-namespace) +using namespace mppp_test; + +using mppp_types = std::tuple, rational<1>, real, real128, complex128, complex>; + +using cpp_types = std::tuple +#if defined(MPPP_HAVE_GCC_INT128) + , + __int128_t, __uint128_t +#endif + >; + +template +struct is_mppp_complex : std::false_type { +}; + +template <> +struct is_mppp_complex : std::true_type { +}; + +template <> +struct is_mppp_complex : std::true_type { +}; + +struct mppp_interop_tester { + template + static void run_ineq_cmp(const A &a, const B &b, std::false_type) + { + REQUIRE(!(a < b)); + REQUIRE(a <= b); + REQUIRE(!(a > b)); + REQUIRE(a >= b); + } + template + static void run_ineq_cmp(const A &, const B &, std::true_type) + { + } + + template + struct runner { + template + void operator()(const U &) const + { + // Construct T from U. + T x1{U{42}}; + REQUIRE(x1 == 42); + + // Assign U to T. + U y1{43}; + x1 = y1; + REQUIRE(x1 == 43); + + // Convert T to U. + // NOTE: this is probably not strictly necessary, + // as it is covered by switching around T and U. + REQUIRE(static_cast(x1) == 43); + + // Basic binary arithmetic. + REQUIRE(x1 + U{4} == 47); + REQUIRE(x1 - U{4} == 39); + REQUIRE(x1 * U{2} == 86); + x1 = 10; + REQUIRE(x1 / U{2} == 5); + + // Basic in-place arithmetics. + REQUIRE((x1 += U{1}) == 11); + REQUIRE((x1 -= U{1}) == 10); + REQUIRE((x1 *= U{2}) == 20); + REQUIRE((x1 /= U{2}) == 10); + + // Exponentiation. + REQUIRE(mppp::pow(x1, U{2}) == 100); + + // Comparison. + REQUIRE(x1 == U{10}); + REQUIRE(x1 != U{11}); + + run_ineq_cmp(x1, U{10}, std::integral_constant < bool, + is_mppp_complex::value || is_mppp_complex::value > {}); + } + void operator()(const T &) const {} + }; + template + void operator()(const T &) const + { + tuple_for_each(mppp_types{}, runner{}); + } +}; + +TEST_CASE("mp++ interop") +{ + tuple_for_each(mppp_types{}, mppp_interop_tester{}); +} + +struct mppp_cpp_interop_tester { + template + static void run_ineq_cmp(const A &a, const B &b, std::false_type) + { + REQUIRE(!(a < b)); + REQUIRE(a <= b); + REQUIRE(!(a > b)); + REQUIRE(a >= b); + } + template + static void run_ineq_cmp(const A &, const B &, std::true_type) + { + } + + template + struct runner { + template + void operator()(const U &) const + { + // NOTE: here T is an mp++ type, + // U a C++ type. + + // Construct T from U. + T x1{U{42}}; + REQUIRE(x1 == 42); + + // Assign U to T. + U y1{43}; + x1 = y1; + REQUIRE(x1 == 43); + + // Convert T to U. + REQUIRE(static_cast(x1) == U{43}); + + // Basic binary arithmetic. + REQUIRE(x1 + U{4} == 47.); + REQUIRE(x1 - U{4} == 39.); + REQUIRE(x1 * U{2} == 86.); + x1 = 10; + REQUIRE(x1 / U{2} == 5.); + + // Basic in-place arithmetics. + REQUIRE((x1 += U{1}) == 11); + REQUIRE((x1 -= U{1}) == 10); + REQUIRE((x1 *= U{2}) == 20); + REQUIRE((x1 /= U{2}) == 10); + + // Exponentiation. + // NOTE: don't check the result due to roundoff errors. + (void)mppp::pow(x1, U{1}); + + // Comparison. + REQUIRE(x1 == U{10}); + REQUIRE(x1 != U{11}); + + run_ineq_cmp(x1, U{10}, std::integral_constant < bool, + is_mppp_complex::value || is_cpp_complex::value > {}); + } + void operator()(const T &) const {} + }; + template + void operator()(const T &) const + { + tuple_for_each(cpp_types{}, runner{}); + } +}; + +TEST_CASE("mppp cpp interop") +{ + tuple_for_each(mppp_types{}, mppp_cpp_interop_tester{}); +} + +struct cpp_mppp_interop_tester { + template + static void run_ineq_cmp(const A &a, const B &b, std::false_type) + { + REQUIRE(!(a < b)); + REQUIRE(a <= b); + REQUIRE(!(a > b)); + REQUIRE(a >= b); + } + template + static void run_ineq_cmp(const A &, const B &, std::true_type) + { + } + + template + struct runner { + template + void operator()(const U &) const + { + // NOTE: here T is a C++ type, + // U an mp++ type. + + // Construct T from U. + T x1{U{42}}; + // NOLINTNEXTLINE(clang-diagnostic-implicit-int-float-conversion) + REQUIRE(x1 == 42.); + + // Assign U to T. + U y1{43}; + x1 = static_cast(y1); + // NOLINTNEXTLINE(clang-diagnostic-implicit-int-float-conversion) + REQUIRE(x1 == 43.); + + // Convert T to U. + REQUIRE(static_cast(x1) == U{43}); + + // Basic binary arithmetic. + REQUIRE(x1 + U{4} == 47.); + REQUIRE(x1 - U{4} == 39.); + REQUIRE(x1 * U{2} == 86.); + x1 = 10; + REQUIRE(x1 / U{2} == 5.); + + // Basic in-place arithmetics. + // NOLINTNEXTLINE(clang-diagnostic-implicit-int-float-conversion) + REQUIRE((x1 += U{1}) == 11.); + // NOLINTNEXTLINE(clang-diagnostic-implicit-int-float-conversion) + REQUIRE((x1 -= U{1}) == 10.); + // NOLINTNEXTLINE(clang-diagnostic-implicit-int-float-conversion) + REQUIRE((x1 *= U{2}) == 20.); + // NOLINTNEXTLINE(clang-diagnostic-implicit-int-float-conversion) + REQUIRE((x1 /= U{2}) == 10.); + + // Exponentiation. + // NOTE: don't check the result due to roundoff errors. + (void)mppp::pow(x1, U{1}); + + // Comparison. + REQUIRE(x1 == U{10}); + REQUIRE(x1 != U{11}); + + run_ineq_cmp(x1, U{10}, std::integral_constant < bool, + is_cpp_complex::value || is_mppp_complex::value > {}); + } + void operator()(const T &) const {} + }; + template + void operator()(const T &) const + { + tuple_for_each(mppp_types{}, runner{}); + } +}; + +TEST_CASE("cpp mppp interop") +{ + tuple_for_each(cpp_types{}, cpp_mppp_interop_tester{}); +} diff --git a/tools/circleci_focal_clang9_msan.sh b/tools/circleci_focal_clang9_msan.sh new file mode 100644 index 000000000..a43caa6da --- /dev/null +++ b/tools/circleci_focal_clang9_msan.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# Echo each command +set -x + +# Exit on error. +set -e + +# Core deps. +sudo apt-get install build-essential cmake wget clang ninja-build + +# Create the build dir and cd into it. +mkdir build +cd build +MPPP_BUILD_DIR=`pwd` + +# libc++ setup. See: +# https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo +git clone --depth=1 https://github.com/llvm/llvm-project +cd llvm-project +mkdir build +cd build +LLVM_BUILD_DIR=`pwd` +cmake -GNinja ../llvm \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi" \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DLLVM_USE_SANITIZER=MemoryWithOrigins +cmake --build . -- cxx cxxabi +# Back to the MPPP build dir. +cd $MPPP_BUILD_DIR + +# Download and compile locally GMP in debug mode. +GMP_VERSION="6.2.0" +wget https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.bz2 -O gmp.tar.bz2 +tar xjvf gmp.tar.bz2 +cd gmp-${GMP_VERSION} +CC=clang CXX=clang++ ./configure --enable-shared --disable-static --enable-assert --enable-alloca=debug --disable-assembly CFLAGS="-g -fsanitize=memory" --prefix=/home/circleci/.local +make -j2 +make install + +# Compile mppp and run the tests. +cd .. +MPPP_MSAN_FLAGS="-fsanitize=memory -stdlib=libc++ -nostdinc++ -isystem ${LLVM_BUILD_DIR}/include/c++/v1 -L${LLVM_BUILD_DIR}/lib -Wl,-rpath,${LLVM_BUILD_DIR}/lib -Wno-unused-command-line-argument" +CC=clang CXX=clang++ cmake ../ -DCMAKE_BUILD_TYPE=Debug -DMPPP_BUILD_TESTS=yes -DCMAKE_CXX_FLAGS="${MPPP_MSAN_FLAGS}" -DCMAKE_C_FLAGS="${MPPP_MSAN_FLAGS}" -DCMAKE_PREFIX_PATH=/home/circleci/.local -DCMAKE_CXX_STANDARD=17 -DMPPP_TEST_NSPLIT=${TEST_NSPLIT} -DMPPP_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} +make -j2 VERBOSE=1 +ctest -j4 -V + +set +e +set +x