about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-03-14 17:24:57 +0100
committerGitHub <noreply@github.com>2022-03-14 17:24:57 +0100
commit774655da5fabdef01f862c50d1796abbe59efb7d (patch)
tree374bc036cc4afb9a781c72568eab013660251808
parent0e423932f89baeaa59ea710caeda7a3834506fdd (diff)
parent210e829010022d2a8e49a2519aec8ac6bee20ed1 (diff)
downloadrust-774655da5fabdef01f862c50d1796abbe59efb7d.tar.gz
rust-774655da5fabdef01f862c50d1796abbe59efb7d.zip
Rollup merge of #93977 - compiler-errors:sized-generic-metadata, r=wesleywiser
Type params and assoc types have unit metadata if they are sized

Extend the logic in `Pointee` projection to ensure that we can satisfy `<T as Pointee>::Metadata = ()` if `T: Sized`.

cc: `@SimonSapin` and #93959
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs35
-rw-r--r--src/test/ui/traits/pointee-tail-is-generic-errors.rs22
-rw-r--r--src/test/ui/traits/pointee-tail-is-generic-errors.stderr40
-rw-r--r--src/test/ui/traits/pointee-tail-is-generic.rs29
5 files changed, 134 insertions, 15 deletions
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index a49189ae92a..e89f68fd0ea 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2252,12 +2252,13 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    /// Returns the type of metadata for (potentially fat) pointers to this type.
+    /// Returns the type of metadata for (potentially fat) pointers to this type,
+    /// and a boolean signifying if this is conditional on this type being `Sized`.
     pub fn ptr_metadata_ty(
         self,
         tcx: TyCtxt<'tcx>,
         normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
-    ) -> Ty<'tcx> {
+    ) -> (Ty<'tcx>, bool) {
         let tail = tcx.struct_tail_with_normalize(self, normalize);
         match tail.kind() {
             // Sized types
@@ -2277,28 +2278,30 @@ impl<'tcx> Ty<'tcx> {
             | ty::Closure(..)
             | ty::Never
             | ty::Error(_)
+            // Extern types have metadata = ().
             | ty::Foreign(..)
             // If returned by `struct_tail_without_normalization` this is a unit struct
             // without any fields, or not a struct, and therefore is Sized.
             | ty::Adt(..)
             // If returned by `struct_tail_without_normalization` this is the empty tuple,
             // a.k.a. unit type, which is Sized
-            | ty::Tuple(..) => tcx.types.unit,
+            | ty::Tuple(..) => (tcx.types.unit, false),
 
-            ty::Str | ty::Slice(_) => tcx.types.usize,
+            ty::Str | ty::Slice(_) => (tcx.types.usize, false),
             ty::Dynamic(..) => {
                 let dyn_metadata = tcx.lang_items().dyn_metadata().unwrap();
-                tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()])
+                (tcx.type_of(dyn_metadata).subst(tcx, &[tail.into()]), false)
             },
 
-            ty::Projection(_)
-            | ty::Param(_)
-            | ty::Opaque(..)
-            | ty::Infer(ty::TyVar(_))
+            // type parameters only have unit metadata if they're sized, so return true
+            // to make sure we double check this during confirmation
+            ty::Param(_) |  ty::Projection(_) | ty::Opaque(..) => (tcx.types.unit, true),
+
+            ty::Infer(ty::TyVar(_))
             | ty::Bound(..)
             | ty::Placeholder(..)
             | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-                bug!("`ptr_metadata_ty` applied to unexpected type: {:?}", tail)
+                bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail)
             }
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index ea48fab1ceb..11f0507d6fd 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1469,6 +1469,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
 
                 let tail = selcx.tcx().struct_tail_with_normalize(self_ty, |ty| {
+                    // We throw away any obligations we get from this, since we normalize
+                    // and confirm these obligations once again during confirmation
                     normalize_with_depth(
                         selcx,
                         obligation.param_env,
@@ -1485,7 +1487,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     | ty::Int(_)
                     | ty::Uint(_)
                     | ty::Float(_)
-                    | ty::Foreign(_)
                     | ty::Str
                     | ty::Array(..)
                     | ty::Slice(_)
@@ -1498,6 +1499,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     | ty::Generator(..)
                     | ty::GeneratorWitness(..)
                     | ty::Never
+                    // Extern types have unit metadata, according to RFC 2850
+                    | ty::Foreign(_)
                     // If returned by `struct_tail_without_normalization` this is a unit struct
                     // without any fields, or not a struct, and therefore is Sized.
                     | ty::Adt(..)
@@ -1506,9 +1509,18 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     // Integers and floats are always Sized, and so have unit type metadata.
                     | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
 
-                    ty::Projection(..)
+                    // type parameters, opaques, and unnormalized projections have pointer
+                    // metadata if they're known (e.g. by the param_env) to be sized
+                    ty::Param(_) | ty::Projection(..) | ty::Opaque(..)
+                        if tail.is_sized(selcx.tcx().at(obligation.cause.span), obligation.param_env) =>
+                    {
+                        true
+                    }
+
+                    // FIXME(compiler-errors): are Bound and Placeholder types ever known sized?
+                    ty::Param(_)
+                    | ty::Projection(..)
                     | ty::Opaque(..)
-                    | ty::Param(..)
                     | ty::Bound(..)
                     | ty::Placeholder(..)
                     | ty::Infer(..)
@@ -1517,7 +1529,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                             candidate_set.mark_ambiguous();
                         }
                         false
-                    },
+                    }
                 }
             }
             super::ImplSource::Param(..) => {
@@ -1727,7 +1739,7 @@ fn confirm_pointee_candidate<'cx, 'tcx>(
     let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
 
     let mut obligations = vec![];
-    let metadata_ty = self_ty.ptr_metadata_ty(tcx, |ty| {
+    let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
         normalize_with_depth_to(
             selcx,
             obligation.param_env,
@@ -1737,6 +1749,19 @@ fn confirm_pointee_candidate<'cx, 'tcx>(
             &mut obligations,
         )
     });
+    if check_is_sized {
+        let sized_predicate = ty::Binder::dummy(ty::TraitRef::new(
+            tcx.require_lang_item(LangItem::Sized, None),
+            tcx.mk_substs_trait(self_ty, &[]),
+        ))
+        .without_const()
+        .to_predicate(tcx);
+        obligations.push(Obligation::new(
+            obligation.cause.clone(),
+            obligation.param_env,
+            sized_predicate,
+        ));
+    }
 
     let substs = tcx.mk_substs([self_ty.into()].iter());
     let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
diff --git a/src/test/ui/traits/pointee-tail-is-generic-errors.rs b/src/test/ui/traits/pointee-tail-is-generic-errors.rs
new file mode 100644
index 00000000000..d081721aca2
--- /dev/null
+++ b/src/test/ui/traits/pointee-tail-is-generic-errors.rs
@@ -0,0 +1,22 @@
+// edition:2018
+
+#![feature(ptr_metadata)]
+#![feature(type_alias_impl_trait)]
+
+type Opaque = impl std::fmt::Debug + ?Sized;
+
+fn opaque() -> &'static Opaque {
+    &[1] as &[i32]
+}
+
+fn a<T: ?Sized>() {
+    is_thin::<T>();
+    //~^ ERROR type mismatch resolving `<T as Pointee>::Metadata == ()`
+
+    is_thin::<Opaque>();
+    //~^ ERROR type mismatch resolving `<impl Debug + ?Sized as Pointee>::Metadata == ()`
+}
+
+fn is_thin<T: std::ptr::Pointee<Metadata = ()> + ?Sized>() {}
+
+fn main() {}
diff --git a/src/test/ui/traits/pointee-tail-is-generic-errors.stderr b/src/test/ui/traits/pointee-tail-is-generic-errors.stderr
new file mode 100644
index 00000000000..fa5fe67e53c
--- /dev/null
+++ b/src/test/ui/traits/pointee-tail-is-generic-errors.stderr
@@ -0,0 +1,40 @@
+error[E0271]: type mismatch resolving `<T as Pointee>::Metadata == ()`
+  --> $DIR/pointee-tail-is-generic-errors.rs:13:5
+   |
+LL |     is_thin::<T>();
+   |     ^^^^^^^^^^^^ expected `()`, found associated type
+   |
+   = note:    expected unit type `()`
+           found associated type `<T as Pointee>::Metadata`
+   = help: consider constraining the associated type `<T as Pointee>::Metadata` to `()`
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+note: required by a bound in `is_thin`
+  --> $DIR/pointee-tail-is-generic-errors.rs:20:33
+   |
+LL | fn is_thin<T: std::ptr::Pointee<Metadata = ()> + ?Sized>() {}
+   |                                 ^^^^^^^^^^^^^ required by this bound in `is_thin`
+
+error[E0271]: type mismatch resolving `<impl Debug + ?Sized as Pointee>::Metadata == ()`
+  --> $DIR/pointee-tail-is-generic-errors.rs:16:5
+   |
+LL | type Opaque = impl std::fmt::Debug + ?Sized;
+   |               ----------------------------- the found opaque type
+...
+LL |     is_thin::<Opaque>();
+   |     ^^^^^^^^^^^^^^^^^ expected `()`, found associated type
+   |
+   = note:    expected unit type `()`
+           found associated type `<impl Debug + ?Sized as Pointee>::Metadata`
+note: required by a bound in `is_thin`
+  --> $DIR/pointee-tail-is-generic-errors.rs:20:33
+   |
+LL | fn is_thin<T: std::ptr::Pointee<Metadata = ()> + ?Sized>() {}
+   |                                 ^^^^^^^^^^^^^ required by this bound in `is_thin`
+help: consider constraining the associated type `<impl Debug + ?Sized as Pointee>::Metadata` to `()`
+   |
+LL | type Opaque = impl std::fmt::Debug<Metadata = ()> + ?Sized;
+   |                                   +++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0271`.
diff --git a/src/test/ui/traits/pointee-tail-is-generic.rs b/src/test/ui/traits/pointee-tail-is-generic.rs
new file mode 100644
index 00000000000..e0da0fc3861
--- /dev/null
+++ b/src/test/ui/traits/pointee-tail-is-generic.rs
@@ -0,0 +1,29 @@
+// check-pass
+// edition:2018
+
+#![feature(ptr_metadata)]
+#![feature(type_alias_impl_trait)]
+
+type Opaque = impl std::future::Future;
+
+fn opaque() -> Opaque {
+    async {}
+}
+
+fn a<T>() {
+    // type parameter T is known to be sized
+    is_thin::<T>();
+    // tail of ADT (which is a type param) is known to be sized
+    is_thin::<std::cell::Cell<T>>();
+    // opaque type is known to be sized
+    is_thin::<Opaque>();
+}
+
+fn a2<T: Iterator>() {
+    // associated type is known to be sized
+    is_thin::<T::Item>();
+}
+
+fn is_thin<T: std::ptr::Pointee<Metadata = ()>>() {}
+
+fn main() {}