Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intel ICC C++17 compatibility #2729

Merged
merged 4 commits into from
Jan 18, 2021
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 43 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ jobs:
name: "🐍 3 • ICC latest • x64"

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2

- name: Add apt repo
run: |
Expand All @@ -488,7 +488,6 @@ jobs:
run: sudo apt-get update; sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic cmake python3-dev python3-numpy python3-pytest python3-pip

- name: Update pip
shell: bash
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
python3 -m pip install --upgrade pip
Expand All @@ -498,43 +497,69 @@ jobs:
set +e; source /opt/intel/oneapi/setvars.sh; set -e
python3 -m pip install -r tests/requirements.txt --prefer-binary

- name: Configure
shell: bash
- name: Configure C++11
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake -S . -B build \
cmake -S . -B build-11 \
-DPYBIND11_WERROR=ON \
-DDOWNLOAD_CATCH=ON \
-DDOWNLOAD_EIGEN=OFF \
-DCMAKE_CXX_STANDARD=11 \
-DCMAKE_CXX_COMPILER=$(which icpc) \
-DCMAKE_VERBOSE_MAKEFILE=ON \
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

- name: Build
shell: bash
- name: Build C++11
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake --build build -j 2
cmake --build build-11 -j 2 -v

- name: Python tests
shell: bash
- name: Python tests C++11
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
sudo service apport stop
cmake --build build --target check
cmake --build build-11 --target check

- name: C++ tests
shell: bash
- name: C++ tests C++11
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake --build build --target cpptest
cmake --build build-11 --target cpptest

- name: Interface test
shell: bash
- name: Interface test C++11
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake --build build-11 --target test_cmake_build

- name: Configure C++17
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake -S . -B build-17 \
-DPYBIND11_WERROR=ON \
-DDOWNLOAD_CATCH=ON \
-DDOWNLOAD_EIGEN=OFF \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_CXX_COMPILER=$(which icpc) \
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")

- name: Build C++17
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake --build build-17 -j 2 -v

- name: Python tests C++17
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
sudo service apport stop
cmake --build build-17 --target check

- name: C++ tests C++17
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake --build build-17 --target cpptest

- name: Interface test C++17
run: |
set +e; source /opt/intel/oneapi/setvars.sh; set -e
cmake --build build --target test_cmake_build
cmake --build build-17 --target test_cmake_build


# Testing on CentOS (manylinux uses a centos base, and this is an easy way
Expand Down
7 changes: 3 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,10 @@ Supported compilers
newer)
2. GCC 4.8 or newer
3. Microsoft Visual Studio 2015 Update 3 or newer
4. Intel C++ compiler 18 or newer
(`possible issue <https://github.com/pybind/pybind11/pull/2573>`_ on 20.2)
4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI)
5. Cygwin/GCC (previously tested on 2.5.1)
6. NVCC (CUDA 11.0 tested)
7. NVIDIA PGI (20.9 tested)
6. NVCC (CUDA 11.0 tested in CI)
7. NVIDIA PGI (20.9 tested in CI)

About
-----
Expand Down
14 changes: 12 additions & 2 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -2172,16 +2172,26 @@ class unpacking_collector {
dict m_kwargs;
};

// [workaround(intel)] Separate function required here
// We need to put this into a separate function because the Intel compiler
// fails to compile enable_if_t<!all_of<is_positional<Args>...>::value>
// (tested with ICC 2021.1 Beta 20200827).
template <typename... Args>
constexpr bool args_are_all_positional()
{
return all_of<is_positional<Args>...>::value;
}

/// Collect only positional arguments for a Python function call
template <return_value_policy policy, typename... Args,
typename = enable_if_t<all_of<is_positional<Args>...>::value>>
typename = enable_if_t<args_are_all_positional<Args...>()>>
simple_collector<policy> collect_arguments(Args &&...args) {
return simple_collector<policy>(std::forward<Args>(args)...);
}

/// Collect all arguments, including keywords and unpacking (only instantiated when needed)
template <return_value_policy policy, typename... Args,
typename = enable_if_t<!all_of<is_positional<Args>...>::value>>
typename = enable_if_t<!args_are_all_positional<Args...>()>>
unpacking_collector<policy> collect_arguments(Args &&...args) {
// Following argument order rules for generalized unpacking according to PEP 448
static_assert(
Expand Down
8 changes: 7 additions & 1 deletion include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,10 @@ template <typename T> using is_function_pointer = bool_constant<
std::is_pointer<T>::value && std::is_function<typename std::remove_pointer<T>::type>::value>;

template <typename F> struct strip_function_object {
// If you are encountering an
// 'error: name followed by "::" must be a class or namespace name'
// with the Intel compiler and a noexcept function here,
// try to use noexcept(true) instead of plain noexcept.
using type = typename remove_class<decltype(&F::operator())>::type;
};

Expand All @@ -689,8 +693,10 @@ template <typename T> using is_lambda = satisfies_none_of<remove_reference_t<T>,
/// Ignore that a variable is unused in compiler warnings
inline void ignore_unused(const int *) { }

// [workaround(intel)] Internal error on fold expression
/// Apply a function over each element of a parameter pack
#ifdef __cpp_fold_expressions
#if defined(__cpp_fold_expressions) && !defined(__INTEL_COMPILER)
// Intel compiler produces an internal error on this fold expression (tested with ICC 19.0.2)
#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...)
#else
using expand_side_effects = bool[];
Expand Down
11 changes: 10 additions & 1 deletion include/pybind11/pytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1271,14 +1271,23 @@ class tuple : public object {
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
};

// We need to put this into a separate function because the Intel compiler
// fails to compile enable_if_t<all_of<is_keyword_or_ds<Args>...>::value> part below
// (tested with ICC 2021.1 Beta 20200827).
template <typename... Args>
constexpr bool args_are_all_keyword_or_ds()
{
return detail::all_of<detail::is_keyword_or_ds<Args>...>::value;
}

class dict : public object {
public:
PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict)
dict() : object(PyDict_New(), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate dict object!");
}
template <typename... Args,
typename = detail::enable_if_t<detail::all_of<detail::is_keyword_or_ds<Args>...>::value>,
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>,
// MSVC workaround: it can't compile an out-of-line definition, so defer the collector
typename collector = detail::deferred_t<detail::unpacking_collector<>, Args...>>
explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }
Expand Down
21 changes: 18 additions & 3 deletions include/pybind11/stl_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,20 @@ struct vector_has_data_and_format : std::false_type {};
template <typename Vector>
struct vector_has_data_and_format<Vector, enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(), std::declval<Vector>().data()), typename Vector::value_type*>::value>> : std::true_type {};

