From 29fb2ff66f9190d2879c1a5e4c0885cdc8da256f Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Sat, 3 Sep 2022 16:28:10 +0300 Subject: [PATCH] Fix undefined behaviour by removing `MayebUninit` usage (#4) * Experiments with less uninit * Some more experimentation * Some progress on box, ptr, ref (still no mut ref) * Upgraded MSRV to 1.60.0 * Added more examples * Mutable ref with const uninit fn * Clean up const generation * Removed core->alloc dependency for ptr+ref * Removed extraneous Static type alias * Feature `const_ptr_offset_from` stabilised in 1.65.0 * Added back derive attrs for custom PhantomData * Cleaned up some TODOs * Added the ! type * Added the ground attribute * Use power tools in CI * Add miri to CI * Explicit miri component in CI * Removed rust-toolchain * Clarified unsafe uninit() API by constructing MaybeUninit * Added initial inhabited calculation * Implemented uninit() for uninhabited types * Fixed enum + union uninit order with #[layout(bound)] --- .github/workflows/ci.yml | 74 ++- README.md | 9 +- const-type-layout-derive/src/lib.rs | 744 ++++++++++++++++------------ rust-toolchain | 3 - src/impls/alloc/boxed.rs | 32 +- src/impls/core/array.rs | 30 +- src/impls/core/cell.rs | 30 +- src/impls/core/cmp.rs | 17 +- src/impls/core/convert.rs | 10 +- src/impls/core/ffi.rs | 10 +- src/impls/core/marker.rs | 16 +- src/impls/core/mem.rs | 31 +- src/impls/core/num.rs | 27 +- src/impls/core/option.rs | 112 +---- src/impls/core/pin.rs | 19 +- src/impls/core/primitive.rs | 43 +- src/impls/core/ptr.rs | 44 +- src/impls/core/ref.rs | 33 +- src/impls/core/result.rs | 157 ++---- src/impls/core/sync/atomic.rs | 106 +++- src/impls/mod.rs | 27 + src/lib.rs | 354 +++++++++++-- src/ser.rs | 74 ++- try-crate/Cargo.toml | 2 +- try-crate/src/main.rs | 118 ++++- 25 files changed, 1417 insertions(+), 705 deletions(-) delete mode 100644 rust-toolchain diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65ae1e4..3378c45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,21 +30,16 @@ jobs: uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} + profile: minimal override: true - - name: Check without default features - run: | - cargo check --all \ - --no-default-features - - - name: Check with the default features - run: | - cargo check --all + - name: Install power tools + uses: taiki-e/install-action@cargo-hack - - name: Check with all features + - name: Check the powerset run: | - cargo check --all \ - --all-features + cargo hack check --all \ + --feature-powerset --keep-going test: name: Test Suite @@ -62,11 +57,16 @@ jobs: uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} + profile: minimal override: true + + - name: Install power tools + uses: taiki-e/install-action@cargo-hack - - name: Run the test-suite + - name: Run the test-suite powerset run: | - cargo test --workspace --no-fail-fast + cargo hack test --workspace \ + --no-fail-fast --feature-powerset --keep-going fmt: name: Rustfmt @@ -80,6 +80,7 @@ jobs: uses: actions-rs/toolchain@v1 with: toolchain: nightly + profile: minimal components: rustfmt override: true @@ -102,22 +103,49 @@ jobs: uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} + profile: minimal components: clippy override: true - - name: Check the code style without default features + - name: Install power tools + uses: taiki-e/install-action@cargo-hack + + - name: Check the code style powerset run: | - cargo clippy --all \ - --no-default-features \ + cargo hack clippy --all \ + --feature-powerset --keep-going \ -- -D warnings - - name: Check the code style with the default features + miri: + name: Miri + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + rust: [nightly] + + steps: + - name: Checkout the Repository + uses: actions/checkout@v2 + + - name: Install the Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + components: miri, rust-src + override: true + + - name: Install power tools + uses: taiki-e/install-action@cargo-hack + + - name: Run the miri powerset tests run: | - cargo clippy --all \ - -- -D warnings + cargo hack miri test --workspace \ + --no-fail-fast --feature-powerset --keep-going - - name: Check the code style with all features + - name: Run the miri trycrate powerset run: | - cargo clippy --all \ - --all-features \ - -- -D warnings + cd try-crate + cargo hack miri run \ + --feature-powerset --keep-going diff --git a/README.md b/README.md index 4a62d31..e7ecb77 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ -# const-type-layout   [![CI Status]][workflow] [![Rust Doc]][docs] [![License Status]][fossa] [![Code Coverage]][codecov] [![Gitpod Ready-to-Code]][gitpod] +# const-type-layout   [![CI Status]][workflow] [![MSRV]][repo] [![Rust Doc]][docs] [![License Status]][fossa] [![Code Coverage]][codecov] [![Gitpod Ready-to-Code]][gitpod] [CI Status]: https://img.shields.io/github/workflow/status/MomoLangenstein/const-type-layout/CI/main?label=CI [workflow]: https://github.com/MomoLangenstein/const-type-layout/actions/workflows/ci.yml?query=branch%3Amain +[MSRV]: https://img.shields.io/badge/MSRV-1.60.0-orange +[repo]: https://github.com/ron-rs/ron + [Rust Doc]: https://img.shields.io/badge/docs-main-blue [docs]: https://momolangenstein.github.io/const-type-layout/const_type_layout @@ -32,7 +35,7 @@ The layout of types is only defined if they're `#[repr(C)]`. This crate works on non-`#[repr(C)]` types, but their layout is unpredictable. ```rust -use type_layout::TypeLayout; +use const_type_layout::TypeLayout; #[derive(TypeLayout)] #[repr(C)] @@ -70,7 +73,7 @@ Over-aligned types have trailing padding, which can be a source of bugs in some FFI scenarios: ```rust -use type_layout::TypeLayout; +use const_type_layout::TypeLayout; #[derive(TypeLayout)] #[repr(C, align(128))] diff --git a/const-type-layout-derive/src/lib.rs b/const-type-layout-derive/src/lib.rs index b73d68b..bb008b1 100644 --- a/const-type-layout-derive/src/lib.rs +++ b/const-type-layout-derive/src/lib.rs @@ -1,5 +1,6 @@ #![deny(clippy::pedantic)] #![feature(iter_intersperse)] +#![feature(let_else)] extern crate proc_macro; @@ -26,14 +27,20 @@ pub fn derive_type_layout(input: TokenStream) -> TokenStream { let ty_name = input.ident; let ty_generics = input.generics.split_for_impl().1; + let mut type_params = input + .generics + .type_params() + .map(|param| ¶m.ident) + .collect::>(); + let Attributes { reprs, - add_bounds, - sub_bounds, - } = parse_attributes(&ty_name, &ty_generics, &input.attrs); + extra_bounds, + ground, + } = parse_attributes(&input.attrs, &mut type_params, &input.data); - let mut consts = Vec::new(); - let layout = layout_of_type(&ty_name, &ty_generics, &input.data, &reprs, &mut consts); + let layout = layout_of_type(&ty_name, &ty_generics, &input.data, &reprs); + let uninit = uninit_for_type(&ty_name, &input.data, &ground); let inner_types = extract_inner_types(&input.data); @@ -45,9 +52,8 @@ pub fn derive_type_layout(input: TokenStream) -> TokenStream { &ty_generics, &input.generics, matches!(input.data, syn::Data::Enum(_)), - &inner_types, - add_bounds, - &sub_bounds, + &extra_bounds, + &type_params, ); let (type_layout_impl_generics, type_layout_ty_generics, type_layout_where_clause) = type_layout_input_generics.split_for_impl(); @@ -55,7 +61,7 @@ pub fn derive_type_layout(input: TokenStream) -> TokenStream { type_graph_input_generics.split_for_impl(); quote! { - unsafe impl #type_layout_impl_generics ::const_type_layout::TypeLayout for + unsafe impl #type_layout_impl_generics const ::const_type_layout::TypeLayout for #ty_name #type_layout_ty_generics #type_layout_where_clause { const TYPE_LAYOUT: ::const_type_layout::TypeLayoutInfo<'static> = { @@ -66,11 +72,13 @@ pub fn derive_type_layout(input: TokenStream) -> TokenStream { structure: #layout, } }; - } - #(impl #type_layout_impl_generics #ty_name #type_layout_ty_generics #type_layout_where_clause { - #consts - })* + unsafe fn uninit() -> ::const_type_layout::MaybeUninhabited< + ::core::mem::MaybeUninit + > { + #uninit + } + } unsafe impl #type_graph_impl_generics const ::const_type_layout::TypeGraph for #ty_name #type_graph_ty_generics #type_graph_where_clause @@ -81,25 +89,55 @@ pub fn derive_type_layout(input: TokenStream) -> TokenStream { } } } - }.into() + } + .into() } struct Attributes { reprs: String, - add_bounds: Vec, - sub_bounds: Vec, + extra_bounds: Vec, + ground: Vec, } +#[allow(clippy::too_many_lines)] fn parse_attributes( - ty_name: &syn::Ident, - ty_generics: &syn::TypeGenerics, attrs: &[syn::Attribute], + type_params: &mut Vec<&syn::Ident>, + data: &syn::Data, ) -> Attributes { // Could parse based on https://github.com/rust-lang/rust/blob/d13e8dd41d44a73664943169d5b7fe39b22c449f/compiler/rustc_attr/src/builtin.rs#L772-L781 instead let mut reprs = Vec::new(); - let mut add_bounds: Vec = Vec::new(); - let mut sub_bounds: Vec = vec![quote!(#ty_name #ty_generics).to_string()]; + let mut extra_bounds: Vec = Vec::new(); + + let mut ground = match data { + syn::Data::Struct(_) => Vec::new(), + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let mut ground = Vec::with_capacity(variants.len()); + + for variant in variants { + if matches!(variant.fields, syn::Fields::Unit) { + ground.push(variant.ident.clone()); + } + } + + for variant in variants { + if !matches!(variant.fields, syn::Fields::Unit) { + ground.push(variant.ident.clone()); + } + } + + ground + }, + syn::Data::Union(syn::DataUnion { + fields: syn::FieldsNamed { named: fields, .. }, + .. + }) => fields + .iter() + .map(|field| field.ident.clone().unwrap()) + .collect(), + }; + let mut groundier = Vec::with_capacity(ground.len()); for attr in attrs { if attr.path.is_ident("repr") { @@ -126,8 +164,21 @@ fn parse_attributes( })) = &meta { if path.is_ident("free") { - match syn::parse_str::(&s.value()) { - Ok(ty) => sub_bounds.push(quote!(#ty).to_string()), + match syn::parse_str::(&s.value()) { + Ok(param) => { + if let Some(i) = type_params.iter().position(|ty| **ty == param) + { + type_params.swap_remove(i); + } else { + emit_error!( + s.span(), + "[const-type-layout]: Invalid #[layout(free)] \ + attribute: \"{}\" is either not a type parameter or \ + has already been freed (duplicate attribute).", + param, + ); + } + }, Err(err) => emit_error!( s.span(), "[const-type-layout]: Invalid #[layout(free = \"\")] \ @@ -137,7 +188,7 @@ fn parse_attributes( } } else if path.is_ident("bound") { match syn::parse_str(&s.value()) { - Ok(bound) => add_bounds.push(bound), + Ok(bound) => extra_bounds.push(bound), Err(err) => emit_error!( s.span(), "[const-type-layout]: Invalid #[layout(bound = \ @@ -145,10 +196,63 @@ fn parse_attributes( err ), } + } else if path.is_ident("ground") { + match syn::parse_str(&s.value()) { + Ok(g) => match data { + syn::Data::Struct(_) => emit_error!( + path.span(), + "[const-type-layout]: Invalid #[layout(ground)] \ + attribute: structs do not have a ground layout." + ), + syn::Data::Union(_) | syn::Data::Enum(_) => { + let g: syn::Ident = g; + + if let Some(i) = ground.iter().position(|e| e == &g) { + let g = ground.remove(i); + groundier.push(g); + } else if groundier.contains(&g) { + emit_error!( + path.span(), + "[const-type-layout]: Duplicate #[layout(ground = \ + \"{}\")] attribute.", + g + ); + } else { + emit_error!( + path.span(), + "[const-type-layout]: Invalid #[layout(ground)] \ + attribute: \"{}\" is not a {} in this {}.", + g, + match data { + syn::Data::Enum(_) => "variant", + syn::Data::Struct(_) | syn::Data::Union(_) => + "field", + }, + match data { + syn::Data::Enum(_) => "enum", + syn::Data::Struct(_) | syn::Data::Union(_) => + "union", + }, + ); + } + }, + }, + Err(err) => emit_error!( + s.span(), + "[const-type-layout]: Invalid #[layout(ground = \"{}\")] \ + attribute: {}.", + match data { + syn::Data::Enum(_) => "variant", + syn::Data::Struct(_) | syn::Data::Union(_) => "field", + }, + err + ), + } } else { emit_error!( path.span(), - "[const-type-layout]: Unknown attribute, use `free` or `bound`." + "[const-type-layout]: Unknown attribute, use `bound`, `free`, or \ + `ground`." ); } } else { @@ -177,10 +281,12 @@ fn parse_attributes( .intersperse(String::from(",")) .collect::(); + groundier.extend(ground); + Attributes { reprs, - add_bounds, - sub_bounds, + extra_bounds, + ground: groundier, } } @@ -255,11 +361,11 @@ fn generate_generics( ty_generics: &syn::TypeGenerics, generics: &syn::Generics, is_enum: bool, - inner_types: &[&syn::Type], - add_bounds: Vec, - sub_bounds: &[String], + extra_bounds: &[syn::WherePredicate], + type_params: &[&syn::Ident], ) -> Generics { let mut type_layout_input_generics = generics.clone(); + let mut type_graph_input_generics = generics.clone(); if is_enum { type_layout_input_generics @@ -268,11 +374,7 @@ fn generate_generics( .push(syn::parse_quote! { [u8; ::core::mem::size_of::<::core::mem::Discriminant<#ty_name #ty_generics>>()]: }); - } - - let mut type_graph_input_generics = generics.clone(); - if is_enum { type_graph_input_generics .make_where_clause() .predicates @@ -281,33 +383,39 @@ fn generate_generics( }); } - for ty in inner_types { - if !sub_bounds.contains("e!(#ty).to_string()) { - type_layout_input_generics - .make_where_clause() - .predicates - .push(syn::parse_quote! { - #ty: ::const_type_layout::TypeLayout - }); - - type_graph_input_generics - .make_where_clause() - .predicates - .push(syn::parse_quote! { - #ty: ~const ::const_type_layout::TypeGraph - }); - } + for ty in type_params { + type_layout_input_generics + .make_where_clause() + .predicates + .push(syn::parse_quote! { + #ty: ~const ::const_type_layout::TypeLayout + }); + + type_graph_input_generics + .make_where_clause() + .predicates + .push(syn::parse_quote! { + #ty: ~const ::const_type_layout::TypeLayout + }); + + type_graph_input_generics + .make_where_clause() + .predicates + .push(syn::parse_quote! { + #ty: ~const ::const_type_layout::TypeGraph + }); } - for bound in add_bounds { + for bound in extra_bounds { type_layout_input_generics .make_where_clause() .predicates .push(bound.clone()); + type_graph_input_generics .make_where_clause() .predicates - .push(bound); + .push(bound.clone()); } Generics { @@ -321,47 +429,32 @@ fn layout_of_type( ty_generics: &syn::TypeGenerics, data: &syn::Data, reprs: &str, - consts: &mut Vec, ) -> proc_macro2::TokenStream { match data { syn::Data::Struct(data) => { - let fields = quote_fields( - ty_name, - None, - "e_structlike_fields(ty_name, ty_generics, &data.fields, false), - consts, - ); + let fields = quote_structlike_fields(ty_name, ty_generics, &data.fields, false); quote! { - ::const_type_layout::TypeStructure::Struct { repr: #reprs, fields: #fields } + ::const_type_layout::TypeStructure::Struct { repr: #reprs, fields: &[#(#fields),*] } } }, syn::Data::Enum(r#enum) => { - let variants = quote_variants( - ty_name, - "e_enum_variants(ty_name, ty_generics, r#enum, consts), - consts, - ); + let variants = quote_enum_variants(ty_name, ty_generics, r#enum); quote! { - ::const_type_layout::TypeStructure::Enum { repr: #reprs, variants: #variants } + ::const_type_layout::TypeStructure::Enum { repr: #reprs, variants: &[#(#variants),*] } } }, syn::Data::Union(union) => { - let fields = quote_fields( + let fields = quote_structlike_fields( ty_name, - None, - "e_structlike_fields( - ty_name, - ty_generics, - &syn::Fields::Named(union.fields.clone()), - true, - ), - consts, + ty_generics, + &syn::Fields::Named(union.fields.clone()), + true, ); quote! { - ::const_type_layout::TypeStructure::Union { repr: #reprs, fields: #fields } + ::const_type_layout::TypeStructure::Union { repr: #reprs, fields: &[#(#fields),*] } } }, } @@ -426,52 +519,14 @@ fn quote_structlike_field_offset( let extra_fields = if is_union { quote!() } else { quote!(..) }; quote! { - let uninit = ::core::mem::MaybeUninit::<#ty_name #ty_generics>::uninit(); - let base_ptr: *const #ty_name #ty_generics = uninit.as_ptr(); - - #[allow(clippy::unneeded_field_pattern)] - let #ty_name { #field_name: _, #extra_fields }: #ty_name #ty_generics; - - #[allow(unused_unsafe)] - let field_ptr = unsafe { - ::core::ptr::addr_of!((*base_ptr).#field_name) - }; - - #[allow(clippy::cast_sign_loss)] - unsafe { field_ptr.cast::().offset_from(base_ptr.cast()) as usize } + ::const_type_layout::struct_field_offset!(#ty_name => #ty_name #ty_generics => (*base_ptr).#field_name => #extra_fields) } } -fn quote_fields( - ty_name: &syn::Ident, - qualifier: Option<&syn::Ident>, - fields: &[proc_macro2::TokenStream], - consts: &mut Vec, -) -> proc_macro2::TokenStream { - let fields_len = fields.len(); - - let ident = syn::Ident::new( - &(if let Some(qualifier) = qualifier { - format!("__{}_{}_fields", ty_name, qualifier) - } else { - format!("__{}_fields", ty_name) - }) - .to_uppercase(), - ty_name.span(), - ); - - consts.push(quote! { - const #ident: &'static [::const_type_layout::Field<'static>; #fields_len] = &[#(#fields),*]; - }); - - quote! { Self :: #ident } -} - fn quote_enum_variants( ty_name: &syn::Ident, ty_generics: &syn::TypeGenerics, r#enum: &syn::DataEnum, - consts: &mut Vec, ) -> Vec { r#enum .variants @@ -480,190 +535,87 @@ fn quote_enum_variants( let variant_name = &variant.ident; let variant_name_str = Literal::string(&variant_name.to_string()); - let variant_constructor = - quote_variant_constructor(ty_name, variant_name, &variant.fields); - let variant_destructor = - quote_variant_destructor(ty_name, variant_name, &variant.fields); + let fields = quote_variant_fields(ty_name, ty_generics, variant_name, &variant.fields); - let fields = quote_fields( - ty_name, - Some(variant_name), - "e_variant_fields( - ty_name, - ty_generics, - &variant.fields, - &variant_constructor, - &variant_destructor, - ), - consts, - ); - - let discriminant_bytes = quote_discriminant_bytes( - ty_name, - ty_generics, - variant_name, - &variant_constructor, - consts, - ); + let discriminant = + quote_discriminant(ty_name, ty_generics, variant_name, &variant.fields); quote! { ::const_type_layout::Variant { name: #variant_name_str, - discriminant: ::const_type_layout::Discriminant { - big_endian_bytes: &#discriminant_bytes, - }, - fields: #fields, + discriminant: #discriminant, + fields: &[#(#fields),*], } } }) .collect::>() } -fn quote_variant_constructor( - ty_name: &syn::Ident, - variant_name: &syn::Ident, - variant_fields: &syn::Fields, -) -> proc_macro2::TokenStream { - match variant_fields { - syn::Fields::Unit => quote! { #ty_name::#variant_name }, - syn::Fields::Unnamed(fields) => { - let initialisers = fields.unnamed.iter().map(|field| { - let field_ty = &field.ty; - - quote! { unsafe { - let mut value: ::core::mem::MaybeUninit<#field_ty> = ::core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::<#field_ty>() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - value.assume_init() - } } - }).collect::>(); - - quote! { #ty_name::#variant_name(#(#initialisers),*) } - }, - syn::Fields::Named(fields) => { - let initialisers = fields.named.iter().map(|field| { - let field_name = field.ident.as_ref().unwrap(); - let field_ty = &field.ty; - - quote! { #field_name: unsafe { - let mut value: ::core::mem::MaybeUninit<#field_ty> = ::core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::<#field_ty>() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - value.assume_init() - } } - }).collect::>(); - - quote! { #ty_name::#variant_name { #(#initialisers),* } } - }, - } -} - -fn quote_variant_destructor( +fn quote_variant_fields( ty_name: &syn::Ident, + ty_generics: &syn::TypeGenerics, variant_name: &syn::Ident, variant_fields: &syn::Fields, -) -> proc_macro2::TokenStream { +) -> Vec { match variant_fields { - syn::Fields::Unit => quote! { #ty_name::#variant_name }, - syn::Fields::Unnamed(fields) => { - let destructors = fields - .unnamed - .iter() - .enumerate() - .map(|(field_index, _)| { - let field_name = quote::format_ident!("__self_{}", field_index); - - quote! { #field_name } - }) - .collect::>(); - - quote! { #ty_name::#variant_name(#(#destructors),*) } - }, - syn::Fields::Named(fields) => { - let destructors = fields - .named - .iter() - .map(|field| { - let field_name = field.ident.as_ref().unwrap(); + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) => { + let field_descriptors = fields + .pairs() + .map(|pair| { + let syn::Field { + ident: field_name, + colon_token: field_colon, + ty: field_ty, + .. + } = pair.value(); + let field_comma = pair.punct(); - quote! { #field_name } + quote!(#field_name #field_colon #field_ty #field_comma) }) .collect::>(); - quote! { #ty_name::#variant_name { #(#destructors),* } } - }, - } -} - -fn quote_variant_fields( - ty_name: &syn::Ident, - ty_generics: &syn::TypeGenerics, - variant_fields: &syn::Fields, - variant_constructor: &proc_macro2::TokenStream, - variant_destructor: &proc_macro2::TokenStream, -) -> Vec { - match variant_fields { - syn::Fields::Named(fields) => { - fields.named.iter().map(|field| { - let field_name = field.ident.as_ref().unwrap(); - let field_name_str = Literal::string(&field_name.to_string()); + fields.iter().map(|field| { + let field_name_str = Literal::string(&field.ident.as_ref().unwrap().to_string()); + let field_index = &field.ident; let field_ty = &field.ty; quote_spanned! { field.span() => ::const_type_layout::Field { name: #field_name_str, - offset: { - let __variant_base: ::core::mem::MaybeUninit<#ty_name #ty_generics> = ::core::mem::MaybeUninit::new(#variant_constructor); - - #[allow( - unused_variables, unreachable_patterns, clippy::cast_sign_loss, - clippy::match_wildcard_for_single_variants - )] - match unsafe { __variant_base.assume_init_ref() } { - #variant_destructor => unsafe { - (#field_name as *const #field_ty).cast::().offset_from(__variant_base.as_ptr().cast()) as usize - }, - _ => unreachable!(), - } - }, + offset: ::const_type_layout::struct_variant_field_offset!( + #ty_name => #ty_name #ty_generics => #variant_name { #(#field_descriptors)* } => #field_index + ), ty: ::core::any::type_name::<#field_ty>(), } } }).collect() }, - syn::Fields::Unnamed(fields) => { - fields.unnamed.iter().enumerate().map(|(field_index, field)| { - let field_name = quote::format_ident!("__self_{}", field_index); + syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => { + let field_descriptors = fields + .pairs() + .enumerate() + .map(|(i, pair)| { + let syn::Field { ty: field_ty, .. } = pair.value(); + let field_name = quote::format_ident!("f_{}", i); + let field_comma = pair.punct(); + + quote!(#field_name: #field_ty #field_comma) + }) + .collect::>(); + + fields.iter().enumerate().map(|(field_index, field)| { let field_name_str = Literal::string(&field_index.to_string()); + let field_index = syn::Index::from(field_index); let field_ty = &field.ty; quote_spanned! { field.span() => ::const_type_layout::Field { name: #field_name_str, - offset: { - let __variant_base: ::core::mem::MaybeUninit<#ty_name #ty_generics> = ::core::mem::MaybeUninit::new(#variant_constructor); - - #[allow( - unused_variables, unreachable_patterns, clippy::cast_sign_loss, - clippy::match_wildcard_for_single_variants - )] - match unsafe { __variant_base.assume_init_ref() } { - #variant_destructor => unsafe { - (#field_name as *const #field_ty).cast::().offset_from(__variant_base.as_ptr().cast()) as usize - }, - _ => unreachable!(), - } - }, + offset: ::const_type_layout::struct_variant_field_offset!( + #ty_name => #ty_name #ty_generics => #variant_name(#(#field_descriptors)*) => #field_index + ), ty: ::core::any::type_name::<#field_ty>(), } } @@ -673,62 +625,244 @@ fn quote_variant_fields( } } -fn quote_discriminant_bytes( +fn quote_discriminant( ty_name: &syn::Ident, ty_generics: &syn::TypeGenerics, variant_name: &syn::Ident, - variant_constructor: &proc_macro2::TokenStream, - consts: &mut Vec, + variant_fields: &syn::Fields, ) -> proc_macro2::TokenStream { - let ident = syn::Ident::new( - &format!("__{}_{}_discriminant", ty_name, variant_name).to_uppercase(), - ty_name.span(), - ); - - consts.push(quote! { - const #ident: [u8; ::core::mem::size_of::<::core::mem::Discriminant<#ty_name #ty_generics>>()] = unsafe { - let variant: ::core::mem::MaybeUninit<#ty_name #ty_generics> = ::core::mem::MaybeUninit::new(#variant_constructor); - - let system_endian_bytes: [u8; ::core::mem::size_of::<::core::mem::Discriminant<#ty_name #ty_generics>>()] = ::core::mem::transmute( - ::core::mem::discriminant(variant.assume_init_ref()) - ); - - let mut big_endian_bytes = [0_u8; ::core::mem::size_of::<::core::mem::Discriminant<#ty_name #ty_generics>>()]; + let variant_descriptor = match variant_fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) => { + let field_descriptors = fields + .pairs() + .map(|pair| { + let syn::Field { + ident: field_name, + colon_token: field_colon, + ty: field_ty, + .. + } = pair.value(); + let field_comma = pair.punct(); - let mut i = 0; + quote!(#field_name #field_colon #field_ty #field_comma) + }) + .collect::>(); - while i < system_endian_bytes.len() { - big_endian_bytes[i] = system_endian_bytes[if cfg!(target_endian = "big") { - i - } else /* cfg!(target_endian = "little") */ { - system_endian_bytes.len() - i - 1 - }]; + quote!(#variant_name { #(#field_descriptors)* }) + }, + syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => { + let field_descriptors = fields + .pairs() + .enumerate() + .map(|(i, pair)| { + let syn::Field { ty: field_ty, .. } = pair.value(); + let field_name = quote::format_ident!("f_{}", i); + let field_comma = pair.punct(); - i += 1; - } + quote!(#field_name: #field_ty #field_comma) + }) + .collect::>(); - big_endian_bytes - }; - }); + quote!(#variant_name(#(#field_descriptors)*)) + }, + syn::Fields::Unit => quote!(#variant_name), + }; - quote! { Self :: #ident } + quote! { + ::const_type_layout::struct_variant_discriminant!( + #ty_name => #ty_name #ty_generics => #variant_descriptor + ) + } } -fn quote_variants( +#[allow(clippy::too_many_lines)] +fn uninit_for_type( ty_name: &syn::Ident, - variants: &[proc_macro2::TokenStream], - consts: &mut Vec, + data: &syn::Data, + ground: &[syn::Ident], ) -> proc_macro2::TokenStream { - let variants_len = variants.len(); + match data { + syn::Data::Struct(data) => { + // Structs are uninhabited if any of their fields in uninhabited + + let fields = match &data.fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) + | syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => fields, + syn::Fields::Unit => { + return quote! { + ::const_type_layout::MaybeUninhabited::Inhabited( + ::core::mem::MaybeUninit::new(#ty_name) + ) + } + }, + }; - let ident = syn::Ident::new( - &format!("__{}_variants", ty_name).to_uppercase(), - ty_name.span(), - ); + let field_names = fields + .iter() + .enumerate() + .map(|(i, field)| match &field.ident { + Some(name) => name.clone(), + None => quote::format_ident!("f_{}", i), + }) + .collect::>(); - consts.push(quote! { - const #ident: &'static [::const_type_layout::Variant<'static>; #variants_len] = &[#(#variants),*]; - }); + let field_initialisers = fields + .iter() + .map(|syn::Field { ty: field_ty, .. }| { + quote! { + < + #field_ty as ::const_type_layout::TypeLayout + >::uninit() + } + }) + .collect::>(); - quote! { Self :: #ident } + let struct_initialiser = if let syn::Fields::Named(_) = &data.fields { + quote!(#ty_name { #(#field_names: #field_names.assume_init()),* }) + } else { + quote!(#ty_name ( #(#field_names.assume_init()),* )) + }; + + quote! { + if let ( + #(::const_type_layout::MaybeUninhabited::Inhabited(#field_names)),* + ) = ( + #(#field_initialisers),* + ) { + ::const_type_layout::MaybeUninhabited::Inhabited( + ::core::mem::MaybeUninit::new( + #struct_initialiser + ) + ) + } else { + ::const_type_layout::MaybeUninhabited::Uninhabited + } + } + }, + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + // Enums are uninhabited if + // (a) they have no variants + // (b) all their variants are uninhabited + // (1) unit variants are always inhabited + // (2) tuple and struct variants are uninhabited + // if any of their fields are uninhabited + + let variant_initialisers = ground.iter().filter_map(|g| variants.iter().find(|v| &v.ident == g)).map(|syn::Variant { + ident: variant_name, + fields: variant_fields, + .. + }| { + let fields = match variant_fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) + | syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => fields, + syn::Fields::Unit => return Err(quote! { + ::const_type_layout::MaybeUninhabited::Inhabited( + ::core::mem::MaybeUninit::new( + #ty_name :: #variant_name + ) + ) + }), + }; + + let field_names = fields.iter().enumerate().map(|(i, field)| match &field.ident { + Some(name) => name.clone(), + None => quote::format_ident!("f_{}", i), + }).collect::>(); + + let field_initialisers = fields + .iter() + .map(|syn::Field { + ty: field_ty, + .. + }| { + quote! { + < + #field_ty as ::const_type_layout::TypeLayout + >::uninit() + } + }) + .collect::>(); + + let variant_initialiser = if let syn::Fields::Named(_) = variant_fields { + quote!(#ty_name :: #variant_name { #(#field_names: #field_names.assume_init()),* }) + } else { + quote!(#ty_name :: #variant_name ( #(#field_names.assume_init()),* )) + }; + + Ok(quote!{ + if let ( + #(::const_type_layout::MaybeUninhabited::Inhabited(#field_names)),* + ) = ( + #(#field_initialisers),* + ) { + return ::const_type_layout::MaybeUninhabited::Inhabited( + ::core::mem::MaybeUninit::new( + #variant_initialiser + ) + ); + } + }) + }).collect::, proc_macro2::TokenStream>>(); + + match variant_initialisers { + Ok(variant_initialisers) => quote! { + #( + #variant_initialisers + )* + + ::const_type_layout::MaybeUninhabited::Uninhabited + }, + Err(unit_variant_initialiser) => unit_variant_initialiser, + } + }, + syn::Data::Union(syn::DataUnion { + fields: syn::FieldsNamed { named: fields, .. }, + .. + }) => { + // Unions are uninhabited if all fields are uninhabited + + let (field_names, field_initialisers) = ground + .iter() + .filter_map(|g| fields.iter().find(|f| f.ident.as_ref() == Some(g))) + .map( + |syn::Field { + ident: field_name, + ty: field_ty, + .. + }| { + ( + field_name, + quote! { + < + #field_ty as ::const_type_layout::TypeLayout + >::uninit() + }, + ) + }, + ) + .unzip::<_, _, Vec<_>, Vec<_>>(); + + quote! { + #( + if let ::const_type_layout::MaybeUninhabited::Inhabited( + #field_names + ) = #field_initialisers { + return ::const_type_layout::MaybeUninhabited::Inhabited( + ::core::mem::MaybeUninit::new( + #ty_name { #field_names: #field_names.assume_init() } + ) + ); + } + )* + + ::const_type_layout::MaybeUninhabited::Uninhabited + } + }, + } } diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index a6f7ffe..0000000 --- a/rust-toolchain +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -channel = "nightly" -components = [ "cargo", "rustfmt", "clippy" ] diff --git a/src/impls/alloc/boxed.rs b/src/impls/alloc/boxed.rs index aea164a..eea0625 100644 --- a/src/impls/alloc/boxed.rs +++ b/src/impls/alloc/boxed.rs @@ -1,15 +1,29 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + impls::leak_uninit_ptr, MaybeUninhabited, Mutability, TypeGraph, TypeLayout, TypeLayoutGraph, + TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for alloc::boxed::Box { +unsafe impl const TypeLayout for alloc::boxed::Box { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Pointer { inner: ::core::any::type_name::(), - mutability: true, + mutability: Mutability::Mutable, }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + if let MaybeUninhabited::Uninhabited = ::uninit() { + return MaybeUninhabited::Uninhabited; + } + + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(alloc::boxed::Box::from_raw_in( + leak_uninit_ptr(), + alloc::alloc::Global, + ))) + } } unsafe impl const TypeGraph for alloc::boxed::Box { @@ -20,16 +34,24 @@ unsafe impl const TypeGraph for alloc::boxed::Box { } } -unsafe impl TypeLayout for alloc::boxed::Box<[T]> { +unsafe impl const TypeLayout for alloc::boxed::Box<[T]> { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Pointer { inner: ::core::any::type_name::(), - mutability: true, + mutability: Mutability::Mutable, }, }; + + #[allow(clippy::borrow_as_ptr)] + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(alloc::boxed::Box::from_raw_in( + &[] as *const [T] as *mut _, + alloc::alloc::Global, + ))) + } } unsafe impl const TypeGraph for alloc::boxed::Box<[T]> { diff --git a/src/impls/core/array.rs b/src/impls/core/array.rs index 71360f0..c2a9eaf 100644 --- a/src/impls/core/array.rs +++ b/src/impls/core/array.rs @@ -1,6 +1,8 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for [T; N] { +unsafe impl const TypeLayout for [T; N] { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -10,6 +12,30 @@ unsafe impl TypeLayout for [T; N] { len: N, }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + let mut uninit_array: [core::mem::MaybeUninit; N] = + core::mem::MaybeUninit::uninit_array(); + + let mut i = 0; + + while i < N { + uninit_array[i] = match ::uninit() { + MaybeUninhabited::Uninhabited => { + core::mem::forget(uninit_array); + + return MaybeUninhabited::Uninhabited; + }, + MaybeUninhabited::Inhabited(uninit) => uninit, + }; + + i += 1; + } + + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new( + core::mem::MaybeUninit::array_assume_init(uninit_array), + )) + } } unsafe impl const TypeGraph for [T; N] { diff --git a/src/impls/core/cell.rs b/src/impls/core/cell.rs index 0d4ede1..e289e1c 100644 --- a/src/impls/core/cell.rs +++ b/src/impls/core/cell.rs @@ -1,6 +1,8 @@ -use crate::{Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for core::cell::UnsafeCell { +unsafe impl const TypeLayout for core::cell::UnsafeCell { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -9,11 +11,20 @@ unsafe impl TypeLayout for core::cell::UnsafeCell { repr: "no_nieche,transparent", fields: &[Field { name: "value", - offset: 0, + offset: unsafe { ::uninit() }.map(0), ty: ::core::any::type_name::(), }], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + match ::uninit() { + MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited, + MaybeUninhabited::Inhabited(uninit) => MaybeUninhabited::Inhabited( + core::mem::MaybeUninit::new(Self::new(uninit.assume_init())), + ), + } + } } unsafe impl const TypeGraph for core::cell::UnsafeCell { @@ -24,7 +35,7 @@ unsafe impl const TypeGraph for core::cell::UnsafeCell { } } -unsafe impl TypeLayout for core::cell::Cell { +unsafe impl const TypeLayout for core::cell::Cell { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -33,11 +44,20 @@ unsafe impl TypeLayout for core::cell::Cell { repr: "transparent", fields: &[Field { name: "value", - offset: 0, + offset: unsafe { ::uninit() }.map(0), ty: ::core::any::type_name::>(), }], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + match ::uninit() { + MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited, + MaybeUninhabited::Inhabited(uninit) => MaybeUninhabited::Inhabited( + core::mem::MaybeUninit::new(Self::new(uninit.assume_init())), + ), + } + } } unsafe impl const TypeGraph for core::cell::Cell { diff --git a/src/impls/core/cmp.rs b/src/impls/core/cmp.rs index f629079..f95de07 100644 --- a/src/impls/core/cmp.rs +++ b/src/impls/core/cmp.rs @@ -1,6 +1,8 @@ -use crate::{Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for core::cmp::Reverse { +unsafe impl const TypeLayout for core::cmp::Reverse { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -9,11 +11,20 @@ unsafe impl TypeLayout for core::cmp::Reverse { repr: "transparent", fields: &[Field { name: "0", - offset: 0, + offset: unsafe { ::uninit() }.map(0), ty: ::core::any::type_name::(), }], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + match ::uninit() { + MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited, + MaybeUninhabited::Inhabited(uninit) => { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(Self(uninit.assume_init()))) + }, + } + } } unsafe impl const TypeGraph for core::cmp::Reverse { diff --git a/src/impls/core/convert.rs b/src/impls/core/convert.rs index b081a78..677c361 100644 --- a/src/impls/core/convert.rs +++ b/src/impls/core/convert.rs @@ -1,6 +1,8 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for core::convert::Infallible { +unsafe impl const TypeLayout for core::convert::Infallible { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -10,6 +12,10 @@ unsafe impl TypeLayout for core::convert::Infallible { variants: &[], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Uninhabited + } } unsafe impl const TypeGraph for core::convert::Infallible { diff --git a/src/impls/core/ffi.rs b/src/impls/core/ffi.rs index 8dab14d..e209987 100644 --- a/src/impls/core/ffi.rs +++ b/src/impls/core/ffi.rs @@ -1,6 +1,8 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for core::ffi::c_void { +unsafe impl const TypeLayout for core::ffi::c_void { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -10,6 +12,10 @@ unsafe impl TypeLayout for core::ffi::c_void { variants: &[], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Uninhabited + } } unsafe impl const TypeGraph for core::ffi::c_void { diff --git a/src/impls/core/marker.rs b/src/impls/core/marker.rs index 1516aab..a63dfda 100644 --- a/src/impls/core/marker.rs +++ b/src/impls/core/marker.rs @@ -1,6 +1,8 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for core::marker::PhantomData { +unsafe impl const TypeLayout for core::marker::PhantomData { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -10,6 +12,10 @@ unsafe impl TypeLayout for core::marker::PhantomData { fields: &[], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(core::marker::PhantomData::)) + } } unsafe impl const TypeGraph for core::marker::PhantomData { @@ -18,7 +24,7 @@ unsafe impl const TypeGraph for core::marker::PhantomData { } } -unsafe impl TypeLayout for core::marker::PhantomPinned { +unsafe impl const TypeLayout for core::marker::PhantomPinned { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -28,6 +34,10 @@ unsafe impl TypeLayout for core::marker::PhantomPinned { fields: &[], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(core::marker::PhantomPinned)) + } } unsafe impl const TypeGraph for core::marker::PhantomPinned { diff --git a/src/impls/core/mem.rs b/src/impls/core/mem.rs index 2cb90c9..7daae9d 100644 --- a/src/impls/core/mem.rs +++ b/src/impls/core/mem.rs @@ -1,6 +1,8 @@ -use crate::{Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for core::mem::ManuallyDrop { +unsafe impl const TypeLayout for core::mem::ManuallyDrop { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -9,11 +11,20 @@ unsafe impl TypeLayout for core::mem::ManuallyDrop { repr: "transparent", fields: &[Field { name: "value", - offset: 0, + offset: unsafe { ::uninit() }.map(0), ty: ::core::any::type_name::(), }], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + match ::uninit() { + MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited, + MaybeUninhabited::Inhabited(uninit) => MaybeUninhabited::Inhabited( + core::mem::MaybeUninit::new(Self::new(uninit.assume_init())), + ), + } + } } unsafe impl const TypeGraph for core::mem::ManuallyDrop { @@ -24,7 +35,7 @@ unsafe impl const TypeGraph for core::mem::ManuallyDrop } } -unsafe impl TypeLayout for core::mem::MaybeUninit { +unsafe impl const TypeLayout for core::mem::MaybeUninit { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -34,24 +45,28 @@ unsafe impl TypeLayout for core::mem::MaybeUninit { fields: &[ Field { name: "uninit", - offset: 0, + offset: MaybeUninhabited::Inhabited(0), ty: ::core::any::type_name::<()>(), }, Field { name: "value", - offset: 0, - ty: ::core::any::type_name::(), + offset: unsafe { ::uninit() }.map(0), + ty: ::core::any::type_name::>(), }, ], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(core::mem::MaybeUninit::uninit())) + } } unsafe impl const TypeGraph for core::mem::MaybeUninit { fn populate_graph(graph: &mut TypeLayoutGraph<'static>) { if graph.insert(&Self::TYPE_LAYOUT) { <() as TypeGraph>::populate_graph(graph); - ::populate_graph(graph); + as TypeGraph>::populate_graph(graph); } } } diff --git a/src/impls/core/num.rs b/src/impls/core/num.rs index 11f333e..ab36010 100644 --- a/src/impls/core/num.rs +++ b/src/impls/core/num.rs @@ -1,8 +1,10 @@ -use crate::{Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; macro_rules! impl_nonzero_type_layout { (impl $nz:ident => $ty:ty) => { - unsafe impl TypeLayout for core::num::$nz { + unsafe impl const TypeLayout for core::num::$nz { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -12,12 +14,18 @@ macro_rules! impl_nonzero_type_layout { fields: &[ Field { name: "0", - offset: 0, + offset: MaybeUninhabited::Inhabited(0), ty: ::core::any::type_name::<$ty>(), }, ], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited( + core::mem::MaybeUninit::new(Self::new(1).unwrap()) + ) + } } unsafe impl const TypeGraph for core::num::$nz { @@ -40,7 +48,7 @@ impl_nonzero_type_layout! { NonZeroU128 => u128, NonZeroUsize => usize } -unsafe impl TypeLayout for core::num::Wrapping { +unsafe impl const TypeLayout for core::num::Wrapping { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -49,11 +57,20 @@ unsafe impl TypeLayout for core::num::Wrapping { repr: "transparent", fields: &[Field { name: "0", - offset: 0, + offset: unsafe { ::uninit() }.map(0), ty: ::core::any::type_name::(), }], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + match ::uninit() { + MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited, + MaybeUninhabited::Inhabited(uninit) => { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(Self(uninit.assume_init()))) + }, + } + } } unsafe impl const TypeGraph for core::num::Wrapping { diff --git a/src/impls/core/option.rs b/src/impls/core/option.rs index c463005..9098760 100644 --- a/src/impls/core/option.rs +++ b/src/impls/core/option.rs @@ -1,76 +1,9 @@ use crate::{ - Discriminant, Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, + Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, Variant, }; -trait OptionDiscriminant: Sized -where - [u8; core::mem::size_of::>()]:, -{ - const NONE_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()]; - const SOME_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()]; -} - -impl OptionDiscriminant for Option -where - [u8; core::mem::size_of::>()]:, -{ - const NONE_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()] = unsafe { - let none: core::mem::MaybeUninit = core::mem::MaybeUninit::new(None); - - let system_endian_bytes: [u8; core::mem::size_of::>()] = - core::mem::transmute(core::mem::discriminant(none.assume_init_ref())); - - let mut big_endian_bytes = [0_u8; core::mem::size_of::>()]; - - let mut i = 0; - - while i < system_endian_bytes.len() { - big_endian_bytes[i] = system_endian_bytes[if cfg!(target_endian = "big") { - i - } else { - system_endian_bytes.len() - i - 1 - }]; - - i += 1; - } - - big_endian_bytes - }; - const SOME_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()] = unsafe { - let mut value: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - let some: core::mem::MaybeUninit = - core::mem::MaybeUninit::new(Some(value.assume_init())); - - let system_endian_bytes: [u8; core::mem::size_of::>()] = - core::mem::transmute(core::mem::discriminant(some.assume_init_ref())); - - let mut big_endian_bytes = [0_u8; core::mem::size_of::>()]; - - let mut i = 0; - - while i < system_endian_bytes.len() { - big_endian_bytes[i] = system_endian_bytes[if cfg!(target_endian = "big") { - i - } else { - system_endian_bytes.len() - i - 1 - }]; - - i += 1; - } - - big_endian_bytes - }; -} - -unsafe impl TypeLayout for core::option::Option +unsafe impl const TypeLayout for core::option::Option where [u8; core::mem::size_of::>()]:, { @@ -83,49 +16,32 @@ where variants: &[ Variant { name: "None", - discriminant: Discriminant { - big_endian_bytes: &::NONE_DISCRIMINANT_BYTES, - }, + discriminant: crate::struct_variant_discriminant!( + Option => Option => None + ), fields: &[], }, Variant { name: "Some", - discriminant: Discriminant { - big_endian_bytes: &::SOME_DISCRIMINANT_BYTES, - }, + discriminant: crate::struct_variant_discriminant!( + Option => Option => Some(f_0: T) + ), fields: &[Field { name: "0", - offset: unsafe { - let mut value: core::mem::MaybeUninit = - core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - let some: core::mem::MaybeUninit = - core::mem::MaybeUninit::new(Some(value.assume_init())); - - #[allow(clippy::cast_sign_loss)] - match some.assume_init_ref() { - Some(val) => (val as *const T) - .cast::() - .offset_from(some.as_ptr().cast()) - as usize, - _ => unreachable!(), - } - }, + offset: crate::struct_variant_field_offset!(Option => Option => Some(f_0: T) => 0), ty: ::core::any::type_name::(), }], }, ], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(None)) + } } -unsafe impl const TypeGraph for core::option::Option +unsafe impl const TypeGraph for core::option::Option where [u8; core::mem::size_of::>()]:, { diff --git a/src/impls/core/pin.rs b/src/impls/core/pin.rs index 3a8c553..4a5d360 100644 --- a/src/impls/core/pin.rs +++ b/src/impls/core/pin.rs @@ -1,6 +1,8 @@ -use crate::{Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; -unsafe impl TypeLayout for core::pin::Pin { +unsafe impl const TypeLayout for core::pin::Pin { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -9,14 +11,23 @@ unsafe impl TypeLayout for core::pin::Pin { repr: "transparent", fields: &[Field { name: "pointer", - offset: 0, + offset: unsafe { ::uninit() }.map(0), ty: ::core::any::type_name::(), }], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + match ::uninit() { + MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited, + MaybeUninhabited::Inhabited(uninit) => MaybeUninhabited::Inhabited( + core::mem::MaybeUninit::new(Self::new_unchecked(uninit.assume_init())), + ), + } + } } -unsafe impl const TypeGraph for core::pin::Pin { +unsafe impl const TypeGraph for core::pin::Pin { fn populate_graph(graph: &mut TypeLayoutGraph<'static>) { if graph.insert(&Self::TYPE_LAYOUT) { ::populate_graph(graph); diff --git a/src/impls/core/primitive.rs b/src/impls/core/primitive.rs index 3241a44..797ca91 100644 --- a/src/impls/core/primitive.rs +++ b/src/impls/core/primitive.rs @@ -1,14 +1,20 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, +}; macro_rules! impl_primitive_type_layout { - (impl $ty:ty) => { - unsafe impl TypeLayout for $ty { + (impl $ty:ty => $val:expr) => { + unsafe impl const TypeLayout for $ty { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Primitive, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new($val)) + } } unsafe impl const TypeGraph for $ty { @@ -17,14 +23,33 @@ macro_rules! impl_primitive_type_layout { } } }; - ($($ty:ty),*) => { - $(impl_primitive_type_layout!{impl $ty})* + ($($ty:ty => $val:expr),*) => { + $(impl_primitive_type_layout!{impl $ty => $val})* }; } impl_primitive_type_layout! { - i8, i16, i32, i64, i128, isize, - u8, u16, u32, u64, u128, usize, - f32, f64, - char, bool, () + i8 => 0, i16 => 0, i32 => 0, i64 => 0, i128 => 0, isize => 0, + u8 => 0, u16 => 0, u32 => 0, u64 => 0, u128 => 0, usize => 0, + f32 => 0.0, f64 => 0.0, + char => '\0', bool => false, () => () +} + +unsafe impl const TypeLayout for ! { + const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { + name: ::core::any::type_name::(), + size: ::core::mem::size_of::(), + alignment: ::core::mem::align_of::(), + structure: TypeStructure::Primitive, + }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Uninhabited + } +} + +unsafe impl const TypeGraph for ! { + fn populate_graph(graph: &mut TypeLayoutGraph<'static>) { + graph.insert(&Self::TYPE_LAYOUT); + } } diff --git a/src/impls/core/ptr.rs b/src/impls/core/ptr.rs index 06c8813..c60c4f0 100644 --- a/src/impls/core/ptr.rs +++ b/src/impls/core/ptr.rs @@ -1,15 +1,24 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + MaybeUninhabited, Mutability, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, + TypeStructure, +}; -unsafe impl TypeLayout for *const T { +unsafe impl const TypeLayout for *const T { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Pointer { inner: ::core::any::type_name::(), - mutability: false, + mutability: Mutability::Immutable, }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new( + core::ptr::NonNull::dangling().as_ptr(), + )) + } } unsafe impl const TypeGraph for *const T { @@ -20,16 +29,22 @@ unsafe impl const TypeGraph for *const T { } } -unsafe impl TypeLayout for *mut T { +unsafe impl const TypeLayout for *mut T { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Pointer { inner: ::core::any::type_name::(), - mutability: true, + mutability: Mutability::Mutable, }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new( + core::ptr::NonNull::dangling().as_ptr(), + )) + } } unsafe impl const TypeGraph for *mut T { @@ -40,16 +55,20 @@ unsafe impl const TypeGraph for *mut T { } } -unsafe impl TypeLayout for core::ptr::NonNull { +unsafe impl const TypeLayout for core::ptr::NonNull { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Pointer { inner: ::core::any::type_name::(), - mutability: true, + mutability: Mutability::Mutable, }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(core::ptr::NonNull::dangling())) + } } unsafe impl const TypeGraph for core::ptr::NonNull { @@ -60,16 +79,23 @@ unsafe impl const TypeGraph for core::ptr::NonNull { } } -unsafe impl TypeLayout for core::ptr::NonNull<[T]> { +unsafe impl const TypeLayout for core::ptr::NonNull<[T]> { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Pointer { inner: ::core::any::type_name::(), - mutability: true, + mutability: Mutability::Mutable, }, }; + + #[allow(clippy::borrow_as_ptr)] + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new( + core::ptr::NonNull::new_unchecked(&[] as *const [T] as *mut _), + )) + } } unsafe impl const TypeGraph for core::ptr::NonNull<[T]> { diff --git a/src/impls/core/ref.rs b/src/impls/core/ref.rs index 1befd95..542579b 100644 --- a/src/impls/core/ref.rs +++ b/src/impls/core/ref.rs @@ -1,18 +1,29 @@ -use crate::{TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + impls::leak_uninit_ptr, MaybeUninhabited, Mutability, TypeGraph, TypeLayout, TypeLayoutGraph, + TypeLayoutInfo, TypeStructure, +}; -unsafe impl<'a, T: TypeLayout + 'static> TypeLayout for &'a T { +unsafe impl<'a, T: ~const TypeLayout + 'a> const TypeLayout for &'a T { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Reference { inner: ::core::any::type_name::(), - mutability: false, + mutability: Mutability::Immutable, }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + if let MaybeUninhabited::Uninhabited = ::uninit() { + return MaybeUninhabited::Uninhabited; + } + + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(&*leak_uninit_ptr())) + } } -unsafe impl<'a, T: ~const TypeGraph + 'static> const TypeGraph for &'a T { +unsafe impl<'a, T: ~const TypeGraph + 'a> const TypeGraph for &'a T { fn populate_graph(graph: &mut TypeLayoutGraph<'static>) { if graph.insert(&Self::TYPE_LAYOUT) { ::populate_graph(graph); @@ -20,19 +31,27 @@ unsafe impl<'a, T: ~const TypeGraph + 'static> const TypeGraph for &'a T { } } -unsafe impl<'a, T: TypeLayout + 'static> TypeLayout for &'a mut T { +unsafe impl<'a, T: ~const TypeLayout + 'a> const TypeLayout for &'a mut T { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), alignment: ::core::mem::align_of::(), structure: TypeStructure::Reference { inner: ::core::any::type_name::(), - mutability: true, + mutability: Mutability::Mutable, }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + if let MaybeUninhabited::Uninhabited = ::uninit() { + return MaybeUninhabited::Uninhabited; + } + + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(&mut *leak_uninit_ptr())) + } } -unsafe impl<'a, T: ~const TypeGraph + 'static> const TypeGraph for &'a mut T { +unsafe impl<'a, T: ~const TypeGraph + 'a> const TypeGraph for &'a mut T { fn populate_graph(graph: &mut TypeLayoutGraph<'static>) { if graph.insert(&Self::TYPE_LAYOUT) { ::populate_graph(graph); diff --git a/src/impls/core/result.rs b/src/impls/core/result.rs index e9f4e7e..a21d3d4 100644 --- a/src/impls/core/result.rs +++ b/src/impls/core/result.rs @@ -1,84 +1,10 @@ use crate::{ - Discriminant, Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, + Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, Variant, }; -trait ResultDiscriminant: Sized -where - [u8; core::mem::size_of::>()]:, -{ - const OK_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()]; - const ERR_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()]; -} - -impl ResultDiscriminant for Result -where - [u8; core::mem::size_of::>()]:, -{ - const ERR_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()] = unsafe { - let mut value: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - let err: core::mem::MaybeUninit = - core::mem::MaybeUninit::new(Err(value.assume_init())); - - let system_endian_bytes: [u8; core::mem::size_of::>()] = - core::mem::transmute(core::mem::discriminant(err.assume_init_ref())); - - let mut big_endian_bytes = [0_u8; core::mem::size_of::>()]; - - let mut i = 0; - - while i < system_endian_bytes.len() { - big_endian_bytes[i] = system_endian_bytes[if cfg!(target_endian = "big") { - i - } else { - system_endian_bytes.len() - i - 1 - }]; - - i += 1; - } - - big_endian_bytes - }; - const OK_DISCRIMINANT_BYTES: [u8; core::mem::size_of::>()] = unsafe { - let mut value: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - let ok: core::mem::MaybeUninit = core::mem::MaybeUninit::new(Ok(value.assume_init())); - - let system_endian_bytes: [u8; core::mem::size_of::>()] = - core::mem::transmute(core::mem::discriminant(ok.assume_init_ref())); - - let mut big_endian_bytes = [0_u8; core::mem::size_of::>()]; - - let mut i = 0; - - while i < system_endian_bytes.len() { - big_endian_bytes[i] = system_endian_bytes[if cfg!(target_endian = "big") { - i - } else { - system_endian_bytes.len() - i - 1 - }]; - - i += 1; - } - - big_endian_bytes - }; -} - -unsafe impl TypeLayout for core::result::Result +unsafe impl const TypeLayout + for core::result::Result where [u8; core::mem::size_of::>()]:, { @@ -91,74 +17,49 @@ where variants: &[ Variant { name: "Ok", - discriminant: Discriminant { - big_endian_bytes: &::OK_DISCRIMINANT_BYTES, - }, + discriminant: crate::struct_variant_discriminant!( + Result => Result => Ok(f_0: T) + ), fields: &[Field { name: "0", - offset: unsafe { - let mut value: core::mem::MaybeUninit = - core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - let ok: core::mem::MaybeUninit = - core::mem::MaybeUninit::new(Ok(value.assume_init())); - - #[allow(clippy::cast_sign_loss)] - match ok.assume_init_ref() { - Ok(val) => (val as *const T) - .cast::() - .offset_from(ok.as_ptr().cast()) - as usize, - _ => unreachable!(), - } - }, + offset: crate::struct_variant_field_offset!(Result => Result => Ok(f_0: T) => 0), ty: ::core::any::type_name::(), }], }, Variant { name: "Err", - discriminant: Discriminant { - big_endian_bytes: &::ERR_DISCRIMINANT_BYTES, - }, + discriminant: crate::struct_variant_discriminant!( + Result => Result => Err(f_0: E) + ), fields: &[Field { name: "0", - offset: unsafe { - let mut value: core::mem::MaybeUninit = - core::mem::MaybeUninit::uninit(); - - let mut i = 0; - while i < core::mem::size_of::() { - *value.as_mut_ptr().cast::().add(i) = 0xFF_u8; - i += 1; - } - - let err: core::mem::MaybeUninit = - core::mem::MaybeUninit::new(Err(value.assume_init())); - - #[allow(clippy::cast_sign_loss)] - match err.assume_init_ref() { - Err(val) => (val as *const E) - .cast::() - .offset_from(err.as_ptr().cast()) - as usize, - _ => unreachable!(), - } - }, + offset: crate::struct_variant_field_offset!(Result => Result => Err(f_0: E) => 0), ty: ::core::any::type_name::(), }], }, ], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + if let MaybeUninhabited::Inhabited(uninit) = ::uninit() { + return MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(Ok( + uninit.assume_init() + ))); + } + + if let MaybeUninhabited::Inhabited(uninit) = ::uninit() { + return MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new(Err( + uninit.assume_init() + ))); + } + + MaybeUninhabited::Uninhabited + } } -unsafe impl const TypeGraph for core::result::Result +unsafe impl const + TypeGraph for core::result::Result where [u8; core::mem::size_of::>()]:, { diff --git a/src/impls/core/sync/atomic.rs b/src/impls/core/sync/atomic.rs index fa65239..0bf397a 100644 --- a/src/impls/core/sync/atomic.rs +++ b/src/impls/core/sync/atomic.rs @@ -1,9 +1,12 @@ -use crate::{Field, TypeGraph, TypeLayout, TypeLayoutGraph, TypeLayoutInfo, TypeStructure}; +use crate::{ + impls::leak_uninit_ptr, Field, MaybeUninhabited, TypeGraph, TypeLayout, TypeLayoutGraph, + TypeLayoutInfo, TypeStructure, +}; macro_rules! impl_atomic_int_layout { - (impl $at:ident ( $align:literal : $cfg:literal ) => $ty:ty) => { + (impl $at:ident ( $align:literal : $cfg:literal ) => $ty:ty => $val:literal) => { #[cfg(target_has_atomic_load_store = $cfg)] - unsafe impl TypeLayout for core::sync::atomic::$at { + unsafe impl const TypeLayout for core::sync::atomic::$at { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -13,12 +16,18 @@ macro_rules! impl_atomic_int_layout { fields: &[ Field { name: "v", - offset: 0, + offset: MaybeUninhabited::Inhabited(0), ty: core::any::type_name::>(), }, ], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited( + core::mem::MaybeUninit::new(Self::new($val)) + ) + } } #[cfg(target_has_atomic_load_store = $cfg)] @@ -30,25 +39,25 @@ macro_rules! impl_atomic_int_layout { } } }; - ($($at:ident ( $align:literal : $cfg:literal ) => $ty:ty),*) => { - $(impl_atomic_int_layout!{impl $at ($align : $cfg) => $ty})* + ($($at:ident ( $align:literal : $cfg:literal ) => $ty:ty => $val:literal),*) => { + $(impl_atomic_int_layout!{impl $at ($align : $cfg) => $ty => $val})* }; } impl_atomic_int_layout! { - AtomicBool (1:"8") => u8, - AtomicI8 (1:"8") => i8, AtomicI16 (2:"16") => i16, - AtomicI32 (4:"32") => i32, AtomicI64 (8:"64") => i64, - AtomicU8 (1:"8") => u8, AtomicU16 (2:"16") => u16, - AtomicU32 (4:"32") => u32, AtomicU64 (8:"64") => u64, - AtomicI128 (16:"128") => i128, AtomicU128 (16:"128") => u128 + AtomicBool (1:"8") => u8 => false, + AtomicI8 (1:"8") => i8 => 0, AtomicI16 (2:"16") => i16 => 0, + AtomicI32 (4:"32") => i32 => 0, AtomicI64 (8:"64") => i64 => 0, + AtomicU8 (1:"8") => u8 => 0, AtomicU16 (2:"16") => u16 => 0, + AtomicU32 (4:"32") => u32 => 0, AtomicU64 (8:"64") => u64 => 0, + AtomicI128 (16:"128") => i128 => 0, AtomicU128 (16:"128") => u128 => 0 } -macro_rules! impl_atomic_ptr_layout { - (impl $at:ident ( $align:literal : $cfg:literal ) => $ty:ty) => { +macro_rules! impl_atomic_int_ptr_sized_layout { + (impl $at:ident ( $align:literal : $cfg:literal ) => $ty:ty => $val:literal) => { #[cfg(target_has_atomic_load_store = "ptr")] #[cfg(target_pointer_width = $cfg)] - unsafe impl TypeLayout for core::sync::atomic::$at { + unsafe impl const TypeLayout for core::sync::atomic::$at { const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { name: ::core::any::type_name::(), size: ::core::mem::size_of::(), @@ -58,12 +67,18 @@ macro_rules! impl_atomic_ptr_layout { fields: &[ Field { name: "v", - offset: 0, + offset: MaybeUninhabited::Inhabited(0), ty: core::any::type_name::>(), }, ], }, }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited( + core::mem::MaybeUninit::new(Self::new($val)) + ) + } } #[cfg(target_has_atomic_load_store = "ptr")] @@ -76,14 +91,61 @@ macro_rules! impl_atomic_ptr_layout { } } }; - ($($at:ident ( $align:literal : $cfg:literal ) => $ty:ty),*) => { - $(impl_atomic_ptr_layout!{impl $at ($align : $cfg) => $ty})* + ($($at:ident ( $align:literal : $cfg:literal ) => $ty:ty => $val:literal),*) => { + $(impl_atomic_int_ptr_sized_layout!{impl $at ($align : $cfg) => $ty => $val})* + }; +} + +impl_atomic_int_ptr_sized_layout! { + AtomicIsize (2:"16") => isize => 0, AtomicIsize (4:"32") => isize => 0, + AtomicIsize (8:"64") => isize => 0, + AtomicUsize (2:"16") => usize => 0, AtomicUsize (4:"32") => usize => 0, + AtomicUsize (8:"64") => usize => 0 +} + +macro_rules! impl_atomic_ptr_layout { + (impl ( $align:literal : $cfg:literal )) => { + #[cfg(target_has_atomic_load_store = "ptr")] + #[cfg(target_pointer_width = $cfg)] + unsafe impl const TypeLayout for core::sync::atomic::AtomicPtr { + const TYPE_LAYOUT: TypeLayoutInfo<'static> = TypeLayoutInfo { + name: ::core::any::type_name::(), + size: ::core::mem::size_of::(), + alignment: ::core::mem::align_of::(), + structure: TypeStructure::Struct { + repr: concat!("C,align(", $align, ")"), + fields: &[ + Field { + name: "v", + offset: MaybeUninhabited::Inhabited(0), + ty: core::any::type_name::>(), + }, + ], + }, + }; + + unsafe fn uninit() -> MaybeUninhabited> { + MaybeUninhabited::Inhabited(core::mem::MaybeUninit::new( + Self::new(leak_uninit_ptr()) + )) + } + } + + #[cfg(target_has_atomic_load_store = "ptr")] + #[cfg(target_pointer_width = $cfg)] + unsafe impl const TypeGraph for core::sync::atomic::AtomicPtr { + fn populate_graph(graph: &mut TypeLayoutGraph<'static>) { + if graph.insert(&Self::TYPE_LAYOUT) { + as TypeGraph>::populate_graph(graph); + } + } + } + }; + ($(( $align:literal : $cfg:literal )),*) => { + $(impl_atomic_ptr_layout!{impl ($align : $cfg)})* }; } impl_atomic_ptr_layout! { - AtomicIsize (2:"16") => isize, AtomicIsize (4:"32") => isize, - AtomicIsize (8:"64") => isize, - AtomicUsize (2:"16") => usize, AtomicUsize (4:"32") => usize, - AtomicUsize (8:"64") => usize + (2:"16"), (4:"32"), (8:"64") } diff --git a/src/impls/mod.rs b/src/impls/mod.rs index d54435c..d6aaa07 100644 --- a/src/impls/mod.rs +++ b/src/impls/mod.rs @@ -1,2 +1,29 @@ +use crate::{MaybeUninhabited, TypeLayout}; + mod alloc; mod core; + +#[doc(hidden)] +pub const unsafe fn leak_uninit_ptr() -> *mut T { + const fn alloc_comptime() -> *mut T { + unsafe { + ::core::intrinsics::const_allocate( + ::core::mem::size_of::(), + ::core::mem::align_of::(), + ) + } + .cast() + } + + fn alloc_runtime() -> *mut T { + unsafe { ::alloc::alloc::alloc(::alloc::alloc::Layout::new::()) }.cast() + } + + let ptr = ::core::intrinsics::const_eval_select((), alloc_comptime, alloc_runtime); + + if let MaybeUninhabited::Inhabited(uninit) = ::uninit() { + ::core::ptr::write(ptr, uninit.assume_init()); + } + + ptr +} diff --git a/src/lib.rs b/src/lib.rs index 3355531..729f1b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,17 +36,12 @@ //! ```rust //! # #![feature(cfg_version)] //! # #![feature(const_type_name)] -//! # #![cfg_attr(not(version("1.58.0")), feature(const_raw_ptr_deref))] -//! # #![cfg_attr(not(version("1.59.0")), feature(const_maybe_uninit_as_ptr))] -//! # #![cfg_attr(version("1.59.0"), feature(const_maybe_uninit_as_mut_ptr))] -//! # #![feature(const_ptr_offset_from)] -//! # #![cfg_attr(not(version("1.57.0")), feature(const_panic))] //! # #![feature(const_refs_to_cell)] -//! # #![feature(const_maybe_uninit_assume_init)] -//! # #![feature(const_discriminant)] //! # #![feature(const_trait_impl)] //! # #![feature(const_mut_refs)] +//! # #![feature(const_transmute_copy)] //! # #![cfg_attr(not(version("1.61.0")), feature(const_fn_trait_bound))] +//! # #![cfg_attr(not(version("1.61.0")), feature(const_ptr_offset))] //! # #![allow(incomplete_features)] //! # #![feature(generic_const_exprs)] //! use const_type_layout::TypeLayout; @@ -54,8 +49,8 @@ //! #[derive(TypeLayout)] //! #[repr(C)] //! struct Foo { -//! a: u8, -//! b: u32, +//! a: u8, +//! b: u32, //! } //! //! println!("{:#?}", Foo::TYPE_LAYOUT); @@ -89,17 +84,12 @@ //! ```rust //! # #![feature(cfg_version)] //! # #![feature(const_type_name)] -//! # #![cfg_attr(not(version("1.58.0")), feature(const_raw_ptr_deref))] -//! # #![cfg_attr(not(version("1.59.0")), feature(const_maybe_uninit_as_ptr))] -//! # #![cfg_attr(version("1.59.0"), feature(const_maybe_uninit_as_mut_ptr))] -//! # #![feature(const_ptr_offset_from)] -//! # #![cfg_attr(not(version("1.57.0")), feature(const_panic))] //! # #![feature(const_refs_to_cell)] -//! # #![feature(const_maybe_uninit_assume_init)] -//! # #![feature(const_discriminant)] //! # #![feature(const_trait_impl)] //! # #![feature(const_mut_refs)] +//! # #![feature(const_transmute_copy)] //! # #![cfg_attr(not(version("1.61.0")), feature(const_fn_trait_bound))] +//! # #![cfg_attr(not(version("1.61.0")), feature(const_ptr_offset))] //! # #![allow(incomplete_features)] //! # #![feature(generic_const_exprs)] //! use const_type_layout::TypeLayout; @@ -107,7 +97,7 @@ //! #[derive(TypeLayout)] //! #[repr(C, align(128))] //! struct OverAligned { -//! value: u8, +//! value: u8, //! } //! //! println!("{:#?}", OverAligned::TYPE_LAYOUT); @@ -134,22 +124,31 @@ #![no_std] #![feature(cfg_version)] #![feature(const_type_name)] -#![cfg_attr(not(version("1.58.0")), feature(const_raw_ptr_deref))] #![cfg_attr(not(version("1.61.0")), feature(const_ptr_offset))] #![feature(const_mut_refs)] -#![feature(const_raw_ptr_comparison)] #![feature(const_trait_impl)] #![cfg_attr(not(version("1.61.0")), feature(const_fn_trait_bound))] -#![cfg_attr(not(version("1.57.0")), feature(const_panic))] #![feature(cfg_target_has_atomic)] #![feature(const_discriminant)] -#![feature(const_maybe_uninit_assume_init)] -#![feature(const_ptr_offset_from)] +#![cfg_attr(not(version("1.65.0")), feature(const_ptr_offset_from))] #![feature(const_refs_to_cell)] -#![cfg_attr(not(version("1.59.0")), feature(const_maybe_uninit_as_ptr))] -#![cfg_attr(version("1.59.0"), feature(const_maybe_uninit_as_mut_ptr))] #![feature(const_option)] #![feature(let_else)] +#![feature(core_intrinsics)] +#![feature(const_heap)] +#![feature(allow_internal_unstable)] +#![feature(decl_macro)] +#![feature(allocator_api)] +#![feature(const_box)] +#![feature(const_pin)] +#![feature(const_ptr_write)] +#![feature(inline_const)] +#![feature(const_eval_select)] +#![feature(never_type)] +#![feature(maybe_uninit_uninit_array)] +#![feature(const_maybe_uninit_uninit_array)] +#![feature(maybe_uninit_array_assume_init)] +#![feature(const_maybe_uninit_array_assume_init)] #![allow(incomplete_features)] #![feature(generic_const_exprs)] #![doc(html_root_url = "https://momolangenstein.github.io/const-type-layout")] @@ -158,23 +157,68 @@ #[doc(hidden)] pub extern crate alloc; -use core::ops::Deref; +use core::{marker::Destruct, ops::Deref}; use alloc::fmt; pub use const_type_layout_derive::TypeLayout; -mod impls; +#[doc(hidden)] +pub mod impls; mod ser; #[cfg(feature = "serde")] mod serde; +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(::serde::Serialize))] +#[cfg_attr(feature = "serde", derive(::serde::Deserialize))] +pub enum MaybeUninhabited { + Uninhabited, + Inhabited(T), +} + +impl Default for MaybeUninhabited { + fn default() -> Self { + Self::Inhabited(T::default()) + } +} + +impl MaybeUninhabited { + #[must_use] + pub const fn map(&self, value: U) -> MaybeUninhabited { + match self { + Self::Inhabited(_) => MaybeUninhabited::Inhabited(value), + Self::Uninhabited => MaybeUninhabited::Uninhabited, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(::serde::Serialize))] +#[cfg_attr(feature = "serde", derive(::serde::Deserialize))] +pub enum Mutability { + Immutable, + Mutable, +} + /// # Safety /// /// It is only safe to implement this trait if it accurately describes the /// type's layout. Use `#[derive(TypeLayout)]` instead. pub unsafe trait TypeLayout: Sized { const TYPE_LAYOUT: TypeLayoutInfo<'static>; + + #[must_use] + /// # Safety + /// + /// 1. The returned value is not safe to be used in any other way than + /// to calculate field offsets and discriminants. + /// + /// 2. The value and any value built with it must NOT be dropped. + /// + /// 3. Must return `MaybeUninhabited::Uninhabited` iff the type is + /// uninhabited. + unsafe fn uninit() -> MaybeUninhabited>; } /// # Safety @@ -258,25 +302,44 @@ pub enum TypeStructure< F: Deref]> = &'a [Field<'a>], V: Deref]> = &'a [Variant<'a, F>], > { - Struct { repr: &'a str, fields: F }, - Union { repr: &'a str, fields: F }, - Enum { repr: &'a str, variants: V }, + Struct { + repr: &'a str, + fields: F, + }, + Union { + repr: &'a str, + fields: F, + }, + Enum { + repr: &'a str, + variants: V, + }, Primitive, - Array { item: &'a str, len: usize }, - Reference { inner: &'a str, mutability: bool }, - Pointer { inner: &'a str, mutability: bool }, + Array { + item: &'a str, + len: usize, + }, + Reference { + inner: &'a str, + mutability: Mutability, + }, + Pointer { + inner: &'a str, + mutability: Mutability, + }, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] pub struct Variant<'a, F: Deref]> = &'a [Field<'a>]> { pub name: &'a str, - pub discriminant: Discriminant<'a>, + pub discriminant: MaybeUninhabited>, pub fields: F, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] +#[repr(transparent)] pub struct Discriminant<'a> { pub big_endian_bytes: &'a [u8], } @@ -285,7 +348,7 @@ pub struct Discriminant<'a> { #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] pub struct Field<'a> { pub name: &'a str, - pub offset: usize, + pub offset: MaybeUninhabited, pub ty: &'a str, } @@ -469,3 +532,226 @@ impl<'a> PartialOrd for Field<'a> { Some(self.cmp(other)) } } + +#[allow_internal_unstable(const_ptr_offset_from)] +pub macro struct_field_offset($ty_name:ident => $ty:ty => (*$base:ident).$field:tt => $($extra_fields:tt)?) { + { + #[allow(clippy::unneeded_field_pattern)] + let $ty_name { $field: _, $($extra_fields)? }: $ty; + + if let $crate::MaybeUninhabited::Inhabited(uninit) = unsafe { <$ty as $crate::TypeLayout>::uninit() } { + let $base: *const $ty = ::core::ptr::addr_of!(uninit).cast(); + + #[allow(unused_unsafe)] + let field_ptr = unsafe { + ::core::ptr::addr_of!((*$base).$field) + }; + + #[allow(clippy::cast_sign_loss)] + let offset = unsafe { field_ptr.cast::().offset_from($base.cast()) as usize }; + + #[allow(clippy::forget_non_drop)] + core::mem::forget(uninit); + + $crate::MaybeUninhabited::Inhabited(offset) + } else { + $crate::MaybeUninhabited::Uninhabited + } + } +} + +#[allow_internal_unstable(const_discriminant)] +pub macro struct_variant_discriminant { + ($ty_name:ident => $ty:ty => $variant_name:ident) => { + $crate::MaybeUninhabited::Inhabited { + 0: $crate::Discriminant { + big_endian_bytes: &{ + let uninit: $ty = $ty_name::$variant_name; + + let system_endian_bytes: [u8; core::mem::size_of::>()] = unsafe { + core::mem::transmute(core::mem::discriminant(&uninit)) + }; + + #[allow(clippy::forget_non_drop)] + core::mem::forget(uninit); + + let mut big_endian_bytes = [0_u8; core::mem::size_of::>()]; + + let mut i = 0; + + while i < system_endian_bytes.len() { + big_endian_bytes[i] = system_endian_bytes[if cfg!(target_endian = "big") { + i + } else { + system_endian_bytes.len() - i - 1 + }]; + + i += 1; + } + + big_endian_bytes + }, + }, + } + }, + ($ty_name:ident => $ty:ty => $variant_name:ident($($field_name:ident: $field_ty:ty),* $(,)?)) => {{ + #[allow(unused_parens)] + if let ( + $($crate::MaybeUninhabited::Inhabited($field_name)),* + ) = ( + $(unsafe { <$field_ty as $crate::TypeLayout>::uninit() }),* + ) { + $crate::MaybeUninhabited::Inhabited { + 0: $crate::Discriminant { + big_endian_bytes: { + let uninit: $ty = $ty_name::$variant_name( + $(unsafe { $field_name.assume_init() }),* + ); + + let system_endian_bytes: [u8; core::mem::size_of::>()] = unsafe { + core::mem::transmute(core::mem::discriminant(&uninit)) + }; + + #[allow(clippy::forget_non_drop)] + core::mem::forget(uninit); + + let big_endian_bytes = unsafe { + &mut *$crate::impls::leak_uninit_ptr::< + [u8; core::mem::size_of::>()] + >() + }; + + let mut i = 0; + + while i < system_endian_bytes.len() { + (*big_endian_bytes)[i] = system_endian_bytes[if cfg!(target_endian = "big") { + i + } else { + system_endian_bytes.len() - i - 1 + }]; + + i += 1; + } + + big_endian_bytes + } + }, + } + } else { + $crate::MaybeUninhabited::Uninhabited + } + }}, + ($ty_name:ident => $ty:ty => $variant_name:ident { $($field_name:ident: $field_ty:ty),* $(,)? }) => {{ + #[allow(unused_parens)] + if let ( + $($crate::MaybeUninhabited::Inhabited($field_name)),* + ) = ( + $(unsafe { <$field_ty as $crate::TypeLayout>::uninit() }),* + ) { + $crate::MaybeUninhabited::Inhabited { + 0: $crate::Discriminant { + big_endian_bytes: { + let uninit: $ty = $ty_name::$variant_name { + $($field_name: unsafe { $field_name.assume_init() }),* + }; + + let system_endian_bytes: [u8; core::mem::size_of::>()] = unsafe { + core::mem::transmute(core::mem::discriminant(&uninit)) + }; + + #[allow(clippy::forget_non_drop)] + core::mem::forget(uninit); + + let big_endian_bytes = unsafe { + &mut *$crate::impls::leak_uninit_ptr::< + [u8; core::mem::size_of::>()] + >() + }; + + let mut i = 0; + + while i < system_endian_bytes.len() { + (*big_endian_bytes)[i] = system_endian_bytes[if cfg!(target_endian = "big") { + i + } else { + system_endian_bytes.len() - i - 1 + }]; + + i += 1; + } + + big_endian_bytes + } + }, + } + } else { + $crate::MaybeUninhabited::Uninhabited + } + }}, +} + +#[allow_internal_unstable(const_ptr_offset_from)] +pub macro struct_variant_field_offset { + ($ty_name:ident => $ty:ty => $variant_name:ident($($field_name:ident: $field_ty:ty),* $(,)?) => $field_index:tt) => {{ + #[allow(unused_parens)] + if let ( + $($crate::MaybeUninhabited::Inhabited($field_name)),* + ) = ( + $(unsafe { <$field_ty as $crate::TypeLayout>::uninit() }),* + ) { + let uninit: $ty = $ty_name::$variant_name( + $(unsafe { $field_name.assume_init() }),* + ); + let base_ptr: *const $ty = ::core::ptr::addr_of!(uninit).cast(); + + let field_ptr: *const u8 = match &uninit { + #[allow(clippy::unneeded_field_pattern, clippy::ptr_as_ptr)] + $ty_name::$variant_name { $field_index: field, .. } => { + field as *const _ as *const u8 + }, + _ => unreachable!(), + }; + + #[allow(clippy::cast_sign_loss)] + let offset = unsafe { field_ptr.cast::().offset_from(base_ptr.cast()) as usize }; + + #[allow(clippy::forget_non_drop)] + core::mem::forget(uninit); + + $crate::MaybeUninhabited::Inhabited(offset) + } else { + $crate::MaybeUninhabited::Uninhabited + } + }}, + ($ty_name:ident => $ty:ty => $variant_name:ident { $($field_name:ident: $field_ty:ty),* $(,)? } => $field_index:ident) => {{ + #[allow(unused_parens)] + if let ( + $($crate::MaybeUninhabited::Inhabited($field_name)),* + ) = ( + $(unsafe { <$field_ty as $crate::TypeLayout>::uninit() }),* + ) { + let uninit: $ty = $ty_name::$variant_name { + $($field_name: unsafe { $field_name.assume_init() }),* + }; + let base_ptr: *const $ty = ::core::ptr::addr_of!(uninit).cast(); + + let field_ptr: *const u8 = match &uninit { + #[allow(clippy::unneeded_field_pattern)] + $ty_name::$variant_name { $field_index: field, .. } => { + field as *const _ as *const u8 + }, + _ => unreachable!(), + }; + + #[allow(clippy::cast_sign_loss)] + let offset = unsafe { field_ptr.cast::().offset_from(base_ptr.cast()) as usize }; + + #[allow(clippy::forget_non_drop)] + core::mem::forget(uninit); + + $crate::MaybeUninhabited::Inhabited(offset) + } else { + $crate::MaybeUninhabited::Uninhabited + } + }}, +} diff --git a/src/ser.rs b/src/ser.rs index 2b31444..f4b3bff 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,6 +1,9 @@ use core::ops::Deref; -use crate::{Discriminant, Field, TypeLayoutGraph, TypeLayoutInfo, TypeStructure, Variant}; +use crate::{ + Discriminant, Field, MaybeUninhabited, Mutability, TypeLayoutGraph, TypeLayoutInfo, + TypeStructure, Variant, +}; pub const fn serialise_str(bytes: &mut [u8], from: usize, value: &str) -> usize { let value_bytes = value.as_bytes(); @@ -80,18 +83,43 @@ pub const fn serialised_byte_len(from: usize, _value: u8) -> usize { from + 1 } -pub const fn serialise_bool(bytes: &mut [u8], from: usize, value: bool) -> usize { +pub const fn serialise_mutability(bytes: &mut [u8], from: usize, value: Mutability) -> usize { assert!( from < bytes.len(), - "bytes is not large enough to contain the serialised bool." + "bytes is not large enough to contain the serialised Mutability." ); - bytes[from] = if value { b'T' } else { b'F' }; + bytes[from] = match value { + Mutability::Immutable => b'i', + Mutability::Mutable => b'm', + }; from + 1 } -pub const fn serialised_bool_len(from: usize, _value: bool) -> usize { +pub const fn serialised_mutability_len(from: usize, _value: Mutability) -> usize { + from + 1 +} + +pub const fn serialise_maybe_uninhabited( + bytes: &mut [u8], + from: usize, + value: MaybeUninhabited<()>, +) -> usize { + assert!( + from < bytes.len(), + "bytes is not large enough to contain the serialised MaybeUninhabited." + ); + + bytes[from] = match value { + MaybeUninhabited::Inhabited(()) => b'h', + MaybeUninhabited::Uninhabited => b'n', + }; + + from + 1 +} + +pub const fn serialised_maybe_uninhabited_len(from: usize, _value: MaybeUninhabited<()>) -> usize { from + 1 } @@ -150,13 +178,21 @@ pub const fn serialised_discriminant_len(from: usize, value: &Discriminant) -> u pub const fn serialise_field<'a>(bytes: &mut [u8], from: usize, value: &Field<'a>) -> usize { let from = serialise_str(bytes, from, value.name); - let from = serialise_usize(bytes, from, value.offset); + let from = serialise_maybe_uninhabited(bytes, from, value.offset.map(())); + let from = match value.offset { + MaybeUninhabited::Inhabited(offset) => serialise_usize(bytes, from, offset), + MaybeUninhabited::Uninhabited => from, + }; serialise_str(bytes, from, value.ty) } pub const fn serialised_field_len(from: usize, value: &Field) -> usize { let from = serialised_str_len(from, value.name); - let from = serialised_usize_len(from, value.offset); + let from = serialised_maybe_uninhabited_len(from, value.offset.map(())); + let from = match value.offset { + MaybeUninhabited::Inhabited(offset) => serialised_usize_len(from, offset), + MaybeUninhabited::Uninhabited => from, + }; serialised_str_len(from, value.ty) } @@ -194,7 +230,13 @@ pub const fn serialise_variant<'a, F: ~const Deref]>>( value: &Variant<'a, F>, ) -> usize { let from = serialise_str(bytes, from, value.name); - let from = serialise_discriminant(bytes, from, &value.discriminant); + let from = serialise_maybe_uninhabited(bytes, from, value.discriminant.map(())); + let from = match &value.discriminant { + MaybeUninhabited::Inhabited(discriminant) => { + serialise_discriminant(bytes, from, discriminant) + }, + MaybeUninhabited::Uninhabited => from, + }; serialise_fields(bytes, from, &value.fields) } @@ -203,7 +245,13 @@ pub const fn serialised_variant_len<'a, F: ~const Deref]>>( value: &Variant<'a, F>, ) -> usize { let from = serialised_str_len(from, value.name); - let from = serialised_discriminant_len(from, &value.discriminant); + let from = serialised_maybe_uninhabited_len(from, value.discriminant.map(())); + let from = match &value.discriminant { + MaybeUninhabited::Inhabited(discriminant) => { + serialised_discriminant_len(from, discriminant) + }, + MaybeUninhabited::Uninhabited => from, + }; serialised_fields_len(from, &value.fields) } @@ -276,12 +324,12 @@ pub const fn serialise_type_structure< TypeStructure::Reference { inner, mutability } => { let from = serialise_byte(bytes, from, b'r'); let from = serialise_str(bytes, from, inner); - serialise_bool(bytes, from, *mutability) + serialise_mutability(bytes, from, *mutability) }, TypeStructure::Pointer { inner, mutability } => { let from = serialise_byte(bytes, from, b'p'); let from = serialise_str(bytes, from, inner); - serialise_bool(bytes, from, *mutability) + serialise_mutability(bytes, from, *mutability) }, } } @@ -319,12 +367,12 @@ pub const fn serialised_type_structure_len< TypeStructure::Reference { inner, mutability } => { let from = serialised_byte_len(from, b'r'); let from = serialised_str_len(from, inner); - serialised_bool_len(from, *mutability) + serialised_mutability_len(from, *mutability) }, TypeStructure::Pointer { inner, mutability } => { let from = serialised_byte_len(from, b'p'); let from = serialised_str_len(from, inner); - serialised_bool_len(from, *mutability) + serialised_mutability_len(from, *mutability) }, } } diff --git a/try-crate/Cargo.toml b/try-crate/Cargo.toml index d847898..d92a144 100644 --- a/try-crate/Cargo.toml +++ b/try-crate/Cargo.toml @@ -11,4 +11,4 @@ edition = "2021" const-type-layout = { path = "..", features = ["serde"] } serde = "1.0" -ron = "0.7" +ron = "0.8" diff --git a/try-crate/src/main.rs b/try-crate/src/main.rs index 73ca3c9..796ffcd 100644 --- a/try-crate/src/main.rs +++ b/try-crate/src/main.rs @@ -1,18 +1,13 @@ #![deny(clippy::pedantic)] #![feature(cfg_version)] #![feature(const_type_name)] -#![cfg_attr(not(version("1.58.0")), feature(const_raw_ptr_deref))] -#![cfg_attr(not(version("1.59.0")), feature(const_maybe_uninit_as_ptr))] -#![cfg_attr(version("1.59.0"), feature(const_maybe_uninit_as_mut_ptr))] -#![feature(const_ptr_offset_from)] -#![cfg_attr(not(version("1.57.0")), feature(const_panic))] #![feature(const_refs_to_cell)] -#![feature(const_maybe_uninit_assume_init)] -#![feature(const_discriminant)] #![feature(const_trait_impl)] #![feature(const_mut_refs)] +#![feature(const_transmute_copy)] #![cfg_attr(not(version("1.61.0")), feature(const_fn_trait_bound))] #![cfg_attr(not(version("1.61.0")), feature(const_ptr_offset))] +#![feature(never_type)] #![allow(incomplete_features)] #![feature(generic_const_exprs)] @@ -43,9 +38,28 @@ struct Foo4(T); #[derive(TypeLayout)] union Bar { a: u8, - b: u16, + b: bool, +} + +#[repr(C)] +#[derive(TypeLayout)] +union SingleUnion { + a: u8, +} + +#[derive(TypeLayout)] +#[layout(ground = "b")] +union RecursiveRef<'a> { + a: &'a RecursiveRef<'a>, + b: (), +} + +#[derive(TypeLayout)] +union RecursivePtr { + a: *const RecursivePtr, } +#[allow(clippy::empty_enum)] #[derive(TypeLayout)] enum Never {} @@ -54,6 +68,17 @@ enum Single { Single, } +#[derive(TypeLayout)] +enum Double { + A = 2, + B = 3, +} + +#[derive(TypeLayout)] +enum WithDouble { + A(Double), +} + #[repr(C)] #[repr(u8)] #[derive(TypeLayout)] @@ -63,15 +88,31 @@ enum Quo { Struct { a: T, b: u16, c: T }, } +#[derive(TypeLayout)] +enum NoUnit { + A(T), + B(T), +} + #[repr(u8, C)] #[derive(TypeLayout)] -#[layout(free = "Box>")] -#[layout(bound = "T: ::const_type_layout::TypeLayout")] enum List { Cons { item: T, next: Box> }, Tail, } +#[derive(TypeLayout)] +#[layout(ground = "Leaf")] +enum Tree { + Node { + left: Box>, + right: Box>, + }, + Leaf { + item: T, + }, +} + #[repr(transparent)] #[derive(TypeLayout)] pub struct Reference<'r, T: 'r> { @@ -79,6 +120,25 @@ pub struct Reference<'r, T: 'r> { reference: std::marker::PhantomData<&'r T>, } +#[repr(transparent)] +#[derive(TypeLayout)] +pub struct MutReference<'r, T: 'r> { + pointer: *mut T, + reference: std::marker::PhantomData<&'r mut T>, +} + +#[derive(TypeLayout)] +pub struct Referencing<'r, T: 'r> { + c: &'r T, + m: &'r mut T, +} + +#[derive(TypeLayout)] +#[layout(free = "T")] +pub struct MyPhantomData { + marker: std::marker::PhantomData, +} + fn main() { println!("{:#?}", Foo1::TYPE_GRAPH); println!("{:#?}", Foo2::TYPE_GRAPH); @@ -86,24 +146,56 @@ fn main() { println!("{:#?}", Foo4::::TYPE_GRAPH); println!("{:#?}", Bar::TYPE_GRAPH); + println!("{:#?}", SingleUnion::TYPE_GRAPH); + println!("{:#?}", RecursiveRef::<'static>::TYPE_GRAPH); + println!("{:#?}", RecursivePtr::TYPE_GRAPH); println!("{:#?}", Never::TYPE_GRAPH); println!("{:#?}", Single::TYPE_GRAPH); + println!("{:#?}", Double::TYPE_GRAPH); + println!("{:#?}", WithDouble::TYPE_GRAPH); println!("{:#?}", Quo::::TYPE_GRAPH); + println!("{:#?}", NoUnit::::TYPE_GRAPH); println!("{:#?}", <()>::TYPE_GRAPH); println!("{:#?}", <[u32; 3]>::TYPE_GRAPH); - println!("{:#?}", >::TYPE_GRAPH); println!("{:#?}", >>::TYPE_GRAPH); println!("{:#?}", >::TYPE_GRAPH); println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + + println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); println!("{:#?}", >::TYPE_GRAPH); println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", ::TYPE_GRAPH); + println!("{:#?}", ::TYPE_GRAPH); + + println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + println!( + "{:#?}", + >::TYPE_GRAPH + ); + + println!("{:#?}", <*const u8>::TYPE_GRAPH); + println!("{:#?}", <*mut u8>::TYPE_GRAPH); + println!("{:#?}", <&u8>::TYPE_GRAPH); + println!("{:#?}", <&mut u8>::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); + + non_static_ref(&0); println!("{:#?}", >::TYPE_GRAPH); + println!("{:#?}", >::TYPE_GRAPH); let mut ascii_escaped_layout = String::new(); for b in SERIALISED_LIST_U8_LAYOUT { @@ -116,6 +208,10 @@ fn main() { println!("{}", ron_layout); } +fn non_static_ref<'a>(_val: &'a u128) { + println!("{:#?}", >::TYPE_GRAPH); +} + const SERIALISED_LIST_U8_LAYOUT: [u8; const_type_layout::serialised_type_graph_len::>()] = const_type_layout::serialise_type_graph::>();