From d81f8d362929673d9925f65d20075250d84a2872 Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:30:49 -0800 Subject: [PATCH 1/3] Recurse into refs when checking impls of ConstParamTy on refs --- compiler/rustc_hir_analysis/messages.ftl | 5 +++ .../rustc_hir_analysis/src/check/wfcheck.rs | 4 ++ .../src/coherence/builtin.rs | 9 ++++ compiler/rustc_hir_analysis/src/errors.rs | 16 +++++++ .../rustc_trait_selection/src/traits/misc.rs | 45 +++++++++++++++++++ .../const_param_ty_impl_bad_referee.rs | 26 +++++++++++ .../const_param_ty_impl_bad_referee.stderr | 27 +++++++++++ .../issues/issue-63322-forbid-dyn.min.stderr | 1 - 8 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs create mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 432c9c12cbfbc..3c59d865f1840 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -78,6 +78,11 @@ 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_infringing_referee = + the trait `ConstParamTy` cannot be implemented for this type + .label = the trait `ConstParamTy` is not implemented for this {$def_descr} + .suggestion = consider annotating this {$def_descr} with `#[derive(ConstParamTy)]` + 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 70213ee0614f8..774b94cc35054 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -987,6 +987,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ty_is_local(ty) } + // Same as above. + Err(ConstParamTyImplementationError::InfringingReferee(def_id)) => { + def_id.is_local() + } // Implments `ConstParamTy`, suggest adding the feature to enable. Ok(..) => true, }; diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 5a3878445937b..06915dc85473f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -135,6 +135,15 @@ fn visit_implementation_of_const_param_ty( Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) } + Err(ConstParamTyImplementationError::InfringingReferee(def_id)) => { + let def_span = tcx.def_span(def_id); + tcx.dcx().emit_err(errors::ConstParamTyImplOnInfringingReferee { + span, + def_span, + def_descr: tcx.def_descr(def_id), + sugg: def_id.is_local().then_some(def_span.shrink_to_lo()), + }); + } } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index f14390b77c6e2..a9982c2f863b9 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -227,6 +227,22 @@ pub struct ConstParamTyImplOnNonAdt { pub span: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_const_param_ty_impl_on_infringing_referee)] +pub struct ConstParamTyImplOnInfringingReferee { + #[primary_span] + pub span: Span, + #[label] + pub def_span: Span, + pub def_descr: &'static str, + #[suggestion( + applicability = "maybe-incorrect", + style = "verbose", + code = "#[derive(ConstParamTy)]\n" + )] + pub sugg: Option, +} + #[derive(Diagnostic)] #[diag(hir_analysis_trait_object_declared_with_no_traits, code = "E0224")] pub struct TraitObjectDeclaredWithNoTraits { diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 0cd376fcbbdc8..644ef690815b3 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -5,6 +5,7 @@ use crate::traits::{self, ObligationCause, ObligationCtxt}; use hir::LangItem; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::query::NoSolution; @@ -22,6 +23,7 @@ pub enum CopyImplementationError<'tcx> { pub enum ConstParamTyImplementationError<'tcx> { InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), + InfringingReferee(DefId), NotAnAdtOrBuiltinAllowed, } @@ -92,6 +94,49 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( parent_cause: ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>> { let (adt, args) = match self_type.kind() { + // Special case for impls like `impl ConstParamTy for &Foo`, where + // Foo doesn't `impl ConstParamTy`. These kinds of impls aren't caught + // by coherence checking since `core`'s impl is restricted to &T where + // T: ConstParamTy. + // `has_concrete_skeleton()` avoids reporting `core`'s blanket impl. + &ty::Ref(.., ty, hir::Mutability::Not) if ty.peel_refs().has_concrete_skeleton() => { + let ty = ty.peel_refs(); + type_allowed_to_implement_const_param_ty(tcx, param_env, ty, parent_cause)?; + // Check the ty behind the ref impls `ConstParamTy` itself. This additional + // logic is needed when checking refs because we need to check not only if + // all fields implement ConstParamTy, but also that the type itself implements + // ConstParamTy. Simply recursing into the ref only checks the former. + if !ty.references_error() + && let &ty::Adt(adt, _) = ty.kind() + { + let adt_did = adt.did(); + let adt_span = tcx.def_span(adt_did); + let trait_did = tcx.require_lang_item(hir::LangItem::ConstParamTy, Some(adt_span)); + + let infcx = tcx.infer_ctxt().build(); + let ocx = traits::ObligationCtxt::new(&infcx); + + let ty = ocx.normalize(&ObligationCause::dummy_with_span(adt_span), param_env, ty); + let norm_errs = ocx.select_where_possible(); + if !norm_errs.is_empty() || ty.references_error() { + return Ok(()); + } + + ocx.register_bound( + ObligationCause::dummy_with_span(adt_span), + param_env, + ty, + trait_did, + ); + let errs = ocx.select_all_or_error(); + if !errs.is_empty() { + return Err(ConstParamTyImplementationError::InfringingReferee(adt_did)); + } + } + + return Ok(()); + } + // `core` provides these impls. ty::Uint(_) | ty::Int(_) diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs new file mode 100644 index 0000000000000..37bd6f63950b8 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs @@ -0,0 +1,26 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +// #112124 + +struct Foo; + +impl ConstParamTy for &Foo {} +//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type + +// #119299 (ICE) + +#[derive(Eq, PartialEq)] +struct Wrapper(*const i32, usize); + +impl ConstParamTy for &Wrapper {} +//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type + +const fn foo() {} + +fn main() { + const FOO: Wrapper = Wrapper(&42 as *const i32, 42); + foo::<{&FOO}>(); +} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr new file mode 100644 index 0000000000000..6f4f8dbbf9c34 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr @@ -0,0 +1,27 @@ +error: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_referee.rs:10:23 + | +LL | struct Foo; + | ---------- the trait `ConstParamTy` is not implemented for this struct +LL | +LL | impl ConstParamTy for &Foo {} + | ^^^^ + | +help: consider annotating this struct with `#[derive(ConstParamTy)]` + | +LL + #[derive(ConstParamTy)] +LL | struct Foo; + | + +error[E0204]: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_referee.rs:18:23 + | +LL | struct Wrapper(*const i32, usize); + | ---------- this field does not implement `ConstParamTy` +LL | +LL | impl ConstParamTy for &Wrapper {} + | ^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr index b9588e23e55ce..16f8a9bd11e8b 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr @@ -5,7 +5,6 @@ LL | fn test() { | ^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types error: aborting due to 1 previous error From 8095dd69c5185eed475daa30d74dd2af9132f294 Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:46:55 -0800 Subject: [PATCH 2/3] Also check the inner ty of arrays, slices, and tuples --- compiler/rustc_hir_analysis/messages.ftl | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/coherence/builtin.rs | 4 +- compiler/rustc_hir_analysis/src/errors.rs | 4 +- .../rustc_trait_selection/src/traits/misc.rs | 60 +++++----- .../const_param_ty_impl_bad_inner_ty.rs | 38 +++++++ .../const_param_ty_impl_bad_inner_ty.stderr | 106 ++++++++++++++++++ .../const_param_ty_impl_bad_referee.rs | 26 ----- .../const_param_ty_impl_bad_referee.stderr | 27 ----- 9 files changed, 184 insertions(+), 87 deletions(-) create mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs create mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr delete mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs delete mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 3c59d865f1840..c78a81e75f0c8 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -78,9 +78,9 @@ 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_infringing_referee = +hir_analysis_const_param_ty_impl_on_infringing_inner_ty = the trait `ConstParamTy` cannot be implemented for this type - .label = the trait `ConstParamTy` is not implemented for this {$def_descr} + .label = this {$def_descr} does not implement `ConstParamTy` .suggestion = consider annotating this {$def_descr} with `#[derive(ConstParamTy)]` hir_analysis_const_param_ty_impl_on_non_adt = diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 774b94cc35054..02c61dd5457f9 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -988,7 +988,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ty_is_local(ty) } // Same as above. - Err(ConstParamTyImplementationError::InfringingReferee(def_id)) => { + Err(ConstParamTyImplementationError::InfringingInnerTy(def_id)) => { def_id.is_local() } // Implments `ConstParamTy`, suggest adding the feature to enable. diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 06915dc85473f..eb95c1ace0f30 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -135,9 +135,9 @@ fn visit_implementation_of_const_param_ty( Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) } - Err(ConstParamTyImplementationError::InfringingReferee(def_id)) => { + Err(ConstParamTyImplementationError::InfringingInnerTy(def_id)) => { let def_span = tcx.def_span(def_id); - tcx.dcx().emit_err(errors::ConstParamTyImplOnInfringingReferee { + tcx.dcx().emit_err(errors::ConstParamTyImplOnInfringingInnerTy { span, def_span, def_descr: tcx.def_descr(def_id), diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index a9982c2f863b9..9a9d3d7405cda 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -228,8 +228,8 @@ pub struct ConstParamTyImplOnNonAdt { } #[derive(Diagnostic)] -#[diag(hir_analysis_const_param_ty_impl_on_infringing_referee)] -pub struct ConstParamTyImplOnInfringingReferee { +#[diag(hir_analysis_const_param_ty_impl_on_infringing_inner_ty)] +pub struct ConstParamTyImplOnInfringingInnerTy { #[primary_span] pub span: Span, #[label] diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 644ef690815b3..42f2152840f7b 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -23,7 +23,7 @@ pub enum CopyImplementationError<'tcx> { pub enum ConstParamTyImplementationError<'tcx> { InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), - InfringingReferee(DefId), + InfringingInnerTy(DefId), NotAnAdtOrBuiltinAllowed, } @@ -93,22 +93,21 @@ 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() { - // Special case for impls like `impl ConstParamTy for &Foo`, where - // Foo doesn't `impl ConstParamTy`. These kinds of impls aren't caught - // by coherence checking since `core`'s impl is restricted to &T where - // T: ConstParamTy. - // `has_concrete_skeleton()` avoids reporting `core`'s blanket impl. - &ty::Ref(.., ty, hir::Mutability::Not) if ty.peel_refs().has_concrete_skeleton() => { - let ty = ty.peel_refs(); - type_allowed_to_implement_const_param_ty(tcx, param_env, ty, parent_cause)?; - // Check the ty behind the ref impls `ConstParamTy` itself. This additional - // logic is needed when checking refs because we need to check not only if - // all fields implement ConstParamTy, but also that the type itself implements - // ConstParamTy. Simply recursing into the ref only checks the former. - if !ty.references_error() - && let &ty::Adt(adt, _) = ty.kind() - { + // Special case for impls like `impl ConstParamTy for &Foo`, where + // Foo doesn't `impl ConstParamTy`. These kinds of impls aren't caught + // by coherence checking since `core`'s impl is restricted to T: ConstParamTy. + // `has_concrete_skeleton()` avoids reporting `core`'s blanket impl. + let check_inner_ty = |ty: Ty<'tcx>, cause| { + let ty = ty.peel_refs(); + if ty.has_concrete_skeleton() { + type_allowed_to_implement_const_param_ty(tcx, param_env, ty, cause)?; + // Check the inner tys in refs, tuples, arrays, or slices implement `ConstParamTy`. + // This additional logic is needed because we need to check not only if all fields + // of the type implement ConstParamTy, but also that the type itself implements + // ConstParamTy. Simply recursing into the inner type only checks the former. + // `type_allowed_to_implement_const_param_ty` takes care of other non-ADT types + // for us. + if let &ty::Adt(adt, _) = ty.kind() { let adt_did = adt.did(); let adt_span = tcx.def_span(adt_did); let trait_did = tcx.require_lang_item(hir::LangItem::ConstParamTy, Some(adt_span)); @@ -130,23 +129,30 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( ); let errs = ocx.select_all_or_error(); if !errs.is_empty() { - return Err(ConstParamTyImplementationError::InfringingReferee(adt_did)); + return Err(ConstParamTyImplementationError::InfringingInnerTy(adt_did)); } } + } + Ok(()) + }; + + let (adt, args) = match self_type.kind() { + // `core` provides these impls, but only where the inner type is `ConstParamTy`, + // so we need to check and deny impls where the inner type is not `ConstParamTy` + &ty::Ref(.., ty, hir::Mutability::Not) | &ty::Array(ty, ..) | &ty::Slice(ty) => { + return check_inner_ty(ty, parent_cause); + } + + &ty::Tuple(tys) => { + for ty in tys { + check_inner_ty(ty, parent_cause.clone())?; + } return Ok(()); } // `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(()), + ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char | ty::Str => return Ok(()), &ty::Adt(adt, args) => (adt, args), diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs new file mode 100644 index 0000000000000..687296f164a51 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs @@ -0,0 +1,38 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +// #112124 + +struct Foo; + +impl ConstParamTy for &Foo {} +//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type + +impl ConstParamTy for &[Foo] {} +//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types +//~| ERROR the trait `ConstParamTy` cannot be implemented for this type + +impl ConstParamTy for [Foo; 4] {} +//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types +//~| ERROR the trait `ConstParamTy` cannot be implemented for this type + +impl ConstParamTy for (Foo, i32, *const u8) {} +//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types +//~| ERROR the trait `ConstParamTy` cannot be implemented for this type + +// #119299 (ICE) + +#[derive(Eq, PartialEq)] +struct Wrapper(*const i32, usize); + +impl ConstParamTy for &Wrapper {} +//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type + +const fn foo() {} + +fn main() { + const FOO: Wrapper = Wrapper(&42 as *const i32, 42); + foo::<{ &FOO }>(); +} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr new file mode 100644 index 0000000000000..174142442730a --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr @@ -0,0 +1,106 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:13:1 + | +LL | impl ConstParamTy for &[Foo] {} + | ^^^^^^^^^^^^^^^^^^^^^^------ + | | | + | | this is not defined in the current crate because slices are always foreign + | impl doesn't use only types from inside the current crate + | + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:17:1 + | +LL | impl ConstParamTy for [Foo; 4] {} + | ^^^^^^^^^^^^^^^^^^^^^^-------- + | | | + | | this is not defined in the current crate because arrays are always foreign + | impl doesn't use only types from inside the current crate + | + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:21:1 + | +LL | impl ConstParamTy for (Foo, i32, *const u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^--------------------- + | | | + | | this is not defined in the current crate because tuples are always foreign + | impl doesn't use only types from inside the current crate + | + = note: define and implement a trait or new type instead + +error: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:10:23 + | +LL | struct Foo; + | ---------- this struct does not implement `ConstParamTy` +LL | +LL | impl ConstParamTy for &Foo {} + | ^^^^ + | +help: consider annotating this struct with `#[derive(ConstParamTy)]` + | +LL + #[derive(ConstParamTy)] +LL | struct Foo; + | + +error: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:13:23 + | +LL | struct Foo; + | ---------- this struct does not implement `ConstParamTy` +... +LL | impl ConstParamTy for &[Foo] {} + | ^^^^^^ + | +help: consider annotating this struct with `#[derive(ConstParamTy)]` + | +LL + #[derive(ConstParamTy)] +LL | struct Foo; + | + +error: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:17:23 + | +LL | struct Foo; + | ---------- this struct does not implement `ConstParamTy` +... +LL | impl ConstParamTy for [Foo; 4] {} + | ^^^^^^^^ + | +help: consider annotating this struct with `#[derive(ConstParamTy)]` + | +LL + #[derive(ConstParamTy)] +LL | struct Foo; + | + +error: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:21:23 + | +LL | struct Foo; + | ---------- this struct does not implement `ConstParamTy` +... +LL | impl ConstParamTy for (Foo, i32, *const u8) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider annotating this struct with `#[derive(ConstParamTy)]` + | +LL + #[derive(ConstParamTy)] +LL | struct Foo; + | + +error[E0204]: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_inner_ty.rs:30:23 + | +LL | struct Wrapper(*const i32, usize); + | ---------- this field does not implement `ConstParamTy` +LL | +LL | impl ConstParamTy for &Wrapper {} + | ^^^^^^^^ + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0117, E0204. +For more information about an error, try `rustc --explain E0117`. diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs deleted file mode 100644 index 37bd6f63950b8..0000000000000 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![feature(adt_const_params)] -#![allow(incomplete_features)] - -use std::marker::ConstParamTy; - -// #112124 - -struct Foo; - -impl ConstParamTy for &Foo {} -//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type - -// #119299 (ICE) - -#[derive(Eq, PartialEq)] -struct Wrapper(*const i32, usize); - -impl ConstParamTy for &Wrapper {} -//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type - -const fn foo() {} - -fn main() { - const FOO: Wrapper = Wrapper(&42 as *const i32, 42); - foo::<{&FOO}>(); -} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr deleted file mode 100644 index 6f4f8dbbf9c34..0000000000000 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_referee.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: the trait `ConstParamTy` cannot be implemented for this type - --> $DIR/const_param_ty_impl_bad_referee.rs:10:23 - | -LL | struct Foo; - | ---------- the trait `ConstParamTy` is not implemented for this struct -LL | -LL | impl ConstParamTy for &Foo {} - | ^^^^ - | -help: consider annotating this struct with `#[derive(ConstParamTy)]` - | -LL + #[derive(ConstParamTy)] -LL | struct Foo; - | - -error[E0204]: the trait `ConstParamTy` cannot be implemented for this type - --> $DIR/const_param_ty_impl_bad_referee.rs:18:23 - | -LL | struct Wrapper(*const i32, usize); - | ---------- this field does not implement `ConstParamTy` -LL | -LL | impl ConstParamTy for &Wrapper {} - | ^^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0204`. From 486c90daf63893905ebe6d768d76f79f09944a94 Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Thu, 25 Jan 2024 19:18:13 -0800 Subject: [PATCH 3/3] Fix inner ty check --- compiler/rustc_hir_analysis/messages.ftl | 8 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 47 ++-- .../src/coherence/builtin.rs | 29 +- compiler/rustc_hir_analysis/src/errors.rs | 26 +- .../rustc_trait_selection/src/traits/misc.rs | 266 +++++++++++------- .../const_param_ty_impl_bad_inner_ty.rs | 2 +- .../const_param_ty_impl_bad_inner_ty.stderr | 25 +- .../const_param_ty_impl_bad_regions.rs | 16 ++ .../const_param_ty_impl_bad_regions.stderr | 22 ++ .../const_param_ty_impl_good_with_bounds.rs | 21 ++ 10 files changed, 288 insertions(+), 174 deletions(-) create mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.rs create mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.stderr create mode 100644 tests/ui/const-generics/adt_const_params/const_param_ty_impl_good_with_bounds.rs diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index c78a81e75f0c8..409d08660417e 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -80,12 +80,14 @@ hir_analysis_const_impl_for_non_const_trait = hir_analysis_const_param_ty_impl_on_infringing_inner_ty = the trait `ConstParamTy` cannot be implemented for this type - .label = this {$def_descr} does not implement `ConstParamTy` - .suggestion = consider annotating this {$def_descr} with `#[derive(ConstParamTy)]` + .label = this type does not implement `ConstParamTy` + +hir_analysis_const_param_ty_impl_on_infringing_inner_ty_sugg = + consider annotating with `#[derive(ConstParamTy)]` 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 + .label = the type `{$ty}` is not a structure or enumeration hir_analysis_const_specialize = cannot specialize on const impl with non-const impl diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 02c61dd5457f9..a0ebcf9260125 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -939,6 +939,22 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), Ok(()) }) } else { + fn ty_is_local(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Adt(adt_def, ..) => adt_def.did().is_local(), + // Arrays and slices use the inner type's `ConstParamTy`. + ty::Array(ty, ..) => ty_is_local(*ty), + ty::Slice(ty) => ty_is_local(*ty), + // `&` references use the inner type's `ConstParamTy`. + // `&mut` are not supported. + ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty), + // Say that a tuple is local if any of its components are local. + // This is not strictly correct, but it's likely that the user can fix the local component. + ty::Tuple(tys) => tys.iter().any(|ty| ty_is_local(ty)), + _ => false, + } + } + let mut diag = match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => return Ok(()), ty::FnPtr(_) => tcx.dcx().struct_span_err( @@ -962,35 +978,16 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), tcx, tcx.param_env(param.def_id), ty, - cause, + &cause, ) { // Can never implement `ConstParamTy`, don't suggest anything. - Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false, + Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed(..)) => 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(..)) => { - fn ty_is_local(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Adt(adt_def, ..) => adt_def.did().is_local(), - // Arrays and slices use the inner type's `ConstParamTy`. - ty::Array(ty, ..) => ty_is_local(*ty), - ty::Slice(ty) => ty_is_local(*ty), - // `&` references use the inner type's `ConstParamTy`. - // `&mut` are not supported. - ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty), - // Say that a tuple is local if any of its components are local. - // This is not strictly correct, but it's likely that the user can fix the local component. - ty::Tuple(tys) => tys.iter().any(|ty| ty_is_local(ty)), - _ => false, - } - } - - ty_is_local(ty) - } - // Same as above. - Err(ConstParamTyImplementationError::InfringingInnerTy(def_id)) => { - def_id.is_local() - } + Err( + ConstParamTyImplementationError::InfrigingFields(..) + | ConstParamTyImplementationError::InfringingInnerTy(..), + ) => ty_is_local(ty), // Implments `ConstParamTy`, suggest adding the feature to enable. Ok(..) => true, }; diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index eb95c1ace0f30..6c502bf7b88c8 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -127,22 +127,31 @@ fn visit_implementation_of_const_param_ty( }; let cause = traits::ObligationCause::misc(span, impl_did); - match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { + match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, &cause) { Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span)) } - Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { - Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) + Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed(ty)) => { + Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span, ty })) } - Err(ConstParamTyImplementationError::InfringingInnerTy(def_id)) => { - let def_span = tcx.def_span(def_id); - tcx.dcx().emit_err(errors::ConstParamTyImplOnInfringingInnerTy { + Err(ConstParamTyImplementationError::InfringingInnerTy(def_ids)) => { + let mut def_spans = vec![]; + let mut suggs = vec![]; + for def_id in def_ids { + let def_span = tcx.def_span(def_id); + def_spans.push(def_span); + if def_id.is_local() { + suggs.push(errors::ConstParamTyImplOnInfringingInnerTySugg { + span: def_span.shrink_to_lo(), + }); + } + } + Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnInfringingInnerTy { span, - def_span, - def_descr: tcx.def_descr(def_id), - sugg: def_id.is_local().then_some(def_span.shrink_to_lo()), - }); + def_spans, + suggs, + })) } } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 9a9d3d7405cda..448852750cb60 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -221,10 +221,11 @@ pub struct CopyImplOnNonAdt { #[derive(Diagnostic)] #[diag(hir_analysis_const_param_ty_impl_on_non_adt)] -pub struct ConstParamTyImplOnNonAdt { +pub struct ConstParamTyImplOnNonAdt<'tcx> { #[primary_span] #[label] pub span: Span, + pub ty: Ty<'tcx>, } #[derive(Diagnostic)] @@ -233,14 +234,21 @@ pub struct ConstParamTyImplOnInfringingInnerTy { #[primary_span] pub span: Span, #[label] - pub def_span: Span, - pub def_descr: &'static str, - #[suggestion( - applicability = "maybe-incorrect", - style = "verbose", - code = "#[derive(ConstParamTy)]\n" - )] - pub sugg: Option, + pub def_spans: Vec, + #[subdiagnostic] + pub suggs: Vec, +} + +#[derive(Subdiagnostic)] +#[suggestion( + hir_analysis_const_param_ty_impl_on_infringing_inner_ty_sugg, + applicability = "maybe-incorrect", + style = "verbose", + code = "#[derive(ConstParamTy)]\n" +)] +pub struct ConstParamTyImplOnInfringingInnerTySugg { + #[primary_span] + pub span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 42f2152840f7b..2f3912db6655e 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -11,6 +11,7 @@ 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, AdtDef, GenericArg, List, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use rustc_span::DUMMY_SP; use super::outlives_bounds::InferCtxtExt; @@ -23,8 +24,8 @@ pub enum CopyImplementationError<'tcx> { pub enum ConstParamTyImplementationError<'tcx> { InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), - InfringingInnerTy(DefId), - NotAnAdtOrBuiltinAllowed, + InfringingInnerTy(Vec), + NotAnAdtOrBuiltinAllowed(Ty<'tcx>), } pub enum InfringingFieldsReason<'tcx> { @@ -69,7 +70,7 @@ pub fn type_allowed_to_implement_copy<'tcx>( self_type, adt, args, - parent_cause, + &parent_cause, hir::LangItem::Copy, ) .map_err(CopyImplementationError::InfringingFields)?; @@ -91,86 +92,97 @@ 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>, + parent_cause: &ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>> { - // Special case for impls like `impl ConstParamTy for &Foo`, where - // Foo doesn't `impl ConstParamTy`. These kinds of impls aren't caught - // by coherence checking since `core`'s impl is restricted to T: ConstParamTy. - // `has_concrete_skeleton()` avoids reporting `core`'s blanket impl. - let check_inner_ty = |ty: Ty<'tcx>, cause| { - let ty = ty.peel_refs(); - if ty.has_concrete_skeleton() { - type_allowed_to_implement_const_param_ty(tcx, param_env, ty, cause)?; - // Check the inner tys in refs, tuples, arrays, or slices implement `ConstParamTy`. - // This additional logic is needed because we need to check not only if all fields - // of the type implement ConstParamTy, but also that the type itself implements - // ConstParamTy. Simply recursing into the inner type only checks the former. - // `type_allowed_to_implement_const_param_ty` takes care of other non-ADT types - // for us. - if let &ty::Adt(adt, _) = ty.kind() { - let adt_did = adt.did(); - let adt_span = tcx.def_span(adt_did); - let trait_did = tcx.require_lang_item(hir::LangItem::ConstParamTy, Some(adt_span)); - - let infcx = tcx.infer_ctxt().build(); - let ocx = traits::ObligationCtxt::new(&infcx); - - let ty = ocx.normalize(&ObligationCause::dummy_with_span(adt_span), param_env, ty); - let norm_errs = ocx.select_where_possible(); - if !norm_errs.is_empty() || ty.references_error() { - return Ok(()); - } - - ocx.register_bound( - ObligationCause::dummy_with_span(adt_span), - param_env, - ty, - trait_did, - ); - let errs = ocx.select_all_or_error(); - if !errs.is_empty() { - return Err(ConstParamTyImplementationError::InfringingInnerTy(adt_did)); - } + // impls like `impl ConstParamTy for &Foo` where Foo doesn't `impl ConstParamTy` + // need special-casing. These kinds of impls aren't caught by coherence checking + // since `core`'s impl is restricted to T: ConstParamTy. + // Additionally, we need to check that the inner tys in refs, tuples, arrays, + // or slices implement `ConstParamTy`. Simply recursing into the inner type only + // checks if all fields of the type implement ConstParamTy. We only do this for + // ADTs because recursing with `type_allowed_to_implement_const_param_ty` takes + // care of non-ADTs for us. + // `has_concrete_skeletion` avoids reporting `core`'s blanket impls. + + let mut adts_and_args = Vec::new(); + let mut inner_tys = Vec::new(); + + match self_type.kind() { + &ty::Ref(.., ty, hir::Mutability::Not) | &ty::Array(ty, ..) | &ty::Slice(ty) => { + let ty = ty.peel_refs(); + if !ty.has_concrete_skeleton() { + return Ok(()); } - } - - Ok(()) - }; - let (adt, args) = match self_type.kind() { - // `core` provides these impls, but only where the inner type is `ConstParamTy`, - // so we need to check and deny impls where the inner type is not `ConstParamTy` - &ty::Ref(.., ty, hir::Mutability::Not) | &ty::Array(ty, ..) | &ty::Slice(ty) => { - return check_inner_ty(ty, parent_cause); + if let &ty::Adt(adt, args) = ty.kind() { + adts_and_args.push((adt, args)); + inner_tys.push((ty, adt.did())); + } else { + type_allowed_to_implement_const_param_ty(tcx, param_env, ty, parent_cause)?; + } } &ty::Tuple(tys) => { for ty in tys { - check_inner_ty(ty, parent_cause.clone())?; + let ty = ty.peel_refs(); + if !ty.has_concrete_skeleton() { + continue; + } + + if let &ty::Adt(adt, args) = ty.kind() { + adts_and_args.push((adt, args)); + inner_tys.push((ty, adt.did())); + } else { + type_allowed_to_implement_const_param_ty(tcx, param_env, ty, parent_cause)?; + } } - return Ok(()); } // `core` provides these impls. ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char | ty::Str => return Ok(()), - &ty::Adt(adt, args) => (adt, args), + &ty::Adt(adt, args) => adts_and_args.push((adt, args)), - _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed), - }; + _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed(self_type)), + } - all_fields_implement_trait( - tcx, - param_env, - self_type, - adt, - args, - parent_cause, - hir::LangItem::ConstParamTy, - ) - .map_err(ConstParamTyImplementationError::InfrigingFields)?; + for (adt, args) in adts_and_args { + all_fields_implement_trait( + tcx, + param_env, + self_type, + adt, + args, + parent_cause, + hir::LangItem::ConstParamTy, + ) + .map_err(ConstParamTyImplementationError::InfrigingFields)?; + } - Ok(()) + let mut infringing = Vec::new(); + for (ty, def_id) in inner_tys { + let span = tcx.def_span(def_id); + if !type_implements_trait( + tcx, + param_env, + self_type, + ty, + span, + ObligationCause::dummy_with_span(span), + parent_cause, + hir::LangItem::ConstParamTy, + ) + .is_empty() + { + infringing.push(def_id); + } + } + + if infringing.is_empty() { + Ok(()) + } else { + Err(ConstParamTyImplementationError::InfringingInnerTy(infringing)) + } } /// Check that all fields of a given `adt` implement `lang_item` trait. @@ -180,20 +192,14 @@ pub fn all_fields_implement_trait<'tcx>( self_type: Ty<'tcx>, adt: AdtDef<'tcx>, args: &'tcx List>, - parent_cause: ObligationCause<'tcx>, + 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() { for field in &variant.fields { - // Do this per-field to get better error messages. - let infcx = tcx.infer_ctxt().build(); - let ocx = traits::ObligationCtxt::new(&infcx); - - let unnormalized_ty = field.ty(tcx, args); - if unnormalized_ty.references_error() { + let field_ty = field.ty(tcx, args); + if field_ty.references_error() { continue; } @@ -216,48 +222,90 @@ pub fn all_fields_implement_trait<'tcx>( } else { ObligationCause::dummy_with_span(field_ty_span) }; - let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty); - let normalization_errors = ocx.select_where_possible(); - - // NOTE: The post-normalization type may also reference errors, - // such as when we project to a missing type or we have a mismatch - // 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.dcx().span_delayed_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id))); - continue; - } - - ocx.register_bound( - ObligationCause::dummy_with_span(field_ty_span), - param_env, - ty, - trait_def_id, - ); - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors))); - } - // Check regions assuming the self type of the impl is WF - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys( + infringing.extend( + type_implements_trait( + tcx, param_env, - parent_cause.body_id, - &FxIndexSet::from_iter([self_type]), - ), + self_type, + field_ty, + field_ty_span, + normalization_cause, + parent_cause, + lang_item, + ) + .into_iter() + .map(|(ty, reason)| (field, ty, reason)), ); - let errors = infcx.resolve_regions(&outlives_env); - if !errors.is_empty() { - infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); - } } } if infringing.is_empty() { Ok(()) } else { Err(infringing) } } +fn type_implements_trait<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, + field_ty: Ty<'tcx>, + field_ty_span: Span, + normalization_cause: ObligationCause<'tcx>, + parent_cause: &ObligationCause<'tcx>, + lang_item: LangItem, +) -> Vec<(Ty<'tcx>, InfringingFieldsReason<'tcx>)> { + let trait_def_id = tcx.require_lang_item(lang_item, Some(parent_cause.span)); + + let infcx = tcx.infer_ctxt().build(); + let ocx = traits::ObligationCtxt::new(&infcx); + + let ty = ocx.normalize(&normalization_cause, param_env, field_ty); + let normalization_errors = ocx.select_where_possible(); + + // NOTE: The post-normalization type may also reference errors, + // such as when we project to a missing type or we have a mismatch + // 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.dcx().span_delayed_bug( + field_ty_span, + format!( + "couldn't normalize struct field `{field_ty}` when checking {tr} implementation", + tr = tcx.def_path_str(trait_def_id) + ), + ); + return Vec::new(); + } + + let mut infringing = Vec::new(); + + ocx.register_bound( + ObligationCause::dummy_with_span(field_ty_span), + param_env, + ty, + trait_def_id, + ); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infringing.push((ty, InfringingFieldsReason::Fulfill(errors))); + } + + // 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.push((ty, InfringingFieldsReason::Regions(errors))); + } + + infringing +} + pub fn check_tys_might_be_eq<'tcx>( tcx: TyCtxt<'tcx>, canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs index 687296f164a51..ca9f720b4f30b 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.rs @@ -20,7 +20,7 @@ impl ConstParamTy for [Foo; 4] {} impl ConstParamTy for (Foo, i32, *const u8) {} //~^ ERROR only traits defined in the current crate can be implemented for arbitrary types -//~| ERROR the trait `ConstParamTy` cannot be implemented for this type +//~| ERROR the trait `ConstParamTy` may not be implemented for this type // #119299 (ICE) diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr index 174142442730a..c118ddbbc9b0b 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_inner_ty.stderr @@ -35,12 +35,12 @@ error: the trait `ConstParamTy` cannot be implemented for this type --> $DIR/const_param_ty_impl_bad_inner_ty.rs:10:23 | LL | struct Foo; - | ---------- this struct does not implement `ConstParamTy` + | ---------- this type does not implement `ConstParamTy` LL | LL | impl ConstParamTy for &Foo {} | ^^^^ | -help: consider annotating this struct with `#[derive(ConstParamTy)]` +help: consider annotating with `#[derive(ConstParamTy)]` | LL + #[derive(ConstParamTy)] LL | struct Foo; @@ -50,12 +50,12 @@ error: the trait `ConstParamTy` cannot be implemented for this type --> $DIR/const_param_ty_impl_bad_inner_ty.rs:13:23 | LL | struct Foo; - | ---------- this struct does not implement `ConstParamTy` + | ---------- this type does not implement `ConstParamTy` ... LL | impl ConstParamTy for &[Foo] {} | ^^^^^^ | -help: consider annotating this struct with `#[derive(ConstParamTy)]` +help: consider annotating with `#[derive(ConstParamTy)]` | LL + #[derive(ConstParamTy)] LL | struct Foo; @@ -65,31 +65,22 @@ error: the trait `ConstParamTy` cannot be implemented for this type --> $DIR/const_param_ty_impl_bad_inner_ty.rs:17:23 | LL | struct Foo; - | ---------- this struct does not implement `ConstParamTy` + | ---------- this type does not implement `ConstParamTy` ... LL | impl ConstParamTy for [Foo; 4] {} | ^^^^^^^^ | -help: consider annotating this struct with `#[derive(ConstParamTy)]` +help: consider annotating with `#[derive(ConstParamTy)]` | LL + #[derive(ConstParamTy)] LL | struct Foo; | -error: the trait `ConstParamTy` cannot be implemented for this type +error: the trait `ConstParamTy` may not be implemented for this type --> $DIR/const_param_ty_impl_bad_inner_ty.rs:21:23 | -LL | struct Foo; - | ---------- this struct does not implement `ConstParamTy` -... LL | impl ConstParamTy for (Foo, i32, *const u8) {} - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: consider annotating this struct with `#[derive(ConstParamTy)]` - | -LL + #[derive(ConstParamTy)] -LL | struct Foo; - | + | ^^^^^^^^^^^^^^^^^^^^^ the type `*const u8` is not a structure or enumeration error[E0204]: the trait `ConstParamTy` cannot be implemented for this type --> $DIR/const_param_ty_impl_bad_inner_ty.rs:30:23 diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.rs new file mode 100644 index 0000000000000..6a758a3324572 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.rs @@ -0,0 +1,16 @@ +// check that we actually take into account region constraints for `ConstParamTy` impl checks + +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +struct Foo<'a>(&'a u32); + +struct Bar(T); + +impl ConstParamTy for Foo<'static> {} +impl<'a> ConstParamTy for Bar> {} +//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.stderr new file mode 100644 index 0000000000000..601115f305bfb --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_regions.stderr @@ -0,0 +1,22 @@ +error[E0204]: the trait `ConstParamTy` cannot be implemented for this type + --> $DIR/const_param_ty_impl_bad_regions.rs:13:27 + | +LL | struct Bar(T); + | - this field does not implement `ConstParamTy` +... +LL | impl<'a> ConstParamTy for Bar> {} + | ^^^^^^^^^^^^ + | +note: the `ConstParamTy` impl for `Foo<'a>` requires that `'a: 'static` + --> $DIR/const_param_ty_impl_bad_regions.rs:10:15 + | +LL | struct Bar(T); + | ^ +help: consider restricting type parameter `'a` + | +LL | impl<'a: 'static> ConstParamTy for Bar> {} + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_good_with_bounds.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_good_with_bounds.rs new file mode 100644 index 0000000000000..03ee30aad29c7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_good_with_bounds.rs @@ -0,0 +1,21 @@ +// check-pass + +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq)] +struct Bar; +#[derive(PartialEq, Eq)] +struct Foo(Bar); + +impl ConstParamTy for Bar +where + Foo: ConstParamTy {} + +// impl checks means that this impl is only valid if `Bar: ConstParamTy` whic +// is only valid if `Foo: ConstParamTy` holds +impl ConstParamTy for Foo {} + +fn main() {}