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..8c9b7bc566d98 --- /dev/null +++ b/compiler/rustc_borrowck/src/compute_rename_later.rs @@ -0,0 +1,143 @@ +// TODO: add description later + +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}; +use crate::{LocalDefId, ParamEnv, RegionVariableOrigin, Ty, TypingMode, fold_regions, ty}; + +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 { + // 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, + }); + + // We add implied bounds from both the unnormalized and normalized ty. + // See issue #87748 + 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 { + // Even if deeply normalize returns no solution, we still need to store the ty for canonicalization later. + norm_sig_tys.push(ty); + continue; + }; + + // 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 { + if let Ok(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)) { + // 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, + }); + + 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. + if let Ok(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 params = GenericArgs::identity_for_item(tcx, typeck_root_def_id); + + let var_value = tcx.mk_args_from_iter( + 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..d552ee72f83d9 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -76,6 +76,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 +110,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 +345,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 +356,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..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,7 +1,6 @@ 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 +8,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::{BorrowckInferCtxt, LocalDefId, SmallVec}; #[derive(Debug)] #[derive(Clone)] // FIXME(#146079) @@ -50,11 +52,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 +164,7 @@ impl UniversalRegionRelations<'_> { struct UniversalRegionRelationsBuilder<'a, 'tcx> { infcx: &'a BorrowckInferCtxt<'tcx>, + def: LocalDefId, universal_regions: UniversalRegions<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, @@ -234,14 +239,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,64 +256,55 @@ 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 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 + .iter() + .chain(normalized_inputs_and_output.iter().map(|ty| GenericArg::from(*ty))) + .collect(); + + 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 }; + + 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, + ); + + 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. + let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); - constraints.extend(c); + // FIXME: let it fail early to see which test fails under this. + self.see_if_bounds_contain_non_universals(bounds.clone()); - // 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); - } - } + self.add_outlives_bounds(bounds); + + if !output_query_region_constraints.is_empty() { + constraints.push(&output_query_region_constraints); + }; for c in constraints { constraint_conversion::ConstraintConversion::new( @@ -371,28 +363,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { known_type_outlives_obligations.push(outlives); } - /// 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 - } - /// Registers the `OutlivesBound` items from `outlives_bounds` in /// the outlives relation as well as the region-bound pairs /// listing. @@ -423,4 +393,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_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..d26117bc4d2bf 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) @@ -943,7 +943,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 +999,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..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; -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; 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`: 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 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`.