Support C++17 aligned new statement (#1582)

* Support C++17 aligned new statement

This patch makes pybind11 aware of nonstandard alignment requirements in
bound types and passes on this information to C++17 aligned 'new'
operator. Pre-C++17, the behavior is unchanged.
diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h
index 985bc57..8732cfe 100644
--- a/include/pybind11/attr.h
+++ b/include/pybind11/attr.h
@@ -214,11 +214,14 @@
     /// How large is the underlying C++ type?
     size_t type_size = 0;
 
+    /// What is the alignment of the underlying C++ type?
+    size_t type_align = 0;
+
     /// How large is the type's holder?
     size_t holder_size = 0;
 
     /// The global operator new can be overridden with a class-specific variant
-    void *(*operator_new)(size_t) = ::operator new;
+    void *(*operator_new)(size_t) = nullptr;
 
     /// Function pointer to class_<..>::init_instance
     void (*init_instance)(instance *, const void *) = nullptr;
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index a21d6ba..2dec23c 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -571,7 +571,17 @@
         // Lazy allocation for unallocated values:
         if (vptr == nullptr) {
             auto *type = v_h.type ? v_h.type : typeinfo;
-            vptr = type->operator_new(type->type_size);
+            if (type->operator_new) {
+                vptr = type->operator_new(type->type_size);
+            } else {
+                #if defined(PYBIND11_CPP17)
+                    if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+                        vptr = ::operator new(type->type_size,
+                                              (std::align_val_t) type->type_align);
+                    else
+                #endif
+                vptr = ::operator new(type->type_size);
+            }
         }
         value = vptr;
     }
diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h
index 78d4afe..ad34415 100644
--- a/include/pybind11/detail/internals.h
+++ b/include/pybind11/detail/internals.h
@@ -116,7 +116,7 @@
 struct type_info {
     PyTypeObject *type;
     const std::type_info *cpptype;
-    size_t type_size, holder_size_in_ptrs;
+    size_t type_size, type_align, holder_size_in_ptrs;
     void *(*operator_new)(size_t);
     void (*init_instance)(instance *, const void *);
     void (*dealloc)(value_and_holder &v_h);
@@ -138,7 +138,7 @@
 };
 
 /// Tracks the `internals` and `type_info` ABI version independent of the main library version
-#define PYBIND11_INTERNALS_VERSION 2
+#define PYBIND11_INTERNALS_VERSION 3
 
 #if defined(WITH_THREAD)
 #  define PYBIND11_INTERNALS_KIND ""
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index 99a1e0a..e45f1a0 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -896,6 +896,7 @@
         tinfo->type = (PyTypeObject *) m_ptr;
         tinfo->cpptype = rec.type;
         tinfo->type_size = rec.type_size;
+        tinfo->type_align = rec.type_align;
         tinfo->operator_new = rec.operator_new;
         tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size);
         tinfo->init_instance = rec.init_instance;
@@ -987,11 +988,21 @@
     : std::true_type { };
 /// Call class-specific delete if it exists or global otherwise. Can also be an overload set.
 template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0>
-void call_operator_delete(T *p, size_t) { T::operator delete(p); }
+void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); }
 template <typename T, enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int> = 0>
-void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); }
+void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); }
 
-inline void call_operator_delete(void *p, size_t) { ::operator delete(p); }
+inline void call_operator_delete(void *p, size_t s, size_t a) {
+    (void)s; (void)a;
+#if defined(PYBIND11_CPP17)
+    if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+        ::operator delete(p, s, std::align_val_t(a));
+    else
+        ::operator delete(p, s);
+#else
+    ::operator delete(p);
+#endif
+}
 
 NAMESPACE_END(detail)
 
@@ -1054,6 +1065,7 @@
         record.name = name;
         record.type = &typeid(type);
         record.type_size = sizeof(conditional_t<has_alias, type_alias, type>);
+        record.type_align = alignof(conditional_t<has_alias, type_alias, type>&);
         record.holder_size = sizeof(holder_type);
         record.init_instance = init_instance;
         record.dealloc = dealloc;
@@ -1329,7 +1341,10 @@
             v_h.set_holder_constructed(false);
         }
         else {
-            detail::call_operator_delete(v_h.value_ptr<type>(), v_h.type->type_size);
+            detail::call_operator_delete(v_h.value_ptr<type>(),
+                v_h.type->type_size,
+                v_h.type->type_align
+            );
         }
         v_h.value_ptr() = nullptr;
     }
diff --git a/tests/test_class.cpp b/tests/test_class.cpp
index 9ed1f50..499d0cc 100644
--- a/tests/test_class.cpp
+++ b/tests/test_class.cpp
@@ -12,6 +12,10 @@
 #include "local_bindings.h"
 #include <pybind11/stl.h>
 
+#if defined(_MSC_VER)
+#  pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier
+#endif
+
 // test_brace_initialization
 struct NoBraceInitialization {
     NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
@@ -354,6 +358,15 @@
           [](StringWrapper) -> NotRegistered { return {}; });
     py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
     py::implicitly_convertible<std::string, StringWrapper>();
+
+    #if defined(PYBIND11_CPP17)
+        struct alignas(1024) Aligned {
+            std::uintptr_t ptr() const { return (uintptr_t) this; }
+        };
+        py::class_<Aligned>(m, "Aligned")
+            .def(py::init<>())
+            .def("ptr", &Aligned::ptr);
+    #endif
 }
 
 template <int N> class BreaksBase { public: virtual ~BreaksBase() = default; };
diff --git a/tests/test_class.py b/tests/test_class.py
index 4a488ab..ed63ca8 100644
--- a/tests/test_class.py
+++ b/tests/test_class.py
@@ -273,3 +273,9 @@
         m.test_error_after_conversions("hello")
     assert str(exc_info.value).startswith(
         "Unable to convert function return value to a Python type!")
+
+
+def test_aligned():
+    if hasattr(m, "Aligned"):
+        p = m.Aligned().ptr()
+        assert p % 1024 == 0