change HIR typeck unification handling approach
This commit is contained in:
parent
831e291d3b
commit
17ac2fc96d
25 changed files with 168 additions and 128 deletions
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue