From 5943c606c883b8247159c070f8bf1cc28d5c6d70 Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 5 Dec 2025 13:31:50 +0000 Subject: [PATCH 1/9] Add test --- tests/ui/borrowck/test-rename-later.rs | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/ui/borrowck/test-rename-later.rs diff --git a/tests/ui/borrowck/test-rename-later.rs b/tests/ui/borrowck/test-rename-later.rs new file mode 100644 index 0000000000000..a86db9fc47b0f --- /dev/null +++ b/tests/ui/borrowck/test-rename-later.rs @@ -0,0 +1,29 @@ +//@ compile-flags: -Znext-solver + +use std::any::Any; + +struct Outlives<'a, T>(Option<&'a T>); +trait Trait { + type Assoc; +} + +impl Trait for T { + type Assoc = T; +} + +// Computing the implied bounds for `foo` normalizes `impl Sized` to +// `Outlives::<'static, ::Assoc>`, adding the implied bound +// `::Assoc: 'static`. +// +// The caller does not have to prove that bound. +fn foo(x: ::Assoc) -> (Box, impl Sized) { + (Box::new(x), Outlives::<'static, ::Assoc>(None)) +} + +fn main() { + let string = String::from("temporary"); + let (any, _proof) = foo::<&str>(string.as_str()); + drop(_proof); + drop(string); + println!("{}", any.downcast_ref::<&str>().unwrap()); +} \ No newline at end of file From 4249a9bb8e28c42c5f47431558bd80388d86fde8 Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 9 Dec 2025 04:41:27 +0000 Subject: [PATCH 2/9] Strip implied bound computation from borrowck --- .../src/compute_rename_later.rs | 138 +++++++++++ compiler/rustc_borrowck/src/lib.rs | 6 + .../src/type_check/free_region_relations.rs | 132 +++++------ compiler/rustc_borrowck/src/type_check/mod.rs | 3 +- .../rustc_borrowck/src/universal_regions.rs | 214 +++++++++++++++++- compiler/rustc_infer/src/traits/mod.rs | 4 +- compiler/rustc_middle/src/query/mod.rs | 10 + compiler/rustc_middle/src/traits/mod.rs | 3 +- 8 files changed, 440 insertions(+), 70 deletions(-) create mode 100644 compiler/rustc_borrowck/src/compute_rename_later.rs diff --git a/compiler/rustc_borrowck/src/compute_rename_later.rs b/compiler/rustc_borrowck/src/compute_rename_later.rs new file mode 100644 index 0000000000000..a75c766650e97 --- /dev/null +++ b/compiler/rustc_borrowck/src/compute_rename_later.rs @@ -0,0 +1,138 @@ +// TODO: add description later + +use std::iter; + +use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::{TyCtxtInferExt, canonical}; +use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::query::OutlivesBound; +use rustc_middle::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; + +use crate::hir::def::DefKind; +use crate::ty::solve::NoSolution; +use crate::ty::{CanonicalVarValues, GenericArg, GenericArgs}; +use crate::universal_regions::{ + compute_inputs_and_output_non_nll, defining_ty_non_nll, + for_each_late_bound_region_in_recursive_scope, +}; +use crate::{LocalDefId, ParamEnv, Ty, TypingMode}; + +pub(crate) fn provide(p: &mut Providers) { + *p = Providers { compute_outlives_bounds_rename, ..*p }; +} + +fn compute_outlives_bounds_rename<'tcx>( + tcx: TyCtxt<'tcx>, + mir_def: LocalDefId, +) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, +> { + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new(&infcx); + let param_env = ParamEnv::empty(); + let defining_ty = defining_ty_non_nll(&infcx, mir_def); + let defining_ty_def_id = defining_ty.def_id().expect_local(); + + let unnormalized_input_output_tys = + compute_inputs_and_output_non_nll(&infcx, mir_def, defining_ty); + let unnormalized_input_output_tys = tcx + .liberate_late_bound_regions(defining_ty_def_id.to_def_id(), unnormalized_input_output_tys); + + let span = tcx.def_span(defining_ty_def_id); + let mut outlives_bounds: Vec> = vec![]; + let mut norm_sig_tys: Vec> = vec![]; + + for ty in unnormalized_input_output_tys { + // We add implied bounds from both the unnormalized and normalized ty. + // See issue #87748 + + let bounds = compute_implied_outlives_bounds_inner(&ocx, param_env, ty, span, false)?; + outlives_bounds.extend(bounds); + + let norm_ty = ocx + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) + .map_err(|_| NoSolution)?; + + // Currently `implied_outlives_bounds` will normalize the provided + // `Ty`, despite this it's still important to normalize the ty ourselves + // as normalization may introduce new region variables (#136547). + // + // If we do not add implied bounds for the type involving these new + // region variables then we'll wind up with the normalized form of + // the signature having not-wf types due to unsatisfied region + // constraints. + // + // 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 bounds = + compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false)?; + outlives_bounds.extend(bounds); + } + + norm_sig_tys.push(norm_ty); + } + + // Add implied bounds from impl header. + // + // We don't use `assumed_wf_types` to source the entire set of implied bounds for + // a few reasons: + // - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't + // - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not + // do so for types in impl headers + // - We must compute the normalized signature and then compute implied bounds from that + // in order to connect any unconstrained region vars created during normalization to + // the types of the locals corresponding to the inputs and outputs of the item. (#136547) + if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { + for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { + let Ok(norm_ty) = + ocx.deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) + else { + continue; + }; + + // We currently add implied bounds from the normalized ty only. + // This is more conservative and matches wfcheck behavior. + let bounds = + compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false)?; + outlives_bounds.extend(bounds); + } + } + + // Get early and late bound params. + let typeck_root_def_id = tcx.typeck_root_def_id(mir_def.to_def_id()); + let mut region_params = GenericArgs::identity_for_item(tcx, typeck_root_def_id); + + // Collect late bound region for closure, coroutine, or inline-const. + if mir_def.to_def_id() != typeck_root_def_id { + for_each_late_bound_region_in_recursive_scope(tcx, tcx.local_parent(mir_def), |r| { + // FIXME: is there a better way of doing this? + region_params = tcx.mk_args_from_iter(region_params.iter().chain(iter::once(r.into()))); + }); + } + + // TODO: not super happy with constantly chaining, take a look at this again later. + // TODO: maybe try to return Ty instead of GenericArg + let var_value = tcx.mk_args_from_iter( + region_params.iter().chain(norm_sig_tys.iter().map(|ty| GenericArg::from(*ty))), + ); + + let var_values: CanonicalVarValues> = + CanonicalVarValues { var_values: tcx.mk_args(var_value) }; + + ocx.make_canonicalized_query_response(var_values, outlives_bounds) +} diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4a059481c326b..3fbaea53bc5c0 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -50,6 +50,8 @@ use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; +//use rustc_trait_selection::traits::query::type_op; +//use rustc_trait_selection::infer::canonical::OriginalQueryValues; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -76,6 +78,7 @@ use crate::type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults} mod borrow_set; mod borrowck_errors; +mod compute_rename_later; mod constraints; mod dataflow; mod def_use; @@ -109,6 +112,7 @@ impl<'tcx> TyCtxtConsts<'tcx> { pub fn provide(providers: &mut Providers) { *providers = Providers { mir_borrowck, ..*providers }; + compute_rename_later::provide(providers); } /// Provider for `query mir_borrowck`. Unlike `typeck`, this must @@ -343,6 +347,7 @@ fn borrowck_collect_region_constraints<'tcx>( let mut polonius_facts = (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default()); + // TODO: figure out if I can reuse the same defId from somewhere instead of passing it. // Run the MIR type-checker. let MirTypeckResults { constraints, @@ -353,6 +358,7 @@ fn borrowck_collect_region_constraints<'tcx>( polonius_context, } = type_check::type_check( root_cx, + def, &infcx, body, &promoted, 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 c4d964441b1d2..1d7dc8df92e4d 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,7 +1,8 @@ +use std::iter; + use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; -use rustc_hir::def::DefKind; -use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_infer::infer::outlives; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; @@ -9,14 +10,17 @@ use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::Span; +use rustc_trait_selection::infer::InferOk; +use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use tracing::{debug, instrument}; use type_op::TypeOpOutput; -use crate::BorrowckInferCtxt; +use crate::ty::{GenericArg, GenericArgs}; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; -use crate::universal_regions::UniversalRegions; +use crate::universal_regions::{UniversalRegions, for_each_late_bound_region_in_recursive_scope}; +use crate::{BorrowckInferCtxt, LocalDefId, SmallVec}; #[derive(Debug)] #[derive(Clone)] // FIXME(#146079) @@ -50,11 +54,13 @@ pub(crate) struct CreateResult<'tcx> { pub(crate) fn create<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, + def: LocalDefId, universal_regions: UniversalRegions<'tcx>, constraints: &mut MirTypeckRegionConstraints<'tcx>, ) -> CreateResult<'tcx> { UniversalRegionRelationsBuilder { infcx, + def, constraints, universal_regions, region_bound_pairs: Default::default(), @@ -160,6 +166,7 @@ impl UniversalRegionRelations<'_> { struct UniversalRegionRelationsBuilder<'a, 'tcx> { infcx: &'a BorrowckInferCtxt<'tcx>, + def: LocalDefId, universal_regions: UniversalRegions<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, @@ -234,14 +241,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // handled later by actual borrow checking. let mut normalized_inputs_and_output = Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1); + 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, span); - if let Some(c) = constraints_unnorm { - constraints.push(c) - } + let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = param_env .and(DeeplyNormalize { value: ty }) @@ -255,65 +258,63 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { constraints.push(c) } - // Currently `implied_outlives_bounds` will normalize the provided - // `Ty`, despite this it's still important to normalize the ty ourselves - // as normalization may introduce new region variables (#136547). - // - // If we do not add implied bounds for the type involving these new - // region variables then we'll wind up with the normalized form of - // the signature having not-wf types due to unsatisfied region - // constraints. - // - // 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, span); - if let Some(c) = constraints_norm { - constraints.push(c) - } - } - normalized_inputs_and_output.push(norm_ty); } - // Add implied bounds from impl header. - // - // We don't use `assumed_wf_types` to source the entire set of implied bounds for - // a few reasons: - // - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't - // - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not - // do so for types in impl headers - // - We must compute the normalized signature and then compute implied bounds from that - // in order to connect any unconstrained region vars created during normalization to - // the types of the locals corresponding to the inputs and outputs of the item. (#136547) - if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { - for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let result: Result<_, ErrorGuaranteed> = param_env - .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span); - let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { - continue; - }; + // Get early and late bound params. + let mut var_values = GenericArgs::identity_for_item(tcx, self.infcx.root_def_id); - constraints.extend(c); - - // We currently add implied bounds from the normalized ty only. - // This is more conservative and matches wfcheck behavior. - let c = self.add_implied_bounds(norm_ty, span); - constraints.extend(c); - } + // Collect late bound region for closure, coroutine, or inline-const. + if self.def != self.infcx.root_def_id { + for_each_late_bound_region_in_recursive_scope(tcx, tcx.local_parent(self.def), |r| { + // FIXME: is there a better way of doing this? + var_values = tcx.mk_args_from_iter(var_values.iter().chain(iter::once(r.into()))); + }); } + // Collect the normalized fn sig to var_values too + let var_values = var_values + .iter() + .chain(normalized_inputs_and_output.iter().map(|ty| GenericArg::from(*ty))) + .collect(); + + // Compute implied bound + // TODO: might move this somewhere else later + let Ok(canonical_result) = tcx.compute_outlives_bounds_rename(self.def) else { todo!() }; + + // Instantiate query result + let mut output_query_region_constraints = QueryRegionConstraints::default(); + let mut universe_map = SmallVec::default(); + universe_map.push(ty::UniverseIndex::ROOT); + let original_query_value = OriginalQueryValues { var_values, universe_map }; + // TODO: we don't need to use obligations? + let Ok(InferOk { value: bounds, obligations: _ }) = + self.infcx.instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy_with_span(span), + param_env, + &original_query_value, + canonical_result, + &mut output_query_region_constraints, + ) + else { + todo!() + }; + + // Add the outlives bound and constraints. + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); + self.add_outlives_bounds(bounds); + + if !output_query_region_constraints.is_empty() { + // FIXME(higher_ranked_auto): Should we register assumptions here? + // We otherwise would get spurious errors if normalizing an implied + // outlives bound required proving some higher-ranked coroutine obl. + + // TODO: copy the comment above from somewhere, figure out if that makes sense. + constraints.push(&output_query_region_constraints); + }; + for c in constraints { constraint_conversion::ConstraintConversion::new( self.infcx, @@ -371,6 +372,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { known_type_outlives_obligations.push(outlives); } +<<<<<<< HEAD /// Compute and add any implied bounds that come from a given type. #[instrument(level = "debug", skip(self))] fn add_implied_bounds( @@ -393,6 +395,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { constraints } +======= +>>>>>>> 7379983f23e (Strip implied bound computation from borrowck) /// Registers the `OutlivesBound` items from `outlives_bounds` in /// the outlives relation as well as the region-bound pairs /// listing. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 98b4e4d81b92b..42bcbb2575e82 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -97,6 +97,7 @@ mod relate_tys; /// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'tcx>( root_cx: &mut BorrowCheckRootCtxt<'tcx>, + def: LocalDefId, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, @@ -121,7 +122,7 @@ pub(crate) fn type_check<'tcx>( region_bound_pairs, normalized_inputs_and_output, known_type_outlives_obligations, - } = free_region_relations::create(infcx, universal_regions, &mut constraints); + } = free_region_relations::create(infcx, def, universal_regions, &mut constraints); { // Scope these variables so it's clear they're not used later diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index aeba5ee70cf17..f7b0f0efe1f89 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -32,8 +32,8 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, kw, sym}; use tracing::{debug, instrument}; -use crate::BorrowckInferCtxt; use crate::renumber::RegionCtxt; +use crate::{BorrowckInferCtxt, InferCtxt}; #[derive(Debug)] #[derive(Clone)] // FIXME(#146079) @@ -471,6 +471,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let first_extern_index = self.infcx.num_region_vars(); let defining_ty = self.defining_ty(); + let _ = defining_ty_non_nll(&self.infcx.infcx, self.mir_def); debug!("build: defining_ty={:?}", defining_ty); let mut indices = self.compute_indices(fr_static, defining_ty); @@ -943,7 +944,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> { /// Iterates over the late-bound regions defined on `mir_def_id` and all of its /// parents, up to the typeck root, and invokes `f` with the liberated form /// of each one. -fn for_each_late_bound_region_in_recursive_scope<'tcx>( +pub(crate) fn for_each_late_bound_region_in_recursive_scope<'tcx>( tcx: TyCtxt<'tcx>, mut mir_def_id: LocalDefId, mut f: impl FnMut(ty::Region<'tcx>), @@ -999,3 +1000,212 @@ fn for_each_late_bound_region_in_item<'tcx>( } } } + +// FIXME: Move this somewhere to be used by the new query +pub(crate) fn compute_inputs_and_output_non_nll<'tcx>( + infcx: &InferCtxt<'tcx>, + mir_def: LocalDefId, + defining_ty: DefiningTy<'tcx>, +) -> ty::Binder<'tcx, &'tcx ty::List>> { + let tcx = infcx.tcx; + + let inputs_and_output = match defining_ty { + DefiningTy::Closure(def_id, args) => { + assert_eq!(mir_def.to_def_id(), def_id); + let closure_sig = args.as_closure().sig(); + let inputs_and_output = closure_sig.inputs_and_output(); + let bound_vars = + tcx.mk_bound_variable_kinds_from_iter(inputs_and_output.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), + )); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::ClosureEnv, + }; + let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let closure_ty = tcx.closure_env_ty( + Ty::new_closure(tcx, def_id, args), + args.as_closure().kind(), + env_region, + ); + + // The "inputs" of the closure in the + // signature appear as a tuple. The MIR side + // flattens this tuple. + let (&output, tuplized_inputs) = inputs_and_output.skip_binder().split_last().unwrap(); + assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); + let &ty::Tuple(inputs) = tuplized_inputs[0].kind() else { + bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]); + }; + + ty::Binder::bind_with_vars( + tcx.mk_type_list_from_iter( + iter::once(closure_ty).chain(inputs).chain(iter::once(output)), + ), + bound_vars, + ) + } + + DefiningTy::Coroutine(def_id, args) => { + assert_eq!(mir_def.to_def_id(), def_id); + let resume_ty = args.as_coroutine().resume_ty(); + let output = args.as_coroutine().return_ty(); + let coroutine_ty = Ty::new_coroutine(tcx, def_id, args); + let inputs_and_output = infcx.tcx.mk_type_list(&[coroutine_ty, resume_ty, output]); + ty::Binder::dummy(inputs_and_output) + } + + // Construct the signature of the CoroutineClosure for the purposes of borrowck. + // This is pretty straightforward -- we: + // 1. first grab the `coroutine_closure_sig`, + // 2. compute the self type (`&`/`&mut`/no borrow), + // 3. flatten the tupled_input_tys, + // 4. construct the correct generator type to return with + // `CoroutineClosureSignature::to_coroutine_given_kind_and_upvars`. + // Then we wrap it all up into a list of inputs and output. + DefiningTy::CoroutineClosure(def_id, args) => { + assert_eq!(mir_def.to_def_id(), def_id); + let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); + let bound_vars = + tcx.mk_bound_variable_kinds_from_iter(closure_sig.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), + )); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::ClosureEnv, + }; + let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let closure_kind = args.as_coroutine_closure().kind(); + + let closure_ty = tcx.closure_env_ty( + Ty::new_coroutine_closure(tcx, def_id, args), + closure_kind, + env_region, + ); + + let inputs = closure_sig.skip_binder().tupled_inputs_ty.tuple_fields(); + let output = closure_sig.skip_binder().to_coroutine_given_kind_and_upvars( + tcx, + args.as_coroutine_closure().parent_args(), + tcx.coroutine_for_closure(def_id), + closure_kind, + env_region, + args.as_coroutine_closure().tupled_upvars_ty(), + args.as_coroutine_closure().coroutine_captures_by_ref_ty(), + ); + + ty::Binder::bind_with_vars( + tcx.mk_type_list_from_iter( + iter::once(closure_ty).chain(inputs).chain(iter::once(output)), + ), + bound_vars, + ) + } + + DefiningTy::FnDef(def_id, _) => { + let sig = tcx.fn_sig(def_id).instantiate_identity(); + let inputs_and_output = sig.inputs_and_output(); + + // C-variadic fns also have a `VaList` input that's not listed in the signature + // (as it's created inside the body itself, not passed in from outside). + if infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() { + let va_list_did = + infcx.tcx.require_lang_item(LangItem::VaList, infcx.tcx.def_span(mir_def)); + + let reg_vid = + infcx.next_nll_region_var(NllRegionVariableOrigin::FreeRegion).as_var(); + + let region = ty::Region::new_var(infcx.tcx, reg_vid); + let va_list_ty = + infcx.tcx.type_of(va_list_did).instantiate(infcx.tcx, &[region.into()]); + + // The signature needs to follow the order [input_tys, va_list_ty, output_ty] + return inputs_and_output.map_bound(|tys| { + let (output_ty, input_tys) = tys.split_last().unwrap(); + tcx.mk_type_list_from_iter( + input_tys.iter().copied().chain([va_list_ty, *output_ty]), + ) + }); + } + + inputs_and_output + } + + DefiningTy::Const(def_id, _) => { + // For a constant body, there are no inputs, and one + // "output" (the type of the constant). + assert_eq!(mir_def.to_def_id(), def_id); + let ty = tcx.type_of(mir_def).instantiate_identity(); + + ty::Binder::dummy(tcx.mk_type_list(&[ty])) + } + + DefiningTy::InlineConst(def_id, args) => { + assert_eq!(mir_def.to_def_id(), def_id); + let ty = args.as_inline_const().ty(); + ty::Binder::dummy(tcx.mk_type_list(&[ty])) + } + + DefiningTy::GlobalAsm(def_id) => { + ty::Binder::dummy(tcx.mk_type_list(&[tcx.type_of(def_id).instantiate_identity()])) + } + }; + + // FIXME(#129952): We probably want a more principled approach here. + if let Err(e) = inputs_and_output.error_reported() { + infcx.set_tainted_by_errors(e); + } + + inputs_and_output +} + +// FIXME: Move this somewhere to be used by the new query +pub(crate) fn defining_ty_non_nll<'tcx>( + infcx: &InferCtxt<'tcx>, + mir_def: LocalDefId, +) -> DefiningTy<'tcx> { + let tcx = infcx.tcx; + let typeck_root_def_id = tcx.typeck_root_def_id(mir_def.to_def_id()); + + match tcx.hir_body_owner_kind(mir_def) { + BodyOwnerKind::Closure | BodyOwnerKind::Fn => { + let defining_ty = tcx.type_of(mir_def).instantiate_identity(); + + match defining_ty.kind() { + ty::Closure(def_id, args) => DefiningTy::Closure(*def_id, args), + ty::Coroutine(def_id, args) => DefiningTy::Coroutine(*def_id, args), + ty::CoroutineClosure(def_id, args) => DefiningTy::CoroutineClosure(*def_id, args), + ty::FnDef(def_id, args) => DefiningTy::FnDef(*def_id, args), + _ => span_bug!( + tcx.def_span(mir_def), + "expected defining type for `{:?}`: `{:?}`", + mir_def, + defining_ty + ), + } + } + + BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { + let args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); + if mir_def.to_def_id() == typeck_root_def_id { + DefiningTy::Const(mir_def.to_def_id(), args) + } else { + // FIXME: this line creates a query dependency between borrowck and typeck. + // + // This is required for `AscribeUserType` canonical query, which will call + // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes + // into borrowck, which is ICE #78174. + // + // As a workaround, inline consts have an additional generic param (`ty` + // below), so that `type_of(inline_const_def_id).args(args)` uses the + // proper type with NLL infer vars. + let ty = tcx.typeck(mir_def).node_type(tcx.local_def_id_to_hir_id(mir_def)); + let args = + InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args: args, ty }).args; + DefiningTy::InlineConst(mir_def.to_def_id(), args) + } + } + + BodyOwnerKind::GlobalAsm => DefiningTy::GlobalAsm(mir_def.to_def_id()), + } +} diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 0536a6c909507..76e597a985ee2 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -10,8 +10,8 @@ pub mod util; use std::cmp; use std::hash::{Hash, Hasher}; -use hir::def_id::LocalDefId; -use rustc_hir as hir; +//use hir::def_id::LocalDefId; look at this later +//use rustc_hir as hir; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cea50f95df4b4..3c467e90ffbd2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2498,6 +2498,16 @@ rustc_queries! { desc { "computing implied outlives bounds for `{}` (hack disabled = {:?})", key.0.canonical.value.value.ty, key.1 } } + + query compute_outlives_bounds_rename( + mir_def: LocalDefId + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, + > { + desc { "rename later" } + } + /// 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/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 3a1682614cbf1..8a83a6f4eb807 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -19,7 +19,8 @@ use rustc_hir::def_id::DefId; use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, }; -use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_span::def_id::CRATE_DEF_ID; +pub use rustc_span::def_id::LocalDefId; use rustc_span::{DUMMY_SP, Span, Symbol}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; From 24ccc9e805ebd69a981829d088a8c99350789978 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 2 Feb 2026 02:48:00 +0000 Subject: [PATCH 3/9] Fix some error handling, and the new test passed --- .../src/compute_rename_later.rs | 45 ++++++++++++++----- .../src/type_check/free_region_relations.rs | 30 +++---------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_borrowck/src/compute_rename_later.rs b/compiler/rustc_borrowck/src/compute_rename_later.rs index a75c766650e97..39d6152f2d2b5 100644 --- a/compiler/rustc_borrowck/src/compute_rename_later.rs +++ b/compiler/rustc_borrowck/src/compute_rename_later.rs @@ -10,6 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; +use crate::debug; use crate::hir::def::DefKind; use crate::ty::solve::NoSolution; @@ -37,11 +38,15 @@ fn compute_outlives_bounds_rename<'tcx>( let defining_ty = defining_ty_non_nll(&infcx, mir_def); let defining_ty_def_id = defining_ty.def_id().expect_local(); + debug!("mir def here is {:?}", mir_def); + let unnormalized_input_output_tys = compute_inputs_and_output_non_nll(&infcx, mir_def, defining_ty); let unnormalized_input_output_tys = tcx .liberate_late_bound_regions(defining_ty_def_id.to_def_id(), unnormalized_input_output_tys); + debug!("unnormalized input output tys is {:?}", unnormalized_input_output_tys); + let span = tcx.def_span(defining_ty_def_id); let mut outlives_bounds: Vec> = vec![]; let mut norm_sig_tys: Vec> = vec![]; @@ -50,12 +55,22 @@ fn compute_outlives_bounds_rename<'tcx>( // We add implied bounds from both the unnormalized and normalized ty. // See issue #87748 - let bounds = compute_implied_outlives_bounds_inner(&ocx, param_env, ty, span, false)?; - outlives_bounds.extend(bounds); + debug!("in the ty loop, current ty is {:?}", ty); + + // TODO: Should the test fail outlive bound computation? visit this later + if let Ok(bounds) = compute_implied_outlives_bounds_inner(&ocx, param_env, ty, span, false) { + debug!("there is solution for the first implied bound computation"); + outlives_bounds.extend(bounds); + } - let norm_ty = ocx - .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) - .map_err(|_| NoSolution)?; + let Ok(norm_ty) = ocx + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) else { + // TODO: this is a hack here, for some reason, the input ty deeply normalized failed. + norm_sig_tys.push(ty); + continue; + }; + + debug!("there is solution for the first norm ty"); // Currently `implied_outlives_bounds` will normalize the provided // `Ty`, despite this it's still important to normalize the ty ourselves @@ -79,9 +94,11 @@ fn compute_outlives_bounds_rename<'tcx>( // ``` // Both &Self::Bar and &() are WF if ty != norm_ty { - let bounds = - compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false)?; - outlives_bounds.extend(bounds); + if let Ok(bounds) = + compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false) { + debug!("there is solution for second outlive bound computation"); + outlives_bounds.extend(bounds); + } } norm_sig_tys.push(norm_ty); @@ -107,9 +124,10 @@ fn compute_outlives_bounds_rename<'tcx>( // We currently add implied bounds from the normalized ty only. // This is more conservative and matches wfcheck behavior. - let bounds = - compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false)?; - outlives_bounds.extend(bounds); + if let Ok(bounds) = + compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false) { + outlives_bounds.extend(bounds); + } } } @@ -125,12 +143,17 @@ fn compute_outlives_bounds_rename<'tcx>( }); } + debug!("region param in query is {:?}", region_params); + debug!("sig tys in query is {:?}", norm_sig_tys); + // TODO: not super happy with constantly chaining, take a look at this again later. // TODO: maybe try to return Ty instead of GenericArg let var_value = tcx.mk_args_from_iter( region_params.iter().chain(norm_sig_tys.iter().map(|ty| GenericArg::from(*ty))), ); + debug!("var value in query is {:?}", var_value); + let var_values: CanonicalVarValues> = CanonicalVarValues { var_values: tcx.mk_args(var_value) }; 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 1d7dc8df92e4d..30fb5a623ff18 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -272,12 +272,17 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { }); } + debug!("region param in borrowck is {:?}", var_values); + // Collect the normalized fn sig to var_values too let var_values = var_values .iter() .chain(normalized_inputs_and_output.iter().map(|ty| GenericArg::from(*ty))) .collect(); + debug!("sig tys in borrowck is {:?}", normalized_inputs_and_output); + debug!("var value in borrowck is {:?}", var_values); + // Compute implied bound // TODO: might move this somewhere else later let Ok(canonical_result) = tcx.compute_outlives_bounds_rename(self.def) else { todo!() }; @@ -372,31 +377,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { known_type_outlives_obligations.push(outlives); } -<<<<<<< HEAD - /// Compute and add any implied bounds that come from a given type. - #[instrument(level = "debug", skip(self))] - fn add_implied_bounds( - &mut self, - ty: Ty<'tcx>, - span: Span, - ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { - let TypeOpOutput { output: bounds, constraints, .. } = self - .infcx - .param_env - .and(type_op::ImpliedOutlivesBounds { ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span) - .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) - .ok()?; - debug!(?bounds, ?constraints); - // Because of #109628, we may have unexpected placeholders. Ignore them! - // FIXME(#109628): panic in this case once the issue is fixed. - let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); - self.add_outlives_bounds(bounds); - constraints - } - -======= ->>>>>>> 7379983f23e (Strip implied bound computation from borrowck) /// Registers the `OutlivesBound` items from `outlives_bounds` in /// the outlives relation as well as the region-bound pairs /// listing. From f290a2e4b1e4c622b25d2c737f27514b34311f6e Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 3 Feb 2026 13:19:05 +0000 Subject: [PATCH 4/9] Replace erased regions with existential variables --- .../rustc_borrowck/src/compute_rename_later.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler/rustc_borrowck/src/compute_rename_later.rs b/compiler/rustc_borrowck/src/compute_rename_later.rs index 39d6152f2d2b5..017363f925c0b 100644 --- a/compiler/rustc_borrowck/src/compute_rename_later.rs +++ b/compiler/rustc_borrowck/src/compute_rename_later.rs @@ -11,6 +11,10 @@ use rustc_middle::ty::TyCtxt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; use crate::debug; +use crate::fold_regions; +use crate::ty; +use crate::RegionVariableOrigin; +use crate::NllRegionVariableOrigin; use crate::hir::def::DefKind; use crate::ty::solve::NoSolution; @@ -52,6 +56,12 @@ fn compute_outlives_bounds_rename<'tcx>( let mut norm_sig_tys: Vec> = vec![]; for ty in unnormalized_input_output_tys { + // Replace erased region with existential variables. + + let ty = fold_regions(tcx, ty, |re, _dbi| match re.kind() { + ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Nll(NllRegionVariableOrigin::Existential{name: None})), + _ => re, + }); // We add implied bounds from both the unnormalized and normalized ty. // See issue #87748 @@ -116,6 +126,12 @@ fn compute_outlives_bounds_rename<'tcx>( // the types of the locals corresponding to the inputs and outputs of the item. (#136547) if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { + + let ty = fold_regions(tcx, ty, |re, _dbi| match re.kind() { + ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Nll(NllRegionVariableOrigin::Existential{name: None})), + _ => re, + }); + let Ok(norm_ty) = ocx.deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) else { From c0822f374da54f2c9847749a42613822d0abde8f Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 3 Feb 2026 14:12:58 +0000 Subject: [PATCH 5/9] More error handling --- .../src/type_check/free_region_relations.rs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) 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 30fb5a623ff18..b2d0e122cd35c 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -272,39 +272,37 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { }); } - debug!("region param in borrowck is {:?}", var_values); - - // Collect the normalized fn sig to var_values too + // Add the normalized fn_sig to var_values too let var_values = var_values .iter() .chain(normalized_inputs_and_output.iter().map(|ty| GenericArg::from(*ty))) .collect(); - debug!("sig tys in borrowck is {:?}", normalized_inputs_and_output); - debug!("var value in borrowck is {:?}", var_values); - - // Compute implied bound - // TODO: might move this somewhere else later - let Ok(canonical_result) = tcx.compute_outlives_bounds_rename(self.def) else { todo!() }; + let Ok(canonical_result) = tcx.compute_outlives_bounds_rename(self.def) else { + // see what will hit this case and think about this later + todo!(); + }; // Instantiate query result let mut output_query_region_constraints = QueryRegionConstraints::default(); let mut universe_map = SmallVec::default(); universe_map.push(ty::UniverseIndex::ROOT); let original_query_value = OriginalQueryValues { var_values, universe_map }; - // TODO: we don't need to use obligations? - let Ok(InferOk { value: bounds, obligations: _ }) = - self.infcx.instantiate_nll_query_response_and_region_obligations( + + let instantiate_res = self.infcx.instantiate_nll_query_response_and_region_obligations( &ObligationCause::dummy_with_span(span), param_env, &original_query_value, canonical_result, &mut output_query_region_constraints, - ) - else { - todo!() + ); + + let bounds = match instantiate_res { + Ok(InferOk { value: bounds, obligations: _ }) => bounds, + Err(_) => vec![], }; + // Add the outlives bound and constraints. // Because of #109628, we may have unexpected placeholders. Ignore them! // FIXME(#109628): panic in this case once the issue is fixed. From 44f6dec3fc9569e2f9b6ba3e9a81d435babd4f42 Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 3 Feb 2026 14:16:05 +0000 Subject: [PATCH 6/9] stderr for the newly added test --- tests/ui/borrowck/test-rename-later.stderr | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/ui/borrowck/test-rename-later.stderr diff --git a/tests/ui/borrowck/test-rename-later.stderr b/tests/ui/borrowck/test-rename-later.stderr new file mode 100644 index 0000000000000..977d7e93dcadd --- /dev/null +++ b/tests/ui/borrowck/test-rename-later.stderr @@ -0,0 +1,75 @@ +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/test-rename-later.rs:19:1 + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/test-rename-later.rs:20:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/test-rename-later.rs:20:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/test-rename-later.rs:20:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/test-rename-later.rs:20:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0310`. From fbe4e141dd1a1bb6e448c26f50ad2245031aaa5b Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 3 Feb 2026 14:50:40 +0000 Subject: [PATCH 7/9] More clean up --- .../src/compute_rename_later.rs | 65 +++++++------------ compiler/rustc_borrowck/src/lib.rs | 2 - .../src/type_check/free_region_relations.rs | 18 ++--- .../rustc_borrowck/src/universal_regions.rs | 1 - compiler/rustc_infer/src/traits/mod.rs | 2 - 5 files changed, 29 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_borrowck/src/compute_rename_later.rs b/compiler/rustc_borrowck/src/compute_rename_later.rs index 017363f925c0b..54eb73b5cf88e 100644 --- a/compiler/rustc_borrowck/src/compute_rename_later.rs +++ b/compiler/rustc_borrowck/src/compute_rename_later.rs @@ -10,11 +10,6 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; -use crate::debug; -use crate::fold_regions; -use crate::ty; -use crate::RegionVariableOrigin; -use crate::NllRegionVariableOrigin; use crate::hir::def::DefKind; use crate::ty::solve::NoSolution; @@ -23,7 +18,7 @@ use crate::universal_regions::{ compute_inputs_and_output_non_nll, defining_ty_non_nll, for_each_late_bound_region_in_recursive_scope, }; -use crate::{LocalDefId, ParamEnv, Ty, TypingMode}; +use crate::{LocalDefId, ParamEnv, RegionVariableOrigin, Ty, TypingMode, fold_regions, ty}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { compute_outlives_bounds_rename, ..*p }; @@ -42,45 +37,36 @@ fn compute_outlives_bounds_rename<'tcx>( let defining_ty = defining_ty_non_nll(&infcx, mir_def); let defining_ty_def_id = defining_ty.def_id().expect_local(); - debug!("mir def here is {:?}", mir_def); - let unnormalized_input_output_tys = compute_inputs_and_output_non_nll(&infcx, mir_def, defining_ty); let unnormalized_input_output_tys = tcx .liberate_late_bound_regions(defining_ty_def_id.to_def_id(), unnormalized_input_output_tys); - debug!("unnormalized input output tys is {:?}", unnormalized_input_output_tys); - let span = tcx.def_span(defining_ty_def_id); let mut outlives_bounds: Vec> = vec![]; let mut norm_sig_tys: Vec> = vec![]; for ty in unnormalized_input_output_tys { - // Replace erased region with existential variables. - + // Replace erased regions with existential variables. let ty = fold_regions(tcx, ty, |re, _dbi| match re.kind() { - ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Nll(NllRegionVariableOrigin::Existential{name: None})), + ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Misc(span)), _ => re, }); + // We add implied bounds from both the unnormalized and normalized ty. // See issue #87748 - - debug!("in the ty loop, current ty is {:?}", ty); - - // TODO: Should the test fail outlive bound computation? visit this later - if let Ok(bounds) = compute_implied_outlives_bounds_inner(&ocx, param_env, ty, span, false) { - debug!("there is solution for the first implied bound computation"); + if let Ok(bounds) = compute_implied_outlives_bounds_inner(&ocx, param_env, ty, span, false) + { outlives_bounds.extend(bounds); } - let Ok(norm_ty) = ocx - .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) else { - // TODO: this is a hack here, for some reason, the input ty deeply normalized failed. - norm_sig_tys.push(ty); - continue; + let Ok(norm_ty) = + ocx.deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) + else { + // Even if deeply normalize returns no solution, we still need to store the ty for canonicalization later. + norm_sig_tys.push(ty); + continue; }; - - debug!("there is solution for the first norm ty"); // Currently `implied_outlives_bounds` will normalize the provided // `Ty`, despite this it's still important to normalize the ty ourselves @@ -105,10 +91,10 @@ fn compute_outlives_bounds_rename<'tcx>( // Both &Self::Bar and &() are WF if ty != norm_ty { if let Ok(bounds) = - compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false) { - debug!("there is solution for second outlive bound computation"); - outlives_bounds.extend(bounds); - } + compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false) + { + outlives_bounds.extend(bounds); + } } norm_sig_tys.push(norm_ty); @@ -126,9 +112,9 @@ fn compute_outlives_bounds_rename<'tcx>( // the types of the locals corresponding to the inputs and outputs of the item. (#136547) if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - + // Replace erased regions with fresh region variables. let ty = fold_regions(tcx, ty, |re, _dbi| match re.kind() { - ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Nll(NllRegionVariableOrigin::Existential{name: None})), + ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Misc(span)), _ => re, }); @@ -141,9 +127,10 @@ fn compute_outlives_bounds_rename<'tcx>( // We currently add implied bounds from the normalized ty only. // This is more conservative and matches wfcheck behavior. if let Ok(bounds) = - compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false) { - outlives_bounds.extend(bounds); - } + compute_implied_outlives_bounds_inner(&ocx, param_env, norm_ty, span, false) + { + outlives_bounds.extend(bounds); + } } } @@ -159,17 +146,11 @@ fn compute_outlives_bounds_rename<'tcx>( }); } - debug!("region param in query is {:?}", region_params); - debug!("sig tys in query is {:?}", norm_sig_tys); - - // TODO: not super happy with constantly chaining, take a look at this again later. - // TODO: maybe try to return Ty instead of GenericArg + // TODO: not happy with constantly chaining here, take a look at this again later. let var_value = tcx.mk_args_from_iter( region_params.iter().chain(norm_sig_tys.iter().map(|ty| GenericArg::from(*ty))), ); - debug!("var value in query is {:?}", var_value); - let var_values: CanonicalVarValues> = CanonicalVarValues { var_values: tcx.mk_args(var_value) }; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 3fbaea53bc5c0..d552ee72f83d9 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -50,8 +50,6 @@ use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; -//use rustc_trait_selection::traits::query::type_op; -//use rustc_trait_selection::infer::canonical::OriginalQueryValues; use smallvec::SmallVec; use tracing::{debug, instrument}; 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 b2d0e122cd35c..8a82ede739f30 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -290,19 +290,18 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { let original_query_value = OriginalQueryValues { var_values, universe_map }; let instantiate_res = self.infcx.instantiate_nll_query_response_and_region_obligations( - &ObligationCause::dummy_with_span(span), - param_env, - &original_query_value, - canonical_result, - &mut output_query_region_constraints, - ); + &ObligationCause::dummy_with_span(span), + param_env, + &original_query_value, + canonical_result, + &mut output_query_region_constraints, + ); let bounds = match instantiate_res { Ok(InferOk { value: bounds, obligations: _ }) => bounds, Err(_) => vec![], }; - // Add the outlives bound and constraints. // Because of #109628, we may have unexpected placeholders. Ignore them! // FIXME(#109628): panic in this case once the issue is fixed. @@ -310,11 +309,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.add_outlives_bounds(bounds); if !output_query_region_constraints.is_empty() { - // FIXME(higher_ranked_auto): Should we register assumptions here? - // We otherwise would get spurious errors if normalizing an implied - // outlives bound required proving some higher-ranked coroutine obl. - - // TODO: copy the comment above from somewhere, figure out if that makes sense. constraints.push(&output_query_region_constraints); }; diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index f7b0f0efe1f89..d26117bc4d2bf 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -471,7 +471,6 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let first_extern_index = self.infcx.num_region_vars(); let defining_ty = self.defining_ty(); - let _ = defining_ty_non_nll(&self.infcx.infcx, self.mir_def); debug!("build: defining_ty={:?}", defining_ty); let mut indices = self.compute_indices(fr_static, defining_ty); diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 76e597a985ee2..1c8443f0baada 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -10,8 +10,6 @@ pub mod util; use std::cmp; use std::hash::{Hash, Hasher}; -//use hir::def_id::LocalDefId; look at this later -//use rustc_hir as hir; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; From 6895fd4429ac6ca4b3c3e41fbdc30106de96240e Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 4 Feb 2026 15:30:38 +0000 Subject: [PATCH 8/9] debug: add bunch of assert --- .../src/compute_rename_later.rs | 4 +-- .../src/type_check/free_region_relations.rs | 33 +++++++++++++++++++ .../query/type_op/implied_outlives_bounds.rs | 3 +- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/compute_rename_later.rs b/compiler/rustc_borrowck/src/compute_rename_later.rs index 54eb73b5cf88e..691556651b901 100644 --- a/compiler/rustc_borrowck/src/compute_rename_later.rs +++ b/compiler/rustc_borrowck/src/compute_rename_later.rs @@ -47,7 +47,7 @@ fn compute_outlives_bounds_rename<'tcx>( let mut norm_sig_tys: Vec> = vec![]; for ty in unnormalized_input_output_tys { - // Replace erased regions with existential variables. + // Replace erased regions with fresh region variables. let ty = fold_regions(tcx, ty, |re, _dbi| match re.kind() { ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Misc(span)), _ => re, @@ -133,12 +133,12 @@ fn compute_outlives_bounds_rename<'tcx>( } } } - // Get early and late bound params. let typeck_root_def_id = tcx.typeck_root_def_id(mir_def.to_def_id()); let mut region_params = GenericArgs::identity_for_item(tcx, typeck_root_def_id); // Collect late bound region for closure, coroutine, or inline-const. + // TODO: remove this? if mir_def.to_def_id() != typeck_root_def_id { for_each_late_bound_region_in_recursive_scope(tcx, tcx.local_parent(mir_def), |r| { // FIXME: is there a better way of doing this? 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 8a82ede739f30..a7440e465fd84 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -306,6 +306,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // Because of #109628, we may have unexpected placeholders. Ignore them! // FIXME(#109628): panic in this case once the issue is fixed. let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); + + // FIXME: let it fail early to see which test fails under this. + self.see_if_bounds_contain_non_universals(bounds.clone()); + self.add_outlives_bounds(bounds); if !output_query_region_constraints.is_empty() { @@ -399,4 +403,33 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } } } + + // FIXME: remove this later, for debug purpose + fn see_if_bounds_contain_non_universals(&mut self, outlives_bounds: I) + where + I: IntoIterator>, + { + for outlives_bound in outlives_bounds { + match outlives_bound { + OutlivesBound::RegionSubRegion(r1, r2) => { + let r1 = self.universal_regions.to_region_vid(r1); + let r2 = self.universal_regions.to_region_vid(r2); + + assert!(self.universal_regions.is_universal_region(r1)); + assert!(self.universal_regions.is_universal_region(r2)); + } + + OutlivesBound::RegionSubParam(r_a, _param_b) => { + let r1 = self.universal_regions.to_region_vid(r_a); + assert!(self.universal_regions.is_universal_region(r1)); + } + + OutlivesBound::RegionSubAlias(r_a, _alias_b) => { + // FIXME remove later? should always return universal vid? + let r1 = self.universal_regions.to_region_vid(r_a); + assert!(self.universal_regions.is_universal_region(r1)); + } + } + } + } } 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 47ff6190fa4f7..43d1abe384272 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 @@ -58,7 +58,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // Inside mir borrowck, each computation starts with an empty list. assert!( ocx.infcx.inner.borrow().region_obligations().is_empty(), - "compute_implied_outlives_bounds assumes region obligations are empty before starting" + "compute_implied_outlives_bounds assumes region obligations are empty before starting {:?}", + ocx.infcx.inner.borrow().region_obligations() ); // FIXME: This doesn't seem right. All call sites already normalize `ty`: From ae4c5af6f425b09cd561ac1be8a894760cd17dc2 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 4 Feb 2026 16:02:02 +0000 Subject: [PATCH 9/9] remove the params getting from closure and other stuff --- .../src/compute_rename_later.rs | 21 +++---------------- .../src/type_check/free_region_relations.rs | 14 ++----------- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_borrowck/src/compute_rename_later.rs b/compiler/rustc_borrowck/src/compute_rename_later.rs index 691556651b901..8c9b7bc566d98 100644 --- a/compiler/rustc_borrowck/src/compute_rename_later.rs +++ b/compiler/rustc_borrowck/src/compute_rename_later.rs @@ -1,7 +1,5 @@ // TODO: add description later -use std::iter; - use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::{TyCtxtInferExt, canonical}; use rustc_infer::traits::ObligationCause; @@ -14,10 +12,7 @@ use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::comp use crate::hir::def::DefKind; use crate::ty::solve::NoSolution; use crate::ty::{CanonicalVarValues, GenericArg, GenericArgs}; -use crate::universal_regions::{ - compute_inputs_and_output_non_nll, defining_ty_non_nll, - for_each_late_bound_region_in_recursive_scope, -}; +use crate::universal_regions::{compute_inputs_and_output_non_nll, defining_ty_non_nll}; use crate::{LocalDefId, ParamEnv, RegionVariableOrigin, Ty, TypingMode, fold_regions, ty}; pub(crate) fn provide(p: &mut Providers) { @@ -135,20 +130,10 @@ fn compute_outlives_bounds_rename<'tcx>( } // Get early and late bound params. let typeck_root_def_id = tcx.typeck_root_def_id(mir_def.to_def_id()); - let mut region_params = GenericArgs::identity_for_item(tcx, typeck_root_def_id); - - // Collect late bound region for closure, coroutine, or inline-const. - // TODO: remove this? - if mir_def.to_def_id() != typeck_root_def_id { - for_each_late_bound_region_in_recursive_scope(tcx, tcx.local_parent(mir_def), |r| { - // FIXME: is there a better way of doing this? - region_params = tcx.mk_args_from_iter(region_params.iter().chain(iter::once(r.into()))); - }); - } + let params = GenericArgs::identity_for_item(tcx, typeck_root_def_id); - // TODO: not happy with constantly chaining here, take a look at this again later. let var_value = tcx.mk_args_from_iter( - region_params.iter().chain(norm_sig_tys.iter().map(|ty| GenericArg::from(*ty))), + params.iter().chain(norm_sig_tys.iter().map(|ty| GenericArg::from(*ty))), ); let var_values: CanonicalVarValues> = 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 a7440e465fd84..a9404a6ddfe27 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,5 +1,3 @@ -use std::iter; - use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; @@ -19,7 +17,7 @@ use type_op::TypeOpOutput; use crate::ty::{GenericArg, GenericArgs}; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; -use crate::universal_regions::{UniversalRegions, for_each_late_bound_region_in_recursive_scope}; +use crate::universal_regions::UniversalRegions; use crate::{BorrowckInferCtxt, LocalDefId, SmallVec}; #[derive(Debug)] @@ -262,15 +260,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } // Get early and late bound params. - let mut var_values = GenericArgs::identity_for_item(tcx, self.infcx.root_def_id); - - // Collect late bound region for closure, coroutine, or inline-const. - if self.def != self.infcx.root_def_id { - for_each_late_bound_region_in_recursive_scope(tcx, tcx.local_parent(self.def), |r| { - // FIXME: is there a better way of doing this? - var_values = tcx.mk_args_from_iter(var_values.iter().chain(iter::once(r.into()))); - }); - } + let var_values = GenericArgs::identity_for_item(tcx, self.infcx.root_def_id); // Add the normalized fn_sig to var_values too let var_values = var_values