Auto merge of #145706 - lcnr:uniquification, r=BoxyUwU

change HIR typeck region uniquification handling approach

rust-lang/rust#144405 causes structural lookup of opaque types to not work during HIR typeck, so instead avoid uniquifying goals and instead only reprove them if MIR borrowck actually encounters an error.

This doesn't perfectly maintain the property that HIR typeck succeeding implies that MIR typeck succeeds, instead weakening this check to only guarantee that HIR typeck implies that MIR typeck succeeds modulo region uniquification. This means we still get the actually desirable ICEs if we MIR building is broken or we forget to check some property in HIR typeck, without having to deal with the fallout of uniquification in HIR typeck itself.

We report errors using the original obligation sources of HIR typeck so diagnostics aren't that negatively impacted either.

Here's the history of region uniquification while working on the new trait solver:
- rust-lang/rust#107981
- rust-lang/rust#110180
- rust-lang/rust#114117
- rust-lang/rust#130821
- rust-lang/rust#144405
- rust-lang/rust#145706 <- we're here 🎉

r? `@BoxyUwU`
This commit is contained in:
bors 2025-08-23 20:16:58 +00:00
commit 69b76df90c
25 changed files with 168 additions and 128 deletions

View File

@ -300,7 +300,7 @@ fn do_mir_borrowck<'tcx>(
def: LocalDefId, def: LocalDefId,
) -> PropagatedBorrowCheckResults<'tcx> { ) -> PropagatedBorrowCheckResults<'tcx> {
let tcx = root_cx.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, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow(); let input_body: &Body<'_> = &input_body.borrow();
let input_promoted: &IndexSlice<_, _> = &promoted.borrow(); let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
@ -590,12 +590,13 @@ fn get_flow_results<'a, 'tcx>(
pub(crate) struct BorrowckInferCtxt<'tcx> { pub(crate) struct BorrowckInferCtxt<'tcx> {
pub(crate) infcx: InferCtxt<'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) param_env: ParamEnv<'tcx>,
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
} }
impl<'tcx> BorrowckInferCtxt<'tcx> { 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() { let typing_mode = if tcx.use_typing_mode_borrowck() {
TypingMode::borrowck(tcx, def_id) TypingMode::borrowck(tcx, def_id)
} else { } else {
@ -603,7 +604,12 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
}; };
let infcx = tcx.infer_ctxt().build(typing_mode); let infcx = tcx.infer_ctxt().build(typing_mode);
let param_env = tcx.param_env(def_id); 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>( 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 /// 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 /// expects the hidden type to be mapped to the definition parameters of the opaque
/// and errors if we end up with distinct hidden types. /// and errors if we end up with distinct hidden types.

View File

@ -39,7 +39,7 @@ where
let old_universe = infcx.universe(); let old_universe = infcx.universe();
let TypeOpOutput { output, constraints: query_constraints, error_info } = 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) { if cfg!(debug_assertions) {
let data = infcx.take_and_reset_region_constraints(); let data = infcx.take_and_reset_region_constraints();
if !data.is_empty() { if !data.is_empty() {
@ -54,7 +54,6 @@ where
infcx, infcx,
universal_regions, universal_regions,
region_bound_pairs, region_bound_pairs,
infcx.param_env,
known_type_outlives_obligations, known_type_outlives_obligations,
locations, locations,
locations.span(body), locations.span(body),

View File

@ -1,10 +1,10 @@
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::SubregionOrigin;
use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
use rustc_infer::infer::{InferCtxt, SubregionOrigin};
use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_infer::traits::query::type_op::DeeplyNormalize;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::{ use rustc_middle::ty::{
@ -18,10 +18,12 @@ use crate::constraints::OutlivesConstraint;
use crate::region_infer::TypeTest; use crate::region_infer::TypeTest;
use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::type_check::{Locations, MirTypeckRegionConstraints};
use crate::universal_regions::UniversalRegions; use crate::universal_regions::UniversalRegions;
use crate::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; use crate::{
BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory,
};
pub(crate) struct ConstraintConversion<'a, 'tcx> { pub(crate) struct ConstraintConversion<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>, infcx: &'a BorrowckInferCtxt<'tcx>,
universal_regions: &'a UniversalRegions<'tcx>, universal_regions: &'a UniversalRegions<'tcx>,
/// Each RBP `GK: 'a` is assumed to be true. These encode /// Each RBP `GK: 'a` is assumed to be true. These encode
/// relationships like `T: 'a` that are added via implicit bounds /// 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 /// logic expecting to see (e.g.) `ReStatic`, and if we supplied
/// our special inference variable there, we would mess that up. /// our special inference variable there, we would mess that up.
region_bound_pairs: &'a RegionBoundPairs<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>],
locations: Locations, locations: Locations,
span: Span, span: Span,
@ -45,10 +46,9 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> {
impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
pub(crate) fn new( pub(crate) fn new(
infcx: &'a InferCtxt<'tcx>, infcx: &'a BorrowckInferCtxt<'tcx>,
universal_regions: &'a UniversalRegions<'tcx>, universal_regions: &'a UniversalRegions<'tcx>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>],
locations: Locations, locations: Locations,
span: Span, span: Span,
@ -59,7 +59,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
infcx, infcx,
universal_regions, universal_regions,
region_bound_pairs, region_bound_pairs,
param_env,
known_type_outlives_obligations, known_type_outlives_obligations,
locations, locations,
span, span,
@ -286,8 +285,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
ConstraintCategory<'tcx>, ConstraintCategory<'tcx>,
)>, )>,
) -> Ty<'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, .. }) => { Ok(TypeOpOutput { output: ty, constraints, .. }) => {
// FIXME(higher_ranked_auto): What should we do with the assumptions here? // FIXME(higher_ranked_auto): What should we do with the assumptions here?
if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints { 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_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives;
use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::region_constraints::GenericKind;
use rustc_infer::infer::{InferCtxt, outlives};
use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_infer::traits::query::type_op::DeeplyNormalize;
use rustc_middle::mir::ConstraintCategory; use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::OutlivesBound; 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 tracing::{debug, instrument};
use type_op::TypeOpOutput; use type_op::TypeOpOutput;
use crate::BorrowckInferCtxt;
use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion};
use crate::universal_regions::UniversalRegions; use crate::universal_regions::UniversalRegions;
@ -47,14 +48,12 @@ pub(crate) struct CreateResult<'tcx> {
} }
pub(crate) fn create<'tcx>( pub(crate) fn create<'tcx>(
infcx: &InferCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
universal_regions: UniversalRegions<'tcx>, universal_regions: UniversalRegions<'tcx>,
constraints: &mut MirTypeckRegionConstraints<'tcx>, constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> CreateResult<'tcx> { ) -> CreateResult<'tcx> {
UniversalRegionRelationsBuilder { UniversalRegionRelationsBuilder {
infcx, infcx,
param_env,
constraints, constraints,
universal_regions, universal_regions,
region_bound_pairs: Default::default(), region_bound_pairs: Default::default(),
@ -177,8 +176,7 @@ impl UniversalRegionRelations<'_> {
} }
struct UniversalRegionRelationsBuilder<'a, 'tcx> { struct UniversalRegionRelationsBuilder<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>, infcx: &'a BorrowckInferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
universal_regions: UniversalRegions<'tcx>, universal_regions: UniversalRegions<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
@ -205,7 +203,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
// Insert the `'a: 'b` we know from the predicates. // Insert the `'a: 'b` we know from the predicates.
// This does not consider the type-outlives. // 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)); self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));
// - outlives is reflexive, so `'r: 'r` for every region `'r` // - 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, .. } = let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } =
param_env param_env
.and(DeeplyNormalize { value: ty }) .and(DeeplyNormalize { value: ty })
.fully_perform(self.infcx, span) .fully_perform(self.infcx, self.infcx.root_def_id, span)
.unwrap_or_else(|guar| TypeOpOutput { .unwrap_or_else(|guar| TypeOpOutput {
output: Ty::new_error(self.infcx.tcx, guar), output: Ty::new_error(self.infcx.tcx, guar),
constraints: None, constraints: None,
@ -298,8 +296,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
// Add implied bounds from impl header. // Add implied bounds from impl header.
if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { 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)) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) {
let result: Result<_, ErrorGuaranteed> = let result: Result<_, ErrorGuaranteed> = param_env
param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, span); .and(DeeplyNormalize { value: ty })
.fully_perform(self.infcx, self.infcx.root_def_id, span);
let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else {
continue; continue;
}; };
@ -318,7 +317,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
self.infcx, self.infcx,
&self.universal_regions, &self.universal_regions,
&self.region_bound_pairs, &self.region_bound_pairs,
param_env,
&known_type_outlives_obligations, &known_type_outlives_obligations,
Locations::All(span), Locations::All(span),
span, span,
@ -353,10 +351,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
output: normalized_outlives, output: normalized_outlives,
constraints: constraints_normalize, constraints: constraints_normalize,
error_info: _, error_info: _,
}) = self }) = self.infcx.param_env.and(DeeplyNormalize { value: outlives }).fully_perform(
.param_env self.infcx,
.and(DeeplyNormalize { value: outlives }) self.infcx.root_def_id,
.fully_perform(self.infcx, span) span,
)
else { else {
self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}")); self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}"));
return; return;
@ -381,9 +380,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
span: Span, span: Span,
) -> Option<&'tcx QueryRegionConstraints<'tcx>> { ) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
let TypeOpOutput { output: bounds, constraints, .. } = self let TypeOpOutput { output: bounds, constraints, .. } = self
.infcx
.param_env .param_env
.and(type_op::ImpliedOutlivesBounds { ty }) .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)) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
.ok()?; .ok()?;
debug!(?bounds, ?constraints); debug!(?bounds, ?constraints);

View File

@ -640,7 +640,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> {
let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty }); 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, .. }) => { Ok(TypeOpOutput { output, constraints, .. }) => {
DropData { dropck_result: output, region_constraint_data: constraints } DropData { dropck_result: output, region_constraint_data: constraints }
} }

