codegen: Guarantee opaque type layout across all architectures.

Fixes #3279
diff --git a/bindgen-tests/tests/expectations/tests/bitfield_large_overflow.rs b/bindgen-tests/tests/expectations/tests/bitfield_large_overflow.rs
index ed7d1b3..ac5d757 100644
--- a/bindgen-tests/tests/expectations/tests/bitfield_large_overflow.rs
+++ b/bindgen-tests/tests/expectations/tests/bitfield_large_overflow.rs
@@ -1,9 +1,17 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C, align(8))]
+pub struct __BindgenOpaqueArray8<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
+    fn default() -> Self {
+        Self([<T as Default>::default(); N])
+    }
+}
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct _bindgen_ty_1 {
     pub _bindgen_align: [u64; 0],
-    pub _bindgen_opaque_blob: [u64; 10usize],
+    pub _bindgen_opaque_blob: __BindgenOpaqueArray8<[u8; 80usize]>,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
 const _: () = {
diff --git a/bindgen-tests/tests/expectations/tests/derive-debug-opaque-template-instantiation.rs b/bindgen-tests/tests/expectations/tests/derive-debug-opaque-template-instantiation.rs
index ac564a3..9d75e74 100644
--- a/bindgen-tests/tests/expectations/tests/derive-debug-opaque-template-instantiation.rs
+++ b/bindgen-tests/tests/expectations/tests/derive-debug-opaque-template-instantiation.rs
@@ -1,17 +1,16 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
 }
 #[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct Instance {
-    pub val: __BindgenOpaqueArray<u32, 50usize>,
+    pub val: __BindgenOpaqueArray<[u32; 50usize]>,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
 const _: () = {
@@ -19,17 +18,3 @@
     ["Alignment of Instance"][::std::mem::align_of::<Instance>() - 4usize];
     ["Offset of field: Instance::val"][::std::mem::offset_of!(Instance, val) - 0usize];
 };
-impl Default for Instance {
-    fn default() -> Self {
-        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
-        unsafe {
-            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
-            s.assume_init()
-        }
-    }
-}
-impl ::std::fmt::Debug for Instance {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        write!(f, "Instance {{ val: opaque }}")
-    }
-}
diff --git a/bindgen-tests/tests/expectations/tests/derive-debug-opaque.rs b/bindgen-tests/tests/expectations/tests/derive-debug-opaque.rs
index c8b3f10..4ec1fb2 100644
--- a/bindgen-tests/tests/expectations/tests/derive-debug-opaque.rs
+++ b/bindgen-tests/tests/expectations/tests/derive-debug-opaque.rs
@@ -1,29 +1,25 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C)]
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
+    fn default() -> Self {
+        Self([<T as Default>::default(); N])
+    }
+}
 #[repr(C)]
 #[repr(align(4))]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct Opaque {
-    pub _bindgen_opaque_blob: [u32; 41usize],
+    pub _bindgen_opaque_blob: __BindgenOpaqueArray<[u32; 41usize]>,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
 const _: () = {
     ["Size of Opaque"][::std::mem::size_of::<Opaque>() - 164usize];
     ["Alignment of Opaque"][::std::mem::align_of::<Opaque>() - 4usize];
 };
-impl Default for Opaque {
-    fn default() -> Self {
-        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
-        unsafe {
-            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
-            s.assume_init()
-        }
-    }
-}
-impl ::std::fmt::Debug for Opaque {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        write!(f, "Opaque {{ opaque }}")
-    }
-}
 #[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
 pub struct OpaqueUser {
     pub opaque: Opaque,
 }
@@ -35,17 +31,3 @@
         "Offset of field: OpaqueUser::opaque",
     ][::std::mem::offset_of!(OpaqueUser, opaque) - 0usize];
 };
