Skip to content

Commit e210a80

Browse files
authored
Rollup merge of rust-lang#80726 - lcnr:unsize-query, r=oli-obk
relax adt unsizing requirements Changes unsizing of structs in case the last struct field shares generic params with other adt fields which do not change. This change is currently insta stable and changes the language, so it at least requires a lang fcp. I feel like the current state is fairly unintuitive. An example for what's now allowed would be https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6dd331d23f5c9ffc8c978175aae2e967 ```rust struct A<T, U: ?Sized>(T, B<T, U>); // previously ERR // struct A<T, U: ?Sized>(T, B<[u32; 1], U>); // ok struct B<T, U: ?Sized>(T, U); fn main() { let x = A([0; 1], B([0; 1], [0; 1])); let y: &A<[u32; 1], [u32]> = &x; assert_eq!(y.1.1.len(), 1); } ```
2 parents 9096865 + 031cce8 commit e210a80

File tree

7 files changed

+96
-18
lines changed

7 files changed

+96
-18
lines changed

compiler/rustc_feature/src/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,9 @@ declare_features! (
631631

632632
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
633633
(active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None),
634+
635+
/// Lessens the requirements for structs to implement `Unsize`.
636+
(active, relaxed_struct_unsize, "1.51.0", Some(1), None),
634637
// -------------------------------------------------------------------------
635638
// feature-group-end: actual feature gates
636639
// -------------------------------------------------------------------------

compiler/rustc_index/src/bit_set.rs

+12
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,18 @@ impl<T: Idx> GrowableBitSet<T> {
707707
self.bit_set.insert(elem)
708708
}
709709

710+
/// Returns `true` if the set has changed.
711+
#[inline]
712+
pub fn remove(&mut self, elem: T) -> bool {
713+
self.ensure(elem.index() + 1);
714+
self.bit_set.remove(elem)
715+
}
716+
717+
#[inline]
718+
pub fn is_empty(&self) -> bool {
719+
self.bit_set.is_empty()
720+
}
721+
710722
#[inline]
711723
pub fn contains(&self, elem: T) -> bool {
712724
let (word_index, mask) = word_index_and_mask(elem);

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ symbols! {
907907
register_attr,
908908
register_tool,
909909
relaxed_adts,
910+
relaxed_struct_unsize,
910911
rem,
911912
rem_assign,
912913
repr,

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+44-18
Original file line numberDiff line numberDiff line change
@@ -823,33 +823,59 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
823823
},
824824
};
825825

826+
// FIXME(eddyb) cache this (including computing `unsizing_params`)
827+
// by putting it in a query; it would only need the `DefId` as it
828+
// looks at declared field types, not anything substituted.
829+
826830
// The last field of the structure has to exist and contain type/const parameters.
827831
let (tail_field, prefix_fields) =
828832
def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?;
829833
let tail_field_ty = tcx.type_of(tail_field.did);
830834

831835
let mut unsizing_params = GrowableBitSet::new_empty();
832-
let mut found = false;
833-
for arg in tail_field_ty.walk() {
834-
if let Some(i) = maybe_unsizing_param_idx(arg) {
835-
unsizing_params.insert(i);
836-
found = true;
836+
if tcx.features().relaxed_struct_unsize {
837+
for arg in tail_field_ty.walk() {
838+
if let Some(i) = maybe_unsizing_param_idx(arg) {
839+
unsizing_params.insert(i);
840+
}
837841
}
838-
}
839-
if !found {
840-
return Err(Unimplemented);
841-
}
842842

843-
// Ensure none of the other fields mention the parameters used
844-
// in unsizing.
845-
// FIXME(eddyb) cache this (including computing `unsizing_params`)
846-
// by putting it in a query; it would only need the `DefId` as it
847-
// looks at declared field types, not anything substituted.
848-
for field in prefix_fields {
849-
for arg in tcx.type_of(field.did).walk() {
843+
// Ensure none of the other fields mention the parameters used
844+
// in unsizing.
845+
for field in prefix_fields {
846+
for arg in tcx.type_of(field.did).walk() {
847+
if let Some(i) = maybe_unsizing_param_idx(arg) {
848+
unsizing_params.remove(i);
849+
}
850+
}
851+
}
852+
853+
if unsizing_params.is_empty() {
854+
return Err(Unimplemented);
855+
}
856+
} else {
857+
let mut found = false;
858+
for arg in tail_field_ty.walk() {
850859
if let Some(i) = maybe_unsizing_param_idx(arg) {
851-
if unsizing_params.contains(i) {
852-
return Err(Unimplemented);
860+
unsizing_params.insert(i);
861+
found = true;
862+
}
863+
}
864+
if !found {
865+
return Err(Unimplemented);
866+
}
867+
868+
// Ensure none of the other fields mention the parameters used
869+
// in unsizing.
870+
// FIXME(eddyb) cache this (including computing `unsizing_params`)
871+
// by putting it in a query; it would only need the `DefId` as it
872+
// looks at declared field types, not anything substituted.
873+
for field in prefix_fields {
874+
for arg in tcx.type_of(field.did).walk() {
875+
if let Some(i) = maybe_unsizing_param_idx(arg) {
876+
if unsizing_params.contains(i) {
877+
return Err(Unimplemented);
878+
}
853879
}
854880
}
855881
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Test that we allow unsizing even if there is an unchanged param in the
2+
// field getting unsized.
3+
struct A<T, U: ?Sized + 'static>(T, B<T, U>);
4+
struct B<T, U: ?Sized>(T, U);
5+
6+
fn main() {
7+
let x: A<[u32; 1], [u32; 1]> = A([0; 1], B([0; 1], [0; 1]));
8+
let y: &A<[u32; 1], [u32]> = &x; //~ ERROR mismatched types
9+
assert_eq!(y.1.1.len(), 1);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/feature-gate-relaxed_struct_unsize.rs:8:34
3+
|
4+
LL | let y: &A<[u32; 1], [u32]> = &x;
5+
| ------------------- ^^ expected slice `[u32]`, found array `[u32; 1]`
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected reference `&A<[u32; 1], [u32]>`
10+
found reference `&A<[u32; 1], [u32; 1]>`
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0308`.
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(relaxed_struct_unsize)]
2+
// run-pass
3+
// Test that we allow unsizing even if there is an unchanged param in the
4+
// field getting unsized.
5+
struct A<T, U: ?Sized + 'static>(T, B<T, U>);
6+
struct B<T, U: ?Sized>(T, U);
7+
8+
fn main() {
9+
let x: A<[u32; 1], [u32; 1]> = A([0; 1], B([0; 1], [0; 1]));
10+
let y: &A<[u32; 1], [u32]> = &x;
11+
assert_eq!(y.1.1.len(), 1);
12+
}

0 commit comments

Comments
 (0)