View File

@ -120,7 +120,7 @@ pub(crate) fn type_check<'tcx>(
region_bound_pairs, region_bound_pairs,
normalized_inputs_and_output, normalized_inputs_and_output,
known_type_outlives_obligations, 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(); let pre_obligations = infcx.take_registered_region_obligations();
assert!( assert!(
@ -408,7 +408,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.infcx, self.infcx,
self.universal_regions, self.universal_regions,
self.region_bound_pairs, self.region_bound_pairs,
self.infcx.param_env,
self.known_type_outlives_obligations, self.known_type_outlives_obligations,
locations, locations,
locations.span(self.body), locations.span(self.body),
@ -2458,12 +2457,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
args: GenericArgsRef<'tcx>, args: GenericArgsRef<'tcx>,
locations: Locations, locations: Locations,
) -> ty::InstantiatedPredicates<'tcx> { ) -> 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) { if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) {
constraint_conversion::ConstraintConversion::new( constraint_conversion::ConstraintConversion::new(
self.infcx, self.infcx,
self.universal_regions, self.universal_regions,
self.region_bound_pairs, self.region_bound_pairs,
self.infcx.param_env,
self.known_type_outlives_obligations, self.known_type_outlives_obligations,
locations, locations,
self.body.span, // irrelevant; will be overridden. 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); .apply_closure_requirements(closure_requirements, def_id, args);
} }
// Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589. // Now equate closure args to regions inherited from `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, root_def_id);
let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id);
let parent_args = match tcx.def_kind(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 // 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( fn fully_perform(
mut self, mut self,
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
root_def_id: LocalDefId,
span: Span, span: Span,
) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
let (mut output, region_constraints) = scrape_region_constraints( let (mut output, region_constraints) =
infcx, scrape_region_constraints(infcx, root_def_id, "InstantiateOpaqueType", span, |ocx| {
|ocx| {
ocx.register_obligations(self.obligations.clone()); ocx.register_obligations(self.obligations.clone());
Ok(()) Ok(())
}, })?;
"InstantiateOpaqueType",
span,
)?;
self.region_constraints = Some(region_constraints); self.region_constraints = Some(region_constraints);
output.error_info = Some(self); output.error_info = Some(self);
Ok(output) Ok(output)

