about summary refs log tree commit diff
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
parentcb12b52f160c633551bd6e5be8d4c6e2e8660950 (diff)
downloadrust-42cc42b942088a94d95bc5169724d77429f20582.tar.gz
rust-42cc42b942088a94d95bc5169724d77429f20582.zip
Forbid `!Sized` types and references
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl4
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs42
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs117
-rw-r--r--library/core/src/marker.rs15
7 files changed, 167 insertions, 39 deletions
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 1db3774222a..7f0c5f6b7a0 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -339,8 +339,8 @@ declare_features! (
     (unstable, abi_riscv_interrupt, "1.73.0", Some(111889)),
     /// Allows `extern "x86-interrupt" fn()`.
     (unstable, abi_x86_interrupt, "1.17.0", Some(40180)),
-    /// Allows additional const parameter types, such as `&'static str` or user defined types
-    (incomplete, adt_const_params, "1.56.0", Some(95174)),
+    /// Allows additional const parameter types, such as `[u8; 10]` or user defined types
+    (unstable, adt_const_params, "1.56.0", Some(95174)),
     /// Allows defining an `#[alloc_error_handler]`.
     (unstable, alloc_error_handler, "1.29.0", Some(51540)),
     /// Allows trait methods with arbitrary self types.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 24c5377a3b1..700b3a2fb00 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -78,6 +78,10 @@ hir_analysis_const_impl_for_non_const_trait =
     .note = marking a trait with `#[const_trait]` ensures all default method bodies are `const`
     .adding = adding a non-const method body in the future would be a breaking change
 
+hir_analysis_const_param_ty_impl_on_unsized =
+    the trait `ConstParamTy` may not be implemented for this type
+    .label = type is not `Sized`
+
 hir_analysis_const_param_ty_impl_on_non_adt =
     the trait `ConstParamTy` may not be implemented for this type
     .label = type is not a structure or enumeration
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index b2ef07d65c5..ecd2834dfd1 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -924,8 +924,6 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
 
             if tcx.features().adt_const_params {
                 enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
-                    let trait_def_id =
-                        tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span));
                     wfcx.register_bound(
                         ObligationCause::new(
                             hir_ty.span,
@@ -934,7 +932,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
                         ),
                         wfcx.param_env,
                         ty,
-                        trait_def_id,
+                        tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span)),
+                    );
+                    wfcx.register_bound(
+                        ObligationCause::new(hir_ty.span, param.def_id, ObligationCauseCode::Misc),
+                        wfcx.param_env,
+                        ty,
+                        tcx.require_lang_item(LangItem::Sized, Some(hir_ty.span)),
                     );
                     Ok(())
                 })
@@ -965,7 +969,11 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
                     cause,
                 ) {
                     // Can never implement `ConstParamTy`, don't suggest anything.
-                    Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false,
+                    Err(
+                        ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed
+                        | ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(..)
+                        | ConstParamTyImplementationError::TypeNotSized,
+                    ) => false,
                     // May be able to implement `ConstParamTy`. Only emit the feature help
                     // if the type is local, since the user may be able to fix the local type.
                     Err(ConstParamTyImplementationError::InfrigingFields(..)) => {
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 2ecb170ec89..16745668f8c 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -103,7 +103,13 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
         Ok(()) => Ok(()),
         Err(CopyImplementationError::InfringingFields(fields)) => {
             let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
-            Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span))
+            Err(infringing_fields_error(
+                tcx,
+                fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)),
+                LangItem::Copy,
+                impl_did,
+                span,
+            ))
         }
         Err(CopyImplementationError::NotAnAdt) => {
             let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
@@ -125,7 +131,7 @@ fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), E
 
     let param_env = tcx.param_env(impl_did);
 
-    if let ty::ImplPolarity::Negative = header.polarity {
+    if let ty::ImplPolarity::Negative | ty::ImplPolarity::Reservation = header.polarity {
         return Ok(());
     }
 
@@ -134,12 +140,32 @@ fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), E
         Ok(()) => Ok(()),
         Err(ConstParamTyImplementationError::InfrigingFields(fields)) => {
             let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
-            Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span))
+            Err(infringing_fields_error(
+                tcx,
+                fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)),
+                LangItem::ConstParamTy,
+                impl_did,
+                span,
+            ))
         }
         Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => {
             let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
             Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span }))
         }
+        Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(infringing_tys)) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
+            Err(infringing_fields_error(
+                tcx,
+                infringing_tys.into_iter().map(|(ty, reason)| (span, ty, reason)),
+                LangItem::ConstParamTy,
+                impl_did,
+                span,
+            ))
+        }
+        Err(ConstParamTyImplementationError::TypeNotSized) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
+            Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnUnsized { span }))
+        }
     }
 }
 
@@ -501,9 +527,9 @@ pub fn coerce_unsized_info<'tcx>(
     Ok(CoerceUnsizedInfo { custom_kind: kind })
 }
 
-fn infringing_fields_error(
-    tcx: TyCtxt<'_>,
-    fields: Vec<(&ty::FieldDef, Ty<'_>, InfringingFieldsReason<'_>)>,
+fn infringing_fields_error<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    infringing_tys: impl Iterator<Item = (Span, Ty<'tcx>, InfringingFieldsReason<'tcx>)>,
     lang_item: LangItem,
     impl_did: LocalDefId,
     impl_span: Span,
@@ -521,13 +547,13 @@ fn infringing_fields_error(
 
     let mut label_spans = Vec::new();
 
-    for (field, ty, reason) in fields {
+    for (span, ty, reason) in infringing_tys {
         // Only report an error once per type.
         if !seen_tys.insert(ty) {
             continue;
         }
 
-        label_spans.push(tcx.def_span(field.did));
+        label_spans.push(span);
 
         match reason {
             InfringingFieldsReason::Fulfill(fulfillment_errors) => {
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 0ee87a13e9e..7f8aefd564d 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -279,6 +279,14 @@ pub struct CopyImplOnNonAdt {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_analysis_const_param_ty_impl_on_unsized)]
+pub struct ConstParamTyImplOnUnsized {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_analysis_const_param_ty_impl_on_non_adt)]
 pub struct ConstParamTyImplOnNonAdt {
     #[primary_span]
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(())
 }
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 21abd7c036b..70bd4f67ebf 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -995,13 +995,22 @@ marker_impls! {
         isize, i8, i16, i32, i64, i128,
         bool,
         char,
-        str /* Technically requires `[u8]: ConstParamTy` */,
         (),
         {T: ConstParamTy, const N: usize} [T; N],
-        {T: ConstParamTy} [T],
-        {T: ?Sized + ConstParamTy} &T,
 }
 
+#[unstable(feature = "adt_const_params", issue = "95174")]
+#[rustc_reservation_impl = "types that are not `Sized` are not supported as the type of a const generic parameter"]
+impl<T> ConstParamTy for [T] {}
+
+#[unstable(feature = "adt_const_params", issue = "95174")]
+#[rustc_reservation_impl = "types that are not `Sized` are not supported as the type of a const generic parameter"]
+impl ConstParamTy for str {}
+
+#[unstable(feature = "adt_const_params", issue = "95174")]
+#[rustc_reservation_impl = "references are not supported as the type of a const generic parameter"]
+impl<T: ?Sized> ConstParamTy for &T {}
+
 /// A common trait implemented by all function pointers.
 #[unstable(
     feature = "fn_ptr_trait",