diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 0c4a82f3d2f3..23ad964c7cd9 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -24,13 +24,13 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use tracing::{debug, instrument}; use super::reverse_sccs::ReverseSccGraph; -use crate::BorrowckInferCtxt; use crate::consumers::RegionInferenceContext; use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::type_check::canonical::fully_perform_op_raw; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::{RegionClassification, UniversalRegions}; +use crate::{BorrowckInferCtxt, CollectRegionConstraintsResult}; mod member_constraints; mod region_ctxt; @@ -126,6 +126,31 @@ fn nll_var_to_universal_region<'tcx>( } } +/// Record info needed to report the same name error later. +#[derive(Copy, Clone, Debug)] +pub(crate) struct UnexpectedHiddenRegion<'tcx> { + // The def_id of the body where this error occurs. + // Needed to handle region vars with their corresponding `infcx`. + def_id: LocalDefId, + opaque_type_key: OpaqueTypeKey<'tcx>, + hidden_type: ProvisionalHiddenType<'tcx>, + member_region: Region<'tcx>, +} + +impl<'tcx> UnexpectedHiddenRegion<'tcx> { + pub(crate) fn to_error(self) -> (LocalDefId, DeferredOpaqueTypeError<'tcx>) { + let UnexpectedHiddenRegion { def_id, opaque_type_key, hidden_type, member_region } = self; + ( + def_id, + DeferredOpaqueTypeError::UnexpectedHiddenRegion { + opaque_type_key, + hidden_type, + member_region, + }, + ) + } +} + /// 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. @@ -176,11 +201,13 @@ struct DefiningUse<'tcx> { /// It also means that this whole function is not really soundness critical as we /// recheck all uses of the opaques regardless. pub(crate) fn compute_definition_site_hidden_types<'tcx>( + def_id: LocalDefId, infcx: &BorrowckInferCtxt<'tcx>, universal_region_relations: &Frozen>, constraints: &MirTypeckRegionConstraints<'tcx>, location_map: Rc, hidden_types: &mut FxIndexMap>, + unconstrained_hidden_type_errors: &mut Vec>, opaque_types: &[(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)], ) -> Vec> { let mut errors = Vec::new(); @@ -204,8 +231,10 @@ pub(crate) fn compute_definition_site_hidden_types<'tcx>( // up equal to one of their choice regions and compute the actual hidden type of // the opaque type definition. This is stored in the `root_cx`. compute_definition_site_hidden_types_from_defining_uses( + def_id, &rcx, hidden_types, + unconstrained_hidden_type_errors, &defining_uses, &mut errors, ); @@ -274,8 +303,10 @@ fn collect_defining_uses<'tcx>( #[instrument(level = "debug", skip(rcx, hidden_types, defining_uses, errors))] fn compute_definition_site_hidden_types_from_defining_uses<'tcx>( + def_id: LocalDefId, rcx: &RegionCtxt<'_, 'tcx>, hidden_types: &mut FxIndexMap>, + unconstrained_hidden_type_errors: &mut Vec>, defining_uses: &[DefiningUse<'tcx>], errors: &mut Vec>, ) { @@ -293,16 +324,29 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>( Ok(hidden_type) => hidden_type, Err(r) => { debug!("UnexpectedHiddenRegion: {:?}", r); - errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion { - hidden_type, - opaque_type_key, - member_region: ty::Region::new_var(tcx, r), - }); - let guar = tcx.dcx().span_delayed_bug( - hidden_type.span, - "opaque type with non-universal region args", - ); - ty::ProvisionalHiddenType::new_error(tcx, guar) + // If we're using the next solver, the unconstrained region may be resolved by a + // fully defining use from another body. + // So we don't generate error eagerly here. + if rcx.infcx.tcx.use_typing_mode_borrowck() { + unconstrained_hidden_type_errors.push(UnexpectedHiddenRegion { + def_id, + hidden_type, + opaque_type_key, + member_region: ty::Region::new_var(tcx, r), + }); + continue; + } else { + errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion { + hidden_type, + opaque_type_key, + member_region: ty::Region::new_var(tcx, r), + }); + let guar = tcx.dcx().span_delayed_bug( + hidden_type.span, + "opaque type with non-universal region args", + ); + ty::ProvisionalHiddenType::new_error(tcx, guar) + } } }; @@ -570,6 +614,40 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>( errors } +/// We handle `UnexpectedHiddenRegion` error lazily in the next solver as +/// there may be a fully defining use in another body. +/// +/// In case such a defining use does not exist, we register an error here. +pub(crate) fn handle_unconstrained_hidden_type_errors<'tcx>( + tcx: TyCtxt<'tcx>, + hidden_types: &mut FxIndexMap>, + unconstrained_hidden_type_errors: &mut Vec>, + collect_region_constraints_results: &mut FxIndexMap< + LocalDefId, + CollectRegionConstraintsResult<'tcx>, + >, +) { + let mut unconstrained_hidden_type_errors = std::mem::take(unconstrained_hidden_type_errors); + unconstrained_hidden_type_errors + .retain(|unconstrained| !hidden_types.contains_key(&unconstrained.opaque_type_key.def_id)); + + unconstrained_hidden_type_errors.iter().for_each(|t| { + tcx.dcx() + .span_delayed_bug(t.hidden_type.span, "opaque type with non-universal region args"); + }); + + // `UnexpectedHiddenRegion` error contains region var which only makes sense in the + // corresponding `infcx`. + // So we need to insert the error to the body where it originates from. + for error in unconstrained_hidden_type_errors { + let (def_id, error) = error.to_error(); + let Some(result) = collect_region_constraints_results.get_mut(&def_id) else { + unreachable!("the body should depend on opaques type if it has opaque use"); + }; + result.deferred_opaque_type_errors.push(error); + } +} + /// In theory `apply_definition_site_hidden_types` could introduce new uses of opaque types. /// We do not check these new uses so this could be unsound. /// diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 4d42055df168..a082aba35b8a 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -12,8 +12,9 @@ use smallvec::SmallVec; use crate::consumers::BorrowckConsumer; use crate::nll::compute_closure_requirements_modulo_opaques; use crate::region_infer::opaque_types::{ - apply_definition_site_hidden_types, clone_and_resolve_opaque_types, + UnexpectedHiddenRegion, apply_definition_site_hidden_types, clone_and_resolve_opaque_types, compute_definition_site_hidden_types, detect_opaque_types_added_while_handling_opaque_types, + handle_unconstrained_hidden_type_errors, }; use crate::type_check::{Locations, constraint_conversion}; use crate::{ @@ -26,7 +27,12 @@ use crate::{ pub(super) struct BorrowCheckRootCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, root_def_id: LocalDefId, + /// This contains fully resolved hidden types or `ty::Error`. hidden_types: FxIndexMap>, + /// This contains unconstrained regions in hidden types. + /// Only used for deferred error reporting. See + /// [`crate::region_infer::opaque_types::handle_unconstrained_hidden_type_errors`] + unconstrained_hidden_type_errors: Vec>, /// The region constraints computed by [borrowck_collect_region_constraints]. This uses /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before /// their parents. @@ -49,6 +55,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { tcx, root_def_id, hidden_types: Default::default(), + unconstrained_hidden_type_errors: Default::default(), collect_region_constraints_results: Default::default(), propagated_borrowck_results: Default::default(), tainted_by_errors: None, @@ -84,23 +91,32 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { fn handle_opaque_type_uses(&mut self) { let mut per_body_info = Vec::new(); - for input in self.collect_region_constraints_results.values_mut() { + for (def_id, input) in &mut self.collect_region_constraints_results { let (num_entries, opaque_types) = clone_and_resolve_opaque_types( &input.infcx, &input.universal_region_relations, &mut input.constraints, ); input.deferred_opaque_type_errors = compute_definition_site_hidden_types( + *def_id, &input.infcx, &input.universal_region_relations, &input.constraints, Rc::clone(&input.location_map), &mut self.hidden_types, + &mut self.unconstrained_hidden_type_errors, &opaque_types, ); per_body_info.push((num_entries, opaque_types)); } + handle_unconstrained_hidden_type_errors( + self.tcx, + &mut self.hidden_types, + &mut self.unconstrained_hidden_type_errors, + &mut self.collect_region_constraints_results, + ); + for (input, (opaque_types_storage_num_entries, opaque_types)) in self.collect_region_constraints_results.values_mut().zip(per_body_info) { diff --git a/tests/ui/traits/next-solver/opaques/eventually-constrained-region.rs b/tests/ui/traits/next-solver/opaques/eventually-constrained-region.rs new file mode 100644 index 000000000000..997bcd6ed9b4 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/eventually-constrained-region.rs @@ -0,0 +1,21 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#264. +// +// Some defining uses of opaque types can't constrain captured regions to universals. +// Previouly, we eagerly report error in this case. +// Now we report error only if there's no fully defining use from all bodies of the typeck root. + +struct Inv<'a>(*mut &'a ()); + +fn mk_static() -> Inv<'static> { todo!() } + +fn guide_closure_sig<'a>(f: impl FnOnce() -> Inv<'a>) {} + +fn unconstrained_in_closure() -> impl Sized { + guide_closure_sig(|| unconstrained_in_closure()); + mk_static() +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/report-all-unexpected-hidden-errors.rs b/tests/ui/traits/next-solver/opaques/report-all-unexpected-hidden-errors.rs new file mode 100644 index 000000000000..61823c1e300d --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/report-all-unexpected-hidden-errors.rs @@ -0,0 +1,34 @@ +//@ compile-flags: -Znext-solver + +// Just for diagnostics completeness. +// This is probably unimportant as we only report one error for such case in HIR typeck. + +#![feature(type_alias_impl_trait)] + +struct Invar<'a>(*mut &'a ()); + +fn mk_invar<'a>(a: &'a i32) -> Invar<'a> { + todo!() +} + +type MultiUse = impl Sized; + +#[define_opaque(MultiUse)] +fn capture_different_universals_not_on_bounds<'a, 'b, 'c>(a: &'a i32, b: &'b i32, c: &'c i32) { + let _ = || -> MultiUse { + //~^ ERROR: hidden type for `MultiUse` captures lifetime that does not appear in bounds [E0700] + mk_invar(a) + }; + let _ = || -> MultiUse { + //~^ ERROR: hidden type for `MultiUse` captures lifetime that does not appear in bounds [E0700] + mk_invar(b) + }; + let _ = || { + let _ = || -> MultiUse { + //~^ ERROR: hidden type for `MultiUse` captures lifetime that does not appear in bounds [E0700] + mk_invar(c) + }; + }; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/report-all-unexpected-hidden-errors.stderr b/tests/ui/traits/next-solver/opaques/report-all-unexpected-hidden-errors.stderr new file mode 100644 index 000000000000..28d9da3d4666 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/report-all-unexpected-hidden-errors.stderr @@ -0,0 +1,36 @@ +error[E0700]: hidden type for `MultiUse` captures lifetime that does not appear in bounds + --> $DIR/report-all-unexpected-hidden-errors.rs:18:19 + | +LL | type MultiUse = impl Sized; + | ---------- opaque type defined here +... +LL | let _ = || -> MultiUse { + | ^^^^^^^^ + | + = note: hidden type `Invar<'_>` captures lifetime `'_` + +error[E0700]: hidden type for `MultiUse` captures lifetime that does not appear in bounds + --> $DIR/report-all-unexpected-hidden-errors.rs:22:19 + | +LL | type MultiUse = impl Sized; + | ---------- opaque type defined here +... +LL | let _ = || -> MultiUse { + | ^^^^^^^^ + | + = note: hidden type `Invar<'_>` captures lifetime `'_` + +error[E0700]: hidden type for `MultiUse` captures lifetime that does not appear in bounds + --> $DIR/report-all-unexpected-hidden-errors.rs:27:23 + | +LL | type MultiUse = impl Sized; + | ---------- opaque type defined here +... +LL | let _ = || -> MultiUse { + | ^^^^^^^^ + | + = note: hidden type `Invar<'_>` captures lifetime `'_` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0700`.