diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 8f91a96f964a..e1c030d3e198 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -293,6 +293,8 @@ language_item_table! { PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0); + ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); + Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; 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_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 1d7965ff5f66..bda9879b5db1 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -35,6 +35,10 @@ hir_analysis_field_already_declared = hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` +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 + hir_analysis_ambiguous_lifetime_bound = ambiguous lifetime bound, explicit lifetime bound required diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 611ce13b739c..0f450ae67b7a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -1,9 +1,11 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. -use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; +use crate::errors::{ + ConstParamTyImplOnNonAdt, CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem, +}; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, MultiSpan}; +use rustc_errors::{struct_span_err, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; @@ -14,9 +16,11 @@ use rustc_infer::infer::{DefineOpaqueTypes, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::misc::{ - type_allowed_to_implement_copy, CopyImplementationError, InfringingFieldsReason, + type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, + ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, }; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; @@ -27,6 +31,7 @@ pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { Checker { tcx, trait_def_id } .check(lang_items.drop_trait(), visit_implementation_of_drop) .check(lang_items.copy_trait(), visit_implementation_of_copy) + .check(lang_items.const_param_ty_trait(), visit_implementation_of_const_param_ty) .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); } @@ -83,110 +88,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { Ok(()) => {} Err(CopyImplementationError::InfringingFields(fields)) => { - let mut err = struct_span_err!( - tcx.sess, - span, - E0204, - "the trait `Copy` cannot be implemented for this type" - ); - - // We'll try to suggest constraining type parameters to fulfill the requirements of - // their `Copy` implementation. - let mut errors: BTreeMap<_, Vec<_>> = Default::default(); - let mut bounds = vec![]; - - let mut seen_tys = FxHashSet::default(); - - for (field, ty, reason) in fields { - // Only report an error once per type. - if !seen_tys.insert(ty) { - continue; - } - - let field_span = tcx.def_span(field.did); - err.span_label(field_span, "this field does not implement `Copy`"); - - match reason { - InfringingFieldsReason::Fulfill(fulfillment_errors) => { - for error in fulfillment_errors { - let error_predicate = error.obligation.predicate; - // Only note if it's not the root obligation, otherwise it's trivial and - // should be self-explanatory (i.e. a field literally doesn't implement Copy). - - // FIXME: This error could be more descriptive, especially if the error_predicate - // contains a foreign type or if it's a deeply nested type... - if error_predicate != error.root_obligation.predicate { - errors - .entry((ty.to_string(), error_predicate.to_string())) - .or_default() - .push(error.obligation.cause.span); - } - if let ty::PredicateKind::Clause(ty::Clause::Trait( - ty::TraitPredicate { - trait_ref, - polarity: ty::ImplPolarity::Positive, - .. - }, - )) = error_predicate.kind().skip_binder() - { - let ty = trait_ref.self_ty(); - if let ty::Param(_) = ty.kind() { - bounds.push(( - format!("{ty}"), - trait_ref.print_only_trait_path().to_string(), - Some(trait_ref.def_id), - )); - } - } - } - } - InfringingFieldsReason::Regions(region_errors) => { - for error in region_errors { - let ty = ty.to_string(); - match error { - RegionResolutionError::ConcreteFailure(origin, a, b) => { - let predicate = format!("{b}: {a}"); - errors - .entry((ty.clone(), predicate.clone())) - .or_default() - .push(origin.span()); - if let ty::RegionKind::ReEarlyBound(ebr) = *b && ebr.has_name() { - bounds.push((b.to_string(), a.to_string(), None)); - } - } - RegionResolutionError::GenericBoundFailure(origin, a, b) => { - let predicate = format!("{a}: {b}"); - errors - .entry((ty.clone(), predicate.clone())) - .or_default() - .push(origin.span()); - if let infer::region_constraints::GenericKind::Param(_) = a { - bounds.push((a.to_string(), b.to_string(), None)); - } - } - _ => continue, - } - } - } - } - } - for ((ty, error_predicate), spans) in errors { - let span: MultiSpan = spans.into(); - err.span_note( - span, - &format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate), - ); - } - suggest_constraining_type_params( - tcx, - tcx.hir().get_generics(impl_did).expect("impls always have generics"), - &mut err, - bounds.iter().map(|(param, constraint, def_id)| { - (param.as_str(), constraint.as_str(), *def_id) - }), - None, - ); - err.emit(); + infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span); } Err(CopyImplementationError::NotAnAdt) => { tcx.sess.emit_err(CopyImplOnNonAdt { span }); @@ -197,6 +99,29 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { } } +fn visit_implementation_of_const_param_ty(tcx: TyCtxt<'_>, impl_did: LocalDefId) { + let self_type = tcx.type_of(impl_did).subst_identity(); + assert!(!self_type.has_escaping_bound_vars()); + + let param_env = tcx.param_env(impl_did); + + let span = match tcx.hir().expect_item(impl_did).expect_impl() { + hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return, + impl_ => impl_.self_ty.span, + }; + + let cause = traits::ObligationCause::misc(span, impl_did); + match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { + Ok(()) => {} + Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { + infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span); + } + Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { + tcx.sess.emit_err(ConstParamTyImplOnNonAdt { span }); + } + } +} + fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'_>, impl_did: LocalDefId) { debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); @@ -593,3 +518,119 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe CoerceUnsizedInfo { custom_kind: kind } } + +fn infringing_fields_error( + tcx: TyCtxt<'_>, + fields: Vec<(&ty::FieldDef, Ty<'_>, InfringingFieldsReason<'_>)>, + lang_item: LangItem, + impl_did: LocalDefId, + impl_span: Span, +) -> ErrorGuaranteed { + let trait_did = tcx.require_lang_item(lang_item, Some(impl_span)); + + let trait_name = tcx.def_path_str(trait_did); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0204, + "the trait `{trait_name}` cannot be implemented for this type" + ); + + // We'll try to suggest constraining type parameters to fulfill the requirements of + // their `Copy` implementation. + let mut errors: BTreeMap<_, Vec<_>> = Default::default(); + let mut bounds = vec![]; + + let mut seen_tys = FxHashSet::default(); + + for (field, ty, reason) in fields { + // Only report an error once per type. + if !seen_tys.insert(ty) { + continue; + } + + let field_span = tcx.def_span(field.did); + err.span_label(field_span, format!("this field does not implement `{trait_name}`")); + + match reason { + InfringingFieldsReason::Fulfill(fulfillment_errors) => { + for error in fulfillment_errors { + let error_predicate = error.obligation.predicate; + // Only note if it's not the root obligation, otherwise it's trivial and + // should be self-explanatory (i.e. a field literally doesn't implement Copy). + + // FIXME: This error could be more descriptive, especially if the error_predicate + // contains a foreign type or if it's a deeply nested type... + if error_predicate != error.root_obligation.predicate { + errors + .entry((ty.to_string(), error_predicate.to_string())) + .or_default() + .push(error.obligation.cause.span); + } + if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { + trait_ref, + polarity: ty::ImplPolarity::Positive, + .. + })) = error_predicate.kind().skip_binder() + { + let ty = trait_ref.self_ty(); + if let ty::Param(_) = ty.kind() { + bounds.push(( + format!("{ty}"), + trait_ref.print_only_trait_path().to_string(), + Some(trait_ref.def_id), + )); + } + } + } + } + InfringingFieldsReason::Regions(region_errors) => { + for error in region_errors { + let ty = ty.to_string(); + match error { + RegionResolutionError::ConcreteFailure(origin, a, b) => { + let predicate = format!("{b}: {a}"); + errors + .entry((ty.clone(), predicate.clone())) + .or_default() + .push(origin.span()); + if let ty::RegionKind::ReEarlyBound(ebr) = *b && ebr.has_name() { + bounds.push((b.to_string(), a.to_string(), None)); + } + } + RegionResolutionError::GenericBoundFailure(origin, a, b) => { + let predicate = format!("{a}: {b}"); + errors + .entry((ty.clone(), predicate.clone())) + .or_default() + .push(origin.span()); + if let infer::region_constraints::GenericKind::Param(_) = a { + bounds.push((a.to_string(), b.to_string(), None)); + } + } + _ => continue, + } + } + } + } + } + for ((ty, error_predicate), spans) in errors { + let span: MultiSpan = spans.into(); + err.span_note( + span, + format!("the `{trait_name}` impl for `{ty}` requires that `{error_predicate}`"), + ); + } + suggest_constraining_type_params( + tcx, + tcx.hir().get_generics(impl_did).expect("impls always have generics"), + &mut err, + bounds + .iter() + .map(|(param, constraint, def_id)| (param.as_str(), constraint.as_str(), *def_id)), + None, + ); + + err.emit() +} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2a3a683489dd..8dcf3b1670dc 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -107,6 +107,14 @@ pub struct CopyImplOnNonAdt { pub span: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_const_param_ty_impl_on_non_adt)] +pub struct ConstParamTyImplOnNonAdt { + #[primary_span] + #[label] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_trait_object_declared_with_no_traits, code = "E0224")] pub struct TraitObjectDeclaredWithNoTraits { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 70b9088de506..d2e23f845144 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -531,6 +531,7 @@ symbols! { const_mut_refs, const_panic, const_panic_fmt, + const_param_ty, const_precise_live_drops, const_raw_ptr_deref, const_raw_ptr_to_usize_cast, diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 63949843aedc..3c43758e50da 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -2,13 +2,14 @@ use crate::traits::{self, ObligationCause, ObligationCtxt}; +use hir::LangItem; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::query::NoSolution; use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError}; -use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, AdtDef, GenericArg, List, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::DUMMY_SP; use super::outlives_bounds::InferCtxtExt; @@ -19,6 +20,11 @@ pub enum CopyImplementationError<'tcx> { HasDestructor, } +pub enum ConstParamTyImplementationError<'tcx> { + InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), + NotAnAdtOrBuiltinAllowed, +} + pub enum InfringingFieldsReason<'tcx> { Fulfill(Vec>), Regions(Vec>), @@ -27,7 +33,10 @@ pub enum InfringingFieldsReason<'tcx> { /// Checks that the fields of the type (an ADT) all implement copy. /// /// If fields don't implement copy, return an error containing a list of -/// those violating fields. If it's not an ADT, returns `Err(NotAnAdt)`. +/// those violating fields. +/// +/// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`, +/// a reference or an array returns `Err(NotAnAdt)`. pub fn type_allowed_to_implement_copy<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -47,12 +56,75 @@ pub fn type_allowed_to_implement_copy<'tcx>( | ty::Ref(_, _, hir::Mutability::Not) | ty::Array(..) => return Ok(()), - ty::Adt(adt, substs) => (adt, substs), + &ty::Adt(adt, substs) => (adt, substs), _ => return Err(CopyImplementationError::NotAnAdt), }; - let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span)); + all_fields_implement_trait( + tcx, + param_env, + self_type, + adt, + substs, + parent_cause, + hir::LangItem::Copy, + ) + .map_err(CopyImplementationError::InfringingFields)?; + + if adt.has_dtor(tcx) { + return Err(CopyImplementationError::HasDestructor); + } + + Ok(()) +} + +/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`. +/// +/// If fields don't implement `ConstParamTy`, return an error containing a list of +/// those violating fields. +/// +/// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`. +pub fn type_allowed_to_implement_const_param_ty<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, + parent_cause: ObligationCause<'tcx>, +) -> Result<(), ConstParamTyImplementationError<'tcx>> { + let (adt, substs) = match self_type.kind() { + // `core` provides these impls. + ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()), + + &ty::Adt(adt, substs) => (adt, substs), + + _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed), + }; + + all_fields_implement_trait( + tcx, + param_env, + self_type, + adt, + substs, + parent_cause, + hir::LangItem::Copy, + ) + .map_err(ConstParamTyImplementationError::InfrigingFields)?; + + Ok(()) +} + +/// Check that all fields of a given `adt` implement `lang_item` trait. +pub fn all_fields_implement_trait<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, + adt: AdtDef<'tcx>, + substs: &'tcx List>, + parent_cause: ObligationCause<'tcx>, + lang_item: LangItem, +) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> { + let trait_def_id = tcx.require_lang_item(lang_item, Some(parent_cause.span)); let mut infringing = Vec::new(); for variant in adt.variants() { @@ -93,7 +165,7 @@ pub fn type_allowed_to_implement_copy<'tcx>( // between expected and found const-generic types. Don't report an // additional copy error here, since it's not typically useful. if !normalization_errors.is_empty() || ty.references_error() { - tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking Copy implementation")); + tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id))); continue; } @@ -101,7 +173,7 @@ pub fn type_allowed_to_implement_copy<'tcx>( ObligationCause::dummy_with_span(field_ty_span), param_env, ty, - copy_def_id, + trait_def_id, ); let errors = ocx.select_all_or_error(); if !errors.is_empty() { @@ -124,15 +196,7 @@ pub fn type_allowed_to_implement_copy<'tcx>( } } - if !infringing.is_empty() { - return Err(CopyImplementationError::InfringingFields(infringing)); - } - - if adt.has_dtor(tcx) { - return Err(CopyImplementationError::HasDestructor); - } - - Ok(()) + if infringing.is_empty() { Ok(()) } else { Err(infringing) } } pub fn check_tys_might_be_eq<'tcx>( diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 1d33236c269e..ff8653c08204 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -912,6 +912,40 @@ pub trait Tuple {} )] pub trait PointerLike {} +/// A marker for types which can be used as types of `const` generic parameters. +#[cfg_attr(not(bootstrap), lang = "const_param_ty")] +#[unstable(feature = "const_param_ty_trait", issue = "none")] +#[rustc_on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] +pub trait ConstParamTy: StructuralEq {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for usize {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for u8 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for u16 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for u32 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for u64 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for u128 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for isize {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for i8 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for i16 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for i32 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for i64 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for i128 {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for bool {} +#[unstable(feature = "const_param_ty_trait", issue = "none")] +impl ConstParamTy for char {} + /// Implementations of `Copy` for primitive types. /// /// Implementations that cannot be described in Rust diff --git a/tests/ui/const-generics/const_patam_ty_impl_bad_field.rs b/tests/ui/const-generics/const_patam_ty_impl_bad_field.rs new file mode 100644 index 000000000000..37cfa1aa7f53 --- /dev/null +++ b/tests/ui/const-generics/const_patam_ty_impl_bad_field.rs @@ -0,0 +1,12 @@ +#![feature(const_param_ty_trait)] + +#[derive(PartialEq)] +struct NotParam; + +#[derive(PartialEq)] +struct CantParam(NotParam); + +impl std::marker::ConstParamTy for CantParam {} +//~^ error: the trait `ConstParamTy` may not be implemented for this type + +fn main() {} diff --git a/tests/ui/const-generics/const_patam_ty_impl_bad_field.stderr b/tests/ui/const-generics/const_patam_ty_impl_bad_field.stderr new file mode 100644 index 000000000000..dd150deffc2d --- /dev/null +++ b/tests/ui/const-generics/const_patam_ty_impl_bad_field.stderr @@ -0,0 +1,12 @@ +error[E0204]: the trait `ConstParamTy` may not be implemented for this type + --> $DIR/const_patam_ty_impl_bad_field.rs:9:36 + | +LL | struct CantParam(NotParam); + | -------- this field does not implement `ConstParamTy` +LL | +LL | impl std::marker::ConstParamTy for CantParam {} + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0204`.