about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-11-12 22:27:18 +0000
committerMichael Goulet <michael@errs.io>2022-11-18 18:23:48 +0000
commitda3c5397a614f790f0daaf61bfdc692b36719e01 (patch)
treec9d5a60b09264d18e19cf9af81f763d44ac431f7
parentfd3bfb35511cbcff59ce1454d3db627b576d7e92 (diff)
downloadrust-da3c5397a614f790f0daaf61bfdc692b36719e01.tar.gz
rust-da3c5397a614f790f0daaf61bfdc692b36719e01.zip
Enforce that dyn* casts are actually pointer-sized
-rw-r--r--compiler/rustc_hir/src/lang_items.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs33
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs27
-rw-r--r--library/core/src/marker.rs9
-rw-r--r--src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs15
-rw-r--r--src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr15
-rw-r--r--src/test/ui/dyn-star/check-size-at-cast-polymorphic.rs16
-rw-r--r--src/test/ui/dyn-star/check-size-at-cast.rs10
-rw-r--r--src/test/ui/dyn-star/check-size-at-cast.stderr11
10 files changed, 131 insertions, 8 deletions
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index a55224d1097..a748af65774 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -270,6 +270,8 @@ language_item_table! {
     TryTraitBranch,          sym::branch,              branch_fn,                  Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
     TryTraitFromYeet,        sym::from_yeet,           from_yeet_fn,               Target::Fn,             GenericRequirement::None;
 
+    PointerSized,            sym::pointer_sized,       pointer_sized,              Target::Trait,          GenericRequirement::Exact(0);
+
     PollReady,               sym::Ready,               poll_ready_variant,         Target::Variant,        GenericRequirement::None;
     PollPending,             sym::Pending,             poll_pending_variant,       Target::Variant,        GenericRequirement::None;
 
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 174b4331382..c1e4ab600f3 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -775,7 +775,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         // Check the obligations of the cast -- for example, when casting
         // `usize` to `dyn* Clone + 'static`:
-        let obligations = predicates
+        let mut obligations: Vec<_> = predicates
             .iter()
             .map(|predicate| {
                 // For each existential predicate (e.g., `?Self: Clone`) substitute
@@ -785,16 +785,33 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 let predicate = predicate.with_self_ty(self.tcx, a);
                 Obligation::new(self.tcx, self.cause.clone(), self.param_env, predicate)
             })
-            // Enforce the region bound (e.g., `usize: 'static`, in our example).
-            .chain([Obligation::new(
+            .chain([
+                // Enforce the region bound (e.g., `usize: 'static`, in our example).
+                Obligation::new(
+                    self.tcx,
+                    self.cause.clone(),
+                    self.param_env,
+                    ty::Binder::dummy(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
+                        a, b_region,
+                    ))),
+                ),
+            ])
+            .collect();
+
+        // Enforce that the type is `usize`/pointer-sized. For now, only those
+        // can be coerced to `dyn*`, except for `dyn* -> dyn*` upcasts.
+        if !a.is_dyn_star() {
+            obligations.push(Obligation::new(
                 self.tcx,
                 self.cause.clone(),
                 self.param_env,
-                self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
-                    ty::OutlivesPredicate(a, b_region),
-                ))),
-            )])
-            .collect();
+                ty::Binder::dummy(ty::TraitRef::new(
+                    self.tcx.require_lang_item(hir::LangItem::PointerSized, Some(self.cause.span)),
+                    self.tcx.mk_substs_trait(a, &[]),
+                ))
+                .to_poly_trait_predicate(),
+            ));
+        }
 
         Ok(InferOk {
             value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 02848bcffb2..199b2d32b9d 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1067,6 +1067,7 @@ symbols! {
         plugins,
         pointee_trait,
         pointer,
+        pointer_sized,
         poll,
         position,
         post_dash_lto: "post-lto",
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 3995ea58db1..c77f035d6b9 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -304,6 +304,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 self.assemble_candidates_for_transmutability(obligation, &mut candidates);
             } else if lang_items.tuple_trait() == Some(def_id) {
                 self.assemble_candidate_for_tuple(obligation, &mut candidates);
+            } else if lang_items.pointer_sized() == Some(def_id) {
+                self.assemble_candidate_for_ptr_sized(obligation, &mut candidates);
             } else {
                 if lang_items.clone_trait() == Some(def_id) {
                     // Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -1047,4 +1049,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             | ty::Placeholder(_) => {}
         }
     }
+
+    fn assemble_candidate_for_ptr_sized(
+        &mut self,
+        obligation: &TraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        // The regions of a type don't affect the size of the type
+        let self_ty = self
+            .tcx()
+            .erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate.self_ty()));
+
+        // But if there are inference variables, we have to wait until it's resolved.
+        if self_ty.has_non_region_infer() {
+            candidates.ambiguous = true;
+            return;
+        }
+
+        let usize_layout =
+            self.tcx().layout_of(ty::ParamEnv::empty().and(self.tcx().types.usize)).unwrap().layout;
+        if let Ok(layout) = self.tcx().layout_of(obligation.param_env.and(self_ty))
+            && layout.layout.size().bytes() == usize_layout.size().bytes()
+        {
+            candidates.vec.push(BuiltinCandidate { has_nested: false });
+        }
+    }
 }
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 3eff6033f8d..42c34280197 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -809,6 +809,15 @@ pub trait Destruct {}
 #[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
 pub trait Tuple {}
 