-impl Default for OpaqueUser {
-    fn default() -> Self {
-        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
-        unsafe {
-            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
-            s.assume_init()
-        }
-    }
-}
-impl ::std::fmt::Debug for OpaqueUser {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-        write!(f, "OpaqueUser {{ opaque: {:?} }}", self.opaque)
-    }
-}
diff --git a/bindgen-tests/tests/expectations/tests/issue-3027.rs b/bindgen-tests/tests/expectations/tests/issue-3027.rs
index 757aa9d..c8c2a7e 100644
--- a/bindgen-tests/tests/expectations/tests/issue-3027.rs
+++ b/bindgen-tests/tests/expectations/tests/issue-3027.rs
@@ -1,12 +1,10 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
 #[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
 pub mod root {
-    /// If Bindgen could only determine the size and alignment of a
-    /// type, it is represented like this.
-    #[derive(PartialEq, Copy, Clone, Debug, Hash)]
+    #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
     #[repr(C)]
-    pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-    impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+    pub struct __BindgenOpaqueArray<T>(pub T);
+    impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
         fn default() -> Self {
             Self([<T as Default>::default(); N])
         }
@@ -19,7 +17,7 @@
         #[repr(C)]
         #[derive(Debug, Default, Copy, Clone)]
         pub struct C {
-            pub a: root::__BindgenOpaqueArray<u8, 3usize>,
+            pub a: root::__BindgenOpaqueArray<[u8; 3usize]>,
         }
         #[allow(clippy::unnecessary_operation, clippy::identity_op)]
         const _: () = {
diff --git a/bindgen-tests/tests/expectations/tests/issue-372.rs b/bindgen-tests/tests/expectations/tests/issue-372.rs
index 62160d6..cf00a70 100644
--- a/bindgen-tests/tests/expectations/tests/issue-372.rs
+++ b/bindgen-tests/tests/expectations/tests/issue-372.rs
@@ -1,12 +1,10 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
 #[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
 pub mod root {
-    /// If Bindgen could only determine the size and alignment of a
-    /// type, it is represented like this.
-    #[derive(PartialEq, Copy, Clone, Debug, Hash)]
-    #[repr(C)]
-    pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-    impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+    #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+    #[repr(C, align(8))]
+    pub struct __BindgenOpaqueArray8<T>(pub T);
+    impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
         fn default() -> Self {
             Self([<T as Default>::default(); N])
         }
@@ -74,8 +72,9 @@
         ai = 11,
     }
     #[repr(C)]
+    #[derive(Debug, Default, Copy, Clone)]
     pub struct F {
-        pub w: root::__BindgenOpaqueArray<u64, 33usize>,
+        pub w: root::__BindgenOpaqueArray8<[u8; 264usize]>,
     }
     #[allow(clippy::unnecessary_operation, clippy::identity_op)]
     const _: () = {
@@ -83,13 +82,4 @@
         ["Alignment of F"][::std::mem::align_of::<F>() - 8usize];
         ["Offset of field: F::w"][::std::mem::offset_of!(F, w) - 0usize];
     };
-    impl Default for F {
-        fn default() -> Self {
-            let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
-            unsafe {
-                ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
-                s.assume_init()
-            }
-        }
-    }
 }
diff --git a/bindgen-tests/tests/expectations/tests/issue-544-stylo-creduce-2.rs b/bindgen-tests/tests/expectations/tests/issue-544-stylo-creduce-2.rs
index 8a752f6..a160d02 100644
--- a/bindgen-tests/tests/expectations/tests/issue-544-stylo-creduce-2.rs
+++ b/bindgen-tests/tests/expectations/tests/issue-544-stylo-creduce-2.rs
@@ -1,10 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
@@ -14,7 +12,7 @@
 pub struct Foo {
     pub member: *mut Foo_SecondAlias,
 }
-pub type Foo_FirstAlias = __BindgenOpaqueArray<u8, 0usize>;
+pub type Foo_FirstAlias = __BindgenOpaqueArray<[u8; 0usize]>;
 pub type Foo_SecondAlias = Foo;
 impl Default for Foo {
     fn default() -> Self {
diff --git a/bindgen-tests/tests/expectations/tests/layout_array.rs b/bindgen-tests/tests/expectations/tests/layout_array.rs
index a2805b7..3afe1c2 100644
--- a/bindgen-tests/tests/expectations/tests/layout_array.rs
+++ b/bindgen-tests/tests/expectations/tests/layout_array.rs
@@ -1,4 +1,12 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C, align(8))]
+pub struct __BindgenOpaqueArray8<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
+    fn default() -> Self {
+        Self([<T as Default>::default(); N])
+    }
+}
 pub const RTE_CACHE_LINE_SIZE: u32 = 64;
 pub const RTE_MEMPOOL_OPS_NAMESIZE: u32 = 32;
 pub const RTE_MEMPOOL_MAX_OPS_IDX: u32 = 16;
@@ -125,7 +133,7 @@
     pub sl: rte_spinlock_t,
     ///< Number of used ops structs in the table.
     pub num_ops: u32,
-    pub __bindgen_padding_0: [u64; 7usize],
+    pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 56usize]>,
     /// Storage for all possible ops structs.
     pub ops: [rte_mempool_ops; 16usize],
 }