View File

@ -2053,3 +2053,29 @@ pub(super) fn check_coroutine_obligations(
Ok(()) 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, collect_return_position_impl_trait_in_trait_tys,
compare_impl_item: compare_impl_item::compare_impl_item, compare_impl_item: compare_impl_item::compare_impl_item,
check_coroutine_obligations: check::check_coroutine_obligations, 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_type_wf: wfcheck::check_type_wf,
check_well_formed: wfcheck::check_well_formed, check_well_formed: wfcheck::check_well_formed,
..*providers ..*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_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc};
use rustc_middle::query::Providers; 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_middle::{bug, span_bug};
use rustc_session::config; use rustc_session::config;
use rustc_span::Span; use rustc_span::Span;
@ -255,21 +255,6 @@ fn typeck_with_inspect<'tcx>(
let typeck_results = fcx.resolve_type_vars_in_body(body); 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(); fcx.detect_opaque_types_added_during_writeback();
// Consistency check our TypeckResults instance can hold all ItemLocalIds // Consistency check our TypeckResults instance can hold all ItemLocalIds

View File

@ -76,6 +76,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
wbcx.visit_coroutine_interior(); wbcx.visit_coroutine_interior();
wbcx.visit_transmutes(); wbcx.visit_transmutes();
wbcx.visit_offset_of_container_types(); wbcx.visit_offset_of_container_types();
wbcx.visit_potentially_region_dependent_goals();
wbcx.typeck_results.rvalue_scopes = wbcx.typeck_results.rvalue_scopes =
mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes); mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes);
@ -775,6 +776,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 fn resolve<T>(&mut self, value: T, span: &dyn Locatable) -> T
where where
T: TypeFoldable<TyCtxt<'tcx>>, T: TypeFoldable<TyCtxt<'tcx>>,

