diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 4004966c40a77..fc898b1b0956c 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -8,7 +8,6 @@ use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; @@ -35,16 +34,9 @@ pub(crate) struct UniversalRegionRelations<'tcx> { inverse_outlives: TransitiveRelation, } -/// As part of computing the free region relations, we also have to -/// normalize the input-output types, which we then need later. So we -/// return those. This vector consists of first the input types and -/// then the output type as the last element. -type NormalizedInputsAndOutput<'tcx> = Vec>; - pub(crate) struct CreateResult<'tcx> { pub(crate) universal_region_relations: Frozen>, pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>, - pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>, } pub(crate) fn create<'tcx>( @@ -222,68 +214,27 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .chain(Some(self.universal_regions.unnormalized_output_ty)); // For each of the input/output types: - // - Normalize the type. This will create some region - // constraints, which we buffer up because we are - // not ready to process them yet. - // - Then compute the implied bounds. This will adjust + // - Compute the implied bounds. This will adjust // the `region_bound_pairs` and so forth. // - After this is done, we'll process the constraints, once // the `relations` is built. - let mut normalized_inputs_and_output = - Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1); - let mut constraints = vec![]; - for ty in unnormalized_input_output_tys { - debug!("build: input_or_output={:?}", ty); - // We add implied bounds from both the unnormalized and normalized ty. - // See issue #87748 - let constraints_unnorm = self.add_implied_bounds(ty); - if let Some(c) = constraints_unnorm { - constraints.push(c) - } - let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self - .param_env - .and(type_op::normalize::Normalize::new(ty)) - .fully_perform(self.infcx) - .unwrap_or_else(|_| { - let guar = self - .infcx - .tcx - .sess - .delay_span_bug(span, &format!("failed to normalize {:?}", ty)); - TypeOpOutput { - output: self.infcx.tcx.ty_error(guar), - constraints: None, - error_info: None, - } - }); - if let Some(c) = constraints_normalize { - constraints.push(c) - } - - // Note: we need this in examples like - // ``` - // trait Foo { - // type Bar; - // fn foo(&self) -> &Self::Bar; - // } - // impl Foo for () { - // type Bar = (); - // fn foo(&self) ->&() {} - // } - // ``` - // Both &Self::Bar and &() are WF - if ty != norm_ty { - let constraints_norm = self.add_implied_bounds(norm_ty); - if let Some(c) = constraints_norm { - constraints.push(c) - } - } - - normalized_inputs_and_output.push(norm_ty); - } - - for c in constraints { - self.push_region_constraints(c, span); + let constraint_sets: Vec<_> = + unnormalized_input_output_tys.flat_map(|ty| self.add_implied_bounds(ty)).collect(); + + // Subtle: We can convert constraints only after the relations are built. + for data in &constraint_sets { + constraint_conversion::ConstraintConversion::new( + self.infcx, + &self.universal_regions, + &self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + Locations::All(span), + span, + ConstraintCategory::Internal, + &mut self.constraints, + ) + .convert_all(data); } CreateResult { @@ -293,28 +244,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { inverse_outlives: self.inverse_outlives.freeze(), }), region_bound_pairs: self.region_bound_pairs, - normalized_inputs_and_output, } } - #[instrument(skip(self, data), level = "debug")] - fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) { - debug!("constraints generated: {:#?}", data); - - constraint_conversion::ConstraintConversion::new( - self.infcx, - &self.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - Locations::All(span), - span, - ConstraintCategory::Internal, - &mut self.constraints, - ) - .convert_all(data); - } - /// Update the type of a single local, which should represent /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 17e702eb8c528..909d528995a9b 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -13,8 +13,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; -use crate::universal_regions::UniversalRegions; - use super::{Locations, TypeChecker}; impl<'a, 'tcx> TypeChecker<'a, 'tcx> { @@ -60,21 +58,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - #[instrument(skip(self, body, universal_regions), level = "debug")] - pub(super) fn equate_inputs_and_outputs( - &mut self, - body: &Body<'tcx>, - universal_regions: &UniversalRegions<'tcx>, - normalized_inputs_and_output: &[Ty<'tcx>], - ) { - let (&normalized_output_ty, normalized_input_tys) = - normalized_inputs_and_output.split_last().unwrap(); + #[instrument(skip(self, body), level = "debug")] + pub(super) fn equate_inputs_and_outputs(&mut self, body: &Body<'tcx>) { + let universal_regions = self.borrowck_context.universal_regions; - debug!(?normalized_output_ty); - debug!(?normalized_input_tys); + debug!(?universal_regions.unnormalized_input_tys, ?body.local_decls); // Equate expected input tys with those in the MIR. - for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() { + for (argument_index, &unnormalized_input_ty) in + universal_regions.unnormalized_input_tys.iter().enumerate() + { if argument_index + 1 >= body.local_decls.len() { self.tcx() .sess @@ -88,6 +81,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let mir_input_ty = body.local_decls[local].ty; let mir_input_span = body.local_decls[local].source_info.span; + let normalized_input_ty = + self.normalize(unnormalized_input_ty, Locations::All(mir_input_span)); self.equate_normalized_input_or_output( normalized_input_ty, mir_input_ty, @@ -125,6 +120,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Return types are a bit more complex. They may contain opaque `impl Trait` types. let mir_output_ty = body.local_decls[RETURN_PLACE].ty; let output_span = body.local_decls[RETURN_PLACE].source_info.span; + let normalized_output_ty = + self.normalize(universal_regions.unnormalized_output_ty, Locations::All(output_span)); if let Err(terr) = self.eq_types( normalized_output_ty, mir_output_ty, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c2a426bea0929..10250501f489b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -147,19 +147,14 @@ pub(crate) fn type_check<'mir, 'tcx>( universe_causes: FxIndexMap::default(), }; - let CreateResult { - universal_region_relations, - region_bound_pairs, - normalized_inputs_and_output, - } = free_region_relations::create( - infcx, - param_env, - implicit_region_bound, - universal_regions, - &mut constraints, - ); - - debug!(?normalized_inputs_and_output); + let CreateResult { universal_region_relations, region_bound_pairs } = + free_region_relations::create( + infcx, + param_env, + implicit_region_bound, + universal_regions, + &mut constraints, + ); for u in ty::UniverseIndex::ROOT..=infcx.universe() { constraints.universe_causes.insert(u, UniverseInfo::other()); @@ -194,7 +189,7 @@ pub(crate) fn type_check<'mir, 'tcx>( checker.typeck_mir(body); } - checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); + checker.equate_inputs_and_outputs(&body); checker.check_signature_annotation(&body); liveness::generate( 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 49665525967fa..24f60188e2322 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -259,10 +259,8 @@ fn compare_method_predicate_entailment<'tcx>( // we have to do this before normalization, since the normalized ty may // not contain the input parameters. See issue #87748. wf_tys.extend(trait_sig.inputs_and_output.iter()); + let trait_sig = ocx.normalize(&norm_cause, param_env, trait_sig); - // We also have to add the normalized trait signature - // as we don't normalize during implied bounds computation. - wf_tys.extend(trait_sig.inputs_and_output.iter()); let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); debug!("compare_impl_method: trait_fty={:?}", trait_fty); diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 6a64dfdedd42f..08c8dc1f78863 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -144,19 +144,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn compute_well_formed_goal( &mut self, - goal: Goal<'tcx, ty::GenericArg<'tcx>>, + Goal { predicate, param_env }: Goal<'tcx, ty::GenericArg<'tcx>>, ) -> QueryResult<'tcx> { - match crate::traits::wf::unnormalized_obligations( - self.infcx, - goal.param_env, - goal.predicate, - ) { - Some(obligations) => { - self.add_goals(obligations.into_iter().map(|o| o.into())); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), - } + debug_assert_eq!(predicate, self.infcx.resolve_vars_if_possible(predicate)); + let obligations = + crate::traits::wf::unnormalized_obligations(self.tcx(), param_env, predicate); + self.add_goals(obligations.into_iter().map(|o| o.into())); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } #[instrument(level = "debug", skip(self), ret)] diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index ec5bd982a3c98..814dd31d20c0f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -77,21 +77,20 @@ pub fn obligations<'tcx>( /// Compute the predicates that are required for a type to be well-formed. /// -/// This is only intended to be used in the new solver, since it does not -/// take into account recursion depth or proper error-reporting spans. +/// This is only intended to be used in implied bounds computation and in +/// the new solver, since it does not take into account recursion depth or +/// proper error-reporting spans. pub fn unnormalized_obligations<'tcx>( - infcx: &InferCtxt<'tcx>, + tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, arg: GenericArg<'tcx>, -) -> Option>> { +) -> Vec> { if let ty::GenericArgKind::Lifetime(..) = arg.unpack() { - return Some(vec![]); + return vec![]; } - debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); - let mut wf = WfPredicates { - tcx: infcx.tcx, + tcx, param_env, body_id: CRATE_DEF_ID, span: DUMMY_SP, @@ -100,7 +99,7 @@ pub fn unnormalized_obligations<'tcx>( item: None, }; wf.compute(arg); - Some(wf.out) + wf.out } /// Returns the obligations that make this trait reference diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index f5bba14d2fb9c..a641589c46134 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -2,18 +2,16 @@ //! Do not call this query directory. See //! [`rustc_trait_selection::traits::query::type_op::implied_outlives_bounds`]. -use rustc_infer::infer::canonical::{self, Canonical}; +use rustc_infer::infer::canonical; use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::query::OutlivesBound; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::def_id::CRATE_DEF_ID; -use rustc_span::source_map::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::{CanonicalTyGoal, Fallible, NoSolution}; use rustc_trait_selection::traits::wf; -use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::ObligationCause; use smallvec::{smallvec, SmallVec}; pub(crate) fn provide(p: &mut Providers) { @@ -23,79 +21,52 @@ pub(crate) fn provide(p: &mut Providers) { fn implied_outlives_bounds<'tcx>( tcx: TyCtxt<'tcx>, goal: CanonicalTyGoal<'tcx>, -) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, - NoSolution, -> { +) -> Fallible>>> { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ty) = key.into_parts(); - compute_implied_outlives_bounds(ocx, param_env, ty) + + compute_implied_outlives_bounds(tcx, param_env, ty, |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()); + Ok(ty) + }) }) } +/// For the sake of completeness, we should be careful when dealing with inference artifacts: +/// - This function shouldn't access an InferCtxt. +/// - `ty` must be fully resolved. +/// - `normalize_op` must return a fully resolved type. fn compute_implied_outlives_bounds<'tcx>( - ocx: &ObligationCtxt<'_, 'tcx>, + tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + normalize_op: impl Fn(Ty<'tcx>) -> Fallible>, ) -> Fallible>> { - 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 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(tcx, param_env, arg) { 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::Clause::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::Clause::Trait(..)) @@ -114,48 +85,27 @@ fn compute_implied_outlives_bounds<'tcx>( | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} // We need to search through *all* WellFormed predicates - ty::PredicateKind::WellFormed(arg) => { - wf_args.push(arg); - } + ty::PredicateKind::WellFormed(arg) => wf_args.push(arg), // We need to register region relationships - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate( - r_a, - r_b, - ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), + ty::PredicateKind::Clause(ty::Clause::RegionOutlives( + ty::OutlivesPredicate(r_a, r_b), + )) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)), ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( ty_a, r_b, - ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), + ))) => { + let ty_a = normalize_op(ty_a)?; + let mut components = smallvec![]; + push_outlives_components(tcx, ty_a, &mut components); + outlives_bounds.extend(implied_bounds_from_components(r_b, components)) + } } } } - // 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 implied_bounds = outlives_bounds - .into_iter() - .flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() { - ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)], - ty::GenericArgKind::Type(ty_a) => { - let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); - let mut components = smallvec![]; - push_outlives_components(tcx, ty_a, &mut components); - implied_bounds_from_components(r_b, components) - } - ty::GenericArgKind::Const(_) => unreachable!(), - }) - .collect(); - - Ok(implied_bounds) + Ok(outlives_bounds) } /// When we have an implied bound that `T: 'a`, we can further break @@ -165,28 +115,22 @@ fn compute_implied_outlives_bounds<'tcx>( fn implied_bounds_from_components<'tcx>( sub_region: ty::Region<'tcx>, sup_components: SmallVec<[Component<'tcx>; 4]>, -) -> Vec> { - sup_components - .into_iter() - .filter_map(|component| { - match component { - Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), - Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), - Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), - Component::EscapingAlias(_) => - // If the projection has escaping regions, don't - // try to infer any implied bounds even for its - // free components. This is conservative, because - // the caller will still have to prove that those - // free components outlive `sub_region`. But the - // idea is that the WAY that the caller proves - // that may change in the future and we want to - // give ourselves room to get smarter here. - { - None - } - Component::UnresolvedInferenceVariable(..) => None, - } - }) - .collect() +) -> impl Iterator> { + sup_components.into_iter().filter_map(move |component| { + match component { + Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), + Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), + Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), + // If the projection has escaping regions, don't + // try to infer any implied bounds even for its + // free components. This is conservative, because + // the caller will still have to prove that those + // free components outlive `sub_region`. But the + // idea is that the WAY that the caller proves + // that may change in the future and we want to + // give ourselves room to get smarter here. + Component::EscapingAlias(_) => None, + Component::UnresolvedInferenceVariable(..) => bug!("inference var in implied bounds"), + } + }) } 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..9522f7c18a874 --- /dev/null +++ b/tests/ui/implied-bounds/from-trait-impl.stderr @@ -0,0 +1,25 @@ +error[E0310]: the parameter type `X` may not live long enough + --> $DIR/from-trait-impl.rs:17:1 + | +LL | fn foo(_: Foo>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ ...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>) {} + | ^^^^^^^^^^^ ...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`.