diff --git a/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs b/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs
index 3be68eb..0ccd40a 100644
--- a/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs
+++ b/bindgen-tests/tests/expectations/tests/layout_large_align_field.rs
@@ -1,4 +1,12 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C, align(8))]
+pub struct __BindgenOpaqueArray8<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
+    fn default() -> Self {
+        Self([<T as Default>::default(); N])
+    }
+}
 #[repr(C)]
 #[derive(Default)]
 pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
@@ -286,7 +294,7 @@
     pub last: *mut ip_frag_pkt,
     ///< LRU list for table entries.
     pub lru: ip_pkt_list,
-    pub __bindgen_padding_0: u64,
+    pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 8usize]>,
     ///< statistics counters.
     pub stat: ip_frag_tbl_stat,
     ///< hash table.
diff --git a/bindgen-tests/tests/expectations/tests/libclang-9/issue-544-stylo-creduce-2.rs b/bindgen-tests/tests/expectations/tests/libclang-9/issue-544-stylo-creduce-2.rs
index 7f0471b..d64a894 100644
--- a/bindgen-tests/tests/expectations/tests/libclang-9/issue-544-stylo-creduce-2.rs
+++ b/bindgen-tests/tests/expectations/tests/libclang-9/issue-544-stylo-creduce-2.rs
@@ -1,10 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
@@ -12,10 +10,10 @@
 #[repr(C)]
 #[derive(Debug, Copy, Clone)]
 pub struct Foo {
-    pub member: *mut __BindgenOpaqueArray<u8, 0usize>,
+    pub member: *mut __BindgenOpaqueArray<[u8; 0usize]>,
 }
-pub type Foo_FirstAlias = __BindgenOpaqueArray<u8, 0usize>;
-pub type Foo_SecondAlias = __BindgenOpaqueArray<u8, 0usize>;
+pub type Foo_FirstAlias = __BindgenOpaqueArray<[u8; 0usize]>;
+pub type Foo_SecondAlias = __BindgenOpaqueArray<[u8; 0usize]>;
 impl Default for Foo {
     fn default() -> Self {
         let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
diff --git a/bindgen-tests/tests/expectations/tests/non-type-params.rs b/bindgen-tests/tests/expectations/tests/non-type-params.rs
index afd21fb..325620b 100644
--- a/bindgen-tests/tests/expectations/tests/non-type-params.rs
+++ b/bindgen-tests/tests/expectations/tests/non-type-params.rs
@@ -1,21 +1,19 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
 }
 pub type Array16 = u8;
-pub type ArrayInt4 = __BindgenOpaqueArray<u32, 4usize>;
+pub type ArrayInt4 = __BindgenOpaqueArray<[u32; 4usize]>;
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct UsesArray {
-    pub array_char_16: __BindgenOpaqueArray<u8, 16usize>,
-    pub array_bool_8: __BindgenOpaqueArray<u8, 8usize>,
+    pub array_char_16: __BindgenOpaqueArray<[u8; 16usize]>,
+    pub array_bool_8: __BindgenOpaqueArray<[u8; 8usize]>,
     pub array_int_4: ArrayInt4,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
diff --git a/bindgen-tests/tests/expectations/tests/nsBaseHashtable.rs b/bindgen-tests/tests/expectations/tests/nsBaseHashtable.rs
index 32fcc37..81b9f00 100644
--- a/bindgen-tests/tests/expectations/tests/nsBaseHashtable.rs
+++ b/bindgen-tests/tests/expectations/tests/nsBaseHashtable.rs
@@ -1,10 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
@@ -24,7 +22,7 @@
 pub struct nsBaseHashtable {
     pub _address: u8,
 }
-pub type nsBaseHashtable_KeyType = __BindgenOpaqueArray<u8, 0usize>;
+pub type nsBaseHashtable_KeyType = __BindgenOpaqueArray<[u8; 0usize]>;
 pub type nsBaseHashtable_EntryType = nsBaseHashtableET;
 #[repr(C)]
 #[derive(Debug, Copy, Clone)]
diff --git a/bindgen-tests/tests/expectations/tests/objc_template.rs b/bindgen-tests/tests/expectations/tests/objc_template.rs
index ee47f67..1c70009 100644
--- a/bindgen-tests/tests/expectations/tests/objc_template.rs
+++ b/bindgen-tests/tests/expectations/tests/objc_template.rs
@@ -3,6 +3,14 @@
 use objc::{self, msg_send, sel, sel_impl, class};
 #[allow(non_camel_case_types)]
 pub type id = *mut objc::runtime::Object;
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C, align(8))]
+pub struct __BindgenOpaqueArray8<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
+    fn default() -> Self {
+        Self([<T as Default>::default(); N])
+    }
+}
 #[repr(transparent)]
 #[derive(Debug, Copy, Clone)]
 pub struct Foo(pub id);
