diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 77914802bf77d..03e2b0e00220e 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -368,7 +368,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(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 774feb94f7dc2..5b264f6f034c9 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -378,7 +378,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(param_env, impl_m_def_id, &wf_tys), ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { @@ -702,7 +702,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(param_env, impl_m_def_id, &wf_tys), ); ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?; @@ -2070,7 +2070,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(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 f7fc0c81b953b..0d8de0cabd1af 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 @@ -158,7 +158,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(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 4772bae58c471..58046173fb1a9 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -15,6 +15,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, @@ -112,8 +113,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(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); @@ -128,10 +127,65 @@ where } } + debug!(?assumed_wf_types); + + let infcx_compat = infcx.fork(); + + // We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always. + let implied_bounds = + infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?; - infcx.tainted_by_errors().error_reported() + let errors = infcx.resolve_regions(&outlives_env); + if errors.is_empty() { + return Ok(()); + } + + let is_bevy = 'is_bevy: { + // We don't want to emit this for dependents of Bevy, for now. + // See #119956 + let is_bevy_paramset = |def: ty::AdtDef<'_>| { + let adt_did = with_no_trimmed_paths!(infcx.tcx.def_path_str(def.0.did)); + adt_did.contains("ParamSet") + }; + for ty in assumed_wf_types.iter() { + match ty.kind() { + ty::Adt(def, _) => { + if is_bevy_paramset(*def) { + break 'is_bevy true; + } + } + ty::Ref(_, ty, _) => match ty.kind() { + ty::Adt(def, _) => { + if is_bevy_paramset(*def) { + break 'is_bevy true; + } + } + _ => {} + }, + _ => {} + } + } + false + }; + + // If we have set `no_implied_bounds_compat`, then do not attempt compatibility. + // We could also just always enter if `is_bevy`, and call `implied_bounds_tys`, + // but that does result in slightly more work when this option is set and + // just obscures what we mean here anyways. Let's just be explicit. + if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { + let implied_bounds = + infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, true); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + let errors_compat = infcx_compat.resolve_regions(&outlives_env); + if errors_compat.is_empty() { + Ok(()) + } else { + Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat)) + } + } else { + Err(infcx.err_ctxt().report_region_errors(body_def_id, &errors)) + } } fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> { @@ -723,7 +777,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(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 93dd2342a4d13..6964446f9c770 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(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_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index b9200f1abf161..7122f13173155 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 1dc7722088108..f5b76f08f0ba7 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1949,7 +1949,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>>>, @@ -1958,6 +1958,15 @@ rustc_queries! { desc { "computing implied outlives bounds for `{}`", goal.value.value } } + query implied_outlives_bounds( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, + > { + desc { "computing implied outlives bounds v2 for `{}`", goal.value.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 5f02dd22961c0..61d96cad57d77 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_session/src/options.rs b/compiler/rustc_session/src/options.rs index f4bf79f93f287..5bc88ae6ed172 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1724,6 +1724,8 @@ options! { "run all passes except codegen; no output"), no_generate_arange_section: bool = (false, parse_no_flag, [TRACKED], "omit DWARF address ranges that give faster lookups"), + no_implied_bounds_compat: bool = (false, parse_bool, [TRACKED], + "disable the compatibility version of the `implied_bounds_ty` query"), no_jump_tables: bool = (false, parse_no_flag, [TRACKED], "disable the jump tables and lookup tables that can be generated from a switch case lowering"), no_leak_check: bool = (false, parse_no_flag, [UNTRACKED], diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 073174127d6a1..0cd376fcbbdc8 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( 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 5b349d576b6a1..52631d4353bfa 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -9,123 +9,153 @@ 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( - &self, + /// Do *NOT* call this directly. + fn implied_bounds_tys_compat( + &'a self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, - ty: Ty<'tcx>, - ) -> Vec>; + tys: &'a FxIndexSet>, + compat: bool, + ) -> BoundsCompat<'a, 'tcx>; + /// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat` + /// with `compat` set to `true`, otherwise `false`. fn implied_bounds_tys( &'a self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, - tys: FxIndexSet>, + tys: &'a FxIndexSet>, ) -> Bounds<'a, 'tcx>; } -impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { - /// Implied bounds are region relationships that we deduce - /// automatically. The idea is that (e.g.) a caller must check that a - /// function's argument types are well-formed immediately before - /// calling that fn, and hence the *callee* can assume that its - /// argument types are well-formed. This may imply certain relationships - /// between generic parameters. For example: - /// ``` - /// fn foo(x: &T) {} - /// ``` - /// can only be called with a `'a` and `T` such that `&'a T` is WF. - /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. - /// - /// # Parameters - /// - /// - `param_env`, the where-clauses in scope - /// - `body_id`, the body-id to use when normalizing assoc types. - /// Note that this may cause outlives obligations to be injected - /// 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( - &self, - param_env: ty::ParamEnv<'tcx>, - body_id: LocalDefId, - ty: Ty<'tcx>, - ) -> Vec> { - let ty = self.resolve_vars_if_possible(ty); - let ty = OpportunisticRegionResolver::new(self).fold_ty(ty); +/// Implied bounds are region relationships that we deduce +/// automatically. The idea is that (e.g.) a caller must check that a +/// function's argument types are well-formed immediately before +/// calling that fn, and hence the *callee* can assume that its +/// argument types are well-formed. This may imply certain relationships +/// between generic parameters. For example: +/// ``` +/// fn foo(x: &T) {} +/// ``` +/// can only be called with a `'a` and `T` such that `&'a T` is WF. +/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. +/// +/// # Parameters +/// +/// - `param_env`, the where-clauses in scope +/// - `body_id`, the body-id to use when normalizing assoc types. +/// Note that this may cause outlives obligations to be injected +/// into the inference context with this body-id. +/// - `ty`, the type that we are supposed to assume is WF. +#[instrument(level = "debug", skip(infcx, param_env, body_id), ret)] +fn implied_outlives_bounds<'a, 'tcx>( + infcx: &'a InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + ty: Ty<'tcx>, + compat: bool, +) -> Vec> { + let ty = infcx.resolve_vars_if_possible(ty); + let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty); - // We do not expect existential variables in implied bounds. - // We may however encounter unconstrained lifetime variables - // in very rare cases. - // - // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for - // an example. - assert!(!ty.has_non_region_infer()); + // We do not expect existential variables in implied bounds. + // We may however encounter unconstrained lifetime variables + // in very rare cases. + // + // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for + // an example. + assert!(!ty.has_non_region_infer()); - let mut canonical_var_values = OriginalQueryValues::default(); - let canonical_ty = self.canonicalize_query(param_env.and(ty), &mut canonical_var_values); - let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else { - return vec![]; - }; + let mut canonical_var_values = OriginalQueryValues::default(); + let canonical_ty = infcx.canonicalize_query(param_env.and(ty), &mut canonical_var_values); + let implied_bounds_result = if compat { + infcx.tcx.implied_outlives_bounds_compat(canonical_ty) + } else { + infcx.tcx.implied_outlives_bounds(canonical_ty) + }; + let Ok(canonical_result) = implied_bounds_result else { + return vec![]; + }; - let mut constraints = QueryRegionConstraints::default(); - let Ok(InferOk { value: mut bounds, obligations }) = self - .instantiate_nll_query_response_and_region_obligations( - &ObligationCause::dummy(), - param_env, - &canonical_var_values, - canonical_result, - &mut constraints, - ) - else { - return vec![]; - }; - assert_eq!(&obligations, &[]); + let mut constraints = QueryRegionConstraints::default(); + let Ok(InferOk { value: mut bounds, obligations }) = infcx + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + canonical_result, + &mut constraints, + ) + else { + return vec![]; + }; + assert_eq!(&obligations, &[]); - // Because of #109628, we may have unexpected placeholders. Ignore them! - // FIXME(#109628): panic in this case once the issue is fixed. - bounds.retain(|bound| !bound.has_placeholders()); + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + bounds.retain(|bound| !bound.has_placeholders()); - if !constraints.is_empty() { - let span = self.tcx.def_span(body_id); + if !constraints.is_empty() { + let span = infcx.tcx.def_span(body_id); - debug!(?constraints); - if !constraints.member_constraints.is_empty() { - span_bug!(span, "{:#?}", constraints.member_constraints); - } + debug!(?constraints); + if !constraints.member_constraints.is_empty() { + span_bug!(span, "{:#?}", constraints.member_constraints); + } - // Instantiation may have produced new inference variables and constraints on those - // variables. Process these constraints. - let ocx = ObligationCtxt::new(self); - let cause = ObligationCause::misc(span, body_id); - for &constraint in &constraints.outlives { - ocx.register_obligation(self.query_outlives_constraint_to_obligation( - constraint, - cause.clone(), - param_env, - )); - } + // Instantiation may have produced new inference variables and constraints on those + // variables. Process these constraints. + let ocx = ObligationCtxt::new(infcx); + let cause = ObligationCause::misc(span, body_id); + for &constraint in &constraints.outlives { + ocx.register_obligation(infcx.query_outlives_constraint_to_obligation( + constraint, + cause.clone(), + param_env, + )); + } - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - self.dcx().span_delayed_bug( - span, - "implied_outlives_bounds failed to solve obligations from instantiation", - ); - } - }; + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.dcx().span_delayed_bug( + span, + "implied_outlives_bounds failed to solve obligations from instantiation", + ); + } + }; - bounds + bounds +} + +impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { + fn implied_bounds_tys_compat( + &'a self, + param_env: ParamEnv<'tcx>, + body_id: LocalDefId, + tys: &'a FxIndexSet>, + compat: bool, + ) -> BoundsCompat<'a, 'tcx> { + tys.iter() + .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat)) } fn implied_bounds_tys( &'a self, param_env: ParamEnv<'tcx>, body_id: LocalDefId, - tys: FxIndexSet>, + tys: &'a FxIndexSet>, ) -> Bounds<'a, 'tcx> { - tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty)) + tys.iter().flat_map(move |ty| { + implied_outlives_bounds( + self, + param_env, + body_id, + *ty, + !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, + ) + }) } } 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 ba6ed29877409..2fdb63d7deebd 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,11 @@ 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}; @@ -62,6 +63,85 @@ 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| { + 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); + let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty); + 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 + // 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(), normalize_op(ty)?.into()]; + + let mut outlives_bounds: Vec> = vec![]; + + while let Some(arg) = wf_args.pop() { + if !checked_wf_args.insert(arg) { + continue; + } + + // 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()); + let Some(pred) = obligation.predicate.kind().no_bound_vars() else { + continue; + }; + match pred { + // 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::Trait(..)) + | 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::NormalizesTo(..) + | 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(OutlivesBound::RegionSubRegion(r_b, r_a)), + + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty_a, + r_b, + ))) => { + 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)) + } + } + } + } + + Ok(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; diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 959838ab348fd..24e91c263e344 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -8,13 +8,29 @@ use rustc_infer::traits::query::OutlivesBound; use rustc_middle::query::Providers; use rustc_middle::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}; 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>( + tcx: TyCtxt<'tcx>, + goal: CanonicalTyGoal<'tcx>, +) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, +> { + tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { + let (param_env, ty) = key.into_parts(); + compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) + }) +} + fn implied_outlives_bounds<'tcx>( tcx: TyCtxt<'tcx>, goal: CanonicalTyGoal<'tcx>, 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/auxiliary/bevy_ecs.rs b/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs deleted file mode 100644 index b373d39f4d940..0000000000000 --- a/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Related to Bevy regression #118553 - -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 index f8e64632676d7..d5de0e9ef2176 100644 --- a/tests/ui/implied-bounds/bevy_world_query.rs +++ b/tests/ui/implied-bounds/bevy_world_query.rs @@ -1,11 +1,26 @@ -// aux-crate:bevy_ecs=bevy_ecs.rs // check-pass -// Related to Bevy regression #118553 -extern crate bevy_ecs; +// We currently special case bevy from erroring on incorrect implied bounds +// from normalization (issue #109628). +// Otherwise, we would expect this to hit that error. -use bevy_ecs::*; +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; fn handler<'a>(_: ParamSet>) {} +fn ref_handler<'a>(_: &ParamSet>) {} + fn main() {} diff --git a/tests/ui/implied-bounds/from-trait-impl.rs b/tests/ui/implied-bounds/from-trait-impl.rs index d13fddd9b8d41..6e126575aa9a2 100644 --- a/tests/ui/implied-bounds/from-trait-impl.rs +++ b/tests/ui/implied-bounds/from-trait-impl.rs @@ -1,6 +1,3 @@ -// check-pass -// known-bug: #109628 - trait Trait { type Assoc; } @@ -14,11 +11,13 @@ where T::Assoc: Clone; // any predicate using `T::Assoc` works here fn func1(foo: Foo<(&str,)>) { + //~^ ERROR `&str` does not fulfill the required lifetime let _: &'static str = foo.0.0; } trait TestTrait {} impl TestTrait for [Foo<(X,)>; 1] {} +//~^ 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..4151d206ae25f --- /dev/null +++ b/tests/ui/implied-bounds/from-trait-impl.stderr @@ -0,0 +1,26 @@ +error[E0477]: the type `&str` does not fulfill the required lifetime + --> $DIR/from-trait-impl.rs:13:15 + | +LL | fn func1(foo: Foo<(&str,)>) { + | ^^^^^^^^^^^^ + | + = note: type must satisfy the static lifetime + +error[E0310]: the parameter type `X` may not live long enough + --> $DIR/from-trait-impl.rs:20:23 + | +LL | impl TestTrait for [Foo<(X,)>; 1] {} + | ^^^^^^^^^^^^^^ + | | + | 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 | impl TestTrait for [Foo<(X,)>; 1] {} + | +++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0310, E0477. +For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/implied-bounds/gluon_salsa.rs b/tests/ui/implied-bounds/gluon_salsa.rs index 98951af8ac2da..cd5500cb458f0 100644 --- a/tests/ui/implied-bounds/gluon_salsa.rs +++ b/tests/ui/implied-bounds/gluon_salsa.rs @@ -1,5 +1,5 @@ // check-pass -// Related to Bevy regression #118553 +// Found in a crater run on #118553 pub trait QueryBase { type Db; @@ -17,8 +17,7 @@ pub struct QueryTable<'me, Q, DB> { _marker: Option<&'me ()>, } -impl<'me, Q> QueryTable<'me, Q, ::Db> -// projection is important +impl<'me, Q> QueryTable<'me, Q, ::Db> // projection is important // ^^^ removing 'me (and in QueryTable) gives a different error where Q: for<'f> AsyncQueryFunction<'f>, 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 e020230d86a48..0000000000000 --- a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0759]: `fn` parameter has lifetime `'x` but it needs to satisfy a `'static` lifetime requirement - --> $DIR/normalization-nested.rs:35:28 - | -LL | pub fn test_wfcheck<'x>(_: Map>) {} - | ^^^^^^^^^^^^^^^^ - | | - | 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[E0759]: `fn` parameter has lifetime `'x` but it needs to satisfy a `'static` lifetime requirement - --> $DIR/normalization-nested.rs:37:29 - | -LL | pub fn test_borrowck<'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 2 previous errors - -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 87903783a678d..3f569aa1acea7 100644 --- a/tests/ui/implied-bounds/normalization-nested.rs +++ b/tests/ui/implied-bounds/normalization-nested.rs @@ -1,11 +1,8 @@ // 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. // // 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/implied-bounds/sod_service_chain.rs b/tests/ui/implied-bounds/sod_service_chain.rs index f45ced71f757b..7443a29f30cbd 100644 --- a/tests/ui/implied-bounds/sod_service_chain.rs +++ b/tests/ui/implied-bounds/sod_service_chain.rs @@ -1,5 +1,4 @@ -// check-pass -// Related to crater regressions on #118553 +// Found in a crater run on #118553 pub trait Debug {} @@ -30,6 +29,9 @@ impl> ServiceChainBuilder { pub fn next>( self, ) -> ServiceChainBuilder, NS> { + //~^ the associated type + //~| the associated type + //~| the associated type panic!(); } } diff --git a/tests/ui/implied-bounds/sod_service_chain.stderr b/tests/ui/implied-bounds/sod_service_chain.stderr new file mode 100644 index 0000000000000..1c0ef573e7d33 --- /dev/null +++ b/tests/ui/implied-bounds/sod_service_chain.stderr @@ -0,0 +1,31 @@ +error[E0310]: the associated type `

::Error` may not live long enough + --> $DIR/sod_service_chain.rs:31:10 + | +LL | ) -> ServiceChainBuilder, NS> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `

::Error` must be valid for the static lifetime... + | ...so that the type `

::Error` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | ) -> ServiceChainBuilder, NS> where

::Error: 'static { + | ++++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Error` may not live long enough + --> $DIR/sod_service_chain.rs:31:10 + | +LL | ) -> ServiceChainBuilder, NS> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Error` must be valid for the static lifetime... + | ...so that the type `::Error` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | ) -> ServiceChainBuilder, NS> where ::Error: 'static { + | ++++++++++++++++++++++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/inference/issue-80409.rs b/tests/ui/inference/issue-80409.rs index 80cad6dfc46e4..d36688978e9f3 100644 --- a/tests/ui/inference/issue-80409.rs +++ b/tests/ui/inference/issue-80409.rs @@ -1,4 +1,12 @@ -// check-pass +// This should not pass, because `usize: Fsm` does not hold. However, it currently ICEs. + +// check-fail +// known-bug: #80409 +// failure-status: 101 +// normalize-stderr-test "note: .*\n\n" -> "" +// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" +// normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +// rustc-env:RUST_BACKTRACE=0 #![allow(unreachable_code, unused)] diff --git a/tests/ui/inference/issue-80409.stderr b/tests/ui/inference/issue-80409.stderr new file mode 100644 index 0000000000000..7bb4786db3a91 --- /dev/null +++ b/tests/ui/inference/issue-80409.stderr @@ -0,0 +1,6 @@ +error: internal compiler error: error performing ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: ImpliedOutlivesBounds { ty: &'?2 mut StateContext<'?3, usize> } } + | + = query stack during panic: +end of query stack +error: aborting due to 1 previous error +