Rollup merge of #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); } ```
This commit is contained in:
commit
676ff77fb7
@ -631,6 +631,9 @@ declare_features! (
|
|||||||
|
|
||||||
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
|
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
|
||||||
(active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None),
|
(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
|
// feature-group-end: actual feature gates
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
@ -707,6 +707,18 @@ impl<T: Idx> GrowableBitSet<T> {
|
|||||||
self.bit_set.insert(elem)
|
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]
|
#[inline]
|
||||||
pub fn contains(&self, elem: T) -> bool {
|
pub fn contains(&self, elem: T) -> bool {
|
||||||
let (word_index, mask) = word_index_and_mask(elem);
|
let (word_index, mask) = word_index_and_mask(elem);
|
||||||
|
@ -907,6 +907,7 @@ symbols! {
|
|||||||
register_attr,
|
register_attr,
|
||||||
register_tool,
|
register_tool,
|
||||||
relaxed_adts,
|
relaxed_adts,
|
||||||
|
relaxed_struct_unsize,
|
||||||
rem,
|
rem,
|
||||||
rem_assign,
|
rem_assign,
|
||||||
repr,
|
repr,
|
||||||
|
@ -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.
|
// The last field of the structure has to exist and contain type/const parameters.
|
||||||
let (tail_field, prefix_fields) =
|
let (tail_field, prefix_fields) =
|
||||||
def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?;
|
def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?;
|
||||||
let tail_field_ty = tcx.type_of(tail_field.did);
|
let tail_field_ty = tcx.type_of(tail_field.did);
|
||||||
|
|
||||||
let mut unsizing_params = GrowableBitSet::new_empty();
|
let mut unsizing_params = GrowableBitSet::new_empty();
|
||||||
let mut found = false;
|
if tcx.features().relaxed_struct_unsize {
|
||||||
for arg in tail_field_ty.walk() {
|
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 let Some(i) = maybe_unsizing_param_idx(arg) {
|
||||||
if unsizing_params.contains(i) {
|
unsizing_params.insert(i);
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
// Test that we allow unsizing even if there is an unchanged param in the
|
||||||
|
// field getting unsized.
|
||||||
|
struct A<T, U: ?Sized + 'static>(T, B<T, U>);
|
||||||
|
struct B<T, U: ?Sized>(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);
|
||||||
|
}
|
@ -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`.
|
12
src/test/ui/unsized/unchanged-param.rs
Normal file
12
src/test/ui/unsized/unchanged-param.rs
Normal file
@ -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, U: ?Sized + 'static>(T, B<T, U>);
|
||||||
|
struct B<T, U: ?Sized>(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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user