@@ -20,7 +28,7 @@
 }
 impl<ObjectType: 'static> IFoo<ObjectType> for Foo {}
 pub trait IFoo<ObjectType: 'static>: Sized + std::ops::Deref {
-    unsafe fn get(&self) -> u64
+    unsafe fn get(&self) -> __BindgenOpaqueArray8<[u8; 8usize]>
     where
         <Self as std::ops::Deref>::Target: objc::Message + Sized,
     {
@@ -48,7 +56,10 @@
     KeyType: 'static,
     ObjectType: 'static,
 >: Sized + std::ops::Deref {
-    unsafe fn objectForKey_(&self, key: u64) -> u64
+    unsafe fn objectForKey_(
+        &self,
+        key: __BindgenOpaqueArray8<[u8; 8usize]>,
+    ) -> __BindgenOpaqueArray8<[u8; 8usize]>
     where
         <Self as std::ops::Deref>::Target: objc::Message + Sized,
     {
diff --git a/bindgen-tests/tests/expectations/tests/opaque-template-inst-member.rs b/bindgen-tests/tests/expectations/tests/opaque-template-inst-member.rs
index cc02195..f765f01 100644
--- a/bindgen-tests/tests/expectations/tests/opaque-template-inst-member.rs
+++ b/bindgen-tests/tests/expectations/tests/opaque-template-inst-member.rs
@@ -1,10 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
@@ -17,8 +15,9 @@
 /** This should not end up deriving Debug/Hash because its `mBlah` field cannot derive
  Debug/Hash because the instantiation's definition cannot derive Debug/Hash.*/
 #[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct ContainsOpaqueTemplate {
-    pub mBlah: __BindgenOpaqueArray<u32, 101usize>,
+    pub mBlah: __BindgenOpaqueArray<[u32; 101usize]>,
     pub mBaz: ::std::os::raw::c_int,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
@@ -36,25 +35,12 @@
         "Offset of field: ContainsOpaqueTemplate::mBaz",
     ][::std::mem::offset_of!(ContainsOpaqueTemplate, mBaz) - 404usize];
 };
-impl Default for ContainsOpaqueTemplate {
-    fn default() -> Self {
-        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
-        unsafe {
-            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
-            s.assume_init()
-        }
-    }
-}
-impl ::std::cmp::PartialEq for ContainsOpaqueTemplate {
-    fn eq(&self, other: &ContainsOpaqueTemplate) -> bool {
-        self.mBlah == other.mBlah && self.mBaz == other.mBaz
-    }
-}
 /** This should not end up deriving Debug/Hash either, for similar reasons, although
  we're exercising base member edges now.*/
 #[repr(C)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct InheritsOpaqueTemplate {
-    pub _base: __BindgenOpaqueArray<u8, 401usize>,
+    pub _base: __BindgenOpaqueArray<[u8; 401usize]>,
     pub wow: *mut ::std::os::raw::c_char,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
@@ -78,8 +64,3 @@
         }
     }
 }
