From 9ff75a767e1c5b0e0d666483f743e7a9a26e9b61 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 3 Aug 2026 20:04:21 +0200 Subject: [PATCH 1/6] make `DefiningTy` independent of borrowck --- .../rustc_borrowck/src/universal_regions.rs | 449 +++++++++--------- 1 file changed, 219 insertions(+), 230 deletions(-) diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 2d4d98d812c65..694f29b942e4f 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -134,6 +134,204 @@ pub(crate) enum DefiningTy<'tcx> { } impl<'tcx> DefiningTy<'tcx> { + #[instrument(level = "debug", skip(tcx), ret)] + fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { + match tcx.hir_body_owner_kind(body_def_id) { + BodyOwnerKind::Closure | BodyOwnerKind::Fn => { + let defining_ty = tcx.type_of(body_def_id).instantiate_identity().skip_norm_wip(); + match *defining_ty.kind() { + ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args), + ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args), + ty::CoroutineClosure(def_id, args) => { + DefiningTy::CoroutineClosure(def_id, args) + } + ty::FnDef(def_id, args) => { + DefiningTy::FnDef(def_id, args.no_bound_vars().unwrap()) + } + _ => span_bug!( + tcx.def_span(body_def_id), + "expected defining type for `{body_def_id:?}`: `{defining_ty:?}`", + ), + } + } + + BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { + match tcx.def_kind(body_def_id) { + DefKind::AnonConst + if tcx.anon_const_kind(body_def_id) + == ty::AnonConstKind::NonTypeSystemInline => + { + // This is required for `AscribeUserType` canonical query, which will call + // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes + // into borrowck, which is ICE #78174. + // + // As a workaround, inline consts have an additional generic param (`ty` + // below), so that `type_of(inline_const_def_id).substs(substs)` uses the + // proper type with NLL infer vars. + // + // Fetch the actual type from MIR, as `type_of` returns something useless + // like ``. + let body = tcx.mir_promoted(body_def_id).0.borrow(); + let ty = body.local_decls[RETURN_PLACE].ty; + let typeck_root_def_id = tcx.typeck_root_def_id(body_def_id.to_def_id()); + let parent_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); + let args = + InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }) + .args; + DefiningTy::InlineConst(body_def_id.to_def_id(), args) + } + _ => { + let args = GenericArgs::identity_for_item(tcx, body_def_id.to_def_id()); + DefiningTy::Const(body_def_id.to_def_id(), args) + } + } + } + + BodyOwnerKind::GlobalAsm => DefiningTy::GlobalAsm(body_def_id.to_def_id()), + } + } + + #[instrument(level = "debug", skip(tcx, c_variadic_region), ret)] + fn inputs_and_output( + self, + tcx: TyCtxt<'tcx>, + c_variadic_region: impl FnOnce() -> ty::Region<'tcx>, + ) -> ty::Binder<'tcx, &'tcx ty::List>> { + match self { + DefiningTy::Closure(def_id, args) => { + let closure_sig = args.as_closure().sig(); + let inputs_and_output = closure_sig.inputs_and_output(); + let bound_vars = tcx.mk_bound_variable_kinds_from_iter( + inputs_and_output.bound_vars().iter().chain(iter::once( + ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), + )), + ); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::ClosureEnv, + }; + let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let closure_ty = tcx.closure_env_ty( + Ty::new_closure(tcx, def_id, args), + args.as_closure().kind(), + env_region, + ); + + // The "inputs" of the closure in the + // signature appear as a tuple. The MIR side + // flattens this tuple. + let (&output, tuplized_inputs) = + inputs_and_output.skip_binder().split_last().unwrap(); + assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); + let &ty::Tuple(inputs) = tuplized_inputs[0].kind() else { + bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]); + }; + + ty::Binder::bind_with_vars( + tcx.mk_type_list_from_iter( + iter::once(closure_ty).chain(inputs).chain(iter::once(output)), + ), + bound_vars, + ) + } + + DefiningTy::Coroutine(def_id, args) => { + let resume_ty = args.as_coroutine().resume_ty(); + let output = args.as_coroutine().return_ty(); + let coroutine_ty = Ty::new_coroutine(tcx, def_id, args); + let inputs_and_output = tcx.mk_type_list(&[coroutine_ty, resume_ty, output]); + ty::Binder::dummy(inputs_and_output) + } + + // Construct the signature of the CoroutineClosure for the purposes of borrowck. + // This is pretty straightforward -- we: + // 1. first grab the `coroutine_closure_sig`, + // 2. compute the self type (`&`/`&mut`/no borrow), + // 3. flatten the tupled_input_tys, + // 4. construct the correct generator type to return with + // `CoroutineClosureSignature::to_coroutine_given_kind_and_upvars`. + // Then we wrap it all up into a list of inputs and output. + DefiningTy::CoroutineClosure(def_id, args) => { + let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); + let bound_vars = + tcx.mk_bound_variable_kinds_from_iter(closure_sig.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), + )); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::ClosureEnv, + }; + let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let closure_kind = args.as_coroutine_closure().kind(); + + let closure_ty = tcx.closure_env_ty( + Ty::new_coroutine_closure(tcx, def_id, args), + closure_kind, + env_region, + ); + + let inputs = closure_sig.skip_binder().tupled_inputs_ty.tuple_fields(); + let output = closure_sig.skip_binder().to_coroutine_given_kind_and_upvars( + tcx, + args.as_coroutine_closure().parent_args(), + tcx.coroutine_for_closure(def_id), + closure_kind, + env_region, + args.as_coroutine_closure().tupled_upvars_ty(), + args.as_coroutine_closure().coroutine_captures_by_ref_ty(), + ); + + ty::Binder::bind_with_vars( + tcx.mk_type_list_from_iter( + iter::once(closure_ty).chain(inputs).chain(iter::once(output)), + ), + bound_vars, + ) + } + + DefiningTy::FnDef(def_id, _) => { + let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); + let inputs_and_output = sig.inputs_and_output(); + + // C-variadic fns also have a `VaList` input that's not listed in the signature + // (as it's created inside the body itself, not passed in from outside). + if tcx.fn_sig(def_id).skip_binder().c_variadic() { + let va_list_did = tcx.require_lang_item(LangItem::VaList, tcx.def_span(def_id)); + + let region = c_variadic_region(); + let va_list_ty = + tcx.type_of(va_list_did).instantiate(tcx, &[region.into()]).skip_norm_wip(); + + // The signature needs to follow the order [input_tys, va_list_ty, output_ty] + return inputs_and_output.map_bound(|tys| { + let (output_ty, input_tys) = tys.split_last().unwrap(); + tcx.mk_type_list_from_iter( + input_tys.iter().copied().chain([va_list_ty, *output_ty]), + ) + }); + } + + inputs_and_output + } + + DefiningTy::Const(def_id, _) => { + // For a constant body, there are no inputs, and one + // "output" (the type of the constant). + let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip(); + ty::Binder::dummy(tcx.mk_type_list(&[ty])) + } + + DefiningTy::InlineConst(_def_id, args) => { + let ty = args.as_inline_const().ty(); + ty::Binder::dummy(tcx.mk_type_list(&[ty])) + } + + DefiningTy::GlobalAsm(def_id) => ty::Binder::dummy( + tcx.mk_type_list(&[tcx.type_of(def_id).instantiate_identity().skip_norm_wip()]), + ), + } + } + /// Returns a list of all the upvar types for this MIR. If this is /// not a closure or coroutine, there are no upvars, and hence it /// will be an empty list. The order of types in this list will @@ -581,82 +779,23 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { } } - /// Returns the "defining type" of the current MIR; - /// see `DefiningTy` for details. + /// Returns the "defining type" of the current MIR; see `DefiningTy` for details. fn defining_ty(&self) -> DefiningTy<'tcx> { - let tcx = self.infcx.tcx; - - match tcx.hir_body_owner_kind(self.mir_def) { - BodyOwnerKind::Closure | BodyOwnerKind::Fn => { - let defining_ty = tcx.type_of(self.mir_def).instantiate_identity().skip_norm_wip(); - - debug!("defining_ty (pre-replacement): {:?}", defining_ty); - - let defining_ty = self.infcx.replace_free_regions_with_nll_infer_vars( - NllRegionVariableOrigin::FreeRegion, - defining_ty, - ); - - match *defining_ty.kind() { - ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args), - ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args), - ty::CoroutineClosure(def_id, args) => { - DefiningTy::CoroutineClosure(def_id, args) - } - ty::FnDef(def_id, args) => { - DefiningTy::FnDef(def_id, args.no_bound_vars().unwrap()) - } - _ => span_bug!( - tcx.def_span(self.mir_def), - "expected defining type for `{:?}`: `{:?}`", - self.mir_def, - defining_ty - ), - } - } - - BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { - match tcx.def_kind(self.mir_def) { - DefKind::AnonConst - if tcx.anon_const_kind(self.mir_def) - == ty::AnonConstKind::NonTypeSystemInline => - { - // This is required for `AscribeUserType` canonical query, which will call - // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes - // into borrowck, which is ICE #78174. - // - // As a workaround, inline consts have an additional generic param (`ty` - // below), so that `type_of(inline_const_def_id).substs(substs)` uses the - // proper type with NLL infer vars. - // - // Fetch the actual type from MIR, as `type_of` returns something useless - // like ``. - let body = tcx.mir_promoted(self.mir_def).0.borrow(); - let ty = body.local_decls[RETURN_PLACE].ty; - let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.to_def_id()); - let parent_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); - let args = - InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }) - .args; - let args = self.infcx.replace_free_regions_with_nll_infer_vars( - NllRegionVariableOrigin::FreeRegion, - args, - ); - DefiningTy::InlineConst(self.mir_def.to_def_id(), args) - } - _ => { - let identity_args = - GenericArgs::identity_for_item(tcx, self.mir_def.to_def_id()); - let args = self.infcx.replace_free_regions_with_nll_infer_vars( - NllRegionVariableOrigin::FreeRegion, - identity_args, - ); - DefiningTy::Const(self.mir_def.to_def_id(), args) - } - } + let defining_ty = DefiningTy::new(self.infcx.tcx, self.mir_def); + let f = |args| { + let fr = NllRegionVariableOrigin::FreeRegion; + self.infcx.replace_free_regions_with_nll_infer_vars(fr, args) + }; + match defining_ty { + DefiningTy::Closure(def_id, args) => DefiningTy::Closure(def_id, f(args)), + DefiningTy::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, f(args)), + DefiningTy::CoroutineClosure(def_id, args) => { + DefiningTy::CoroutineClosure(def_id, f(args)) } - - BodyOwnerKind::GlobalAsm => DefiningTy::GlobalAsm(self.mir_def.to_def_id()), + DefiningTy::FnDef(def_id, args) => DefiningTy::FnDef(def_id, f(args)), + DefiningTy::Const(def_id, args) => DefiningTy::Const(def_id, f(args)), + DefiningTy::InlineConst(def_id, args) => DefiningTy::InlineConst(def_id, f(args)), + DefiningTy::GlobalAsm(def_id) => DefiningTy::GlobalAsm(def_id), } } @@ -694,163 +833,13 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { defining_ty: DefiningTy<'tcx>, ) -> ty::Binder<'tcx, &'tcx ty::List>> { let tcx = self.infcx.tcx; + let inputs_and_output = defining_ty.inputs_and_output(tcx, || { + self.infcx.next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || { + RegionCtxt::Free(sym::c_dash_variadic) + }) + }); - let inputs_and_output = match defining_ty { - DefiningTy::Closure(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let closure_sig = args.as_closure().sig(); - let inputs_and_output = closure_sig.inputs_and_output(); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter( - inputs_and_output.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - )), - ); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); - let closure_ty = tcx.closure_env_ty( - Ty::new_closure(tcx, def_id, args), - args.as_closure().kind(), - env_region, - ); - - // The "inputs" of the closure in the - // signature appear as a tuple. The MIR side - // flattens this tuple. - let (&output, tuplized_inputs) = - inputs_and_output.skip_binder().split_last().unwrap(); - assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); - let &ty::Tuple(inputs) = tuplized_inputs[0].kind() else { - bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]); - }; - - ty::Binder::bind_with_vars( - tcx.mk_type_list_from_iter( - iter::once(closure_ty).chain(inputs).chain(iter::once(output)), - ), - bound_vars, - ) - } - - DefiningTy::Coroutine(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let resume_ty = args.as_coroutine().resume_ty(); - let output = args.as_coroutine().return_ty(); - let coroutine_ty = Ty::new_coroutine(tcx, def_id, args); - let inputs_and_output = - self.infcx.tcx.mk_type_list(&[coroutine_ty, resume_ty, output]); - ty::Binder::dummy(inputs_and_output) - } - - // Construct the signature of the CoroutineClosure for the purposes of borrowck. - // This is pretty straightforward -- we: - // 1. first grab the `coroutine_closure_sig`, - // 2. compute the self type (`&`/`&mut`/no borrow), - // 3. flatten the tupled_input_tys, - // 4. construct the correct generator type to return with - // `CoroutineClosureSignature::to_coroutine_given_kind_and_upvars`. - // Then we wrap it all up into a list of inputs and output. - DefiningTy::CoroutineClosure(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(closure_sig.bound_vars().iter().chain( - iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), - )); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); - let closure_kind = args.as_coroutine_closure().kind(); - - let closure_ty = tcx.closure_env_ty( - Ty::new_coroutine_closure(tcx, def_id, args), - closure_kind, - env_region, - ); - - let inputs = closure_sig.skip_binder().tupled_inputs_ty.tuple_fields(); - let output = closure_sig.skip_binder().to_coroutine_given_kind_and_upvars( - tcx, - args.as_coroutine_closure().parent_args(), - tcx.coroutine_for_closure(def_id), - closure_kind, - env_region, - args.as_coroutine_closure().tupled_upvars_ty(), - args.as_coroutine_closure().coroutine_captures_by_ref_ty(), - ); - - ty::Binder::bind_with_vars( - tcx.mk_type_list_from_iter( - iter::once(closure_ty).chain(inputs).chain(iter::once(output)), - ), - bound_vars, - ) - } - - DefiningTy::FnDef(def_id, _) => { - let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); - let sig = indices.fold_to_region_vids(tcx, sig); - let inputs_and_output = sig.inputs_and_output(); - - // C-variadic fns also have a `VaList` input that's not listed in the signature - // (as it's created inside the body itself, not passed in from outside). - if self.infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() { - let va_list_did = self - .infcx - .tcx - .require_lang_item(LangItem::VaList, self.infcx.tcx.def_span(self.mir_def)); - - let reg_vid = self - .infcx - .next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || { - RegionCtxt::Free(sym::c_dash_variadic) - }) - .as_var(); - - let region = ty::Region::new_var(self.infcx.tcx, reg_vid); - let va_list_ty = self - .infcx - .tcx - .type_of(va_list_did) - .instantiate(self.infcx.tcx, &[region.into()]) - .skip_norm_wip(); - - // The signature needs to follow the order [input_tys, va_list_ty, output_ty] - return inputs_and_output.map_bound(|tys| { - let (output_ty, input_tys) = tys.split_last().unwrap(); - tcx.mk_type_list_from_iter( - input_tys.iter().copied().chain([va_list_ty, *output_ty]), - ) - }); - } - - inputs_and_output - } - - DefiningTy::Const(def_id, _) => { - // For a constant body, there are no inputs, and one - // "output" (the type of the constant). - assert_eq!(self.mir_def.to_def_id(), def_id); - let ty = tcx.type_of(self.mir_def).instantiate_identity().skip_norm_wip(); - - let ty = indices.fold_to_region_vids(tcx, ty); - ty::Binder::dummy(tcx.mk_type_list(&[ty])) - } - - DefiningTy::InlineConst(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let ty = args.as_inline_const().ty(); - ty::Binder::dummy(tcx.mk_type_list(&[ty])) - } - - DefiningTy::GlobalAsm(def_id) => ty::Binder::dummy( - tcx.mk_type_list(&[tcx.type_of(def_id).instantiate_identity().skip_norm_wip()]), - ), - }; + let inputs_and_output = indices.fold_to_region_vids(tcx, inputs_and_output); // FIXME(#129952): We probably want a more principled approach here. if let Err(e) = inputs_and_output.error_reported() { From c0bd2ec43e083de8915c6a039357909ad4e122f2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 4 Aug 2026 11:48:44 +0200 Subject: [PATCH 2/6] cleanup `DefiningTy::new` --- .../rustc_borrowck/src/universal_regions.rs | 52 ++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 694f29b942e4f..4540193f60baa 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -155,36 +155,28 @@ impl<'tcx> DefiningTy<'tcx> { } } - BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { - match tcx.def_kind(body_def_id) { - DefKind::AnonConst - if tcx.anon_const_kind(body_def_id) - == ty::AnonConstKind::NonTypeSystemInline => - { - // This is required for `AscribeUserType` canonical query, which will call - // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes - // into borrowck, which is ICE #78174. - // - // As a workaround, inline consts have an additional generic param (`ty` - // below), so that `type_of(inline_const_def_id).substs(substs)` uses the - // proper type with NLL infer vars. - // - // Fetch the actual type from MIR, as `type_of` returns something useless - // like ``. - let body = tcx.mir_promoted(body_def_id).0.borrow(); - let ty = body.local_decls[RETURN_PLACE].ty; - let typeck_root_def_id = tcx.typeck_root_def_id(body_def_id.to_def_id()); - let parent_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); - let args = - InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }) - .args; - DefiningTy::InlineConst(body_def_id.to_def_id(), args) - } - _ => { - let args = GenericArgs::identity_for_item(tcx, body_def_id.to_def_id()); - DefiningTy::Const(body_def_id.to_def_id(), args) - } - } + BodyOwnerKind::Const { inline: true } => { + // This is required for `AscribeUserType` canonical query, which will call + // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes + // into borrowck, which is ICE #78174. + // + // As a workaround, inline consts have an additional generic param (`ty` + // below), so that `type_of(inline_const_def_id).substs(substs)` uses the + // proper type with NLL infer vars. + // + // Fetch the actual type from MIR, as `type_of` returns something useless + // like ``. + let body = tcx.mir_promoted(body_def_id).0.borrow(); + let ty = body.local_decls[RETURN_PLACE].ty; + let typeck_root_def_id = tcx.typeck_root_def_id(body_def_id.to_def_id()); + let parent_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); + let args = InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }).args; + DefiningTy::InlineConst(body_def_id.to_def_id(), args) + } + + BodyOwnerKind::Const { inline: false } | BodyOwnerKind::Static(..) => { + let args = GenericArgs::identity_for_item(tcx, body_def_id.to_def_id()); + DefiningTy::Const(body_def_id.to_def_id(), args) } BodyOwnerKind::GlobalAsm => DefiningTy::GlobalAsm(body_def_id.to_def_id()), From cb29ba54a36159b9b9d8bddb8ecde3eeaef220c3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 4 Aug 2026 11:09:44 +0200 Subject: [PATCH 3/6] make the c_variadic region late bound --- .../rustc_borrowck/src/universal_regions.rs | 94 +++++++++++++------ tests/ui/c-variadic/not-async.stderr | 18 ++-- tests/ui/c-variadic/variadic-ffi-4.stderr | 8 +- .../note-and-explain-ReVar-124973.stderr | 9 +- 4 files changed, 79 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 4540193f60baa..fff59d4e07a78 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -26,8 +26,7 @@ use rustc_macros::extension; use rustc_middle::mir::RETURN_PLACE; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionExt, RegionVid, - Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, + self, BoundVariableKind, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, kw, sym}; @@ -183,21 +182,52 @@ impl<'tcx> DefiningTy<'tcx> { } } - #[instrument(level = "debug", skip(tcx, c_variadic_region), ret)] - fn inputs_and_output( - self, - tcx: TyCtxt<'tcx>, - c_variadic_region: impl FnOnce() -> ty::Region<'tcx>, - ) -> ty::Binder<'tcx, &'tcx ty::List>> { + /// The bound variables for a given defining type. This differs from their usual bound vars + /// in that closures and coroutine closures have an additional `'env`, while C-variadic + /// functions have an additional region for their implicit `VaList` input. + pub(crate) fn bound_vars(self, tcx: TyCtxt<'tcx>) -> &'tcx List> { + match self { + DefiningTy::Closure(_, args) => { + let closure_sig = args.as_closure().sig(); + let inputs_and_output = closure_sig.inputs_and_output(); + tcx.mk_bound_variable_kinds_from_iter(inputs_and_output.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), + )) + } + + DefiningTy::CoroutineClosure(_, args) => { + let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); + tcx.mk_bound_variable_kinds_from_iter(closure_sig.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), + )) + } + + DefiningTy::FnDef(def_id, _) => { + let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); + if sig.skip_binder().c_variadic() { + // FIXME(#160495): Don't use an anonymous region here + tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)), + )) + } else { + sig.bound_vars() + } + } + + DefiningTy::Coroutine(..) + | DefiningTy::Const(..) + | DefiningTy::InlineConst(..) + | DefiningTy::GlobalAsm(..) => ty::List::empty(), + } + } + + #[instrument(level = "debug", skip(tcx), ret)] + fn inputs_and_output(self, tcx: TyCtxt<'tcx>) -> ty::Binder<'tcx, &'tcx ty::List>> { match self { DefiningTy::Closure(def_id, args) => { let closure_sig = args.as_closure().sig(); let inputs_and_output = closure_sig.inputs_and_output(); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter( - inputs_and_output.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - )), - ); + let bound_vars = self.bound_vars(tcx); let br = ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind: ty::BoundRegionKind::ClosureEnv, @@ -245,10 +275,7 @@ impl<'tcx> DefiningTy<'tcx> { // Then we wrap it all up into a list of inputs and output. DefiningTy::CoroutineClosure(def_id, args) => { let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(closure_sig.bound_vars().iter().chain( - iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), - )); + let bound_vars = self.bound_vars(tcx); let br = ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind: ty::BoundRegionKind::ClosureEnv, @@ -290,17 +317,24 @@ impl<'tcx> DefiningTy<'tcx> { if tcx.fn_sig(def_id).skip_binder().c_variadic() { let va_list_did = tcx.require_lang_item(LangItem::VaList, tcx.def_span(def_id)); - let region = c_variadic_region(); + let bound_vars = self.bound_vars(tcx); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::Anon, + }; + let region = ty::Region::new_bound(tcx, ty::INNERMOST, br); let va_list_ty = tcx.type_of(va_list_did).instantiate(tcx, &[region.into()]).skip_norm_wip(); // The signature needs to follow the order [input_tys, va_list_ty, output_ty] - return inputs_and_output.map_bound(|tys| { - let (output_ty, input_tys) = tys.split_last().unwrap(); + let (output_ty, input_tys) = + inputs_and_output.skip_binder().split_last().unwrap(); + return ty::Binder::bind_with_vars( tcx.mk_type_list_from_iter( input_tys.iter().copied().chain([va_list_ty, *output_ty]), - ) - }); + ), + bound_vars, + ); } inputs_and_output @@ -678,7 +712,9 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { } else { // If this is a closure, coroutine, or inline-const, then the late-bound regions from the enclosing // function/closures are actually external regions to us. For example, here, 'a is not local - // to the closure c (although it is local to the fn foo): + // to the closure c (although it is local to the fn foo). We need to add them as they could be + // explicitly named in this body: + // // fn foo<'a>() { // let c = || { let x: &'a u32 = ...; } // } @@ -708,8 +744,9 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { // on its signature are local. // // We manually loop over `bound_inputs_and_output` instead of using - // `for_each_late_bound_region_in_item` as we may need to add the otherwise - // implicit `ClosureEnv` region. + // `for_each_late_bound_region_in_item` as both closures and function + // definitions have implicit late bound regions. Closures have a `'env` + // regions while c-variadic function definitions have a `&VaList` argument. let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); for (idx, bound_var) in bound_inputs_and_output.bound_vars().iter().enumerate() { if let ty::BoundVariableKind::Region(kind) = bound_var { @@ -825,12 +862,7 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { defining_ty: DefiningTy<'tcx>, ) -> ty::Binder<'tcx, &'tcx ty::List>> { let tcx = self.infcx.tcx; - let inputs_and_output = defining_ty.inputs_and_output(tcx, || { - self.infcx.next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || { - RegionCtxt::Free(sym::c_dash_variadic) - }) - }); - + let inputs_and_output = defining_ty.inputs_and_output(tcx); let inputs_and_output = indices.fold_to_region_vids(tcx, inputs_and_output); // FIXME(#129952): We probably want a more principled approach here. diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr index 921210382236c..9a81e0ce270d6 100644 --- a/tests/ui/c-variadic/not-async.stderr +++ b/tests/ui/c-variadic/not-async.stderr @@ -14,21 +14,19 @@ error[E0700]: hidden type for `impl Future` captures lifetime that --> $DIR/not-async.rs:4:65 | LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, _: ...) {} - | -^^ - | | - | opaque type defined here - | - = note: hidden type `{async fn body of fn_cannot_be_async()}` captures lifetime `'_` + | ----------------------------------------------------------------^^ + | | | + | | opaque type defined here + | hidden type `{async fn body of fn_cannot_be_async()}` captures the anonymous lifetime as defined here error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds --> $DIR/not-async.rs:11:73 | LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, _: ...) {} - | -^^ - | | - | opaque type defined here - | - = note: hidden type `{async fn body of S::method_cannot_be_async()}` captures lifetime `'_` + | --------------------------------------------------------------------^^ + | | | + | | opaque type defined here + | hidden type `{async fn body of S::method_cannot_be_async()}` captures the anonymous lifetime as defined here error: aborting due to 4 previous errors diff --git a/tests/ui/c-variadic/variadic-ffi-4.stderr b/tests/ui/c-variadic/variadic-ffi-4.stderr index d53f1f527748c..a92a5fd4bf61d 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.stderr +++ b/tests/ui/c-variadic/variadic-ffi-4.stderr @@ -30,9 +30,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:21:5 | LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { - | ------- ------- has type `VaList<'1>` + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaList<'2>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` | @@ -44,9 +44,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:21:5 | LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { - | ------- ------- has type `VaList<'1>` + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaList<'2>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` | diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr index 3610fa82754b9..3ba76eb2ece18 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.stderr +++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr @@ -8,11 +8,10 @@ error[E0700]: hidden type for `impl Future` captures lifetime that --> $DIR/note-and-explain-ReVar-124973.rs:3:76 | LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, _: ...) {} - | -^^ - | | - | opaque type defined here - | - = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_` + | ---------------------------------------------------------------------------^^ + | | | + | | opaque type defined here + | hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures the anonymous lifetime as defined here error: aborting due to 2 previous errors From c52f9d86a45efbcd5385d7e3c810fd66a73d7c5b Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 4 Aug 2026 11:42:01 +0200 Subject: [PATCH 4/6] move implied bounds computation out of borrowck --- compiler/rustc_borrowck/src/implied_bounds.rs | 186 ++++++++++++++++++ compiler/rustc_borrowck/src/lib.rs | 4 +- .../src/type_check/free_region_relations.rs | 181 ++++++++--------- .../rustc_borrowck/src/universal_regions.rs | 16 +- .../src/infer/outlives/obligations.rs | 11 ++ compiler/rustc_middle/src/arena.rs | 6 + compiler/rustc_middle/src/queries.rs | 13 +- compiler/rustc_middle/src/traits/query.rs | 10 + .../query/type_op/implied_outlives_bounds.rs | 124 +++++------- .../src/implied_outlives_bounds.rs | 4 +- .../associated-inherent-types/issue-109789.rs | 1 - .../issue-109789.stderr | 10 +- .../issue-111404-1.rs | 1 - .../issue-111404-1.stderr | 10 +- .../bound-var-in-ty-not-wf.rs | 1 - .../bound-var-in-ty-not-wf.stderr | 10 +- ...rr => wf-check-hidden-type.current.stderr} | 3 +- .../wf-check-hidden-type.next.stderr | 14 ++ tests/ui/impl-trait/wf-check-hidden-type.rs | 11 +- ...-preserve-equality.borrowck_current.stderr | 28 --- .../normalization-preserve-equality.rs | 12 +- ...iguity-due-to-uniquification-4.next.stderr | 9 - .../ambiguity-due-to-uniquification-4.rs | 12 +- ..._outlives_bounds_not_resolving_vars_ice.rs | 1 - ...lives_bounds_not_resolving_vars_ice.stderr | 8 +- ...ied-bounds-leak-hidden-ty-2.current.stderr | 17 ++ ...mplied-bounds-leak-hidden-ty-2.next.stderr | 31 +++ .../implied-bounds-leak-hidden-ty-2.rs | 28 +++ ...ied-bounds-leak-hidden-ty-3.current.stderr | 75 +++++++ ...mplied-bounds-leak-hidden-ty-3.next.stderr | 75 +++++++ .../implied-bounds-leak-hidden-ty-3.rs | 44 +++++ .../implied-bounds-leak-hidden-ty-pass.rs | 38 ++++ ...ounds-leak-hidden-ty-rpitit.current.stderr | 117 +++++++++++ ...d-bounds-leak-hidden-ty-rpitit.next.stderr | 117 +++++++++++ .../implied-bounds-leak-hidden-ty-rpitit.rs | 49 +++++ ...plied-bounds-leak-hidden-ty.current.stderr | 60 ++++++ .../implied-bounds-leak-hidden-ty.next.stderr | 60 ++++++ .../opaques/implied-bounds-leak-hidden-ty.rs | 25 +++ 38 files changed, 1149 insertions(+), 273 deletions(-) create mode 100644 compiler/rustc_borrowck/src/implied_bounds.rs rename tests/ui/impl-trait/{wf-check-hidden-type.stderr => wf-check-hidden-type.current.stderr} (91%) create mode 100644 tests/ui/impl-trait/wf-check-hidden-type.next.stderr delete mode 100644 tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr delete mode 100644 tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.current.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.next.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.rs create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.current.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.next.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.rs create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-pass.rs create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.current.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.next.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.rs create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.current.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.next.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.rs diff --git a/compiler/rustc_borrowck/src/implied_bounds.rs b/compiler/rustc_borrowck/src/implied_bounds.rs new file mode 100644 index 0000000000000..54eed7fb761a4 --- /dev/null +++ b/compiler/rustc_borrowck/src/implied_bounds.rs @@ -0,0 +1,186 @@ +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::query::MirBorrowckImpliedOutlivesBounds; +use rustc_middle::infer::canonical::{Canonical, QueryResponse}; +use rustc_middle::ty::{ + self, CanonicalVarValues, GenericArg, Ty, TyCtxt, TypeVisitableExt, TypingEnv, fold_regions, +}; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::solve::NoSolution; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ + compute_implied_outlives_bounds_inner, consider_implied_bounds_hack_for_ty, +}; +use smallvec::SmallVec; +use tracing::instrument; + +use crate::universal_regions::DefiningTy; + +/// Computes the implied bounds for `body_def_id`. This is a separate query +/// as it must not reveal the hidden type of opaques defined by `body_def_id`. +pub(super) fn mir_borrowck_implied_outlives_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + body_def_id: LocalDefId, +) -> Result< + &'tcx Canonical<'tcx, QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx>>>, + NoSolution, +> { + // We do not want to reveal the hidden types of any opaque types in this function. + let typing_env = TypingEnv::non_body_analysis(tcx, body_def_id); + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + let ocx = ObligationCtxt::new(&infcx); + + let defining_ty = DefiningTy::new(tcx, body_def_id); + + let inputs_and_output = defining_ty.inputs_and_output(tcx); + let inputs_and_output = + tcx.liberate_late_bound_regions(body_def_id.to_def_id(), inputs_and_output); + let inputs_and_output = replace_erased_regions_with_placeholders(tcx, inputs_and_output); + + let mut outlives_bounds = vec![]; + // Need to return the normalized signature used to compute implied bounds back to borrowck + // to deal with unconstrained regions due to #136547. + let mut normalized_inputs_and_output = Vec::with_capacity(inputs_and_output.len()); + for &ty in &inputs_and_output { + let num_registered_region_obligations = infcx.num_registered_region_obligations(); + let normalized_ty = ocx + .deeply_normalize(&ObligationCause::dummy(), param_env, ty::Unnormalized::new_wip(ty)) + .map_err(|_| NoSolution)?; + + outlives_bounds.extend(compute_implied_outlives_bounds_inner( + &ocx, + param_env, + ty, + normalized_ty, + DUMMY_SP, + )?); + + outlives_bounds.extend(consider_implied_bounds_hack_for_ty(&ocx, normalized_ty, || { + infcx.registered_region_obligations_since(num_registered_region_obligations) + })); + + normalized_inputs_and_output.push(normalized_ty); + } + + // Add implied bounds from impl header. + // + // We don't use `assumed_wf_types` to source the entire set of implied bounds for + // a few reasons: + // - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't + // - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not + // do so for types in impl headers + // - We must compute the normalized signature and then compute implied bounds from that + // in order to connect any unconstrained region vars created during normalization to + // the types of the locals corresponding to the inputs and outputs of the item. #136547 + if matches!(tcx.def_kind(body_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) { + for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(body_def_id)) { + let normalized_ty = ocx + .deeply_normalize( + &ObligationCause::dummy(), + param_env, + ty::Unnormalized::new_wip(ty), + ) + .map_err(|_| NoSolution)?; + + // We don't consider the constraints from normalizing the impl header + // for the bevy implied bounds hack. + let num_registered_region_obligations = infcx.num_registered_region_obligations(); + outlives_bounds.extend(compute_implied_outlives_bounds_inner( + &ocx, + param_env, + normalized_ty, + normalized_ty, + DUMMY_SP, + )?); + + outlives_bounds.extend(consider_implied_bounds_hack_for_ty( + &ocx, + normalized_ty, + || infcx.registered_region_obligations_since(num_registered_region_obligations), + )); + } + } + + let var_values = implied_bounds_query_var_values(tcx, &inputs_and_output, |r| match r.kind() { + ty::RePlaceholder(_) => true, + ty::ReEarlyParam(_) + | ty::ReLateParam(_) + | ty::ReBound(..) + | ty::ReStatic + | ty::ReError(_) => false, + ty::ReVar(..) | ty::ReErased => unreachable!(), + }); + let input_values = CanonicalVarValues { var_values: tcx.mk_args(&var_values) }; + + ocx.make_canonicalized_query_response( + input_values, + MirBorrowckImpliedOutlivesBounds { outlives_bounds, normalized_inputs_and_output }, + ) +} + +/// This computes the `var_values` used by the `mir_borrowck_implied_outlives_bounds` query. +/// The old solver canonicalization does not replace early and late bound parameters, +/// so the only `var_values` we need are external regions as we don't have a shared unified +/// representation between this query and MIR borrowck. +/// +/// We never late bound regions from a parent while computing implied bounds for the current item. +/// Any free region in the signature of nested body gets replaced with `'erased` at the end of HIR typeck, +/// so even if a late bound region of a parent is mentioned in our signature, it will have been erased +/// and will get represented as an external region instead. +#[instrument(level = "debug", skip(tcx, is_external_region), ret)] +pub(crate) fn implied_bounds_query_var_values<'tcx>( + tcx: TyCtxt<'tcx>, + unnormalized_inputs_and_output: &[Ty<'tcx>], + mut is_external_region: impl FnMut(ty::Region<'tcx>) -> bool, +) -> SmallVec<[GenericArg<'tcx>; 8]> { + let mut values: SmallVec<[GenericArg<'tcx>; 8]> = Default::default(); + + for ty in unnormalized_inputs_and_output { + tcx.for_each_free_region(ty, |region| { + if is_external_region(region) { + values.push(region.into()); + } + }); + } + + values +} + +/// This replaces all external regions in the signature of the current item with +/// a unique placeholder to collect its implied bounds. This mirrors the way MIR +/// borrowck replaces all of them with unique NLL vars. +fn replace_erased_regions_with_placeholders<'tcx>( + tcx: TyCtxt<'tcx>, + inputs_and_output: &[Ty<'tcx>], +) -> Vec> { + debug_assert!(!inputs_and_output.has_placeholders()); + let mut next_placeholder = 0; + inputs_and_output + .iter() + .map(|&ty| { + fold_regions(tcx, ty, |r, _| match r.kind() { + ty::ReErased => { + let var = ty::BoundVar::from_usize(next_placeholder); + next_placeholder += 1; + ty::Region::new_placeholder( + tcx, + ty::PlaceholderRegion::new( + ty::UniverseIndex::ROOT, + ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }, + ), + ) + } + ty::ReEarlyParam(_) + | ty::ReLateParam(_) + | ty::ReBound(..) + | ty::ReStatic + | ty::ReError(_) => r, + ty::ReVar(..) | ty::RePlaceholder(..) => { + panic!("unexpected region: {r:?}") + } + }) + }) + .collect() +} diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 96c99e68f95e8..281327b9eba66 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -59,6 +59,7 @@ use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows}; use crate::diagnostics::{ AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName, }; +use crate::implied_bounds::mir_borrowck_implied_outlives_bounds; use crate::path_utils::*; use crate::place_ext::PlaceExt; use crate::places_conflict::{PlaceConflictBias, places_conflict}; @@ -81,6 +82,7 @@ mod dataflow; mod def_use; mod diagnostics; mod handle_placeholders; +mod implied_bounds; mod nll; mod path_utils; mod place_ext; @@ -106,7 +108,7 @@ impl<'tcx> TyCtxtConsts<'tcx> { } pub fn provide(providers: &mut Providers) { - *providers = Providers { mir_borrowck, ..*providers }; + *providers = Providers { mir_borrowck, mir_borrowck_implied_outlives_bounds, ..*providers }; } /// Provider for `query mir_borrowck`. Unlike `typeck`, this must diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index a89d45c4b0b2a..5a773a58e4395 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,20 +1,23 @@ 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::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; +use rustc_infer::infer::{InferOk, outlives}; +use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::query::MirBorrowckImpliedOutlivesBounds; use rustc_infer::traits::query::type_op::Normalize; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; +use rustc_trait_selection::solve::NoSolution; use rustc_trait_selection::traits::query::type_op; use tracing::{debug, instrument}; use type_op::TypeOpOutput; use crate::BorrowckInferCtxt; +use crate::implied_bounds::implied_bounds_query_var_values; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; use crate::universal_regions::UniversalRegions; @@ -181,8 +184,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn create(mut self) -> CreateResult<'tcx> { let tcx = self.infcx.tcx; - let defining_ty_def_id = self.universal_regions.defining_ty.def_id().expect_local(); - let span = tcx.def_span(defining_ty_def_id); + let body_def_id = self.universal_regions.defining_ty.def_id().expect_local(); + let span = tcx.def_span(body_def_id); // Insert the `'a: 'b` we know from the predicates. // This does not consider the type-outlives. @@ -216,35 +219,84 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { }; } - let unnormalized_input_output_tys = self + let unnormalized_input_output_tys: Vec<_> = self .universal_regions .unnormalized_input_tys .iter() .cloned() - .chain(Some(self.universal_regions.unnormalized_output_ty)); - - // For each of the input/output types: - // - Normalize the type. This will create some region - // constraints, which we buffer up because we are - // not ready to process them yet. - // - Then compute the implied bounds. This will adjust - // the `region_bound_pairs` and so forth. - // - After this is done, we'll register the constraints in - // the `BorrowckInferCtxt`. Checking these constraints is - // handled later by actual borrow checking. + .chain(Some(self.universal_regions.unnormalized_output_ty)) + .collect(); + + // Compute the implied bounds of the current function based on its signature. + // + // We need to make sure that all implied bounds are checked by the user of this + // item. For this, we compute the implied bounds in a different `TypingEnv`. + // + // This also returns the function signature as normalized in that separate environment + // which we then renormalize. This is necessary to correctly handle implied bounds + // involving unconstrained regions due to #136547. + let var_values = + implied_bounds_query_var_values(tcx, &unnormalized_input_output_tys, |region| { + self.universal_regions.is_external_free_region(region.as_var()) + }); + let original_query_values = OriginalQueryValues { var_values, ..Default::default() }; + let mut query_constraints = QueryRegionConstraints::default(); + let MirBorrowckImpliedOutlivesBounds { + outlives_bounds, + normalized_inputs_and_output: query_normalized_inputs_and_output, + } = match tcx.mir_borrowck_implied_outlives_bounds(body_def_id) { + Ok(canonical_result) => { + // `instantiate_nll_query_response_and_region_obligations` should never fail + // here as all our `var_values` are unique generic parameters. + let InferOk { value, obligations } = self + .infcx + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy_with_span(span), + param_env, + &original_query_values, + canonical_result, + &mut query_constraints, + ) + .unwrap(); + assert!(obligations.is_empty()); + if !query_constraints.is_empty() { + constraints.push(&query_constraints); + }; + value + } + Err(NoSolution) => { + self.infcx.dcx().span_delayed_bug( + span, + format!("error computing implied bounds {body_def_id:?}"), + ); + MirBorrowckImpliedOutlivesBounds { + outlives_bounds: Vec::new(), + normalized_inputs_and_output: unnormalized_input_output_tys, + } + } + }; + + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + let bounds = outlives_bounds.into_iter().filter(|bound| !bound.has_placeholders()); + self.add_outlives_bounds(bounds); + + // We need to renormalize the signature returned by the implied bounds query. This + // query normalizes the signature in a context which does not define any opaque types + // while this current function does actually reveal opaque types. + // + // We will later equate this signature with the type of the arguments and return local + // of this MIR body, so we need to normalize again. + // + // This does assume that `unnormalized_input_output_tys` would normalize to the same + // thing as `query_normalized_inputs_and_output`. let mut normalized_inputs_and_output = Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1); - for ty in unnormalized_input_output_tys { - debug!("build: input_or_output={:?}", ty); - // We add implied bounds from both the unnormalized and normalized ty. - // See issue #87748 - let constraints_unnorm = self.add_implied_bounds(ty, span); - if let Some(c) = constraints_unnorm { - constraints.push(c) - } + for ty in query_normalized_inputs_and_output { + let ty = ty::set_aliases_to_non_rigid(tcx, ty); let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self .infcx - .fully_perform(Normalize { value: ty::Unnormalized::new_wip(ty) }, span) + .fully_perform(Normalize { value: ty }, span) .unwrap_or_else(|guar| TypeOpOutput { output: Ty::new_error(self.infcx.tcx, guar), constraints: None, @@ -254,66 +306,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { constraints.push(c) } - // Currently `implied_outlives_bounds` will normalize the provided - // `Ty`, despite this it's still important to normalize the ty ourselves - // as normalization may introduce new region variables (#136547). - // - // If we do not add implied bounds for the type involving these new - // region variables then we'll wind up with the normalized form of - // the signature having not-wf types due to unsatisfied region - // constraints. - // - // Note: we need this in examples like - // ``` - // trait Foo { - // type Bar; - // fn foo(&self) -> &Self::Bar; - // } - // impl Foo for () { - // type Bar = (); - // fn foo(&self) -> &() {} - // } - // ``` - // Both &Self::Bar and &() are WF - if ty != norm_ty { - let constraints_norm = self.add_implied_bounds(norm_ty, span); - if let Some(c) = constraints_norm { - constraints.push(c) - } - } - normalized_inputs_and_output.push(norm_ty); } - // Add implied bounds from impl header. - // - // We don't use `assumed_wf_types` to source the entire set of implied bounds for - // a few reasons: - // - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't - // - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not - // do so for types in impl headers - // - We must compute the normalized signature and then compute implied bounds from that - // in order to connect any unconstrained region vars created during normalization to - // the types of the locals corresponding to the inputs and outputs of the item. (#136547) - 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> = self - .infcx - .fully_perform(Normalize { value: ty::Unnormalized::new_wip(ty) }, span); - let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { - continue; - }; - - constraints.extend(c); - - // We currently add implied bounds from the normalized ty only. - // This is more conservative and matches wfcheck behavior. - let c = self.add_implied_bounds(norm_ty, span); - constraints.extend(c); - } - } - for c in constraints { constraint_conversion::ConstraintConversion::new( self.infcx, @@ -373,26 +368,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { known_type_outlives_obligations.push(outlives); } - /// Compute and add any implied bounds that come from a given type. - #[instrument(level = "debug", skip(self))] - fn add_implied_bounds( - &mut self, - ty: Ty<'tcx>, - span: Span, - ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { - let TypeOpOutput { output: bounds, constraints, .. } = self - .infcx - .fully_perform(type_op::ImpliedOutlivesBounds { ty }, span) - .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) - .ok()?; - debug!(?bounds, ?constraints); - // Because of #109628, we may have unexpected placeholders. Ignore them! - // FIXME(#109628): panic in this case once the issue is fixed. - let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); - self.add_outlives_bounds(bounds); - constraints - } - /// Registers the `OutlivesBound` items from `outlives_bounds` in /// the outlives relation as well as the region-bound pairs /// listing. diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index fff59d4e07a78..9349560e4b62b 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -26,7 +26,8 @@ use rustc_macros::extension; use rustc_middle::mir::RETURN_PLACE; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, BoundVariableKind, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, + self, BoundVariableKind, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, + List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, kw, sym}; @@ -134,7 +135,7 @@ pub(crate) enum DefiningTy<'tcx> { impl<'tcx> DefiningTy<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] - fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { + pub(crate) fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { match tcx.hir_body_owner_kind(body_def_id) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { let defining_ty = tcx.type_of(body_def_id).instantiate_identity().skip_norm_wip(); @@ -222,7 +223,10 @@ impl<'tcx> DefiningTy<'tcx> { } #[instrument(level = "debug", skip(tcx), ret)] - fn inputs_and_output(self, tcx: TyCtxt<'tcx>) -> ty::Binder<'tcx, &'tcx ty::List>> { + pub(crate) fn inputs_and_output( + self, + tcx: TyCtxt<'tcx>, + ) -> ty::Binder<'tcx, &'tcx ty::List>> { match self { DefiningTy::Closure(def_id, args) => { let closure_sig = args.as_closure().sig(); @@ -565,6 +569,10 @@ impl<'tcx> UniversalRegions<'tcx> { self.region_classification(r) == Some(RegionClassification::Local) } + pub(crate) fn is_external_free_region(&self, r: RegionVid) -> bool { + self.region_classification(r) == Some(RegionClassification::External) + } + /// Returns the number of universal regions created in any category. pub(crate) fn len(&self) -> usize { self.num_universals @@ -579,7 +587,7 @@ impl<'tcx> UniversalRegions<'tcx> { self.first_local_index } - /// Gets an iterator over all the early-bound regions that have names. + /// Gets an iterator over all early bound regions starting with `'static`. pub(crate) fn named_universal_regions_iter( &self, ) -> impl Iterator, ty::RegionVid)> { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 058aaa017cad4..8b14ae4a6a485 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -186,6 +186,17 @@ impl<'tcx> InferCtxt<'tcx> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } + pub fn num_registered_region_obligations(&self) -> usize { + self.inner.borrow().region_obligations.len() + } + + pub fn registered_region_obligations_since( + &self, + prev: usize, + ) -> Vec> { + self.inner.borrow().region_obligations.iter().skip(prev).cloned().collect() + } + pub fn clone_registered_region_obligations(&self) -> Vec> { self.inner.borrow().region_obligations.clone() } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index ef943d70c3ecf..5995c048d8b92 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -70,6 +70,12 @@ rustc_arena::declare_arena! { Vec> > >, + mir_borrowck_implied_outlives_bounds: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::MirBorrowckImpliedOutlivesBounds<'tcx> + > + >, dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>, candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index f1c432779f217..ca6555077940b 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -103,8 +103,8 @@ use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, - DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, - OutlivesBound, + DropckOutlivesResult, MethodAutoderefStepsResult, MirBorrowckImpliedOutlivesBounds, NoSolution, + NormalizationResult, OutlivesBound, }; use crate::traits::{ CodegenObligationError, DynCompatibilityViolation, EvaluationResult, ImplSource, @@ -2532,6 +2532,15 @@ rustc_queries! { desc { "computing implied outlives bounds for `{}` (hack disabled = {:?})", key.0.canonical.value.value.ty, key.1 } } + query mir_borrowck_implied_outlives_bounds( + mir_def: LocalDefId + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx> >>, + NoSolution, + > { + desc { "computing implied outlives bounds for borrowck for `{}`", tcx.def_path_str(mir_def) } + } + /// Do not call this query directly: /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. query dropck_outlives( diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 92aa6fe47a7e4..217f84fbd8c0d 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -91,6 +91,16 @@ pub type CanonicalImpliedOutlivesBoundsGoal<'tcx> = pub type CanonicalDropckOutlivesGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::DropckOutlives<'tcx>>>; +/// The implied bounds and normalized MIR signature used by borrowck. +#[derive(Clone, Debug, StableHash, TypeFoldable, TypeVisitable)] +pub struct MirBorrowckImpliedOutlivesBounds<'tcx> { + pub outlives_bounds: Vec>, + + /// The normalized function signature. We need to return this from implied + /// bounds computation to deal with #136547. + pub normalized_inputs_and_output: Vec>, +} + #[derive(Clone, Debug, Default, StableHash, TypeFoldable, TypeVisitable)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec>, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index a126cc1f09b2e..55a0cf77431fe 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,13 +1,10 @@ use std::ops::ControlFlow; use rustc_infer::infer::TypeOutlivesConstraint; -use rustc_infer::infer::canonical::CanonicalQueryInput; use rustc_infer::traits::query::OutlivesBound; -use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; -use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::outlives::{Component, push_outlives_components}; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitable, TypeVisitor, Unnormalized}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeVisitor, Unnormalized}; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::{DUMMY_SP, Span, sym}; use smallvec::{SmallVec, smallvec}; @@ -15,73 +12,14 @@ use smallvec::{SmallVec, smallvec}; use crate::traits::query::NoSolution; use crate::traits::{ObligationCtxt, wf}; -impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { - type QueryResponse = Vec>; - - fn try_fast_path( - _tcx: TyCtxt<'tcx>, - key: &ParamEnvAnd<'tcx, Self>, - ) -> Option { - // Don't go into the query for things that can't possibly have lifetimes. - match key.value.ty.kind() { - ty::Tuple(elems) if elems.is_empty() => Some(vec![]), - ty::Never | ty::Str | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { - Some(vec![]) - } - _ => None, - } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Result, NoSolution> { - tcx.implied_outlives_bounds((canonicalized, false)) - } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - span: Span, - ) -> Result { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span, false) - } -} - pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + normalized_ty: Ty<'tcx>, span: Span, - disable_implied_bounds_hack: bool, ) -> Result>, NoSolution> { - // Inside mir borrowck, each computation starts with an empty list. - assert!( - ocx.infcx.inner.borrow().region_obligations().is_empty(), - "compute_implied_outlives_bounds assumes region obligations are empty before starting" - ); - let tcx = ocx.infcx.tcx; - - // FIXME: This doesn't seem right. All call sites already normalize `ty`: - // - `Ty`s from the `DefiningTy` in Borrowck: we have to normalize in the caller - // in order to get implied bounds involving any unconstrained region vars - // created as part of normalizing the sig. See #136547 - // - `Ty`s from impl headers in Borrowck and in Non-Borrowck contexts: we have - // to normalize in the caller as computing implied bounds from unnormalized - // types would be unsound. See #100989 - // - // We must normalize the type so we can compute the right outlives components. - // for example, if we have some constrained param type like `T: Trait`, - // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. - let normalized_ty = ocx - .deeply_normalize( - &ObligationCause::dummy_with_span(span), - param_env, - Unnormalized::new_wip(ty), - ) - .map_err(|_| NoSolution)?; - // Sometimes when we ask what it takes for T: WF, we get back that // U: WF is required; in that case, we push U onto this stack and // process it next. Because the resulting predicates aren't always @@ -151,20 +89,60 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( } } - // If we detect `bevy_ecs::*::ParamSet` in the WF args list (and `disable_implied_bounds_hack` - // or `-Zno-implied-bounds-compat` are not set), then use the registered outlives obligations - // as implied bounds. - if !disable_implied_bounds_hack - && !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat - && ty.visit_with(&mut ContainsBevyParamSet { tcx: ocx.infcx.tcx }).is_break() + Ok(outlives_bounds) +} + +/// If we're at a callsite which should apply the bevy implied bounds hack and +/// `-Zno-implied-bounds-compat` has not been set, then use the registered outlives +/// obligations as implied bounds if we detect `bevy_ecs::*::ParamSet` in the arg. +/// +/// cc #119956 +pub fn consider_implied_bounds_hack_for_ty<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + ty: Ty<'tcx>, + region_constraints: impl FnOnce() -> Vec>, +) -> Vec> { + let tcx = ocx.infcx.tcx; + if !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat + && ty.visit_with(&mut ContainsBevyParamSet { tcx }).is_break() { - for TypeOutlivesConstraint { sup_type, sub_region, .. } in - ocx.infcx.clone_registered_region_obligations() - { + let mut outlives_bounds = vec![]; + for TypeOutlivesConstraint { sup_type, sub_region, .. } in region_constraints() { let mut components = smallvec![]; push_outlives_components(tcx, sup_type, &mut components); outlives_bounds.extend(implied_bounds_from_components(tcx, sub_region, components)); } + outlives_bounds + } else { + vec![] + } +} + +pub fn query_compute_implied_outlives_bounds<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, + disable_implied_bounds_hack: bool, +) -> Result>, NoSolution> { + // FIXME: This doesn't seem right. All call sites already normalize `ty`. + // We have to normalize in the caller as computing implied bounds from unnormalized + // types would be unsound. See #100989 + // + // We must normalize the type so we can compute the right outlives components. + // for example, if we have some constrained param type like `T: Trait`, + // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. + let normalized_ty = ocx + .deeply_normalize(&ObligationCause::dummy(), param_env, Unnormalized::new_wip(ty)) + .map_err(|_| NoSolution)?; + + let mut outlives_bounds = + compute_implied_outlives_bounds_inner(ocx, param_env, ty, normalized_ty, span)?; + + if !disable_implied_bounds_hack { + outlives_bounds.extend(consider_implied_bounds_hack_for_ty(ocx, ty, || { + ocx.infcx.clone_registered_region_obligations() + })); } Ok(outlives_bounds) diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 0e953e6b070da..ca6cb54b123ef 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -10,7 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; +use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::query_compute_implied_outlives_bounds; use rustc_trait_selection::traits::query::{CanonicalImpliedOutlivesBoundsGoal, NoSolution}; pub(crate) fn provide(p: &mut Providers) { @@ -26,7 +26,7 @@ fn implied_outlives_bounds<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let ParamEnvAnd { param_env, value: ImpliedOutlivesBounds { ty } } = key; - compute_implied_outlives_bounds_inner( + query_compute_implied_outlives_bounds( ocx, param_env, ty, diff --git a/tests/ui/associated-inherent-types/issue-109789.rs b/tests/ui/associated-inherent-types/issue-109789.rs index e3c490b2dc842..46dd4590141d0 100644 --- a/tests/ui/associated-inherent-types/issue-109789.rs +++ b/tests/ui/associated-inherent-types/issue-109789.rs @@ -20,6 +20,5 @@ fn bar(_: Foo fn(&'a ())>::Assoc) {} //~| ERROR mismatched types //~| ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109789.stderr b/tests/ui/associated-inherent-types/issue-109789.stderr index db860a64826d6..c6ea6c5541d23 100644 --- a/tests/ui/associated-inherent-types/issue-109789.stderr +++ b/tests/ui/associated-inherent-types/issue-109789.stderr @@ -31,14 +31,6 @@ LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: higher-ranked subtype error - --> $DIR/issue-109789.rs:18:1 - | -LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/issue-111404-1.rs b/tests/ui/associated-inherent-types/issue-111404-1.rs index cad6d48b1c5af..3255bf20ebd1b 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.rs +++ b/tests/ui/associated-inherent-types/issue-111404-1.rs @@ -12,6 +12,5 @@ fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} //~| ERROR mismatched types [E0308] //~| ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111404-1.stderr b/tests/ui/associated-inherent-types/issue-111404-1.stderr index 9a5b69497c0cf..8305725d3cec5 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.stderr +++ b/tests/ui/associated-inherent-types/issue-111404-1.stderr @@ -23,20 +23,12 @@ error: higher-ranked subtype error LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: higher-ranked subtype error - --> $DIR/issue-111404-1.rs:10:1 - | -LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: higher-ranked subtype error --> $DIR/issue-111404-1.rs:10:8 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs index b66dff43a3d1d..8fb816fbaf0e0 100644 --- a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs +++ b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs @@ -21,7 +21,6 @@ fn take( >, ) {} //~^^^ ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error trait Project { type Out; } impl Project for fn(T) -> T { type Out = T; } diff --git a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr index f2f69aad4ee65..1ac126428e870 100644 --- a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr +++ b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr @@ -4,13 +4,5 @@ error: higher-ranked subtype error LL | K = const { () } | ^^^^^^^^^^^^ -error: higher-ranked subtype error - --> $DIR/bound-var-in-ty-not-wf.rs:20:13 - | -LL | K = const { () } - | ^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/wf-check-hidden-type.stderr b/tests/ui/impl-trait/wf-check-hidden-type.current.stderr similarity index 91% rename from tests/ui/impl-trait/wf-check-hidden-type.stderr rename to tests/ui/impl-trait/wf-check-hidden-type.current.stderr index 86ba7aff54ada..254bc45796ca2 100644 --- a/tests/ui/impl-trait/wf-check-hidden-type.stderr +++ b/tests/ui/impl-trait/wf-check-hidden-type.current.stderr @@ -1,10 +1,11 @@ error: lifetime may not live long enough - --> $DIR/wf-check-hidden-type.rs:14:5 + --> $DIR/wf-check-hidden-type.rs:21:5 | LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here +LL | LL | None::<&'_ &'_ ()> | ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | diff --git a/tests/ui/impl-trait/wf-check-hidden-type.next.stderr b/tests/ui/impl-trait/wf-check-hidden-type.next.stderr new file mode 100644 index 0000000000000..88da3916955da --- /dev/null +++ b/tests/ui/impl-trait/wf-check-hidden-type.next.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/wf-check-hidden-type.rs:19:1 + | +LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> { + | ^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | | + | | | lifetime `'b` defined here + | | lifetime `'a` defined here + | requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/wf-check-hidden-type.rs b/tests/ui/impl-trait/wf-check-hidden-type.rs index c3b1182a98f48..1146d966eeb0d 100644 --- a/tests/ui/impl-trait/wf-check-hidden-type.rs +++ b/tests/ui/impl-trait/wf-check-hidden-type.rs @@ -1,4 +1,10 @@ -//! Regression test for #114728. +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +//! Regression test for #114728. This also catched +//! trait-system-refactor-initiative#159 with the new +//! solver. trait Extend<'a, 'b> { fn extend(self, _: &'a str) -> &'b str; @@ -11,7 +17,8 @@ impl<'a, 'b> Extend<'a, 'b> for Option<&'b &'a ()> { } fn boom<'a, 'b>() -> impl Extend<'a, 'b> { - None::<&'_ &'_ ()> //~ ERROR lifetime may not live long enough + //[next]~^ ERROR lifetime may not live long enough + None::<&'_ &'_ ()> //[current]~ ERROR lifetime may not live long enough } fn main() { diff --git a/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr b/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr deleted file mode 100644 index fae1838b32fca..0000000000000 --- a/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/normalization-preserve-equality.rs:27:1 - | -LL | fn test_borrowck<'a, 'b>(_: ( as Trait>::Ty, Equal<'a, 'b>)) { - | ^^^^^^^^^^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | | - | | | lifetime `'b` defined here - | | lifetime `'a` defined here - | requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/normalization-preserve-equality.rs:27:1 - | -LL | fn test_borrowck<'a, 'b>(_: ( as Trait>::Ty, Equal<'a, 'b>)) { - | ^^^^^^^^^^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | | - | | | lifetime `'b` defined here - | | lifetime `'a` defined here - | requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - -help: `'a` and `'b` must be the same: replace one with the other - -error: aborting due to 2 previous errors - diff --git a/tests/ui/implied-bounds/normalization-preserve-equality.rs b/tests/ui/implied-bounds/normalization-preserve-equality.rs index 0d50d26b0488b..9675a3a3c65ce 100644 --- a/tests/ui/implied-bounds/normalization-preserve-equality.rs +++ b/tests/ui/implied-bounds/normalization-preserve-equality.rs @@ -2,11 +2,15 @@ // //@ ignore-compare-mode-next-solver (explicit revisions) //@ revisions: wfcheck borrowck_current borrowck_next -//@ [wfcheck] check-pass -//@ [borrowck_current] check-fail -//@ [borrowck_current] known-bug: #106569 //@ [borrowck_next] compile-flags: -Znext-solver -//@ [borrowck_next] check-pass +//@ check-pass + + +// We previously computed implied bounds while using region variables for +// `'a` and `'b`. That resulted in implied bounds computation actually +// just equating these two regions, and resolving `'b` to `'a`, causing +// the implied bound to be useless. See #106569. We're now properly using +// universal regions (params and placeholders) when computing implied bounds. struct Equal<'a, 'b>(&'a &'b (), &'b &'a ()); // implies 'a == 'b diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr deleted file mode 100644 index effab17e8c3bc..0000000000000 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/ambiguity-due-to-uniquification-4.rs:17:47 - | -LL | pub fn f<'a, 'b, T: Trait<'a> + Trait<'b>>(v: >::Type) {} - | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for associated type `>::Type` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs index c40b472678d4b..44c2c9fb24ce3 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs @@ -1,21 +1,21 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass // A regression test for https://github.com/rust-lang/rust/issues/151318. // -// Unlike in the previous other tests, this fails to compile with the old solver as well. -// Although we were already stashing goals which depend on inference variables and then -// reproving them at the end of HIR typeck to avoid causing an ICE during MIR borrowck, -// it wasn't enough because the type op itself can result in an error due to uniquification, -// e.g. while normalizing a projection type. +// Unlike the previous tests, this fails with the old trait solver. It does +// pass with the next solver as we now normalize the function signature outsid +// of MIR borrowck. This means we prefer the `Trait<'a>` candidate as it has +// no constraints. pub trait Trait<'a> { type Type; } pub fn f<'a, 'b, T: Trait<'a> + Trait<'b>>(v: >::Type) {} -//~^ ERROR type annotations needed +//[current]~^ ERROR type annotations needed //[current]~| ERROR type annotations needed fn main() {} diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs index a1e60c38fbbc8..17a81b5705dfb 100644 --- a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs @@ -12,7 +12,6 @@ impl<'a> Foo { fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} //~^ ERROR: higher-ranked subtype error -//~| ERROR: higher-ranked subtype error //~| ERROR: lifetime bound not satisfied [E0478] //~| ERROR: lifetime bound not satisfied [E0478] diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr index 6a79474f60915..47227014ec567 100644 --- a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr @@ -12,18 +12,12 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: higher-ranked subtype error - --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:1 - | -LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: higher-ranked subtype error --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:8 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.current.stderr new file mode 100644 index 0000000000000..ea1b22dd80333 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.current.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-2.rs:18:5 + | +LL | into_y(t) + | ^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn wat(t: T) -> impl Sized + 'static { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.next.stderr new file mode 100644 index 0000000000000..b34013763219e --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.next.stderr @@ -0,0 +1,31 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-2.rs:16:1 + | +LL | fn wat(t: T) -> impl Sized + 'static { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn wat(t: T) -> impl Sized + 'static { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-2.rs:18:5 + | +LL | into_y(t) + | ^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn wat(t: T) -> impl Sized + 'static { + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.rs new file mode 100644 index 0000000000000..5c699dfc53199 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.rs @@ -0,0 +1,28 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#159. We need to make sure +// that computing the implied assumptions of `wat` does not look into the hidden +// type `impl Sized`, as doing so adds a `T: 'static` implied bound which +// its caller does not have to prove. + +fn into_y(t: T) -> impl Sized +where + T: 'static, +{ + t +} +fn wat(t: T) -> impl Sized + 'static { + //[next]~^ ERROR the parameter type `T` may not live long enough + into_y(t) //~ ERROR the parameter type `T` may not live long enough +} + +fn leak(t: &T) -> &'static T { + *(&wat(t) as &dyn std::any::Any).downcast_ref().unwrap() +} + +fn main() { + let buf = leak(&vec![vec![1]]); + dbg!(buf[0][0]); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.current.stderr new file mode 100644 index 0000000000000..0bbd76e54fd4b --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.current.stderr @@ -0,0 +1,75 @@ +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:5 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.next.stderr new file mode 100644 index 0000000000000..813e682eaa939 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.next.stderr @@ -0,0 +1,75 @@ +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:28:1 + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.rs new file mode 100644 index 0000000000000..f2ddc2c6c7c21 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.rs @@ -0,0 +1,44 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// The original regression test for trait-system-refactor-initiative#159. +// Unlike the other tests here the hidden type has a fresh region var which +// makes the implied bound `::Assoc: 'infer_var`. While this +// variable will end up equal to `'static` later on, we don't really support +// non alias-outlives assumptions with non-universal variables in them. This +// makes this test more involved than the others. + +use std::any::Any; + +struct Outlives<'a, T>(Option<&'a T>); +trait Trait { + type Assoc; +} + +impl Trait for T { + type Assoc = T; +} + +// Computing the implied bounds for `foo` normalizes `impl Sized` to +// `Outlives::<'static, ::Assoc>`, adding the implied bound +// `::Assoc: 'static`. +// +// The caller does not have to prove that bound. +fn foo(x: ::Assoc) -> (Box, impl Sized) { + //[next]~^ ERROR the associated type `::Assoc` may not live long enough + (Box::new(x), Outlives::<'static, ::Assoc>(None)) + //~^ ERROR the associated type `::Assoc` may not live long enough + //~| ERROR the associated type `::Assoc` may not live long enough + //~| ERROR the associated type `::Assoc` may not live long enough + //~| ERROR the associated type `::Assoc` may not live long enough + //[current]~| ERROR the associated type `::Assoc` may not live long enough +} + +fn main() { + let string = String::from("temporary"); + let (any, _proof) = foo::<&str>(string.as_str()); + drop(_proof); + drop(string); + println!("{}", any.downcast_ref::<&str>().unwrap()); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-pass.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-pass.rs new file mode 100644 index 0000000000000..3bbf200f3fb3d --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-pass.rs @@ -0,0 +1,38 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 +//@ check-pass + +// Regression test for the `typesensei` crater breakage caused by +// trait-system-refactor-initiative#159. Getting an incorrect +// `batch_action::{opaque}: 'a` implied bound means there are now +// two ways to prove that `Action<'a, batch_action::{opaque}>` is +// well-formed. This causes us to emit a type test instead of a +// region constraint, causing this to fail as type tests are checked +// on the frozen region graph. + +use std::{future::Future, marker::PhantomData}; + +pub fn batch_emplace<'a>(s: &'a str) -> Action<'a, impl Future + 'a> { + if false { + let n: Action<'a, _> = loop {}; + n + } else { + new(s, batch_action(s)) + } +} + +// The outlive bound is necessary. +pub struct Action<'a, Fut: 'a> { + _phantom: PhantomData<(&'a str, Fut)>, +} +fn new<'a, Fut>(api: &'a str, fut: Fut) -> Action<'a, Fut> { + loop {} +} + +fn batch_action<'a>(s: &'a str) -> impl Future + 'a { + async {} +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.current.stderr new file mode 100644 index 0000000000000..96609458e51b9 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.current.stderr @@ -0,0 +1,117 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:9 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:23 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:9 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:23 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.next.stderr new file mode 100644 index 0000000000000..4b98cf1c352fb --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.next.stderr @@ -0,0 +1,117 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:20:5 + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:23 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:32:5 + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:23 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.rs new file mode 100644 index 0000000000000..f1452a1aade04 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.rs @@ -0,0 +1,49 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#159. We need to make sure +// that computing the implied assumptions of `foo` does not look into the hidden +// type of `impl Sized`, as doing so adds a `T: 'static` implied bound which +// its caller does not have to prove. +// +// In this test the opaque is introduced via an RPITIT synthetic associated type +// in the signature and a `Projection(synthetic_assoc_ty, opaque_ty)` clause in the +// `ParamEnv`. We're initially fixing this bug by incorrectly marking opaque types +// as rigid. This test makes sure we also do so for opaque types in the `ParamEnv`. + +use std::any::Any; + +struct Outlives(Option); + +trait Trait { + fn foo(x: T) -> (Box, impl Sized) { + //[next]~^ ERROR the parameter type `T` may not live long enough + (Box::new(x), Outlives::(None)) + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //[current]~| ERROR the parameter type `T` may not live long enough + } +} + +impl Trait for i32 {} +impl Trait for u32 { + fn foo(x: T) -> (Box, impl Sized) { + //[next]~^ ERROR the parameter type `T` may not live long enough + (Box::new(x), Outlives::(None)) + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //[current]~| ERROR the parameter type `T` may not live long enough + } +} + + +fn main() { + let any = ::foo(String::from("temporary").as_str()).0; + println!("{}", any.downcast_ref::<&str>().unwrap()); + + let any = ::foo(String::from("temporary").as_str()).0; + println!("{}", any.downcast_ref::<&str>().unwrap()); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.current.stderr new file mode 100644 index 0000000000000..fc3a4ed9e6cd3 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.current.stderr @@ -0,0 +1,60 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:5 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:6 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:6 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:19 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.next.stderr new file mode 100644 index 0000000000000..caa6c316d169c --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.next.stderr @@ -0,0 +1,60 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:13:1 + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:6 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:6 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:19 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.rs new file mode 100644 index 0000000000000..cf03b17fbaed7 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#159. We need to make sure +// that computing the implied assumptions of `foo` does not look into the hidden +// type of `impl Sized`, as doing so adds a `T: 'static` implied bound which +// its caller does not have to prove. + +use std::any::Any; + +struct Outlives(Option); +fn foo(x: T) -> (Box, impl Sized) { + //[next]~^ ERROR the parameter type `T` may not live long enough + (Box::new(x), Outlives::(None)) + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //[current]~| ERROR the parameter type `T` may not live long enough +} + +fn main() { + let any = foo(String::from("temporary").as_str()).0; + println!("{}", any.downcast_ref::<&str>().unwrap()); +} From a33a3616ff88e95c80321d5fead05db39e1fac40 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 4 Aug 2026 13:29:52 +0200 Subject: [PATCH 5/6] fix implied bound computation for nested bodies --- compiler/rustc_borrowck/src/implied_bounds.rs | 22 ++++- .../src/type_check/free_region_relations.rs | 18 +++- compiler/rustc_middle/src/ty/mod.rs | 9 +- ...paque-hidden-in-closure-sig.current.stderr | 85 +++++++++++++++++++ ...ied-bounds-opaque-hidden-in-closure-sig.rs | 42 +++++++++ 5 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.current.stderr create mode 100644 tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs diff --git a/compiler/rustc_borrowck/src/implied_bounds.rs b/compiler/rustc_borrowck/src/implied_bounds.rs index 54eed7fb761a4..3baf321b0eaa6 100644 --- a/compiler/rustc_borrowck/src/implied_bounds.rs +++ b/compiler/rustc_borrowck/src/implied_bounds.rs @@ -19,7 +19,11 @@ use tracing::instrument; use crate::universal_regions::DefiningTy; /// Computes the implied bounds for `body_def_id`. This is a separate query -/// as it must not reveal the hidden type of opaques defined by `body_def_id`. +/// as it must not reveal the hidden type of opaques defined by `body_def_id` +/// for typeck roots. +/// +/// However, nested bodies are checked in the scope of their parent. This means +/// we should actually normalize opaques when computing their implied bounds. pub(super) fn mir_borrowck_implied_outlives_bounds<'tcx>( tcx: TyCtxt<'tcx>, body_def_id: LocalDefId, @@ -27,8 +31,20 @@ pub(super) fn mir_borrowck_implied_outlives_bounds<'tcx>( &'tcx Canonical<'tcx, QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx>>>, NoSolution, > { - // We do not want to reveal the hidden types of any opaque types in this function. - let typing_env = TypingEnv::non_body_analysis(tcx, body_def_id); + // If we're in a typeck root we don't want to reveal any opaque types. We need to + // make sure the caller actually checks that all our implied bounds actually hold. + // This is not the case with the hidden types of opaque types if we're a defining-scope + // and the caller is not. + // + // However, for nested bodies, we always check that they are well-formed in their + // parent body, so for these we do want to define opaque types. Not doing so can result + // in incorrect errors when normalizing implied bounds. + let typing_env = if tcx.is_typeck_child(body_def_id.to_def_id()) { + TypingEnv::post_typeck_until_borrowck(tcx, body_def_id) + } else { + TypingEnv::non_body_analysis(tcx, body_def_id) + }; + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); let ocx = ObligationCtxt::new(&infcx); diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 5a773a58e4395..752a04a90c6f6 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -258,7 +258,23 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { &mut query_constraints, ) .unwrap(); - assert!(obligations.is_empty()); + + // `mir_borrowck_implied_outlives_bounds` for nested bodies can result in + // defining uses of opaques. + for obligation in obligations { + let predicate = obligation.predicate; + match self + .infcx + .fully_perform(type_op::prove_predicate::ProvePredicate { predicate }, span) + { + Ok(TypeOpOutput { constraints: obligation_constraints, .. }) => { + if let Some(c) = obligation_constraints { + constraints.push(c); + } + } + Err::<_, ErrorGuaranteed>(_) => {} + } + } if !query_constraints.is_empty() { constraints.push(&query_constraints); }; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4105930684947..3f3373575c24d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1259,7 +1259,14 @@ impl<'tcx> TypingEnv<'tcx> { Self::new(tcx.param_env(def_id), TypingMode::non_body_analysis()) } - /// Ideally we just use `TypingMode::PostTypeckUntilBorrowck`. + /// The `TypingEnv` which should be for everything happens after HIR typeck + /// up-to and including borrowck itself. + pub fn post_typeck_until_borrowck(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> TypingEnv<'tcx> { + let param_env = tcx.param_env(def_id.to_def_id()); + TypingEnv::new(param_env, ty::TypingMode::borrowck(tcx, def_id)) + } + + /// Ideally we just use `TypingMode::post_typeck_until_borrowck`. /// But that's not compatible with the old solver yet. /// /// FIXME: this should not be needed in the long term. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.current.stderr new file mode 100644 index 0000000000000..955d5b4970d34 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.current.stderr @@ -0,0 +1,85 @@ +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:25 + | +LL | (|_| ())(RequiresWf(opaque)); + | ---------- ^^^^^^ the trait `Trait` is not implemented for `impl Sized` + | | + | required by a bound introduced by this call + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this tuple struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:14 + | +LL | (|_| ())(RequiresWf(opaque)); + | ^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Sized` + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:7 + | +LL | (|_| ())(RequiresWf(opaque)); + | ^ the trait `Trait` is not implemented for `impl Sized` + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:5 + | +LL | (|_| ())(RequiresWf(opaque)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Sized` + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs new file mode 100644 index 0000000000000..6fe74f0d4f9d3 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs @@ -0,0 +1,42 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +// A test for an edge case of #160443. While we must not use the +// hidden type of opaques when computing the implied bounds for a function +// we should do so for nested bodies. This is necessary as otherwise +// normalizing their well-formedness requirements can fail. +// +// Closures are always checked for WF in their parent body, which can also +// reveal the hidden types of opaque types. + +trait Trait { + type Assoc; +} +impl Trait for () { + type Assoc = (); +} + +trait Func { + type Output; +} +impl R, R> Func for F { + type Output = R; +} + +struct RequiresWf(F) +where + F: Func, + F::Output: Trait, + ::Assoc: 'static; + +fn opaque() -> impl Sized { + (|_| ())(RequiresWf(opaque)); + //[current]~^ ERROR the trait bound `impl Sized: Trait` is not satisfied + //[current]~| ERROR the trait bound `impl Sized: Trait` is not satisfied + //[current]~| ERROR the trait bound `impl Sized: Trait` is not satisfied + //[current]~| ERROR the trait bound `impl Sized: Trait` is not satisfied +} + +fn main() {} From 4dd46897041c39d8c16863e90615a36ab577b15b Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 6 Aug 2026 14:20:26 +0200 Subject: [PATCH 6/6] move compute implied bounds into sub-fn --- .../src/type_check/free_region_relations.rs | 158 ++++++++++-------- 1 file changed, 90 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 752a04a90c6f6..3b5651032c918 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,5 +1,6 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; @@ -228,74 +229,12 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .collect(); // Compute the implied bounds of the current function based on its signature. - // - // We need to make sure that all implied bounds are checked by the user of this - // item. For this, we compute the implied bounds in a different `TypingEnv`. - // - // This also returns the function signature as normalized in that separate environment - // which we then renormalize. This is necessary to correctly handle implied bounds - // involving unconstrained regions due to #136547. - let var_values = - implied_bounds_query_var_values(tcx, &unnormalized_input_output_tys, |region| { - self.universal_regions.is_external_free_region(region.as_var()) - }); - let original_query_values = OriginalQueryValues { var_values, ..Default::default() }; - let mut query_constraints = QueryRegionConstraints::default(); - let MirBorrowckImpliedOutlivesBounds { - outlives_bounds, - normalized_inputs_and_output: query_normalized_inputs_and_output, - } = match tcx.mir_borrowck_implied_outlives_bounds(body_def_id) { - Ok(canonical_result) => { - // `instantiate_nll_query_response_and_region_obligations` should never fail - // here as all our `var_values` are unique generic parameters. - let InferOk { value, obligations } = self - .infcx - .instantiate_nll_query_response_and_region_obligations( - &ObligationCause::dummy_with_span(span), - param_env, - &original_query_values, - canonical_result, - &mut query_constraints, - ) - .unwrap(); - - // `mir_borrowck_implied_outlives_bounds` for nested bodies can result in - // defining uses of opaques. - for obligation in obligations { - let predicate = obligation.predicate; - match self - .infcx - .fully_perform(type_op::prove_predicate::ProvePredicate { predicate }, span) - { - Ok(TypeOpOutput { constraints: obligation_constraints, .. }) => { - if let Some(c) = obligation_constraints { - constraints.push(c); - } - } - Err::<_, ErrorGuaranteed>(_) => {} - } - } - if !query_constraints.is_empty() { - constraints.push(&query_constraints); - }; - value - } - Err(NoSolution) => { - self.infcx.dcx().span_delayed_bug( - span, - format!("error computing implied bounds {body_def_id:?}"), - ); - MirBorrowckImpliedOutlivesBounds { - outlives_bounds: Vec::new(), - normalized_inputs_and_output: unnormalized_input_output_tys, - } - } - }; - - // Because of #109628, we may have unexpected placeholders. Ignore them! - // FIXME(#109628): panic in this case once the issue is fixed. - let bounds = outlives_bounds.into_iter().filter(|bound| !bound.has_placeholders()); - self.add_outlives_bounds(bounds); + let query_normalized_inputs_and_output = self.compute_implied_bounds( + body_def_id, + span, + unnormalized_input_output_tys, + &mut constraints, + ); // We need to renormalize the signature returned by the implied bounds query. This // query normalizes the signature in a context which does not define any opaque types @@ -351,6 +290,89 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } } + /// Computes the implied bounds for the current body by using a separate query. + /// This is necessary as we need to make sure that all implied bounds are checked + /// by the user of this item. + /// + /// If we're a defining scope but can be used by caller which does not define the + /// same opaques, we must not get any assumptions from the hidden type of an opaque + /// type in our signature. We avoid this by computing implied bounds in a different + /// context which cannot normalize opaque types if we're in a typeck root. + /// + /// This also returns the function signature as normalized in that separate environment + /// which we then renormalize. This is necessary to correctly handle implied bounds + /// involving unconstrained regions due to #136547. + fn compute_implied_bounds( + &mut self, + body_def_id: LocalDefId, + span: Span, + unnormalized_inputs_and_output: Vec>, + constraints: &mut Vec<&QueryRegionConstraints<'tcx>>, + ) -> Vec> { + let infcx = self.infcx; + let tcx = infcx.tcx; + let var_values = + implied_bounds_query_var_values(tcx, &unnormalized_inputs_and_output, |region| { + self.universal_regions.is_external_free_region(region.as_var()) + }); + let original_query_values = OriginalQueryValues { var_values, ..Default::default() }; + match tcx.mir_borrowck_implied_outlives_bounds(body_def_id) { + Ok(canonical_result) => { + // `instantiate_nll_query_response_and_region_obligations` should never fail + // here as all our `var_values` are unique generic parameters. + let mut query_constraints = QueryRegionConstraints::default(); + let InferOk { value, obligations } = self + .infcx + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy_with_span(span), + infcx.param_env, + &original_query_values, + canonical_result, + &mut query_constraints, + ) + .unwrap(); + if !query_constraints.is_empty() { + constraints.push(infcx.tcx.arena.alloc(query_constraints)); + }; + + // `mir_borrowck_implied_outlives_bounds` for nested bodies can result in + // defining uses of opaques. + for obligation in obligations { + let predicate = obligation.predicate; + match infcx + .fully_perform(type_op::prove_predicate::ProvePredicate { predicate }, span) + { + Ok(TypeOpOutput { constraints: obligation_constraints, .. }) => { + if let Some(c) = obligation_constraints { + constraints.push(c); + } + } + Err::<_, ErrorGuaranteed>(_) => {} + } + } + + let MirBorrowckImpliedOutlivesBounds { + outlives_bounds, + normalized_inputs_and_output, + } = value; + + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + let bounds = outlives_bounds.into_iter().filter(|bound| !bound.has_placeholders()); + self.add_outlives_bounds(bounds); + + normalized_inputs_and_output + } + Err(NoSolution) => { + self.infcx.dcx().span_delayed_bug( + span, + format!("error computing implied bounds {body_def_id:?}"), + ); + unnormalized_inputs_and_output + } + } + } + fn normalize_and_push_type_outlives_obligation( &self, mut outlives: ty::PolyTypeOutlivesPredicate<'tcx>,