diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 818f6c70de01e..4f38e06002367 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_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 0b501da7cd975..100824f4b9448 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_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index df23b4006b347..86f8061a24aff 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 3015188fd447a..ed3e117fcfabb 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -823,33 +823,59 @@ 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(); - 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 tcx.features().relaxed_struct_unsize { + for arg in tail_field_ty.walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + } } - } - 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() { + // 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) { - if unsizing_params.contains(i) { - return Err(Unimplemented); + 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); + } } } } 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 0000000000000..0cfd0a0b9784c --- /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 0000000000000..f62def47726f9 --- /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 new file mode 100644 index 0000000000000..83199e8112e71 --- /dev/null +++ b/src/test/ui/unsized/unchanged-param.rs @@ -0,0 +1,12 @@ +#![feature(relaxed_struct_unsize)] +// 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); +}