+/// A marker for things
+#[unstable(feature = "pointer_sized_trait", issue = "none")]
+#[cfg_attr(not(bootstrap), lang = "pointer_sized")]
+#[rustc_on_unimplemented(
+    message = "`{Self}` needs to be a pointer-sized type",
+    label = "`{Self}` needs to be a pointer-sized type"
+)]
+pub trait PointerSized {}
+
 /// Implementations of `Copy` for primitive types.
 ///
 /// Implementations that cannot be described in Rust
diff --git a/src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs b/src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs
new file mode 100644
index 00000000000..e19e36cc7d7
--- /dev/null
+++ b/src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.rs
@@ -0,0 +1,15 @@
+#![feature(dyn_star)]
+#![allow(incomplete_features)]
+
+use std::fmt::Debug;
+
+fn dyn_debug(_: (dyn* Debug + '_)) {
+
+}
+
+fn polymorphic<T: Debug + ?Sized>(t: &T) {
+    dyn_debug(t);
+    //~^ ERROR `&T` needs to be a pointer-sized type
+}
+
+fn main() {}
diff --git a/src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr b/src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr
new file mode 100644
index 00000000000..53ccbe43dcc
--- /dev/null
+++ b/src/test/ui/dyn-star/check-size-at-cast-polymorphic-bad.stderr
@@ -0,0 +1,15 @@
+error[E0277]: `&T` needs to be a pointer-sized type
+  --> $DIR/check-size-at-cast-polymorphic-bad.rs:11:15
+   |
+LL |     dyn_debug(t);
+   |               ^ `&T` needs to be a pointer-sized type
+   |
+   = help: the trait `PointerSized` is not implemented for `&T`
+help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
+   |
+LL | fn polymorphic<T: Debug + ?Sized>(t: &T) where &T: PointerSized {
+   |                                          ++++++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/dyn-star/check-size-at-cast-polymorphic.rs b/src/test/ui/dyn-star/check-size-at-cast-polymorphic.rs
new file mode 100644
index 00000000000..5c0a3d256f6
--- /dev/null
+++ b/src/test/ui/dyn-star/check-size-at-cast-polymorphic.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+#![feature(dyn_star)]
+#![allow(incomplete_features)]
+
+use std::fmt::Debug;
+
+fn dyn_debug(_: (dyn* Debug + '_)) {
+
+}
+
+fn polymorphic<T: Debug>(t: &T) {
+    dyn_debug(t);
+}
+
+fn main() {}
diff --git a/src/test/ui/dyn-star/check-size-at-cast.rs b/src/test/ui/dyn-star/check-size-at-cast.rs
new file mode 100644
index 00000000000..1f22f798361
--- /dev/null
+++ b/src/test/ui/dyn-star/check-size-at-cast.rs
@@ -0,0 +1,10 @@
+#![feature(dyn_star)]
+#![allow(incomplete_features)]
+
+use std::fmt::Debug;
+
+fn main() {
+    let i = [1, 2, 3, 4] as dyn* Debug;
+    //~^ ERROR `[i32; 4]` needs to be a pointer-sized type
+    dbg!(i);
+}
diff --git a/src/test/ui/dyn-star/check-size-at-cast.stderr b/src/test/ui/dyn-star/check-size-at-cast.stderr
new file mode 100644
index 00000000000..af2a1ccf71c
--- /dev/null
+++ b/src/test/ui/dyn-star/check-size-at-cast.stderr
@@ -0,0 +1,11 @@
+error[E0277]: `[i32; 4]` needs to be a pointer-sized type
+  --> $DIR/check-size-at-cast.rs:7:13
+   |
+LL |     let i = [1, 2, 3, 4] as dyn* Debug;
+   |             ^^^^^^^^^^^^ `[i32; 4]` needs to be a pointer-sized type
+   |
+   = help: the trait `PointerSized` is not implemented for `[i32; 4]`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.