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, 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 @@ -278,6 +278,14 @@ pub struct CopyImplOnNonAdt { pub span: Span, } +#[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 { 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 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 ConstParamTy for &T {} + /// A common trait implemented by all function pointers. #[unstable( feature = "fn_ptr_trait",