change HIR typeck unification handling approach

This commit is contained in:
lcnr 2025-08-22 13:39:38 +02:00
parent 831e291d3b
commit 17ac2fc96d
25 changed files with 168 additions and 128 deletions

View file

@ -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<FxIndexMap<ty::RegionVid, RegionCtxt>>,
pub(crate) root_def_id: LocalDefId,
pub(crate) param_env: ParamEnv<'tcx>,
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
}
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<F>(

View file

@ -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.

View file

@ -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),

View file

@ -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 {

View file

@ -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);

View file

@ -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 }
}

View file

@ -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<TypeOpOutput<'tcx, Self>, 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)

View file

@ -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)) }
}

View file

@ -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

View file

@ -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

View file

@ -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<T>(&mut self, value: T, span: &dyn Locatable) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,

View file

@ -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()
}

View file

@ -158,7 +158,8 @@ pub struct InferCtxtInner<'tcx> {
region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
/// `-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<PredicateObligation<'tcx>>,

View file

@ -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> {

View file

@ -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<ClosureSizeProfileData<'tcx>>,
@ -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(),
}

View file

@ -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<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
pub fn canonicalize_input<P: TypeFoldable<I>>(
delegate: &'a D,
variables: &'a mut Vec<I::GenericArg>,
is_hir_typeck_root_goal: bool,
input: QueryInput<I, P>,
) -> ty::Canonical<I, QueryInput<I, P>> {
// First canonicalize the `param_env` while keeping `'static`
@ -201,9 +196,7 @@ impl<'a, D: SolverDelegate<Interner = I>, 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<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> 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);

View file

@ -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<I, I::Predicate>,
opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) {
@ -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

View file

@ -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(

View file

@ -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);
}
}

View file

@ -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<TypeOpOutput<'tcx, Self>, 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<F> fmt::Debug for CustomTypeOp<F> {
/// constraints that result, creating query-region-constraints.
pub fn scrape_region_constraints<'tcx, Op, R>(
infcx: &InferCtxt<'tcx>,
op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
root_def_id: LocalDefId,
name: &'static str,
span: Span,
op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed>
where
R: TypeFoldable<TyCtxt<'tcx>>,
@ -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()

View file

@ -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<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>;
}
@ -140,6 +142,7 @@ where
fn fully_perform(
self,
infcx: &InferCtxt<'tcx>,
root_def_id: LocalDefId,
span: Span,
) -> Result<TypeOpOutput<'tcx, Self>, 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());

View file

@ -148,10 +148,6 @@ pub trait InferCtxtLike: Sized {
true
}
fn in_hir_typeck(&self) -> bool {
false
}
fn typing_mode(&self) -> TypingMode<Self::Interner>;
fn universe(&self) -> ty::UniverseIndex;

View file

@ -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

View file

@ -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() {}

View file

@ -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<T>` is implemented for `()`
note: required by a bound in `impls_trait`
--> $DIR/ambiguity-due-to-uniquification-3.rs:24:19