about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_feature/src/active.rs3
-rw-r--r--compiler/rustc_index/src/bit_set.rs12
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs62
-rw-r--r--src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.rs10
-rw-r--r--src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.stderr14
-rw-r--r--src/test/ui/unsized/unchanged-param.rs12
7 files changed, 96 insertions, 18 deletions
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 818f6c70de0..4f38e060023 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 0b501da7cd9..100824f4b94 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -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);
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index df23b4006b3..86f8061a24a 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 3015188fd44..ed3e117fcfa 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 00000000000..0cfd0a0b978
--- /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, 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);
+}
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 00000000000..f62def47726
--- /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 00000000000..83199e8112e
--- /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, 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);
+}