From aeeba0c37a1a5cf2e0637653241cab8ae2d8c029 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Wed, 22 Mar 2023 13:32:24 +0300 Subject: [PATCH 1/7] correctly handle normalization in implied bounds --- .../query/type_op/implied_outlives_bounds.rs | 118 ++++++------------ .../issue-111404-1.rs | 1 + .../issue-111404-1.stderr | 10 +- .../normalization-nested.lifetime.stderr | 18 --- .../ui/implied-bounds/normalization-nested.rs | 7 +- tests/ui/inference/issue-80409.rs | 5 +- 6 files changed, 52 insertions(+), 107 deletions(-) delete mode 100644 tests/ui/implied-bounds/normalization-nested.lifetime.stderr diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 56844f39478fb..e42009ccac91d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -9,8 +9,6 @@ use rustc_infer::traits::query::OutlivesBound; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::def_id::CRATE_DEF_ID; -use rustc_span::DUMMY_SP; use smallvec::{smallvec, SmallVec}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] @@ -58,12 +56,29 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { } } +/// For the sake of completeness, we should be careful when dealing with inference artifacts: +/// - `ty` must be fully resolved. +/// - `normalize_op` must return a fully resolved type. pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Result>, NoSolution> { - let tcx = ocx.infcx.tcx; + let normalize_op = |ty: Ty<'tcx>| { + let ty = if ocx.infcx.next_trait_solver() { + solve::deeply_normalize(ocx.infcx.at(&ObligationCause::dummy(), param_env), ty) + // FIXME(-Ztrait-solver=next) + .unwrap_or_else(|_errs| ty) + } else { + ocx.normalize(&ObligationCause::dummy(), param_env, ty) + }; + if !ocx.select_all_or_error().is_empty() { + return Err(NoSolution); + } + let ty = ocx.infcx.resolve_vars_if_possible(ty); + assert!(!ty.has_non_region_infer()); + Ok(ty) + }; // Sometimes when we ask what it takes for T: WF, we get back that // U: WF is required; in that case, we push U onto this stack and @@ -71,56 +86,22 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // guaranteed to be a subset of the original type, so we need to store the // WF args we've computed in a set. let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); - let mut wf_args = vec![ty.into()]; + let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()]; - let mut outlives_bounds: Vec, ty::Region<'tcx>>> = - vec![]; + let mut outlives_bounds: Vec> = vec![]; while let Some(arg) = wf_args.pop() { if !checked_wf_args.insert(arg) { continue; } - // Compute the obligations for `arg` to be well-formed. If `arg` is - // an unresolved inference variable, just substituted an empty set - // -- because the return type here is going to be things we *add* - // to the environment, it's always ok for this set to be smaller - // than the ultimate set. (Note: normally there won't be - // unresolved inference variables here anyway, but there might be - // during typeck under some circumstances.) - // - // FIXME(@lcnr): It's not really "always fine", having fewer implied - // bounds can be backward incompatible, e.g. #101951 was caused by - // us not dealing with inference vars in `TypeOutlives` predicates. - let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) - .unwrap_or_default(); - - for obligation in obligations { - debug!(?obligation); + // From the full set of obligations, just filter down to the region relationships. + for obligation in + wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten() + { assert!(!obligation.has_escaping_bound_vars()); - - // While these predicates should all be implied by other parts of - // the program, they are still relevant as they may constrain - // inference variables, which is necessary to add the correct - // implied bounds in some cases, mostly when dealing with projections. - // - // Another important point here: we only register `Projection` - // predicates, since otherwise we might register outlives - // predicates containing inference variables, and we don't - // learn anything new from those. - if obligation.predicate.has_non_region_infer() { - match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) - | ty::PredicateKind::AliasRelate(..) => { - ocx.register_obligation(obligation.clone()); - } - _ => {} - } - } - - let pred = match obligation.predicate.kind().no_bound_vars() { - None => continue, - Some(pred) => pred, + let Some(pred) = obligation.predicate.kind().no_bound_vars() else { + continue; }; match pred { ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) @@ -143,53 +124,24 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( } // We need to register region relationships - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( - r_a, - r_b, - ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( + ty::OutlivesPredicate(r_a, r_b), + )) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)), ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( ty_a, r_b, - ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), - } - } - } - - // This call to `select_all_or_error` is necessary to constrain inference variables, which we - // use further down when computing the implied bounds. - match ocx.select_all_or_error().as_slice() { - [] => (), - _ => return Err(NoSolution), - } - - // We lazily compute the outlives components as - // `select_all_or_error` constrains inference variables. - let mut implied_bounds = Vec::new(); - for ty::OutlivesPredicate(a, r_b) in outlives_bounds { - match a.unpack() { - ty::GenericArgKind::Lifetime(r_a) => { - implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)) - } - ty::GenericArgKind::Type(ty_a) => { - let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); - // Need to manually normalize in the new solver as `wf::obligations` does not. - if ocx.infcx.next_trait_solver() { - ty_a = solve::deeply_normalize( - ocx.infcx.at(&ObligationCause::dummy(), param_env), - ty_a, - ) - .map_err(|_errs| NoSolution)?; + ))) => { + let ty_a = normalize_op(ty_a)?; + let mut components = smallvec![]; + push_outlives_components(ocx.infcx.tcx, ty_a, &mut components); + outlives_bounds.extend(implied_bounds_from_components(r_b, components)) } - let mut components = smallvec![]; - push_outlives_components(tcx, ty_a, &mut components); - implied_bounds.extend(implied_bounds_from_components(r_b, components)) } - ty::GenericArgKind::Const(_) => unreachable!(), } } - Ok(implied_bounds) + Ok(outlives_bounds) } /// When we have an implied bound that `T: 'a`, we can further break diff --git a/tests/ui/associated-inherent-types/issue-111404-1.rs b/tests/ui/associated-inherent-types/issue-111404-1.rs index dd62e59f07d22..74f9434b8818a 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.rs +++ b/tests/ui/associated-inherent-types/issue-111404-1.rs @@ -10,5 +10,6 @@ impl<'a> Foo { fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} //~^ ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error +//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111404-1.stderr b/tests/ui/associated-inherent-types/issue-111404-1.stderr index cf4d4a5f19b12..1613161a873f7 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.stderr +++ b/tests/ui/associated-inherent-types/issue-111404-1.stderr @@ -12,5 +12,13 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error: higher-ranked subtype error + --> $DIR/issue-111404-1.rs:10:1 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors diff --git a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr b/tests/ui/implied-bounds/normalization-nested.lifetime.stderr deleted file mode 100644 index abffee57a0f09..0000000000000 --- a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0759]: `fn` parameter has lifetime `'x` but it needs to satisfy a `'static` lifetime requirement - --> $DIR/normalization-nested.rs:35:20 - | -LL | pub fn test<'x>(_: Map>, s: &'x str) -> &'static str { - | ^^^^^^^^^^^^^^^^ - | | - | this data with lifetime `'x`... - | ...is used and required to live as long as `'static` here - | -note: `'static` lifetime requirement introduced by this bound - --> $DIR/normalization-nested.rs:33:14 - | -LL | I::Item: 'static; - | ^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0759`. diff --git a/tests/ui/implied-bounds/normalization-nested.rs b/tests/ui/implied-bounds/normalization-nested.rs index 5f1cbb3f69779..0935fe0f91b5c 100644 --- a/tests/ui/implied-bounds/normalization-nested.rs +++ b/tests/ui/implied-bounds/normalization-nested.rs @@ -1,11 +1,10 @@ // Test for normalization of projections that appear in the item bounds // (versus those that appear directly in the input types). -// Both revisions should pass. `lifetime` revision is a bug. +// Both revisions should pass. +// `lifetime` revision was incorrectly rejected. See #109799. // // revisions: param_ty lifetime -// [param_ty] check-pass -// [lifetime] check-fail -// [lifetime] known-bug: #109799 +// check-pass pub trait Iter { type Item; diff --git a/tests/ui/inference/issue-80409.rs b/tests/ui/inference/issue-80409.rs index 80cad6dfc46e4..3a1ea30af5cde 100644 --- a/tests/ui/inference/issue-80409.rs +++ b/tests/ui/inference/issue-80409.rs @@ -1,4 +1,7 @@ -// check-pass +// This is an ICE because of #104478. +// known-bug: unknown +// failure-status: 101 +// dont-check-compiler-stderr #![allow(unreachable_code, unused)] From 6c0916d95f3bd96aeb6d8df1ddf34a884577c546 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Thu, 23 Mar 2023 05:20:57 +0300 Subject: [PATCH 2/7] add test --- tests/ui/implied-bounds/from-trait-impl.rs | 21 +++++++++++++ .../ui/implied-bounds/from-trait-impl.stderr | 31 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/ui/implied-bounds/from-trait-impl.rs create mode 100644 tests/ui/implied-bounds/from-trait-impl.stderr diff --git a/tests/ui/implied-bounds/from-trait-impl.rs b/tests/ui/implied-bounds/from-trait-impl.rs new file mode 100644 index 0000000000000..770c47d9d2077 --- /dev/null +++ b/tests/ui/implied-bounds/from-trait-impl.rs @@ -0,0 +1,21 @@ +// Foo> shouldn't imply X: 'static. +// We don't use region constraints from trait impls in implied bounds. + +trait Trait { + type Assoc; +} + +impl Trait for Vec { + type Assoc = (); +} + +struct Foo(T) +where + T::Assoc: 'static, // any predicate naming T::Assoc +; + +fn foo(_: Foo>) {} +//~^ ERROR `X` may not live long enough +//~| ERROR `X` may not live long enough + +fn main() {} diff --git a/tests/ui/implied-bounds/from-trait-impl.stderr b/tests/ui/implied-bounds/from-trait-impl.stderr new file mode 100644 index 0000000000000..bf5ce6d3a0166 --- /dev/null +++ b/tests/ui/implied-bounds/from-trait-impl.stderr @@ -0,0 +1,31 @@ +error[E0310]: the parameter type `X` may not live long enough + --> $DIR/from-trait-impl.rs:17:1 + | +LL | fn foo(_: Foo>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `X` must be valid for the static lifetime... + | ...so that the type `X` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(_: Foo>) {} + | +++++++++ + +error[E0310]: the parameter type `X` may not live long enough + --> $DIR/from-trait-impl.rs:17:14 + | +LL | fn foo(_: Foo>) {} + | ^^^^^^^^^^^ + | | + | the parameter type `X` must be valid for the static lifetime... + | ...so that the type `X` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(_: Foo>) {} + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. From 8ef7369ef5dad3ecb21909055ccf56f14fd62cd0 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 30 Nov 2023 15:49:09 -0300 Subject: [PATCH 3/7] Rename implied_bounds_tys to implied_bounds_tys_compat --- compiler/rustc_hir_analysis/src/check/check.rs | 2 +- compiler/rustc_hir_analysis/src/check/compare_impl_item.rs | 6 +++--- .../src/check/compare_impl_item/refine.rs | 2 +- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 4 ++-- .../src/impl_wf_check/min_specialization.rs | 2 +- compiler/rustc_trait_selection/src/traits/misc.rs | 2 +- .../rustc_trait_selection/src/traits/outlives_bounds.rs | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index fbed6f33e5928..3ea3c785d07f5 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -355,7 +355,7 @@ fn check_opaque_meets_bounds<'tcx>( // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias { .. } => { let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?; - let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys); + let implied_bounds = infcx.implied_bounds_tys_compat(param_env, def_id, wf_tys); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 16fd1a951b5a1..c7f5c0e5dd28c 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -404,7 +404,7 @@ fn compare_method_predicate_entailment<'tcx>( // lifetime parameters. let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys), + infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, wf_tys), ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { @@ -880,7 +880,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // lifetime parameters. let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys), + infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, wf_tys), ); ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?; @@ -2240,7 +2240,7 @@ pub(super) fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types); + let implied_bounds = infcx.implied_bounds_tys_compat(param_env, impl_ty_def_id, assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index bcd317f78ef0c..1cb44815e5ba0 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -161,7 +161,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( } let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types), + infcx.implied_bounds_tys_compat(param_env, impl_m.def_id.expect_local(), implied_wf_types), ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 177e4611cc92e..b37c443657c7d 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -110,7 +110,7 @@ where let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; - let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types); + let implied_bounds = infcx.implied_bounds_tys_compat(param_env, body_def_id, assumed_wf_types); let errors = wfcx.select_all_or_error(); if !errors.is_empty() { @@ -717,7 +717,7 @@ fn resolve_regions_with_wf_tys<'tcx>( let infcx = tcx.infer_ctxt().build(); let outlives_environment = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), + infcx.implied_bounds_tys_compat(param_env, id, wf_tys.clone()), ); let region_bound_pairs = outlives_environment.region_bound_pairs(); diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 7941861fd2f7d..17e8a6a3cb1f1 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -202,7 +202,7 @@ fn get_impl_args( return Err(guar); } - let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types); + let implied_bounds = infcx.implied_bounds_tys_compat(param_env, impl1_def_id, assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else { diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index ab07b10c65f58..e4fac514dd97c 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -191,7 +191,7 @@ pub fn all_fields_implement_trait<'tcx>( // Check regions assuming the self type of the impl is WF let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys( + infcx.implied_bounds_tys_compat( param_env, parent_cause.body_id, FxIndexSet::from_iter([self_type]), diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 1c2966bb3e533..9f9d374d5ff1d 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -18,7 +18,7 @@ pub trait InferCtxtExt<'a, 'tcx> { ty: Ty<'tcx>, ) -> Vec>; - fn implied_bounds_tys( + fn implied_bounds_tys_compat( &'a self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, @@ -121,7 +121,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { bounds } - fn implied_bounds_tys( + fn implied_bounds_tys_compat( &'a self, param_env: ParamEnv<'tcx>, body_id: LocalDefId, From 6525e6f6d89a0d209e3deeedf75e7ec76034c611 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 30 Nov 2023 16:26:29 -0300 Subject: [PATCH 4/7] Rename implied_outlives_bounds to implied_outlives_bounds_compat --- compiler/rustc_middle/src/arena.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 2 +- .../src/traits/outlives_bounds.rs | 11 ++++++----- .../traits/query/type_op/implied_outlives_bounds.rs | 2 +- compiler/rustc_traits/src/implied_outlives_bounds.rs | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 9b41b77928e67..59d28ef9a20e9 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -53,7 +53,7 @@ macro_rules! arena_types { rustc_middle::traits::query::NormalizationResult<'tcx> > >, - [] implied_outlives_bounds: + [] implied_outlives_bounds_compat: rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, Vec> diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 29decd0f050ee..7790892a6f433 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1953,7 +1953,7 @@ rustc_queries! { desc { "normalizing `{}`", goal.value } } - query implied_outlives_bounds( + query implied_outlives_bounds_compat( goal: CanonicalTyGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 9f9d374d5ff1d..99a55d7e330c1 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -11,7 +11,7 @@ pub use rustc_middle::traits::query::OutlivesBound; pub type Bounds<'a, 'tcx: 'a> = impl Iterator> + 'a; pub trait InferCtxtExt<'a, 'tcx> { - fn implied_outlives_bounds( + fn implied_outlives_bounds_compat( &self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, @@ -47,7 +47,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { /// into the inference context with this body-id. /// - `ty`, the type that we are supposed to assume is WF. #[instrument(level = "debug", skip(self, param_env, body_id), ret)] - fn implied_outlives_bounds( + fn implied_outlives_bounds_compat( &self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, @@ -67,7 +67,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { let mut canonical_var_values = OriginalQueryValues::default(); let canonical_ty = self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values); - let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else { + let Ok(canonical_result) = self.tcx.implied_outlives_bounds_compat(canonical_ty) else { return vec![]; }; @@ -113,7 +113,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { if !errors.is_empty() { self.tcx.sess.delay_span_bug( span, - "implied_outlives_bounds failed to solve obligations from instantiation", + "implied_outlives_bounds_compat failed to solve obligations from instantiation", ); } }; @@ -127,6 +127,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { body_id: LocalDefId, tys: FxIndexSet>, ) -> Bounds<'a, 'tcx> { - tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty)) + tys.into_iter() + .flat_map(move |ty| self.implied_outlives_bounds_compat(param_env, body_id, ty)) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index e42009ccac91d..1dc1186d8d6f6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -45,7 +45,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { param_env.and(ty) }); - tcx.implied_outlives_bounds(canonicalized) + tcx.implied_outlives_bounds_compat(canonicalized) } fn perform_locally_in_new_solver( diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 959838ab348fd..35b7179b0f34e 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -12,10 +12,10 @@ use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::comp use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; pub(crate) fn provide(p: &mut Providers) { - *p = Providers { implied_outlives_bounds, ..*p }; + *p = Providers { implied_outlives_bounds_compat, ..*p }; } -fn implied_outlives_bounds<'tcx>( +fn implied_outlives_bounds_compat<'tcx>( tcx: TyCtxt<'tcx>, goal: CanonicalTyGoal<'tcx>, ) -> Result< From 4d629f5e0c8c5e2802a5aee390953ac2806b59e7 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 30 Nov 2023 18:14:03 -0300 Subject: [PATCH 5/7] lint implied bounds from trait impl --- .../rustc_hir_analysis/src/check/wfcheck.rs | 41 ++++- compiler/rustc_lint_defs/src/builtin.rs | 42 +++++ compiler/rustc_middle/src/query/erase.rs | 4 + compiler/rustc_middle/src/query/mod.rs | 9 + compiler/rustc_middle/src/traits/query.rs | 2 +- .../src/traits/outlives_bounds.rs | 28 ++- .../query/type_op/implied_outlives_bounds.rs | 160 ++++++++++++++++-- .../src/implied_outlives_bounds.rs | 20 ++- .../issue-111404-1.rs | 1 - .../issue-111404-1.stderr | 10 +- tests/ui/implied-bounds/from-trait-impl.rs | 34 +++- .../ui/implied-bounds/from-trait-impl.stderr | 86 +++++++--- .../normalization-nested.lifetime.stderr | 10 ++ .../ui/implied-bounds/normalization-nested.rs | 7 +- tests/ui/inference/issue-80409.rs | 5 +- 15 files changed, 392 insertions(+), 67 deletions(-) create mode 100644 tests/ui/implied-bounds/normalization-nested.lifetime.stderr diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b37c443657c7d..0cdc395549307 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -110,8 +110,6 @@ where let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; - let implied_bounds = infcx.implied_bounds_tys_compat(param_env, body_def_id, assumed_wf_types); - let errors = wfcx.select_all_or_error(); if !errors.is_empty() { let err = infcx.err_ctxt().report_fulfillment_errors(errors); @@ -126,9 +124,46 @@ where } } + let infcx_compat = infcx.fork(); + + let implied_bounds = infcx.implied_bounds_tys(param_env, &assumed_wf_types); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + + let errors = infcx.resolve_regions(&outlives_env); + if errors.is_empty() { + return Ok(()); + } + + let implied_bounds = + infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + let errors_compat = infcx_compat.resolve_regions(&outlives_env); + if !errors_compat.is_empty() { + return Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat)); + } + + let hir_id = tcx.local_def_id_to_hir_id(body_def_id); + let (lint_level, _) = tcx + .lint_level_at_node(rustc_session::lint::builtin::IMPLIED_BOUNDS_FROM_TRAIT_IMPL, hir_id); + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::IMPLIED_BOUNDS_FROM_TRAIT_IMPL, + hir_id, + tcx.def_span(body_def_id), + format!("{} is missing necessary lifetime bounds", tcx.def_descr(body_def_id.into())), + |lint| { + if !lint_level.is_error() { + lint.note( + "to get more detailed errors, use `#[deny(implied_bounds_from_trait_impl)]`", + ) + } else { + lint.note("more concrete lifetime errors are emitted below") + } + }, + ); + if lint_level.is_error() { + infcx.err_ctxt().report_region_errors(body_def_id, &errors); + } - wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?; infcx.tainted_by_errors().error_reported() } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a2243817df95f..7cb87b02844b8 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3388,6 +3388,7 @@ declare_lint_pass! { ILL_FORMED_ATTRIBUTE_INPUT, ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, IMPLIED_BOUNDS_ENTAILMENT, + IMPLIED_BOUNDS_FROM_TRAIT_IMPL, INCOMPLETE_INCLUDE, INDIRECT_STRUCTURAL_MATCH, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, @@ -4620,3 +4621,44 @@ declare_lint! { reference: "issue #115010 ", }; } + +declare_lint! { + /// The `implied_bounds_from_trait_impl` lint detects + /// a compiler bug allowed some code to assume invalid implied lifetime bounds. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(implied_bounds_from_trait_impl)] + /// + /// trait Trait { + /// type Assoc; + /// } + /// + /// impl Trait for (X,) { + /// type Assoc = (); + /// } + /// + /// struct Foo(T) + /// where + /// T::Assoc: Clone; // any bound using `T::Assoc` + /// + /// fn func(foo: Foo<(&str,)>) { + /// let _: &'static str = foo.0.0; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// FIXME: explanataion + /// + pub IMPLIED_BOUNDS_FROM_TRAIT_IMPL, + Warn, + "item uses illegal implied bounds derived from a trait impl", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #109628 ", + }; +} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index cdde6a596a80e..6bb2e36c1d553 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -74,6 +74,10 @@ impl EraseType for Result<&'_ T, traits::query::NoSolution> { type Result = [u8; size_of::>()]; } +impl EraseType for Result<&'_ [T], traits::query::NoSolution> { + type Result = [u8; size_of::>()]; +} + impl EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> { type Result = [u8; size_of::>()]; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7790892a6f433..6a6139acdb1e7 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1962,6 +1962,15 @@ rustc_queries! { desc { "computing implied outlives bounds for `{}`", goal.value.value } } + query implied_outlives_bounds( + goal: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Result< + &'tcx [OutlivesBound<'tcx>], + NoSolution, + > { + desc { "computing implied outlives bounds v2 for `{}`", goal.value } + } + /// Do not call this query directly: /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. query dropck_outlives( diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 34c56eb0d7b1e..f7ceec7546951 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -191,7 +191,7 @@ pub struct NormalizationResult<'tcx> { /// case they are called implied bounds). They are fed to the /// `OutlivesEnv` which in turn is supplied to the region checker and /// other parts of the inference system. -#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable)] pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 99a55d7e330c1..e437e8338b7fb 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -9,6 +9,7 @@ use rustc_span::def_id::LocalDefId; pub use rustc_middle::traits::query::OutlivesBound; +pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator> + 'a; pub type Bounds<'a, 'tcx: 'a> = impl Iterator> + 'a; pub trait InferCtxtExt<'a, 'tcx> { fn implied_outlives_bounds_compat( @@ -23,6 +24,12 @@ pub trait InferCtxtExt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, tys: FxIndexSet>, + ) -> BoundsCompat<'a, 'tcx>; + + fn implied_bounds_tys( + &'a self, + param_env: ty::ParamEnv<'tcx>, + tys: &'a FxIndexSet>, ) -> Bounds<'a, 'tcx>; } @@ -126,8 +133,27 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { param_env: ParamEnv<'tcx>, body_id: LocalDefId, tys: FxIndexSet>, - ) -> Bounds<'a, 'tcx> { + ) -> BoundsCompat<'a, 'tcx> { tys.into_iter() .flat_map(move |ty| self.implied_outlives_bounds_compat(param_env, body_id, ty)) } + + fn implied_bounds_tys( + &'a self, + param_env: ParamEnv<'tcx>, + tys: &'a FxIndexSet>, + ) -> Bounds<'a, 'tcx> { + tys.iter() + .flat_map(move |&ty| { + let ty = self.resolve_vars_if_possible(ty); + let ty = OpportunisticRegionResolver::new(self).fold_ty(ty); + + if ty.has_infer() { + return &[] as &[OutlivesBound<'_>]; + } + + self.tcx.implied_outlives_bounds(param_env.and(ty)).unwrap_or(&[]) + }) + .copied() + } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 1dc1186d8d6f6..13c969bccfe21 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -5,10 +5,13 @@ use crate::traits::ObligationCtxt; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; +use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::OutlivesBound; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; +use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::DUMMY_SP; use smallvec::{smallvec, SmallVec}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] @@ -52,7 +55,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty) } } @@ -63,20 +66,15 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, -) -> Result>, NoSolution> { - let normalize_op = |ty: Ty<'tcx>| { - let ty = if ocx.infcx.next_trait_solver() { - solve::deeply_normalize(ocx.infcx.at(&ObligationCause::dummy(), param_env), ty) - // FIXME(-Ztrait-solver=next) - .unwrap_or_else(|_errs| ty) - } else { - ocx.normalize(&ObligationCause::dummy(), param_env, ty) - }; +) -> Result<&'tcx [OutlivesBound<'tcx>], NoSolution> { + let normalize_op = |ty| { + let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty); if !ocx.select_all_or_error().is_empty() { return Err(NoSolution); } let ty = ocx.infcx.resolve_vars_if_possible(ty); - assert!(!ty.has_non_region_infer()); + let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty); + assert!(!ty.has_infer()); Ok(ty) }; @@ -141,14 +139,148 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( } } - Ok(outlives_bounds) + Ok(ocx.infcx.tcx.arena.alloc_slice(&outlives_bounds)) +} + +pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + let tcx = ocx.infcx.tcx; + + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Because the resulting predicates aren't always + // guaranteed to be a subset of the original type, so we need to store the + // WF args we've computed in a set. + let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); + let mut wf_args = vec![ty.into()]; + + let mut outlives_bounds: Vec, ty::Region<'tcx>>> = + vec![]; + + while let Some(arg) = wf_args.pop() { + if !checked_wf_args.insert(arg) { + continue; + } + + // Compute the obligations for `arg` to be well-formed. If `arg` is + // an unresolved inference variable, just substituted an empty set + // -- because the return type here is going to be things we *add* + // to the environment, it's always ok for this set to be smaller + // than the ultimate set. (Note: normally there won't be + // unresolved inference variables here anyway, but there might be + // during typeck under some circumstances.) + // + // FIXME(@lcnr): It's not really "always fine", having fewer implied + // bounds can be backward incompatible, e.g. #101951 was caused by + // us not dealing with inference vars in `TypeOutlives` predicates. + let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) + .unwrap_or_default(); + + for obligation in obligations { + debug!(?obligation); + assert!(!obligation.has_escaping_bound_vars()); + + // While these predicates should all be implied by other parts of + // the program, they are still relevant as they may constrain + // inference variables, which is necessary to add the correct + // implied bounds in some cases, mostly when dealing with projections. + // + // Another important point here: we only register `Projection` + // predicates, since otherwise we might register outlives + // predicates containing inference variables, and we don't + // learn anything new from those. + if obligation.predicate.has_non_region_infer() { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) + | ty::PredicateKind::AliasRelate(..) => { + ocx.register_obligation(obligation.clone()); + } + _ => {} + } + } + + let pred = match obligation.predicate.kind().no_bound_vars() { + None => continue, + Some(pred) => pred, + }; + match pred { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) + // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound + // if we ever support that + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::AliasRelate(..) + => {} + + // We need to search through *all* WellFormed predicates + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + wf_args.push(arg); + } + + // We need to register region relationships + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( + r_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), + + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), + } + } + } + + // This call to `select_all_or_error` is necessary to constrain inference variables, which we + // use further down when computing the implied bounds. + match ocx.select_all_or_error().as_slice() { + [] => (), + _ => return Err(NoSolution), + } + + // We lazily compute the outlives components as + // `select_all_or_error` constrains inference variables. + let mut implied_bounds = Vec::new(); + for ty::OutlivesPredicate(a, r_b) in outlives_bounds { + match a.unpack() { + ty::GenericArgKind::Lifetime(r_a) => { + implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)) + } + ty::GenericArgKind::Type(ty_a) => { + let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); + // Need to manually normalize in the new solver as `wf::obligations` does not. + if ocx.infcx.next_trait_solver() { + ty_a = solve::deeply_normalize( + ocx.infcx.at(&ObligationCause::dummy(), param_env), + ty_a, + ) + .map_err(|_errs| NoSolution)?; + } + let mut components = smallvec![]; + push_outlives_components(tcx, ty_a, &mut components); + implied_bounds.extend(implied_bounds_from_components(r_b, components)) + } + ty::GenericArgKind::Const(_) => unreachable!(), + } + } + + Ok(implied_bounds) } /// When we have an implied bound that `T: 'a`, we can further break /// this down to determine what relationships would have to hold for /// `T: 'a` to hold. We get to assume that the caller has validated /// those relationships. -fn implied_bounds_from_components<'tcx>( +pub fn implied_bounds_from_components<'tcx>( sub_region: ty::Region<'tcx>, sup_components: SmallVec<[Component<'tcx>; 4]>, ) -> Vec> { diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 35b7179b0f34e..c74e74e0c8171 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -6,13 +6,17 @@ use rustc_infer::infer::canonical::{self, Canonical}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::query::OutlivesBound; use rustc_middle::query::Providers; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; +use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ + compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, +}; use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc_trait_selection::traits::ObligationCtxt; pub(crate) fn provide(p: &mut Providers) { *p = Providers { implied_outlives_bounds_compat, ..*p }; + *p = Providers { implied_outlives_bounds, ..*p }; } fn implied_outlives_bounds_compat<'tcx>( @@ -24,6 +28,16 @@ fn implied_outlives_bounds_compat<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ty) = key.into_parts(); - compute_implied_outlives_bounds_inner(ocx, param_env, ty) + compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) }) } + +fn implied_outlives_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + goal: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Result<&'tcx [OutlivesBound<'tcx>], NoSolution> { + let (param_env, goal_ty) = goal.into_parts(); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + compute_implied_outlives_bounds_inner(&ocx, param_env, goal_ty) +} diff --git a/tests/ui/associated-inherent-types/issue-111404-1.rs b/tests/ui/associated-inherent-types/issue-111404-1.rs index 74f9434b8818a..dd62e59f07d22 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.rs +++ b/tests/ui/associated-inherent-types/issue-111404-1.rs @@ -10,6 +10,5 @@ impl<'a> Foo { fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} //~^ ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111404-1.stderr b/tests/ui/associated-inherent-types/issue-111404-1.stderr index 1613161a873f7..cf4d4a5f19b12 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.stderr +++ b/tests/ui/associated-inherent-types/issue-111404-1.stderr @@ -12,13 +12,5 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: higher-ranked subtype error - --> $DIR/issue-111404-1.rs:10:1 - | -LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/implied-bounds/from-trait-impl.rs b/tests/ui/implied-bounds/from-trait-impl.rs index 770c47d9d2077..f18bd5099b1c6 100644 --- a/tests/ui/implied-bounds/from-trait-impl.rs +++ b/tests/ui/implied-bounds/from-trait-impl.rs @@ -1,21 +1,39 @@ -// Foo> shouldn't imply X: 'static. -// We don't use region constraints from trait impls in implied bounds. - trait Trait { type Assoc; } -impl Trait for Vec { +impl Trait for (X,) { type Assoc = (); } struct Foo(T) where - T::Assoc: 'static, // any predicate naming T::Assoc -; + T::Assoc: Clone; // any predicate using `T::Assoc` works here + +fn func1(foo: Foo<(&str,)>) { + //~^ WARN function is missing necessary lifetime bounds + //~| WARN this was previously accepted + let _: &'static str = foo.0.0; +} + +#[deny(implied_bounds_from_trait_impl)] +fn func2(foo: Foo<(&str,)>) { + //~^ ERROR function is missing necessary lifetime bounds + //~| WARN this was previously accepted + //~| ERROR `&str` does not fulfill the required lifetime + let _: &'static str = foo.0.0; +} + +trait TestTrait {} + +impl TestTrait for [Foo<(X,)>; 1] {} +//~^ WARN implementation is missing necessary lifetime bounds +//~| WARN this was previously accepted -fn foo(_: Foo>) {} -//~^ ERROR `X` may not live long enough +#[deny(implied_bounds_from_trait_impl)] +impl TestTrait for [Foo<(X,)>; 2] {} +//~^ ERROR implementation is missing necessary lifetime bounds +//~| WARN this was previously accepted //~| ERROR `X` may not live long enough fn main() {} diff --git a/tests/ui/implied-bounds/from-trait-impl.stderr b/tests/ui/implied-bounds/from-trait-impl.stderr index bf5ce6d3a0166..a8543aba7dfa9 100644 --- a/tests/ui/implied-bounds/from-trait-impl.stderr +++ b/tests/ui/implied-bounds/from-trait-impl.stderr @@ -1,31 +1,77 @@ -error[E0310]: the parameter type `X` may not live long enough - --> $DIR/from-trait-impl.rs:17:1 +warning: function is missing necessary lifetime bounds + --> $DIR/from-trait-impl.rs:13:1 | -LL | fn foo(_: Foo>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | the parameter type `X` must be valid for the static lifetime... - | ...so that the type `X` will meet its required lifetime bounds +LL | fn func1(foo: Foo<(&str,)>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: consider adding an explicit lifetime bound + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #109628 + = note: to get more detailed errors, use `#[deny(implied_bounds_from_trait_impl)]` + = note: `#[warn(implied_bounds_from_trait_impl)]` on by default + +error: function is missing necessary lifetime bounds + --> $DIR/from-trait-impl.rs:20:1 + | +LL | fn func2(foo: Foo<(&str,)>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #109628 + = note: more concrete lifetime errors are emitted below +note: the lint level is defined here + --> $DIR/from-trait-impl.rs:19:8 + | +LL | #[deny(implied_bounds_from_trait_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0477]: the type `&str` does not fulfill the required lifetime + --> $DIR/from-trait-impl.rs:20:15 + | +LL | fn func2(foo: Foo<(&str,)>) { + | ^^^^^^^^^^^^ + | + = note: type must satisfy the static lifetime + +warning: implementation is missing necessary lifetime bounds + --> $DIR/from-trait-impl.rs:29:1 + | +LL | impl TestTrait for [Foo<(X,)>; 1] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #109628 + = note: to get more detailed errors, use `#[deny(implied_bounds_from_trait_impl)]` + +error: implementation is missing necessary lifetime bounds + --> $DIR/from-trait-impl.rs:34:1 + | +LL | impl TestTrait for [Foo<(X,)>; 2] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #109628 + = note: more concrete lifetime errors are emitted below +note: the lint level is defined here + --> $DIR/from-trait-impl.rs:33:8 | -LL | fn foo(_: Foo>) {} - | +++++++++ +LL | #[deny(implied_bounds_from_trait_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0310]: the parameter type `X` may not live long enough - --> $DIR/from-trait-impl.rs:17:14 + --> $DIR/from-trait-impl.rs:34:23 | -LL | fn foo(_: Foo>) {} - | ^^^^^^^^^^^ - | | - | the parameter type `X` must be valid for the static lifetime... - | ...so that the type `X` will meet its required lifetime bounds +LL | impl TestTrait for [Foo<(X,)>; 2] {} + | ^^^^^^^^^^^^^^ + | | + | the parameter type `X` must be valid for the static lifetime... + | ...so that the type `X` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | -LL | fn foo(_: Foo>) {} - | +++++++++ +LL | impl TestTrait for [Foo<(X,)>; 2] {} + | +++++++++ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors; 2 warnings emitted -For more information about this error, try `rustc --explain E0310`. +Some errors have detailed explanations: E0310, E0477. +For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr b/tests/ui/implied-bounds/normalization-nested.lifetime.stderr new file mode 100644 index 0000000000000..dc69ebc9bca05 --- /dev/null +++ b/tests/ui/implied-bounds/normalization-nested.lifetime.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/normalization-nested.rs:37:5 + | +LL | pub fn test<'x>(_: Map>, s: &'x str) -> &'static str { + | -- lifetime `'x` defined here +LL | s + | ^ returning this value requires that `'x` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/implied-bounds/normalization-nested.rs b/tests/ui/implied-bounds/normalization-nested.rs index 0935fe0f91b5c..5f1cbb3f69779 100644 --- a/tests/ui/implied-bounds/normalization-nested.rs +++ b/tests/ui/implied-bounds/normalization-nested.rs @@ -1,10 +1,11 @@ // Test for normalization of projections that appear in the item bounds // (versus those that appear directly in the input types). -// Both revisions should pass. -// `lifetime` revision was incorrectly rejected. See #109799. +// Both revisions should pass. `lifetime` revision is a bug. // // revisions: param_ty lifetime -// check-pass +// [param_ty] check-pass +// [lifetime] check-fail +// [lifetime] known-bug: #109799 pub trait Iter { type Item; diff --git a/tests/ui/inference/issue-80409.rs b/tests/ui/inference/issue-80409.rs index 3a1ea30af5cde..80cad6dfc46e4 100644 --- a/tests/ui/inference/issue-80409.rs +++ b/tests/ui/inference/issue-80409.rs @@ -1,7 +1,4 @@ -// This is an ICE because of #104478. -// known-bug: unknown -// failure-status: 101 -// dont-check-compiler-stderr +// check-pass #![allow(unreachable_code, unused)] From 2969bd9a8d61b765bbf8d31db6d384d034aca3b7 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sat, 2 Dec 2023 18:44:18 -0500 Subject: [PATCH 6/7] Don't emit IMPLIED_BOUNDS_FROM_TRAIT_IMPL for Bevy dependents --- .../rustc_hir_analysis/src/check/check.rs | 2 +- .../src/check/compare_impl_item.rs | 7 ++++--- .../src/check/compare_impl_item/refine.rs | 2 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 19 +++++++++++++++++-- .../src/impl_wf_check/min_specialization.rs | 3 ++- .../rustc_trait_selection/src/traits/misc.rs | 2 +- .../src/traits/outlives_bounds.rs | 7 +++---- tests/ui/implied-bounds/auxiliary/bevy_ecs.rs | 16 ++++++++++++++++ tests/ui/implied-bounds/bevy_world_query.rs | 13 +++++++++++++ 9 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 tests/ui/implied-bounds/auxiliary/bevy_ecs.rs create mode 100644 tests/ui/implied-bounds/bevy_world_query.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3ea3c785d07f5..bb7a743806252 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -355,7 +355,7 @@ fn check_opaque_meets_bounds<'tcx>( // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias { .. } => { let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?; - let implied_bounds = infcx.implied_bounds_tys_compat(param_env, def_id, wf_tys); + let implied_bounds = infcx.implied_bounds_tys_compat(param_env, def_id, &wf_tys); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index c7f5c0e5dd28c..e7f5d9054a1e8 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -404,7 +404,7 @@ fn compare_method_predicate_entailment<'tcx>( // lifetime parameters. let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, wf_tys), + infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys), ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { @@ -880,7 +880,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // lifetime parameters. let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, wf_tys), + infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys), ); ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?; @@ -2240,7 +2240,8 @@ pub(super) fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let implied_bounds = infcx.implied_bounds_tys_compat(param_env, impl_ty_def_id, assumed_wf_types); + let implied_bounds = + infcx.implied_bounds_tys_compat(param_env, impl_ty_def_id, &assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 1cb44815e5ba0..7f141ae3e2c80 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -161,7 +161,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( } let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys_compat(param_env, impl_m.def_id.expect_local(), implied_wf_types), + infcx.implied_bounds_tys_compat(param_env, impl_m.def_id.expect_local(), &implied_wf_types), ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0cdc395549307..f71bfa81239a6 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -13,6 +13,7 @@ use rustc_infer::infer::outlives::obligations::TypeOutlives; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::query::Providers; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, @@ -126,6 +127,7 @@ where let infcx_compat = infcx.fork(); + debug!(?assumed_wf_types); let implied_bounds = infcx.implied_bounds_tys(param_env, &assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); @@ -135,13 +137,26 @@ where } let implied_bounds = - infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, assumed_wf_types); + infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let errors_compat = infcx_compat.resolve_regions(&outlives_env); if !errors_compat.is_empty() { return Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat)); } + // We don't want to emit this for dependents of Bevy, for now. + for ty in assumed_wf_types.iter() { + match ty.kind() { + ty::Adt(def, _) => { + let adt_did = with_no_trimmed_paths!(infcx.tcx.def_path_str(def.0.did)); + if adt_did == "bevy_ecs::system::ParamSet" { + return Ok(()); + } + } + _ => {} + } + } + let hir_id = tcx.local_def_id_to_hir_id(body_def_id); let (lint_level, _) = tcx .lint_level_at_node(rustc_session::lint::builtin::IMPLIED_BOUNDS_FROM_TRAIT_IMPL, hir_id); @@ -752,7 +767,7 @@ fn resolve_regions_with_wf_tys<'tcx>( let infcx = tcx.infer_ctxt().build(); let outlives_environment = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys_compat(param_env, id, wf_tys.clone()), + infcx.implied_bounds_tys_compat(param_env, id, wf_tys), ); let region_bound_pairs = outlives_environment.region_bound_pairs(); diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 17e8a6a3cb1f1..768c42c67b8df 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -202,7 +202,8 @@ fn get_impl_args( return Err(guar); } - let implied_bounds = infcx.implied_bounds_tys_compat(param_env, impl1_def_id, assumed_wf_types); + let implied_bounds = + infcx.implied_bounds_tys_compat(param_env, impl1_def_id, &assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else { diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index e4fac514dd97c..06c872f7168b6 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -194,7 +194,7 @@ pub fn all_fields_implement_trait<'tcx>( infcx.implied_bounds_tys_compat( param_env, parent_cause.body_id, - FxIndexSet::from_iter([self_type]), + &FxIndexSet::from_iter([self_type]), ), ); let errors = infcx.resolve_regions(&outlives_env); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index e437e8338b7fb..65827fbe455cb 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -23,7 +23,7 @@ pub trait InferCtxtExt<'a, 'tcx> { &'a self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, - tys: FxIndexSet>, + tys: &'a FxIndexSet>, ) -> BoundsCompat<'a, 'tcx>; fn implied_bounds_tys( @@ -132,10 +132,9 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { &'a self, param_env: ParamEnv<'tcx>, body_id: LocalDefId, - tys: FxIndexSet>, + tys: &'a FxIndexSet>, ) -> BoundsCompat<'a, 'tcx> { - tys.into_iter() - .flat_map(move |ty| self.implied_outlives_bounds_compat(param_env, body_id, ty)) + tys.iter().flat_map(move |ty| self.implied_outlives_bounds_compat(param_env, body_id, *ty)) } fn implied_bounds_tys( diff --git a/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs b/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs new file mode 100644 index 0000000000000..809cfa3bb6b5a --- /dev/null +++ b/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs @@ -0,0 +1,16 @@ +pub mod system { + pub trait WorldQuery {} + impl WorldQuery for &u8 {} + + pub struct Query(Q); + + pub trait SystemParam { + type State; + } + impl SystemParam for Query { + type State = (); + // `Q: 'static` is required because we need the TypeId of Q ... + } + + pub struct ParamSet(T) where T::State: Sized; +} diff --git a/tests/ui/implied-bounds/bevy_world_query.rs b/tests/ui/implied-bounds/bevy_world_query.rs new file mode 100644 index 0000000000000..0aa73341f894a --- /dev/null +++ b/tests/ui/implied-bounds/bevy_world_query.rs @@ -0,0 +1,13 @@ +// aux-crate:bevy_ecs=bevy_ecs.rs +// check-pass + +// We currently special case bevy from emitting the `IMPLIED_BOUNDS_FROM_TRAIT_IMPL` lint. +// Otherwise, we would expect this to hit the lint. + +extern crate bevy_ecs; + +use bevy_ecs::system::*; + +fn handler<'a>(_: ParamSet>) {} + +fn main() {} From b4485be1aa48cf8542aafb4a67ab48d2fdd34f70 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sat, 2 Dec 2023 18:46:24 -0500 Subject: [PATCH 7/7] Also report region errors for crater --- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f71bfa81239a6..c70a886181250 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -175,7 +175,7 @@ where } }, ); - if lint_level.is_error() { + if true || lint_level.is_error() { infcx.err_ctxt().report_region_errors(body_def_id, &errors); }