Skip to content

Commit

Permalink
Rollup merge of #80726 - lcnr:unsize-query, r=oli-obk
Browse files Browse the repository at this point in the history
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);
}
```
  • Loading branch information
m-ou-se authored Feb 5, 2021
2 parents deec6a9 + 031cce8 commit 676ff77
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 18 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// -------------------------------------------------------------------------
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_index/src/bit_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,7 @@ symbols! {
register_attr,
register_tool,
relaxed_adts,
relaxed_struct_unsize,
rem,
rem_assign,
repr,
Expand Down
62 changes: 44 additions & 18 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.rs
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions src/test/ui/unsized/unchanged-param.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 676ff77

Please sign in to comment.