View File

@ -22,10 +22,6 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.next_trait_solver self.next_trait_solver
} }
fn in_hir_typeck(&self) -> bool {
self.in_hir_typeck
}
fn typing_mode(&self) -> ty::TypingMode<'tcx> { fn typing_mode(&self) -> ty::TypingMode<'tcx> {
self.typing_mode() self.typing_mode()
} }

View File

@ -158,7 +158,8 @@ pub struct InferCtxtInner<'tcx> {
region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>, region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
/// `-Znext-solver`: Successfully proven goals during HIR typeck which /// `-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. /// See the documentation of `InferCtxt::in_hir_typeck` for more details.
hir_typeck_potentially_region_dependent_goals: Vec<PredicateObligation<'tcx>>, hir_typeck_potentially_region_dependent_goals: Vec<PredicateObligation<'tcx>>,

View File

@ -696,6 +696,22 @@ rustc_queries! {
return_result_from_ensure_ok 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 /// 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. /// 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> { 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 /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs
pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, 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` /// Contains the data for evaluating the effect of feature `capture_disjoint_fields`
/// on closure size. /// on closure size.
pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>, pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>,
@ -245,6 +255,7 @@ impl<'tcx> TypeckResults<'tcx> {
closure_fake_reads: Default::default(), closure_fake_reads: Default::default(),
rvalue_scopes: Default::default(), rvalue_scopes: Default::default(),
coroutine_stalled_predicates: Default::default(), coroutine_stalled_predicates: Default::default(),
potentially_region_dependent_goals: Default::default(),
closure_size_eval: Default::default(), closure_size_eval: Default::default(),
transmutes_to_check: Default::default(), transmutes_to_check: Default::default(),
offset_of_data: 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 /// trait candidates relies on it when deciding whether a where-bound
/// is trivial. /// is trivial.
ParamEnv, ParamEnv,
/// When canonicalizing predicates, we don't keep `'static`. If we're /// When canonicalizing predicates, we don't keep `'static`.
/// currently outside of the trait solver and canonicalize the root goal Predicate,
/// 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 },
} }
/// Whether we're canonicalizing a query input or the query response. /// 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>>( pub fn canonicalize_input<P: TypeFoldable<I>>(
delegate: &'a D, delegate: &'a D,
variables: &'a mut Vec<I::GenericArg>, variables: &'a mut Vec<I::GenericArg>,
is_hir_typeck_root_goal: bool,
input: QueryInput<I, P>, input: QueryInput<I, P>,
) -> ty::Canonical<I, QueryInput<I, P>> { ) -> ty::Canonical<I, QueryInput<I, P>> {
// First canonicalize the `param_env` while keeping `'static` // 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. // while *mostly* reusing the canonicalizer from above.
let mut rest_canonicalizer = Canonicalizer { let mut rest_canonicalizer = Canonicalizer {
delegate, delegate,
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate),
is_hir_typeck_root_goal,
}),
variables, variables,
variable_lookup_table, 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 { let var = self.get_or_insert_bound_var(r, kind);
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)
};
Region::new_anon_bound(self.cx(), self.binder_index, var) Region::new_anon_bound(self.cx(), self.binder_index, var)
} }
fn fold_ty(&mut self, t: I::Ty) -> I::Ty { fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
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)) {
ty ty
} else { } else {
let res = self.inner_fold_ty(t); let res = self.inner_fold_ty(t);

View File

@ -57,7 +57,6 @@ where
/// This expects `goal` and `opaque_types` to be eager resolved. /// This expects `goal` and `opaque_types` to be eager resolved.
pub(super) fn canonicalize_goal( pub(super) fn canonicalize_goal(
&self, &self,
is_hir_typeck_root_goal: bool,
goal: Goal<I, I::Predicate>, goal: Goal<I, I::Predicate>,
opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>, opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) { ) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) {
@ -65,7 +64,6 @@ where
let canonical = Canonicalizer::canonicalize_input( let canonical = Canonicalizer::canonicalize_input(
self.delegate, self.delegate,
&mut orig_values, &mut orig_values,
is_hir_typeck_root_goal,
QueryInput { QueryInput {
goal, goal,
predefined_opaques_in_body: self predefined_opaques_in_body: self

View File

@ -459,10 +459,7 @@ where
let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let opaque_types = self.delegate.clone_opaque_types_lookup_table();
let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types));
let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root) let (orig_values, canonical_goal) = self.canonicalize_goal(goal, opaque_types);
&& self.delegate.in_hir_typeck();
let (orig_values, canonical_goal) =
self.canonicalize_goal(is_hir_typeck_root_goal, goal, opaque_types);
let mut goal_evaluation = let mut goal_evaluation =
self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
let canonical_result = self.search_graph.evaluate_goal( 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 // 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 // storage, we can also rely on structural identity of `?x` even if we
// later uniquify it in MIR borrowck. // 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); infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
} }
} }

