From 17ac2fc96d7dc93b5a02b8f2fe5f03edade6f739 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 22 Aug 2025 13:39:38 +0200 Subject: [PATCH] change HIR typeck unification handling approach --- compiler/rustc_borrowck/src/lib.rs | 14 +++++--- compiler/rustc_borrowck/src/root_cx.rs | 4 +++ .../src/type_check/canonical.rs | 3 +- .../src/type_check/constraint_conversion.rs | 20 ++++++----- .../src/type_check/free_region_relations.rs | 32 ++++++++--------- .../src/type_check/liveness/trace.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 21 +++++------ .../rustc_hir_analysis/src/check/check.rs | 26 ++++++++++++++ compiler/rustc_hir_analysis/src/check/mod.rs | 1 + compiler/rustc_hir_typeck/src/lib.rs | 17 +-------- compiler/rustc_hir_typeck/src/writeback.rs | 27 ++++++++++++++ compiler/rustc_infer/src/infer/context.rs | 4 --- compiler/rustc_infer/src/infer/mod.rs | 3 +- compiler/rustc_middle/src/query/mod.rs | 16 +++++++++ .../rustc_middle/src/ty/typeck_results.rs | 11 ++++++ .../src/canonicalizer.rs | 35 +++---------------- .../src/solve/eval_ctxt/canonical.rs | 2 -- .../src/solve/eval_ctxt/mod.rs | 5 +-- .../src/solve/fulfill.rs | 4 ++- .../src/traits/query/type_op/custom.rs | 9 +++-- .../src/traits/query/type_op/mod.rs | 16 ++++----- compiler/rustc_type_ir/src/infer_ctxt.rs | 4 --- ...iguity-due-to-uniquification-2.next.stderr | 8 ++--- .../ambiguity-due-to-uniquification-2.rs | 2 +- ...iguity-due-to-uniquification-3.next.stderr | 10 +++--- 25 files changed, 168 insertions(+), 128 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a081e0dee5d43..ce78ae203a4d8 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -300,7 +300,7 @@ fn do_mir_borrowck<'tcx>( def: LocalDefId, ) -> PropagatedBorrowCheckResults<'tcx> { let tcx = root_cx.tcx; - let infcx = BorrowckInferCtxt::new(tcx, def); + let infcx = BorrowckInferCtxt::new(tcx, def, root_cx.root_def_id()); let (input_body, promoted) = tcx.mir_promoted(def); let input_body: &Body<'_> = &input_body.borrow(); let input_promoted: &IndexSlice<_, _> = &promoted.borrow(); @@ -590,12 +590,13 @@ fn get_flow_results<'a, 'tcx>( pub(crate) struct BorrowckInferCtxt<'tcx> { pub(crate) infcx: InferCtxt<'tcx>, - pub(crate) reg_var_to_origin: RefCell>, + pub(crate) root_def_id: LocalDefId, pub(crate) param_env: ParamEnv<'tcx>, + pub(crate) reg_var_to_origin: RefCell>, } impl<'tcx> BorrowckInferCtxt<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, root_def_id: LocalDefId) -> Self { let typing_mode = if tcx.use_typing_mode_borrowck() { TypingMode::borrowck(tcx, def_id) } else { @@ -603,7 +604,12 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { }; let infcx = tcx.infer_ctxt().build(typing_mode); let param_env = tcx.param_env(def_id); - BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env } + BorrowckInferCtxt { + infcx, + root_def_id, + reg_var_to_origin: RefCell::new(Default::default()), + param_env, + } } pub(crate) fn next_region_var( diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 40c0448cf0ba0..4e90ae391bb29 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -38,6 +38,10 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { } } + pub(super) fn root_def_id(&self) -> LocalDefId { + self.root_def_id + } + /// Collect all defining uses of opaque types inside of this typeck root. This /// expects the hidden type to be mapped to the definition parameters of the opaque /// and errors if we end up with distinct hidden types. diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index a8a48248ffd87..2627ed899a933 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -39,7 +39,7 @@ where let old_universe = infcx.universe(); let TypeOpOutput { output, constraints: query_constraints, error_info } = - op.fully_perform(infcx, locations.span(body))?; + op.fully_perform(infcx, infcx.root_def_id, locations.span(body))?; if cfg!(debug_assertions) { let data = infcx.take_and_reset_region_constraints(); if !data.is_empty() { @@ -54,7 +54,6 @@ where infcx, universal_regions, region_bound_pairs, - infcx.param_env, known_type_outlives_obligations, locations, locations.span(body), diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 9bb96b94506a4..703223e2e54a3 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,10 +1,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::SubregionOrigin; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; -use rustc_infer::infer::{InferCtxt, SubregionOrigin}; use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::bug; use rustc_middle::ty::{ @@ -18,10 +18,12 @@ use crate::constraints::OutlivesConstraint; use crate::region_infer::TypeTest; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; -use crate::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; +use crate::{ + BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory, +}; pub(crate) struct ConstraintConversion<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, + infcx: &'a BorrowckInferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, /// Each RBP `GK: 'a` is assumed to be true. These encode /// relationships like `T: 'a` that are added via implicit bounds @@ -34,7 +36,6 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { /// logic expecting to see (e.g.) `ReStatic`, and if we supplied /// our special inference variable there, we would mess that up. region_bound_pairs: &'a RegionBoundPairs<'tcx>, - param_env: ty::ParamEnv<'tcx>, known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, @@ -45,10 +46,9 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { pub(crate) fn new( - infcx: &'a InferCtxt<'tcx>, + infcx: &'a BorrowckInferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, - param_env: ty::ParamEnv<'tcx>, known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, @@ -59,7 +59,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { infcx, universal_regions, region_bound_pairs, - param_env, known_type_outlives_obligations, locations, span, @@ -286,8 +285,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { - match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span) - { + match self.infcx.param_env.and(DeeplyNormalize { value: ty }).fully_perform( + self.infcx, + self.infcx.root_def_id, + self.span, + ) { Ok(TypeOpOutput { output: ty, constraints, .. }) => { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints { 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 f642d34ea6735..7bf2df914707e 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -2,9 +2,9 @@ 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::outlives; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; -use rustc_infer::infer::{InferCtxt, outlives}; use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; @@ -14,6 +14,7 @@ use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use tracing::{debug, instrument}; use type_op::TypeOpOutput; +use crate::BorrowckInferCtxt; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; use crate::universal_regions::UniversalRegions; @@ -47,14 +48,12 @@ pub(crate) struct CreateResult<'tcx> { } pub(crate) fn create<'tcx>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, constraints: &mut MirTypeckRegionConstraints<'tcx>, ) -> CreateResult<'tcx> { UniversalRegionRelationsBuilder { infcx, - param_env, constraints, universal_regions, region_bound_pairs: Default::default(), @@ -177,8 +176,7 @@ impl UniversalRegionRelations<'_> { } struct UniversalRegionRelationsBuilder<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + infcx: &'a BorrowckInferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, @@ -205,7 +203,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // Insert the `'a: 'b` we know from the predicates. // This does not consider the type-outlives. - let param_env = self.param_env; + let param_env = self.infcx.param_env; self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env)); // - outlives is reflexive, so `'r: 'r` for every region `'r` @@ -263,7 +261,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = param_env .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, span) + .fully_perform(self.infcx, self.infcx.root_def_id, span) .unwrap_or_else(|guar| TypeOpOutput { output: Ty::new_error(self.infcx.tcx, guar), constraints: None, @@ -298,8 +296,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // Add implied bounds from impl header. 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, span); + 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; }; @@ -318,7 +317,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.infcx, &self.universal_regions, &self.region_bound_pairs, - param_env, &known_type_outlives_obligations, Locations::All(span), span, @@ -353,10 +351,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { output: normalized_outlives, constraints: constraints_normalize, error_info: _, - }) = self - .param_env - .and(DeeplyNormalize { value: outlives }) - .fully_perform(self.infcx, span) + }) = self.infcx.param_env.and(DeeplyNormalize { value: outlives }).fully_perform( + self.infcx, + self.infcx.root_def_id, + span, + ) else { self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}")); return; @@ -381,9 +380,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, '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, span) + .fully_perform(self.infcx, self.infcx.root_def_id, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 5d30fa71e92c5..b704d8f0a7692 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -640,7 +640,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty }); - match op.fully_perform(typeck.infcx, DUMMY_SP) { + match op.fully_perform(typeck.infcx, typeck.root_cx.root_def_id(), DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 0e1dd5c701fe5..e55f5b7b43efe 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -120,7 +120,7 @@ pub(crate) fn type_check<'tcx>( region_bound_pairs, normalized_inputs_and_output, known_type_outlives_obligations, - } = free_region_relations::create(infcx, infcx.param_env, universal_regions, &mut constraints); + } = free_region_relations::create(infcx, universal_regions, &mut constraints); let pre_obligations = infcx.take_registered_region_obligations(); assert!( @@ -408,7 +408,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.infcx, self.universal_regions, self.region_bound_pairs, - self.infcx.param_env, self.known_type_outlives_obligations, locations, locations.span(self.body), @@ -2458,12 +2457,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { args: GenericArgsRef<'tcx>, locations: Locations, ) -> ty::InstantiatedPredicates<'tcx> { + let root_def_id = self.root_cx.root_def_id(); if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) { constraint_conversion::ConstraintConversion::new( self.infcx, self.universal_regions, self.region_bound_pairs, - self.infcx.param_env, self.known_type_outlives_obligations, locations, self.body.span, // irrelevant; will be overridden. @@ -2473,9 +2472,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .apply_closure_requirements(closure_requirements, def_id, args); } - // Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589. - let typeck_root_def_id = tcx.typeck_root_def_id(self.body.source.def_id()); - let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id); + // Now equate closure args to regions inherited from `root_def_id`. Fixes #98589. + let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, root_def_id); let parent_args = match tcx.def_kind(def_id) { // We don't want to dispatch on 3 different kind of closures here, so take @@ -2550,17 +2548,14 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { fn fully_perform( mut self, infcx: &InferCtxt<'tcx>, + root_def_id: LocalDefId, span: Span, ) -> Result, ErrorGuaranteed> { - let (mut output, region_constraints) = scrape_region_constraints( - infcx, - |ocx| { + let (mut output, region_constraints) = + scrape_region_constraints(infcx, root_def_id, "InstantiateOpaqueType", span, |ocx| { ocx.register_obligations(self.obligations.clone()); Ok(()) - }, - "InstantiateOpaqueType", - span, - )?; + })?; self.region_constraints = Some(region_constraints); output.error_info = Some(self); Ok(output) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 161a8566b04f2..eccb88a938fba 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2053,3 +2053,29 @@ pub(super) fn check_coroutine_obligations( Ok(()) } + +pub(super) fn check_potentially_region_dependent_goals<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + if !tcx.next_trait_solver_globally() { + return Ok(()); + } + let typeck_results = tcx.typeck(def_id); + let param_env = tcx.param_env(def_id); + + // We use `TypingMode::Borrowck` as we want to use the opaque types computed by HIR typeck. + let typing_mode = TypingMode::borrowck(tcx, def_id); + let infcx = tcx.infer_ctxt().ignoring_regions().build(typing_mode); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + for (predicate, cause) in &typeck_results.potentially_region_dependent_goals { + let predicate = fold_regions(tcx, *predicate, |_, _| { + infcx.next_region_var(RegionVariableOrigin::Misc(cause.span)) + }); + ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); + } + + let errors = ocx.select_all_or_error(); + debug!(?errors); + if errors.is_empty() { Ok(()) } else { Err(infcx.err_ctxt().report_fulfillment_errors(errors)) } +} diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 85445cb3c004d..2e4b151d4dc8c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -109,6 +109,7 @@ pub(super) fn provide(providers: &mut Providers) { collect_return_position_impl_trait_in_trait_tys, compare_impl_item: compare_impl_item::compare_impl_item, check_coroutine_obligations: check::check_coroutine_obligations, + check_potentially_region_dependent_goals: check::check_potentially_region_dependent_goals, check_type_wf: wfcheck::check_type_wf, check_well_formed: wfcheck::check_well_formed, ..*providers diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index aae870f7ee3ee..3ec4155e8ce63 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use rustc_hir_analysis::check::{check_abi, check_custom_abi}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -259,21 +259,6 @@ fn typeck_with_inspect<'tcx>( let typeck_results = fcx.resolve_type_vars_in_body(body); - // Handle potentially region dependent goals, see `InferCtxt::in_hir_typeck`. - if let None = fcx.infcx.tainted_by_errors() { - for obligation in fcx.take_hir_typeck_potentially_region_dependent_goals() { - let obligation = fcx.resolve_vars_if_possible(obligation); - if obligation.has_non_region_infer() { - bug!("unexpected inference variable after writeback: {obligation:?}"); - } - fcx.register_predicate(obligation); - } - fcx.select_obligations_where_possible(|_| {}); - if let None = fcx.infcx.tainted_by_errors() { - fcx.report_ambiguity_errors(); - } - } - fcx.detect_opaque_types_added_during_writeback(); // Consistency check our TypeckResults instance can hold all ItemLocalIds diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 093de950d6369..cac5ca11d0784 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -75,6 +75,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.visit_user_provided_sigs(); wbcx.visit_coroutine_interior(); wbcx.visit_offset_of_container_types(); + wbcx.visit_potentially_region_dependent_goals(); wbcx.typeck_results.rvalue_scopes = mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes); @@ -762,6 +763,32 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_potentially_region_dependent_goals(&mut self) { + let obligations = self.fcx.take_hir_typeck_potentially_region_dependent_goals(); + if let None = self.fcx.tainted_by_errors() { + for obligation in obligations { + let (predicate, mut cause) = + self.fcx.resolve_vars_if_possible((obligation.predicate, obligation.cause)); + if predicate.has_non_region_infer() { + self.fcx.dcx().span_delayed_bug( + cause.span, + format!("unexpected inference variable after writeback: {predicate:?}"), + ); + } else { + let predicate = self.tcx().erase_regions(predicate); + if cause.has_infer() || cause.has_placeholders() { + // We can't use the the obligation cause as it references + // information local to this query. + cause = self.fcx.misc(cause.span); + } + self.typeck_results + .potentially_region_dependent_goals + .insert((predicate, cause)); + } + } + } + } + fn resolve(&mut self, value: T, span: &dyn Locatable) -> T where T: TypeFoldable>, diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 21e999b080d51..bb9c88500936f 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -22,10 +22,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.next_trait_solver } - fn in_hir_typeck(&self) -> bool { - self.in_hir_typeck - } - fn typing_mode(&self) -> ty::TypingMode<'tcx> { self.typing_mode() } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 9ff06bda89bad..d1507f08c0613 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -158,7 +158,8 @@ pub struct InferCtxtInner<'tcx> { region_assumptions: Vec>, /// `-Znext-solver`: Successfully proven goals during HIR typeck which - /// reference inference variables and get reproven after writeback. + /// reference inference variables and get reproven in case MIR type check + /// fails to prove something. /// /// See the documentation of `InferCtxt::in_hir_typeck` for more details. hir_typeck_potentially_region_dependent_goals: Vec>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 3bb8353f49e84..3e66f7dcb2ce3 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -696,6 +696,22 @@ rustc_queries! { return_result_from_ensure_ok } + /// Used in case `mir_borrowck` fails to prove an obligation. We generally assume that + /// all goals we prove in MIR type check hold as we've already checked them in HIR typeck. + /// + /// However, we replace each free region in the MIR body with a unique region inference + /// variable. As we may rely on structural identity when proving goals this may cause a + /// goal to no longer hold. We store obligations for which this may happen during HIR + /// typeck in the `TypeckResults`. We then uniquify and reprove them in case MIR typeck + /// encounters an unexpected error. We expect this to result in an error when used and + /// delay a bug if it does not. + query check_potentially_region_dependent_goals(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { + |tcx| "reproving potentially region dependent HIR typeck goals for `{}", + tcx.def_path_str(key) + } + } + /// MIR after our optimization passes have run. This is MIR that is ready /// for codegen. This is also the only query that can fetch non-local MIR, at present. query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 6b187c5325a9b..44b080fb0d099 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -206,6 +206,16 @@ pub struct TypeckResults<'tcx> { /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, + /// Goals proven during HIR typeck which may be potentially region dependent. + /// + /// Borrowck *uniquifies* regions which may cause these goal to be ambiguous in MIR + /// type check. We ICE if goals fail in borrowck to detect bugs during MIR building or + /// missed checks in HIR typeck. To avoid ICE due to region dependence we store all + /// goals which may be region dependent and reprove them in case borrowck encounters + /// an error. + pub potentially_region_dependent_goals: + FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, + /// Contains the data for evaluating the effect of feature `capture_disjoint_fields` /// on closure size. pub closure_size_eval: LocalDefIdMap>, @@ -240,6 +250,7 @@ impl<'tcx> TypeckResults<'tcx> { closure_fake_reads: Default::default(), rvalue_scopes: Default::default(), coroutine_stalled_predicates: Default::default(), + potentially_region_dependent_goals: Default::default(), closure_size_eval: Default::default(), offset_of_data: Default::default(), } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 3e1f48610ffc0..da05c49756ff9 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -25,12 +25,8 @@ enum CanonicalizeInputKind { /// trait candidates relies on it when deciding whether a where-bound /// is trivial. ParamEnv, - /// When canonicalizing predicates, we don't keep `'static`. If we're - /// currently outside of the trait solver and canonicalize the root goal - /// during HIR typeck, we replace each occurrence of a region with a - /// unique region variable. See the comment on `InferCtxt::in_hir_typeck` - /// for more details. - Predicate { is_hir_typeck_root_goal: bool }, + /// When canonicalizing predicates, we don't keep `'static`. + Predicate, } /// Whether we're canonicalizing a query input or the query response. @@ -191,7 +187,6 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { pub fn canonicalize_input>( delegate: &'a D, variables: &'a mut Vec, - is_hir_typeck_root_goal: bool, input: QueryInput, ) -> ty::Canonical> { // First canonicalize the `param_env` while keeping `'static` @@ -201,9 +196,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // while *mostly* reusing the canonicalizer from above. let mut rest_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { - is_hir_typeck_root_goal, - }), + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), variables, variable_lookup_table, @@ -481,31 +474,13 @@ impl, I: Interner> TypeFolder for Canonicaliz } }; - let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { - is_hir_typeck_root_goal: true, - }) = self.canonicalize_mode - { - let var = ty::BoundVar::from(self.variables.len()); - self.variables.push(r.into()); - self.var_kinds.push(kind); - var - } else { - self.get_or_insert_bound_var(r, kind) - }; + let var = self.get_or_insert_bound_var(r, kind); Region::new_anon_bound(self.cx(), self.binder_index, var) } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { - is_hir_typeck_root_goal: true, - }) = self.canonicalize_mode - { - // If we're canonicalizing a root goal during HIR typeck, we - // must not use the `cache` as we want to map each occurrence - // of a region to a unique existential variable. - self.inner_fold_ty(t) - } else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { ty } else { let res = self.inner_fold_ty(t); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 74c5b49ea92f7..4644b145b18a3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -57,7 +57,6 @@ where /// This expects `goal` and `opaque_types` to be eager resolved. pub(super) fn canonicalize_goal( &self, - is_hir_typeck_root_goal: bool, goal: Goal, opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, ) -> (Vec, CanonicalInput) { @@ -65,7 +64,6 @@ where let canonical = Canonicalizer::canonicalize_input( self.delegate, &mut orig_values, - is_hir_typeck_root_goal, QueryInput { goal, predefined_opaques_in_body: self diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index a4738306181cc..0230f784e4694 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -459,10 +459,7 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root) - && self.delegate.in_hir_typeck(); - let (orig_values, canonical_goal) = - self.canonicalize_goal(is_hir_typeck_root_goal, goal, opaque_types); + let (orig_values, canonical_goal) = self.canonicalize_goal(goal, opaque_types); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_result = self.search_graph.evaluate_goal( diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 3f628d8066203..575e0472e0e38 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -252,7 +252,9 @@ where // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the // storage, we can also rely on structural identity of `?x` even if we // later uniquify it in MIR borrowck. - if infcx.in_hir_typeck && obligation.has_non_region_infer() { + if infcx.in_hir_typeck + && (obligation.has_non_region_infer() || obligation.has_free_regions()) + { infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 0ca2d2162288f..6ce68507d6521 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -1,6 +1,7 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{TyCtxt, TypeFoldable}; @@ -42,13 +43,14 @@ where fn fully_perform( self, infcx: &InferCtxt<'tcx>, + root_def_id: LocalDefId, span: Span, ) -> Result, ErrorGuaranteed> { if cfg!(debug_assertions) { info!("fully_perform({:?})", self); } - Ok(scrape_region_constraints(infcx, self.closure, self.description, span)?.0) + Ok(scrape_region_constraints(infcx, root_def_id, self.description, span, self.closure)?.0) } } @@ -62,9 +64,10 @@ impl fmt::Debug for CustomTypeOp { /// constraints that result, creating query-region-constraints. pub fn scrape_region_constraints<'tcx, Op, R>( infcx: &InferCtxt<'tcx>, - op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, + root_def_id: LocalDefId, name: &'static str, span: Span, + op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, ) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> where R: TypeFoldable>, @@ -94,6 +97,8 @@ where let errors = ocx.select_all_or_error(); if errors.is_empty() { Ok(value) + } else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) { + Err(guar) } else { Err(infcx .dcx() diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 018e9748cf068..4b8bf86812317 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -1,6 +1,7 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::LocalDefId; use rustc_infer::traits::PredicateObligations; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{ParamEnvAnd, TyCtxt, TypeFoldable}; @@ -37,6 +38,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug { fn fully_perform( self, infcx: &InferCtxt<'tcx>, + root_def_id: LocalDefId, span: Span, ) -> Result, ErrorGuaranteed>; } @@ -140,6 +142,7 @@ where fn fully_perform( self, infcx: &InferCtxt<'tcx>, + root_def_id: LocalDefId, span: Span, ) -> Result, ErrorGuaranteed> { // In the new trait solver, query type ops are performed locally. This @@ -152,9 +155,10 @@ where if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, - |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span), + root_def_id, "query type op", span, + |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span), )? .0); } @@ -166,19 +170,15 @@ where // we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a // hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually // collecting region constraints via `region_constraints`. - let (mut output, _) = scrape_region_constraints( - infcx, - |ocx| { + let (mut output, _) = + scrape_region_constraints(infcx, root_def_id, "fully_perform", span, |ocx| { let (output, ei, obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; error_info = ei; ocx.register_obligations(obligations); Ok(output) - }, - "fully_perform", - span, - )?; + })?; output.error_info = error_info; if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints { region_constraints.outlives.extend(outlives.iter().cloned()); diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index e39b99e992b03..b4462294700ac 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -148,10 +148,6 @@ pub trait InferCtxtLike: Sized { true } - fn in_hir_typeck(&self) -> bool { - false - } - fn typing_mode(&self) -> TypingMode; fn universe(&self) -> ty::UniverseIndex; diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr index 3b47888999695..e75be1e813751 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr @@ -1,10 +1,10 @@ -error[E0283]: type annotations needed: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` - --> $DIR/ambiguity-due-to-uniquification-2.rs:16:23 +error[E0283]: type annotations needed: cannot satisfy `impl Trait<'_> + Trait<'_>: Trait<'_>` + --> $DIR/ambiguity-due-to-uniquification-2.rs:16:5 | LL | impls_trait::<'y, _>(foo::<'x, 'y>()); - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` + = note: cannot satisfy `impl Trait<'_> + Trait<'_>: Trait<'_>` = help: the trait `Trait<'t>` is implemented for `()` note: required by a bound in `impls_trait` --> $DIR/ambiguity-due-to-uniquification-2.rs:13:23 diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs index 2a9a8b80cc069..30df70396f448 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs @@ -14,7 +14,7 @@ fn impls_trait<'x, T: Trait<'x>>(_: T) {} fn bar<'x, 'y>() { impls_trait::<'y, _>(foo::<'x, 'y>()); - //[next]~^ ERROR type annotations needed: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` + //[next]~^ ERROR type annotations needed: cannot satisfy `impl Trait<'_> + Trait<'_>: Trait<'_>` } fn main() {} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr index e25f892b3657a..7fa8905493177 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr @@ -1,12 +1,10 @@ -error[E0283]: type annotations needed: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>` - --> $DIR/ambiguity-due-to-uniquification-3.rs:28:17 +error[E0283]: type annotations needed: cannot satisfy `dyn Object<&(), &()>: Trait<&()>` + --> $DIR/ambiguity-due-to-uniquification-3.rs:28:5 | LL | impls_trait(obj, t); - | ----------- ^^^ - | | - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^ | - = note: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>` + = note: cannot satisfy `dyn Object<&(), &()>: Trait<&()>` = help: the trait `Trait` is implemented for `()` note: required by a bound in `impls_trait` --> $DIR/ambiguity-due-to-uniquification-3.rs:24:19