-impl ::std::cmp::PartialEq for InheritsOpaqueTemplate {
-    fn eq(&self, other: &InheritsOpaqueTemplate) -> bool {
-        self._base == other._base && self.wow == other.wow
-    }
-}
diff --git a/bindgen-tests/tests/expectations/tests/partial-specialization-and-inheritance.rs b/bindgen-tests/tests/expectations/tests/partial-specialization-and-inheritance.rs
index a4234d2..cba5cfb 100644
--- a/bindgen-tests/tests/expectations/tests/partial-specialization-and-inheritance.rs
+++ b/bindgen-tests/tests/expectations/tests/partial-specialization-and-inheritance.rs
@@ -1,10 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
@@ -26,7 +24,7 @@
 }
 unsafe extern "C" {
     #[link_name = "\u{1}_ZN5Usage13static_memberE"]
-    pub static mut Usage_static_member: __BindgenOpaqueArray<u32, 2usize>;
+    pub static mut Usage_static_member: __BindgenOpaqueArray<[u32; 2usize]>;
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
 const _: () = {
diff --git a/bindgen-tests/tests/expectations/tests/size_t_template.rs b/bindgen-tests/tests/expectations/tests/size_t_template.rs
index 9126c50..08ffa22 100644
--- a/bindgen-tests/tests/expectations/tests/size_t_template.rs
+++ b/bindgen-tests/tests/expectations/tests/size_t_template.rs
@@ -1,10 +1,8 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
 #[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+pub struct __BindgenOpaqueArray<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
@@ -12,7 +10,7 @@
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct C {
-    pub arr: __BindgenOpaqueArray<u32, 3usize>,
+    pub arr: __BindgenOpaqueArray<[u32; 3usize]>,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
 const _: () = {
diff --git a/bindgen-tests/tests/expectations/tests/unknown_attr.rs b/bindgen-tests/tests/expectations/tests/unknown_attr.rs
index bdfebb2..d749dad 100644
--- a/bindgen-tests/tests/expectations/tests/unknown_attr.rs
+++ b/bindgen-tests/tests/expectations/tests/unknown_attr.rs
@@ -1,10 +1,18 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C, align(8))]
+pub struct __BindgenOpaqueArray8<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
+    fn default() -> Self {
+        Self([<T as Default>::default(); N])
+    }
+}
 #[repr(C)]
 #[repr(align(16))]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct max_align_t {
     pub __clang_max_align_nonce1: ::std::os::raw::c_longlong,
-    pub __bindgen_padding_0: u64,
+    pub __bindgen_padding_0: __BindgenOpaqueArray8<[u8; 8usize]>,
     pub __clang_max_align_nonce2: ::std::os::raw::c_longlong,
 }
 #[allow(clippy::unnecessary_operation, clippy::identity_op)]
diff --git a/bindgen-tests/tests/expectations/tests/va_list_aarch64_linux.rs b/bindgen-tests/tests/expectations/tests/va_list_aarch64_linux.rs
index 31f75ce..b33f489 100644
--- a/bindgen-tests/tests/expectations/tests/va_list_aarch64_linux.rs
+++ b/bindgen-tests/tests/expectations/tests/va_list_aarch64_linux.rs
@@ -1,18 +1,16 @@
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-/// If Bindgen could only determine the size and alignment of a
-/// type, it is represented like this.
-#[derive(PartialEq, Copy, Clone, Debug, Hash)]
-#[repr(C)]
-pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
+#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+#[repr(C, align(8))]
+pub struct __BindgenOpaqueArray8<T>(pub T);
+impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray8<[T; N]> {
     fn default() -> Self {
         Self([<T as Default>::default(); N])
     }
 }
-pub type va_list = __BindgenOpaqueArray<u64, 4usize>;
+pub type va_list = __BindgenOpaqueArray8<[u8; 32usize]>;
 unsafe extern "C" {
     pub fn vprintf(
         format: *const ::std::os::raw::c_char,
-        vlist: __BindgenOpaqueArray<u64, 4usize>,
+        vlist: __BindgenOpaqueArray8<[u8; 32usize]>,
     ) -> ::std::os::raw::c_int;
 }
diff --git a/bindgen/codegen/helpers.rs b/bindgen/codegen/helpers.rs
index 9b2e525..19338ee 100644
--- a/bindgen/codegen/helpers.rs
+++ b/bindgen/codegen/helpers.rs
@@ -4,6 +4,7 @@
 
 use crate::ir::context::BindgenContext;
 use crate::ir::layout::Layout;
