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:
Mara Bos 2021-02-05 12:25:52 +01:00 committed by GitHub
commit 676ff77fb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 20 deletions

View File

@ -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
// -------------------------------------------------------------------------

View File

@ -707,6 +707,18 @@ impl<T: Idx> GrowableBitSet<T> {
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);

View File

@ -907,6 +907,7 @@ symbols! {
register_attr,
register_tool,
relaxed_adts,
relaxed_struct_unsize,
rem,
rem_assign,
repr,

View File

@ -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 !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 tcx.features().relaxed_struct_unsize {
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);
}
}
// 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);
}
}
}
}

View File

@ -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);
}

View File

@ -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`.

View 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);
}