about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src/traits/misc.rs
diff options
context:
space:
mode:
authorBoxy <supbscripter@gmail.com>2024-07-14 12:50:41 +0100
committerBoxy <rust@boxyuwu.dev>2024-07-17 11:01:29 +0100
commit42cc42b942088a94d95bc5169724d77429f20582 (patch)
treea965ba4940888954b2524ec4446bbf6dc290aafb /compiler/rustc_trait_selection/src/traits/misc.rs
parentcb12b52f160c633551bd6e5be8d4c6e2e8660950 (diff)
downloadrust-42cc42b942088a94d95bc5169724d77429f20582.tar.gz
rust-42cc42b942088a94d95bc5169724d77429f20582.zip
Forbid `!Sized` types and references
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/misc.rs')
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs117
1 files changed, 95 insertions, 22 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index baec2268629..3150bd894ec 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -19,6 +19,8 @@ pub enum CopyImplementationError<'tcx> {
 }
 
 pub enum ConstParamTyImplementationError<'tcx> {
+    TypeNotSized,
+    InvalidInnerTyOfBuiltinTy(Vec<(Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
     InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
     NotAnAdtOrBuiltinAllowed,
 }
@@ -89,33 +91,104 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
     self_type: Ty<'tcx>,
     parent_cause: ObligationCause<'tcx>,
 ) -> Result<(), ConstParamTyImplementationError<'tcx>> {
-    let (adt, args) = match self_type.kind() {
-        // `core` provides these impls.
-        ty::Uint(_)
-        | ty::Int(_)
-        | ty::Bool
-        | ty::Char
-        | ty::Str
-        | ty::Array(..)
-        | ty::Slice(_)
-        | ty::Ref(.., hir::Mutability::Not)
-        | ty::Tuple(_) => return Ok(()),
+    {
+        // Check for sizedness before recursing into ADT fields so that if someone tries to write:
+        // ```rust
+        //  #[derive(ConstParamTy)]
+        //  struct Foo([u8])
+        // ```
+        // They are told that const parameter types must be sized, instead of it saying that
+        // the trait implementation `[u8]: ConstParamTy` is not satisfied.
+        let infcx = tcx.infer_ctxt().build();
+        let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
 
-        &ty::Adt(adt, args) => (adt, args),
+        ocx.register_bound(
+            parent_cause.clone(),
+            param_env,
+            self_type,
+            tcx.require_lang_item(LangItem::Sized, Some(parent_cause.span)),
+        );
+
+        if !ocx.select_all_or_error().is_empty() {
+            return Err(ConstParamTyImplementationError::TypeNotSized);
+        }
+    };
+
+    let inner_tys: Vec<_> = match *self_type.kind() {
+        // Trivially okay as these types are all:
+        // - Sized
+        // - Contain no nested types
+        // - Have structural equality
+        ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()),
+
+        ty::Ref(..) => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
+
+        // Even if we currently require const params to be `Sized` we may aswell handle them correctly
+        // here anyway.
+        ty::Slice(inner_ty) | ty::Array(inner_ty, _) => vec![inner_ty],
+        // `str` morally acts like a newtype around `[u8]`
+        ty::Str => vec![Ty::new_slice(tcx, tcx.types.u8)],
+
+        ty::Tuple(inner_tys) => inner_tys.into_iter().collect(),
+
+        ty::Adt(adt, args) if adt.is_enum() || adt.is_struct() => {
+            all_fields_implement_trait(
+                tcx,
+                param_env,
+                self_type,
+                adt,
+                args,
+                parent_cause.clone(),
+                hir::LangItem::ConstParamTy,
+            )
+            .map_err(ConstParamTyImplementationError::InfrigingFields)?;
+
+            vec![]
+        }
 
         _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
     };
 
-    all_fields_implement_trait(
-        tcx,
-        param_env,
-        self_type,
-        adt,
-        args,
-        parent_cause,
-        hir::LangItem::ConstParamTy,
-    )
-    .map_err(ConstParamTyImplementationError::InfrigingFields)?;
+    let mut infringing_inner_tys = vec![];
+    for inner_ty in inner_tys {
+        // We use an ocx per inner ty for better diagnostics
+        let infcx = tcx.infer_ctxt().build();
+        let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
+
+        ocx.register_bound(
+            parent_cause.clone(),
+            param_env,
+            inner_ty,
+            tcx.require_lang_item(LangItem::ConstParamTy, Some(parent_cause.span)),
+        );
+
+        let errors = ocx.select_all_or_error();
+        if !errors.is_empty() {
+            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors)));
+            continue;
+        }
+
+        // Check regions assuming the self type of the impl is WF
+        let outlives_env = OutlivesEnvironment::with_bounds(
+            param_env,
+            infcx.implied_bounds_tys(
+                param_env,
+                parent_cause.body_id,
+                &FxIndexSet::from_iter([self_type]),
+            ),
+        );
+        let errors = infcx.resolve_regions(&outlives_env);
+        if !errors.is_empty() {
+            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
+            continue;
+        }
+    }
+
+    if !infringing_inner_tys.is_empty() {
+        return Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(
+            infringing_inner_tys,
+        ));
+    }
 
     Ok(())
 }