View File

@ -1,6 +1,7 @@
use std::fmt; use std::fmt;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::region_constraints::RegionConstraintData;
use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_middle::ty::{TyCtxt, TypeFoldable};
@ -42,13 +43,14 @@ where
fn fully_perform( fn fully_perform(
self, self,
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
root_def_id: LocalDefId,
span: Span, span: Span,
) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
info!("fully_perform({:?})", self); 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. /// constraints that result, creating query-region-constraints.
pub fn scrape_region_constraints<'tcx, Op, R>( pub fn scrape_region_constraints<'tcx, Op, R>(
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>, root_def_id: LocalDefId,
name: &'static str, name: &'static str,
span: Span, span: Span,
op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> ) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed>
where where
R: TypeFoldable<TyCtxt<'tcx>>, R: TypeFoldable<TyCtxt<'tcx>>,
@ -94,6 +97,8 @@ where
let errors = ocx.select_all_or_error(); let errors = ocx.select_all_or_error();
if errors.is_empty() { if errors.is_empty() {
Ok(value) Ok(value)
} else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) {
Err(guar)
} else { } else {
Err(infcx Err(infcx
.dcx() .dcx()

View File

@ -1,6 +1,7 @@
use std::fmt; use std::fmt;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::traits::PredicateObligations; use rustc_infer::traits::PredicateObligations;
use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt, TypeFoldable}; use rustc_middle::ty::{ParamEnvAnd, TyCtxt, TypeFoldable};
@ -37,6 +38,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug {
fn fully_perform( fn fully_perform(
self, self,
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
root_def_id: LocalDefId,
span: Span, span: Span,
) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>; ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>;
} }
@ -140,6 +142,7 @@ where
fn fully_perform( fn fully_perform(
self, self,
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
root_def_id: LocalDefId,
span: Span, span: Span,
) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
// In the new trait solver, query type ops are performed locally. This // In the new trait solver, query type ops are performed locally. This
@ -152,9 +155,10 @@ where
if infcx.next_trait_solver() { if infcx.next_trait_solver() {
return Ok(scrape_region_constraints( return Ok(scrape_region_constraints(
infcx, infcx,
|ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span), root_def_id,
"query type op", "query type op",
span, span,
|ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span),
)? )?
.0); .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 // 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 // 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`. // collecting region constraints via `region_constraints`.
let (mut output, _) = scrape_region_constraints( let (mut output, _) =
infcx, scrape_region_constraints(infcx, root_def_id, "fully_perform", span, |ocx| {
|ocx| {
let (output, ei, obligations, _) = let (output, ei, obligations, _) =
Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; Q::fully_perform_into(self, infcx, &mut region_constraints, span)?;
error_info = ei; error_info = ei;
ocx.register_obligations(obligations); ocx.register_obligations(obligations);
Ok(output) Ok(output)
}, })?;
"fully_perform",
span,
)?;
output.error_info = error_info; output.error_info = error_info;
if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints { if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints {
region_constraints.outlives.extend(outlives.iter().cloned()); region_constraints.outlives.extend(outlives.iter().cloned());

View File

@ -148,10 +148,6 @@ pub trait InferCtxtLike: Sized {
true true
} }
fn in_hir_typeck(&self) -> bool {
false
}
fn typing_mode(&self) -> TypingMode<Self::Interner>; fn typing_mode(&self) -> TypingMode<Self::Interner>;
fn universe(&self) -> ty::UniverseIndex; 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>` error[E0283]: type annotations needed: cannot satisfy `impl Trait<'_> + Trait<'_>: Trait<'_>`
--> $DIR/ambiguity-due-to-uniquification-2.rs:16:23 --> $DIR/ambiguity-due-to-uniquification-2.rs:16:5
| |
LL | impls_trait::<'y, _>(foo::<'x, 'y>()); 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 `()` = help: the trait `Trait<'t>` is implemented for `()`
note: required by a bound in `impls_trait` note: required by a bound in `impls_trait`
--> $DIR/ambiguity-due-to-uniquification-2.rs:13:23 --> $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>() { fn bar<'x, 'y>() {
impls_trait::<'y, _>(foo::<'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() {} fn main() {}

View File

@ -1,12 +1,10 @@
error[E0283]: type annotations needed: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>` error[E0283]: type annotations needed: cannot satisfy `dyn Object<&(), &()>: Trait<&()>`
--> $DIR/ambiguity-due-to-uniquification-3.rs:28:17 --> $DIR/ambiguity-due-to-uniquification-3.rs:28:5
| |
LL | impls_trait(obj, t); 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 `()` = help: the trait `Trait<T>` is implemented for `()`
note: required by a bound in `impls_trait` note: required by a bound in `impls_trait`
--> $DIR/ambiguity-due-to-uniquification-3.rs:24:19 --> $DIR/ambiguity-due-to-uniquification-3.rs:24:19