+use crate::ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
 
 pub(crate) mod attributes {
     use proc_macro2::{Ident, Span, TokenStream};
@@ -85,32 +86,33 @@
     layout: Layout,
     ffi_safe: bool,
 ) -> syn::Type {
-    let opaque = layout.opaque();
-
-    // FIXME(emilio, #412): We fall back to byte alignment, but there are
-    // some things that legitimately are more than 8-byte aligned.
-    //
-    // Eventually we should be able to `unwrap` here, but...
-    let ty = opaque.known_rust_type_for_array().unwrap_or_else(|| {
-        warn!("Found unknown alignment on code generation!");
-        syn::parse_quote! { u8 }
-    });
-
-    let data_len = opaque.array_size().unwrap_or(layout.size);
-
-    if data_len == 1 {
-        ty
-    } else if ffi_safe {
-        ctx.generated_opaque_array();
-        if ctx.options().enable_cxx_namespaces {
-            syn::parse_quote! { root::__BindgenOpaqueArray<#ty, #data_len> }
+    let align = layout.align.max(1);
+    // For alignments <= 4, it holds that the integer type of the same size aligns to that same
+    // size. For bigger alignments that's not guaranteed, e.g. on x86 u64 is aligned to 4 bytes.
+    if align <= 4 {
+        let ty = Layout::known_type_for_size(align).unwrap();
+        let len = layout.size / align;
+        return if len == 1 {
+            ty
+        } else if !ffi_safe && len <= RUST_DERIVE_IN_ARRAY_LIMIT {
+            syn::parse_quote! { [#ty; #len] }
         } else {
-            syn::parse_quote! { __BindgenOpaqueArray<#ty, #data_len> }
-        }
+            ctx.generated_opaque_array(1);
+            if ctx.options().enable_cxx_namespaces {
+                syn::parse_quote! { root::__BindgenOpaqueArray<[#ty; #len]> }
+            } else {
+                syn::parse_quote! { __BindgenOpaqueArray<[#ty; #len]> }
+            }
+        };
+    }
+
+    ctx.generated_opaque_array(align);
+    let ident = format_ident!("__BindgenOpaqueArray{}", align);
+    let size = layout.size;
+    if ctx.options().enable_cxx_namespaces {
+        syn::parse_quote! { root::#ident<[u8; #size]> }
     } else {
-        // This is not FFI safe as an argument; the struct above is
-        // preferable.
-        syn::parse_quote! { [ #ty ; #data_len ] }
+        syn::parse_quote! { #ident<[u8; #size]> }
     }
 }
 
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 60e3404..295029e 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -580,9 +580,7 @@
                 if ctx.need_bindgen_complex_type() {
                     utils::prepend_complex_type(&mut *result);
                 }
-                if ctx.need_opaque_array_type() {
-                    utils::prepend_opaque_array_type(&mut *result);
-                }
+                utils::prepend_opaque_array_types(ctx, &mut *result);
                 if result.saw_objc {
                     utils::prepend_objc_header(ctx, &mut *result);
                 }
@@ -2259,14 +2257,13 @@
 
             if has_address {
                 let layout = Layout::new(1, 1);
-                let ty = helpers::blob(ctx, Layout::new(1, 1), false);
                 struct_layout.saw_field_with_layout(
                     "_address",
                     layout,
                     /* offset = */ Some(0),
                 );
                 fields.push(quote! {
-                    pub _address: #ty,
+                    pub _address: u8,
                 });
             }
         }
@@ -2275,7 +2272,6 @@
             match layout {
                 Some(l) => {
                     explicit_align = Some(l.align);
-
                     let ty = helpers::blob(ctx, l, false);
                     fields.push(quote! {
                         pub _bindgen_opaque_blob: #ty ,
@@ -2312,7 +2308,7 @@
                 if !struct_layout.is_rust_union() {
                     let ty = helpers::blob(ctx, layout, false);
                     fields.push(quote! {
-                        pub bindgen_union_field: #ty ,
+                        pub bindgen_union_field: #ty,
                     });
                 }
             }
@@ -5610,23 +5606,37 @@
         result.extend(old_items);
     }
 
-    pub(crate) fn prepend_opaque_array_type(
+    pub(crate) fn prepend_opaque_array_types(
+        ctx: &BindgenContext,
         result: &mut Vec<proc_macro2::TokenStream>,
     ) {
-        let ty = quote! {
-            /// If Bindgen could only determine the size and alignment of a
-            /// type, it is represented like this.
-            #[derive(PartialEq, Copy, Clone, Debug, Hash)]
-            #[repr(C)]
-            pub struct __BindgenOpaqueArray<T: Copy, const N: usize>(pub [T; N]);
-            impl<T: Copy + Default, const N: usize> Default for __BindgenOpaqueArray<T, N> {
-                fn default() -> Self {
-                    Self([<T as Default>::default(); N])
+        let mut tys = vec![];
+        // If Bindgen could only determine the size and alignment of a type, it is represented like
+        // this.
+        for align in ctx.opaque_array_types_needed() {
+            let ident = if align == 1 {
+                format_ident!("__BindgenOpaqueArray")
+            } else {
+                format_ident!("__BindgenOpaqueArray{}", align)
+            };
+            let repr = if align <= 1 {
+                quote! { #[repr(C)] }
+            } else {
+                let explicit = super::helpers::ast_ty::int_expr(align as i64);
+                quote! { #[repr(C, align(#explicit))] }
+            };
+            tys.push(quote! {
+                #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
+                #repr
+                pub struct #ident<T>(pub T);
+                impl<T: Copy + Default, const N: usize> Default for #ident<[T; N]> {
+                    fn default() -> Self {
+                        Self([<T as Default>::default(); N])
+                    }
                 }
-            }
-        };
-
-        result.insert(0, ty);
+            });
+        }
+        result.splice(0..0, tys);
     }
 
     pub(crate) fn build_path(
diff --git a/bindgen/ir/analysis/derive.rs b/bindgen/ir/analysis/derive.rs
index b1f167f..7316950 100644
--- a/bindgen/ir/analysis/derive.rs
+++ b/bindgen/ir/analysis/derive.rs
@@ -179,26 +179,11 @@
                 return CanDerive::No;
             }
 
-            let layout_can_derive =
-                ty.layout(self.ctx).map_or(CanDerive::Yes, |l| {
-                    l.opaque().array_size_within_derive_limit()
-                });
-
-            match layout_can_derive {
-                CanDerive::Yes => {
-                    trace!(
-                        "    we can trivially derive {} for the layout",
-                        self.derive_trait
-                    );
-                }
-                _ => {
-                    trace!(
-                        "    we cannot derive {} for the layout",
-                        self.derive_trait
-                    );
-                }
-            }
-            return layout_can_derive;
+            trace!(
+                "    we can trivially derive {} for the layout",
+                self.derive_trait
+            );
+            return CanDerive::Yes;
         }
 
         match *ty.kind() {
@@ -338,25 +323,11 @@
                             return CanDerive::No;
                         }
 
-                        let layout_can_derive =
-                            ty.layout(self.ctx).map_or(CanDerive::Yes, |l| {
-                                l.opaque().array_size_within_derive_limit()
-                            });
-                        match layout_can_derive {
-                            CanDerive::Yes => {
-                                trace!(
-                                    "    union layout can trivially derive {}",
-                                    self.derive_trait
-                                );
-                            }
-                            _ => {
-                                trace!(
-                                    "    union layout cannot derive {}",
-                                    self.derive_trait
-                                );
-                            }
-                        }
-                        return layout_can_derive;
+                        trace!(
+                            "    union layout can trivially derive {}",
+                            self.derive_trait
+                        );
+                        return CanDerive::Yes;
                     }
                 }
 
diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs
index 47f837f..346d293 100644
--- a/bindgen/ir/context.rs
+++ b/bindgen/ir/context.rs
@@ -387,7 +387,7 @@
     options: BindgenOptions,
 
     /// Whether an opaque array was generated
-    generated_opaque_array: Cell<bool>,
+    generated_opaque_array: RefCell<HashSet<usize>>,
 
     /// Whether a bindgen complex was generated
     generated_bindgen_complex: Cell<bool>,
@@ -596,7 +596,7 @@
             options,
             generated_bindgen_complex: Cell::new(false),
             generated_bindgen_float16: Cell::new(false),
-            generated_opaque_array: Cell::new(false),
+            generated_opaque_array: Default::default(),
             allowlisted: None,
             blocklisted_types_implement_traits: Default::default(),
             codegen_items: None,
@@ -2588,13 +2588,20 @@
     }
 
     /// Call if an opaque array is generated
-    pub(crate) fn generated_opaque_array(&self) {
-        self.generated_opaque_array.set(true);
+    pub(crate) fn generated_opaque_array(&self, align: usize) {
+        self.generated_opaque_array.borrow_mut().insert(align);
     }
 
     /// Whether we need to generate the opaque array type
-    pub(crate) fn need_opaque_array_type(&self) -> bool {
-        self.generated_opaque_array.get()
+    pub(crate) fn opaque_array_types_needed(&self) -> Vec<usize> {
+        let mut alignments = self
+            .generated_opaque_array
+            .borrow()
+            .iter()
+            .copied()
+            .collect::<Vec<_>>();
+        alignments.sort_unstable();
+        alignments
     }
 
     /// Call if a bindgen complex is generated
diff --git a/bindgen/ir/item.rs b/bindgen/ir/item.rs
index d38879f..260c5e8 100644
--- a/bindgen/ir/item.rs
+++ b/bindgen/ir/item.rs
@@ -12,7 +12,6 @@
 use super::dot::DotAttributes;
 use super::function::{Function, FunctionKind};
 use super::item_kind::ItemKind;
-use super::layout::Opaque;
 use super::module::Module;
 use super::template::{AsTemplateParam, TemplateParameters};
 use super::traversal::{EdgeKind, Trace, Tracer};
@@ -451,7 +450,7 @@
         ctx: &mut BindgenContext,
     ) -> TypeId {
         let location = ty.declaration().location();
-        let ty = Opaque::from_clang_ty(ty, ctx);
+        let ty = Type::new_opaque_from_clang_ty(ty, ctx);
         let kind = ItemKind::Type(ty);
         let parent = ctx.root_module().into();
         ctx.add_item(
diff --git a/bindgen/ir/layout.rs b/bindgen/ir/layout.rs
index 905e47c..ba570e3 100644
--- a/bindgen/ir/layout.rs
+++ b/bindgen/ir/layout.rs
@@ -1,10 +1,6 @@
 //! Intermediate representation for the physical layout of some type.
 
-use super::derive::CanDerive;
-use super::ty::{Type, TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
-use crate::clang;
 use crate::ir::context::BindgenContext;
-use std::cmp;
 
 /// A type that represents the struct layout of a type.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -71,56 +67,4 @@
     pub(crate) fn for_size(ctx: &BindgenContext, size: usize) -> Self {
         Self::for_size_internal(ctx.target_pointer_size(), size)
     }
-
-    /// Get this layout as an opaque type.
-    pub(crate) fn opaque(&self) -> Opaque {
-        Opaque(*self)
-    }
-}
-
-/// When we are treating a type as opaque, it is just a blob with a `Layout`.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) struct Opaque(pub(crate) Layout);
-
-impl Opaque {
-    /// Construct a new opaque type from the given clang type.
-    pub(crate) fn from_clang_ty(
-        ty: &clang::Type,
-        ctx: &BindgenContext,
-    ) -> Type {
-        let layout = Layout::new(ty.size(ctx), ty.align(ctx));
-        let ty_kind = TypeKind::Opaque;
-        let is_const = ty.is_const();
-        Type::new(None, Some(layout), ty_kind, is_const)
-    }
-
-    /// Return the known rust type we should use to create a correctly-aligned
-    /// field with this layout.
-    pub(crate) fn known_rust_type_for_array(&self) -> Option<syn::Type> {
-        Layout::known_type_for_size(self.0.align)
-    }
-
-    /// Return the array size that an opaque type for this layout should have if
-    /// we know the correct type for it, or `None` otherwise.
-    pub(crate) fn array_size(&self) -> Option<usize> {
-        if self.known_rust_type_for_array().is_some() {
-            Some(self.0.size / cmp::max(self.0.align, 1))
-        } else {
-            None
-        }
-    }
-
-    /// Return `true` if this opaque layout's array size will fit within the
-    /// maximum number of array elements that Rust allows deriving traits
-    /// with. Return `false` otherwise.
-    pub(crate) fn array_size_within_derive_limit(&self) -> CanDerive {
-        if self
-            .array_size()
-            .is_some_and(|size| size <= RUST_DERIVE_IN_ARRAY_LIMIT)
-        {
-            CanDerive::Yes
-        } else {
-            CanDerive::Manually
-        }
-    }
 }
diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs
index 5819f11..a53de31 100644
--- a/bindgen/ir/ty.rs
+++ b/bindgen/ir/ty.rs
@@ -6,7 +6,7 @@
 use super::enum_ty::Enum;
 use super::function::FunctionSig;
 use super::item::{IsOpaque, Item};
-use super::layout::{Layout, Opaque};
+use super::layout::Layout;
 use super::objc::ObjCInterface;
 use super::template::{
     AsTemplateParam, TemplateInstantiation, TemplateParameters,
@@ -67,6 +67,17 @@
         }
     }
 
+    /// Construct an opaque item from a clang type.
+    pub(crate) fn new_opaque_from_clang_ty(
+        ty: &clang::Type,
+        ctx: &BindgenContext,
+    ) -> Self {
+        let layout = Layout::new(ty.size(ctx), ty.align(ctx));
+        let ty_kind = TypeKind::Opaque;
+        let is_const = ty.is_const();
+        Type::new(None, Some(layout), ty_kind, is_const)
+    }
+
     /// Which kind of type is this?
     pub(crate) fn kind(&self) -> &TypeKind {
         &self.kind
@@ -737,7 +748,7 @@
                  opaque type instead."
             );
             return Ok(ParseResult::New(
-                Opaque::from_clang_ty(&canonical_ty, ctx),
+                Self::new_opaque_from_clang_ty(&canonical_ty, ctx),
                 None,
             ));
         }
@@ -868,7 +879,8 @@
                                          from class template or base \
                                          specifier, using opaque blob"
                                     );
-                                    let opaque = Opaque::from_clang_ty(ty, ctx);
+                                    let opaque =
+                                        Self::new_opaque_from_clang_ty(ty, ctx);
                                     return Ok(ParseResult::New(opaque, None));
                                 }
                             }