// [workaround(intel)] Separate function required here
// Workaround as the Intel compiler does not compile the enable_if_t part below
// (tested with icc (ICC) 2021.1 Beta 20200827)
template <typename... Args>
constexpr bool args_any_are_buffer() {
return detail::any_of<std::is_same<Args, buffer_protocol>...>::value;
}

// [workaround(intel)] Separate function required here
// [workaround(msvc)] Can't use constexpr bool in return type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is MSVC failing on the constexpr or on the combination with std::enable_if?

Copy link
Collaborator

@henryiii henryiii Jan 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original all-in-one was too complicated for ICC to compile. Refactoring it to match the others, with a constexpr function, confused MSVC if it was in the return type. This is simpler to compile and avoids a constexpr function in a return type. As I don't think that this function is an external interface, and it's only used here, I could avoid the intermediate function and call the "impl" style function directly, if that's seen as cleaner.


// Add the buffer interface to a vector
template <typename Vector, typename Class_, typename... Args>
enable_if_t<detail::any_of<std::is_same<Args, buffer_protocol>...>::value>
vector_buffer(Class_& cl) {
void vector_buffer_impl(Class_& cl, std::true_type) {
using T = typename Vector::value_type;

static_assert(vector_has_data_and_format<Vector>::value, "There is not an appropriate format descriptor for this vector");
Expand Down Expand Up @@ -416,7 +426,12 @@ vector_buffer(Class_& cl) {
}

template <typename Vector, typename Class_, typename... Args>
enable_if_t<!detail::any_of<std::is_same<Args, buffer_protocol>...>::value> vector_buffer(Class_&) {}
void vector_buffer_impl(Class_&, std::false_type) {}

template <typename Vector, typename Class_, typename... Args>
void vector_buffer(Class_& cl) {
vector_buffer_impl<Vector, Class_, Args...>(cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{});
}

PYBIND11_NAMESPACE_END(detail)

Expand Down
9 changes: 9 additions & 0 deletions tests/test_builtin_casters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("bool_passthrough", [](bool arg) { return arg; });
m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert());

// TODO: This should be disabled and fixed in future Intel compilers
#if !defined(__INTEL_COMPILER)
// Test "bool_passthrough_noconvert" again, but using () instead of {} to construct py::arg
// When compiled with the Intel compiler, this results in segmentation faults when importing
// the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when
// a newer version of icc is available.
m.def("bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert());
#endif

// test_reference_wrapper
m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); });
m.def("refwrap_usertype", [](std::reference_wrapper<UserType> p) { return p.get().value(); });
Expand Down
2 changes: 2 additions & 0 deletions tests/test_callbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ TEST_SUBMODULE(callbacks, m) {
class AbstractBase {
public:
// [workaround(intel)] = default does not work here
// Defaulting this destructor results in linking errors with the Intel compiler
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
virtual ~AbstractBase() {}; // NOLINT(modernize-use-equals-default)
virtual unsigned int func() = 0;
};
Expand Down
9 changes: 9 additions & 0 deletions tests/test_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
BSD-style license that can be found in the LICENSE file.
*/

#if defined(__INTEL_COMPILER) && __cplusplus >= 201703L
// Intel compiler requires a separate header file to support aligned new operators
// and does not set the __cpp_aligned_new feature macro.
// This header needs to be included before pybind11.
#include <aligned_new>
#endif

#include "pybind11_tests.h"
#include "constructor_stats.h"
#include "local_bindings.h"
Expand Down Expand Up @@ -324,6 +331,8 @@ TEST_SUBMODULE(class_, m) {
class PublicistB : public ProtectedB {
public:
// [workaround(intel)] = default does not work here
// Removing or defaulting this destructor results in linking errors with the Intel compiler
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
~PublicistB() override {}; // NOLINT(modernize-use-equals-default)
using ProtectedB::foo;
};
Expand Down
7 changes: 7 additions & 0 deletions tests/test_constants_and_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ std::string print_bytes(py::bytes bytes) {
// Test that we properly handle C++17 exception specifiers (which are part of the function signature
// in C++17). These should all still work before C++17, but don't affect the function signature.
namespace test_exc_sp {
// [workaround(intel)] Unable to use noexcept instead of noexcept(true)
// Make the f1 test basically the same as the f2 test in C++17 mode for the Intel compiler as
// it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827).
#if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17)
int f1(int x) noexcept(true) { return x+1; }
#else
int f1(int x) noexcept { return x+1; }
#endif
int f2(int x) noexcept(true) { return x+2; }
int f3(int x) noexcept(false) { return x+3; }
#if defined(__GNUG__)
Expand Down