From 50e394a05e3a908e11b4cbeda28b4f8f4c0ea6ed Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Tue, 5 Jan 2021 16:33:10 +0100 Subject: [PATCH 1/2] relax adt unsizing requirements --- compiler/rustc_index/src/bit_set.rs | 12 ++++++++++++ .../src/traits/select/confirmation.rs | 13 +++++-------- src/test/ui/unsized/unchanged-param.rs | 11 +++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/unsized/unchanged-param.rs diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 0b501da7cd9..100824f4b94 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -707,6 +707,18 @@ impl GrowableBitSet { self.bit_set.insert(elem) } + /// Returns `true` if the set has changed. + #[inline] + pub fn remove(&mut self, elem: T) -> bool { + self.ensure(elem.index() + 1); + self.bit_set.remove(elem) + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.bit_set.is_empty() + } + #[inline] pub fn contains(&self, elem: T) -> bool { let (word_index, mask) = word_index_and_mask(elem); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 3015188fd44..3e3e945f654 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -829,16 +829,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tail_field_ty = tcx.type_of(tail_field.did); let mut unsizing_params = GrowableBitSet::new_empty(); - let mut found = false; for arg in tail_field_ty.walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { unsizing_params.insert(i); - found = true; } } - if !found { - return Err(Unimplemented); - } // Ensure none of the other fields mention the parameters used // in unsizing. @@ -848,13 +843,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for field in prefix_fields { for arg in tcx.type_of(field.did).walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { - if unsizing_params.contains(i) { - return Err(Unimplemented); - } + unsizing_params.remove(i); } } } + if unsizing_params.is_empty() { + return Err(Unimplemented); + } + // Extract `TailField` and `TailField` from `Struct` and `Struct`. let source_tail = tail_field_ty.subst(tcx, substs_a); let target_tail = tail_field_ty.subst(tcx, substs_b); diff --git a/src/test/ui/unsized/unchanged-param.rs b/src/test/ui/unsized/unchanged-param.rs new file mode 100644 index 00000000000..93c7af68ac3 --- /dev/null +++ b/src/test/ui/unsized/unchanged-param.rs @@ -0,0 +1,11 @@ +// run-pass +// Test that we allow unsizing even if there is an unchanged param in the +// field getting unsized. +struct A(T, B); +struct B(T, U); + +fn main() { + let x: A<[u32; 1], [u32; 1]> = A([0; 1], B([0; 1], [0; 1])); + let y: &A<[u32; 1], [u32]> = &x; + assert_eq!(y.1.1.len(), 1); +} From 031cce8cfc7fef922989e8b820da236ee17e016a Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Wed, 3 Feb 2021 23:56:47 +0100 Subject: [PATCH 2/2] add `relaxed_struct_unsize` feature gate --- compiler/rustc_feature/src/active.rs | 3 + compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/select/confirmation.rs | 63 ++++++++++++++----- .../feature-gate-relaxed_struct_unsize.rs | 10 +++ .../feature-gate-relaxed_struct_unsize.stderr | 14 +++++ src/test/ui/unsized/unchanged-param.rs | 1 + 6 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.rs create mode 100644 src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 818f6c70de0..4f38e060023 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -631,6 +631,9 @@ declare_features! ( /// Allows `extern "C-cmse-nonsecure-call" fn()`. (active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None), + + /// Lessens the requirements for structs to implement `Unsize`. + (active, relaxed_struct_unsize, "1.51.0", Some(1), None), // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index df23b4006b3..86f8061a24a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -907,6 +907,7 @@ symbols! { register_attr, register_tool, relaxed_adts, + relaxed_struct_unsize, rem, rem_assign, repr, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 3e3e945f654..ed3e117fcfa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -823,33 +823,62 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }, }; + // FIXME(eddyb) cache this (including computing `unsizing_params`) + // by putting it in a query; it would only need the `DefId` as it + // looks at declared field types, not anything substituted. + // The last field of the structure has to exist and contain type/const parameters. let (tail_field, prefix_fields) = def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?; let tail_field_ty = tcx.type_of(tail_field.did); let mut unsizing_params = GrowableBitSet::new_empty(); - for arg in tail_field_ty.walk() { - if let Some(i) = maybe_unsizing_param_idx(arg) { - unsizing_params.insert(i); - } - } - - // Ensure none of the other fields mention the parameters used - // in unsizing. - // FIXME(eddyb) cache this (including computing `unsizing_params`) - // by putting it in a query; it would only need the `DefId` as it - // looks at declared field types, not anything substituted. - for field in prefix_fields { - for arg in tcx.type_of(field.did).walk() { + if tcx.features().relaxed_struct_unsize { + for arg in tail_field_ty.walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { - unsizing_params.remove(i); + unsizing_params.insert(i); } } - } - if unsizing_params.is_empty() { - return Err(Unimplemented); + // Ensure none of the other fields mention the parameters used + // in unsizing. + for field in prefix_fields { + for arg in tcx.type_of(field.did).walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.remove(i); + } + } + } + + if unsizing_params.is_empty() { + return Err(Unimplemented); + } + } else { + let mut found = false; + for arg in tail_field_ty.walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + found = true; + } + } + if !found { + return Err(Unimplemented); + } + + // Ensure none of the other fields mention the parameters used + // in unsizing. + // FIXME(eddyb) cache this (including computing `unsizing_params`) + // by putting it in a query; it would only need the `DefId` as it + // looks at declared field types, not anything substituted. + for field in prefix_fields { + for arg in tcx.type_of(field.did).walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + if unsizing_params.contains(i) { + return Err(Unimplemented); + } + } + } + } } // Extract `TailField` and `TailField` from `Struct` and `Struct`. diff --git a/src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.rs b/src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.rs new file mode 100644 index 00000000000..0cfd0a0b978 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.rs @@ -0,0 +1,10 @@ +// Test that we allow unsizing even if there is an unchanged param in the +// field getting unsized. +struct A(T, B); +struct B(T, U); + +fn main() { + let x: A<[u32; 1], [u32; 1]> = A([0; 1], B([0; 1], [0; 1])); + let y: &A<[u32; 1], [u32]> = &x; //~ ERROR mismatched types + assert_eq!(y.1.1.len(), 1); +} diff --git a/src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.stderr b/src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.stderr new file mode 100644 index 00000000000..f62def47726 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-relaxed_struct_unsize.rs:8:34 + | +LL | let y: &A<[u32; 1], [u32]> = &x; + | ------------------- ^^ expected slice `[u32]`, found array `[u32; 1]` + | | + | expected due to this + | + = note: expected reference `&A<[u32; 1], [u32]>` + found reference `&A<[u32; 1], [u32; 1]>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/unsized/unchanged-param.rs b/src/test/ui/unsized/unchanged-param.rs index 93c7af68ac3..83199e8112e 100644 --- a/src/test/ui/unsized/unchanged-param.rs +++ b/src/test/ui/unsized/unchanged-param.rs @@ -1,3 +1,4 @@ +#![feature(relaxed_struct_unsize)] // run-pass // Test that we allow unsizing even if there is an unchanged param in the // field getting unsized.