From 7e515fccf84efd36734c73a4e358e93531a73e8f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 25 Dec 2025 15:44:56 +0200 Subject: [PATCH] Store closures with "tupled" inputs And remove it when needed, the opposite of what was previously, where we stored without a tuple and tupled for the solver (because it requires that). Because this is what rustc does, and generally, the closer we follow rustc, the easier our lives become. The weird name `signature_unclosure()` also comes from rustc. --- crates/hir-ty/src/display.rs | 53 ++++++++---------- crates/hir-ty/src/infer/closure.rs | 11 +++- crates/hir-ty/src/infer/closure/analysis.rs | 4 +- crates/hir-ty/src/infer/coerce.rs | 38 +++++++------ crates/hir-ty/src/layout.rs | 10 ++-- crates/hir-ty/src/mir/borrowck.rs | 3 +- crates/hir-ty/src/mir/eval.rs | 6 +- crates/hir-ty/src/mir/lower.rs | 9 +-- crates/hir-ty/src/next_solver/generic_arg.rs | 58 ++------------------ crates/hir-ty/src/next_solver/ty.rs | 27 +++++++-- 10 files changed, 99 insertions(+), 120 deletions(-) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index b9e23464e9..0f989d6c58 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1383,37 +1383,30 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } _ => (), } - let sig = substs - .split_closure_args_untupled() - .closure_sig_as_fn_ptr_ty - .callable_sig(interner); - if let Some(sig) = sig { - let sig = sig.skip_binder(); - let InternedClosure(def, _) = db.lookup_intern_closure(id); - let infer = InferenceResult::for_body(db, def); - let (_, kind) = infer.closure_info(id); - match f.closure_style { - ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?, - ClosureStyle::RANotation => write!(f, "|")?, - _ => unreachable!(), - } - if sig.inputs().is_empty() { - } else if f.should_truncate() { - write!(f, "{TYPE_HINT_TRUNCATION}")?; - } else { - f.write_joined(sig.inputs(), ", ")?; - }; - match f.closure_style { - ClosureStyle::ImplFn => write!(f, ")")?, - ClosureStyle::RANotation => write!(f, "|")?, - _ => unreachable!(), - } - if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() { - write!(f, " -> ")?; - sig.output().hir_fmt(f)?; - } + let sig = interner.signature_unclosure(substs.as_closure().sig(), Safety::Safe); + let sig = sig.skip_binder(); + let InternedClosure(def, _) = db.lookup_intern_closure(id); + let infer = InferenceResult::for_body(db, def); + let (_, kind) = infer.closure_info(id); + match f.closure_style { + ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?, + ClosureStyle::RANotation => write!(f, "|")?, + _ => unreachable!(), + } + if sig.inputs().is_empty() { + } else if f.should_truncate() { + write!(f, "{TYPE_HINT_TRUNCATION}")?; } else { - write!(f, "{{closure}}")?; + f.write_joined(sig.inputs(), ", ")?; + }; + match f.closure_style { + ClosureStyle::ImplFn => write!(f, ")")?, + ClosureStyle::RANotation => write!(f, "|")?, + _ => unreachable!(), + } + if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() { + write!(f, " -> ")?; + sig.output().hir_fmt(f)?; } } TyKind::CoroutineClosure(id, args) => { diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 19ffa3a939..d1391ad24e 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -68,7 +68,6 @@ impl<'db> InferenceContext<'_, 'db> { let ClosureSignatures { bound_sig, liberated_sig } = self.sig_of_closure(arg_types, ret_type, expected_sig); let body_ret_ty = bound_sig.output().skip_binder(); - let sig_ty = Ty::new_fn_ptr(interner, bound_sig); let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into()); // FIXME: Make this an infer var and infer it later. @@ -117,6 +116,16 @@ impl<'db> InferenceContext<'_, 'db> { } None => {} }; + let sig = bound_sig.map_bound(|sig| { + interner.mk_fn_sig( + [Ty::new_tup(interner, sig.inputs())], + sig.output(), + sig.c_variadic, + sig.safety, + sig.abi, + ) + }); + let sig_ty = Ty::new_fn_ptr(interner, sig); // FIXME: Infer the kind later if needed. let parts = ClosureArgsParts { parent_args: parent_args.as_slice(), diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs index 5b0360071d..6ae6f75525 100644 --- a/crates/hir-ty/src/infer/closure/analysis.rs +++ b/crates/hir-ty/src/infer/closure/analysis.rs @@ -15,7 +15,7 @@ use hir_def::{ }; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::{IntoKind, Ty as _}; +use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, Ty as _}; use smallvec::{SmallVec, smallvec}; use stdx::{format_to, never}; use syntax::utils::is_raw_identifier; @@ -103,7 +103,7 @@ impl CapturedItem { pub fn ty<'db>(&self, db: &'db dyn HirDatabase, subst: GenericArgs<'db>) -> Ty<'db> { let interner = DbInterner::new_no_crate(db); - self.ty.get().instantiate(interner, subst.split_closure_args_untupled().parent_args) + self.ty.get().instantiate(interner, subst.as_closure().parent_args()) } pub fn kind(&self) -> CaptureKind { diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index bb9cb1c1ca..ec7dad0fa0 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -46,7 +46,9 @@ use rustc_type_ir::{ BoundVar, DebruijnIndex, TyVid, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, error::TypeError, - inherent::{Const as _, GenericArg as _, IntoKind, Safety, SliceLike, Ty as _}, + inherent::{ + Const as _, GenericArg as _, GenericArgs as _, IntoKind, Safety as _, SliceLike, Ty as _, + }, }; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -63,6 +65,7 @@ use crate::{ Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind, TypingMode, + abi::Safety, infer::{ DbInternerInferExt, InferCtxt, InferOk, InferResult, relate::RelateResult, @@ -921,10 +924,8 @@ where // or // `unsafe fn(arg0,arg1,...) -> _` let safety = hdr.safety; - let closure_sig = args_a.closure_sig_untupled().map_bound(|mut sig| { - sig.safety = hdr.safety; - sig - }); + let closure_sig = + self.interner().signature_unclosure(args_a.as_closure().sig(), safety); let pointer_ty = Ty::new_fn_ptr(self.interner(), closure_sig); debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); self.unify_and( @@ -1125,23 +1126,28 @@ impl<'db> InferenceContext<'_, 'db> { } (TyKind::Closure(_, args), TyKind::FnDef(..)) => { let b_sig = new_ty.fn_sig(self.table.interner()); - let a_sig = args.closure_sig_untupled().map_bound(|mut sig| { - sig.safety = b_sig.safety(); - sig - }); + let a_sig = self + .interner() + .signature_unclosure(args.as_closure().sig(), b_sig.safety()); (Some(a_sig), Some(b_sig)) } (TyKind::FnDef(..), TyKind::Closure(_, args)) => { let a_sig = prev_ty.fn_sig(self.table.interner()); - let b_sig = args.closure_sig_untupled().map_bound(|mut sig| { - sig.safety = a_sig.safety(); - sig - }); + let b_sig = self + .interner() + .signature_unclosure(args.as_closure().sig(), a_sig.safety()); (Some(a_sig), Some(b_sig)) } - (TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => { - (Some(args_a.closure_sig_untupled()), Some(args_b.closure_sig_untupled())) - } + (TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => ( + Some( + self.interner() + .signature_unclosure(args_a.as_closure().sig(), Safety::Safe), + ), + Some( + self.interner() + .signature_unclosure(args_b.as_closure().sig(), Safety::Safe), + ), + ), _ => (None, None), } } diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index b6ad3624ae..525100439f 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -14,7 +14,10 @@ use rustc_abi::{ TargetDataLayout, WrappingRange, }; use rustc_index::IndexVec; -use rustc_type_ir::{FloatTy, IntTy, UintTy, inherent::IntoKind}; +use rustc_type_ir::{ + FloatTy, IntTy, UintTy, + inherent::{GenericArgs as _, IntoKind}, +}; use triomphe::Arc; use crate::{ @@ -335,10 +338,7 @@ pub fn layout_of_ty_query( let fields = captures .iter() .map(|it| { - let ty = it - .ty - .get() - .instantiate(interner, args.split_closure_args_untupled().parent_args); + let ty = it.ty.get().instantiate(interner, args.as_closure().parent_args()); db.layout_of_ty(ty.store(), trait_env.clone()) }) .collect::, _>>()?; diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 941b6c75bf..dece61a57d 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -8,6 +8,7 @@ use std::iter; use hir_def::{DefWithBodyId, HasModule}; use la_arena::ArenaMap; use rustc_hash::FxHashMap; +use rustc_type_ir::inherent::GenericArgs as _; use stdx::never; use triomphe::Arc; @@ -123,7 +124,7 @@ fn make_fetch_closure_field<'db>( let InternedClosure(def, _) = db.lookup_intern_closure(c); let infer = InferenceResult::for_body(db, def); let (captures, _) = infer.closure_info(c); - let parent_subst = subst.split_closure_args_untupled().parent_args; + let parent_subst = subst.as_closure().parent_args(); let interner = DbInterner::new_no_crate(db); captures.get(f).expect("broken closure field").ty.get().instantiate(interner, parent_subst) } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index c7156bb11e..2d1368858b 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -27,7 +27,7 @@ use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ AliasTyKind, - inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _}, + inherent::{AdtDef, GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, }; use span::FileId; use stdx::never; @@ -731,7 +731,7 @@ impl<'db> Evaluator<'db> { let InternedClosure(def, _) = self.db.lookup_intern_closure(c); let infer = InferenceResult::for_body(self.db, def); let (captures, _) = infer.closure_info(c); - let parent_subst = subst.split_closure_args_untupled().parent_args; + let parent_subst = subst.as_closure().parent_args(); captures .get(f) .expect("broken closure field") @@ -2771,7 +2771,7 @@ impl<'db> Evaluator<'db> { TyKind::Closure(closure, subst) => self.exec_closure( closure.0, func_data, - GenericArgs::new_from_slice(subst.split_closure_args_untupled().parent_args), + GenericArgs::new_from_slice(subst.as_closure().parent_args()), destination, &args[1..], locals, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index e8d42bed9f..762f91fb0d 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -19,7 +19,7 @@ use hir_expand::name::Name; use la_arena::ArenaMap; use rustc_apfloat::Float; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{Const as _, IntoKind, Ty as _}; +use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; use span::{Edition, FileId}; use syntax::TextRange; use triomphe::Arc; @@ -44,6 +44,7 @@ use crate::{ next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, TypingMode, UnevaluatedConst, + abi::Safety, infer::{DbInternerInferExt, InferCtxt}, }, traits::FnTrait, @@ -2138,11 +2139,7 @@ pub fn mir_body_for_closure_query<'db>( .store(), }); ctx.result.param_locals.push(closure_local); - let Some(sig) = - substs.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.callable_sig(ctx.interner()) - else { - implementation_error!("closure has not callable sig"); - }; + let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe); let resolver_guard = ctx.resolver.update_to_inner_scope(db, owner, expr); let current = ctx.lower_params_and_bindings( args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)), diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs index 9936e44321..72cf2f9f07 100644 --- a/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/crates/hir-ty/src/next_solver/generic_arg.rs @@ -11,9 +11,9 @@ use std::{hint::unreachable_unchecked, marker::PhantomData, ptr::NonNull}; use hir_def::{GenericDefId, GenericParamId}; use intern::InternedRef; use rustc_type_ir::{ - ClosureArgs, ConstVid, CoroutineArgs, CoroutineClosureArgs, FallibleTypeFolder, FnSigTys, - GenericTypeVisitable, Interner, TyKind, TyVid, TypeFoldable, TypeFolder, TypeVisitable, - TypeVisitor, Variance, + ClosureArgs, ConstVid, CoroutineArgs, CoroutineClosureArgs, FallibleTypeFolder, + GenericTypeVisitable, Interner, TyVid, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, + Variance, inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _}, relate::{Relate, VarianceDiagInfo}, walk::TypeWalker, @@ -21,12 +21,11 @@ use rustc_type_ir::{ use smallvec::SmallVec; use crate::next_solver::{ - ConstInterned, PolyFnSig, RegionInterned, TyInterned, impl_foldable_for_interned_slice, - interned_slice, + ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, interned_slice, }; use super::{ - Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, + Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, generics::Generics, }; @@ -566,33 +565,6 @@ impl<'db> GenericArgs<'db> { } } - pub fn closure_sig_untupled(self) -> PolyFnSig<'db> { - let TyKind::FnPtr(inputs_and_output, hdr) = - self.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.kind() - else { - unreachable!("not a function pointer") - }; - inputs_and_output.with(hdr) - } - - /// A "sensible" `.split_closure_args()`, where the arguments are not in a tuple. - pub fn split_closure_args_untupled(self) -> rustc_type_ir::ClosureArgsParts> { - // FIXME: should use `ClosureSubst` when possible - match self.as_slice() { - [parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => { - rustc_type_ir::ClosureArgsParts { - parent_args, - closure_sig_as_fn_ptr_ty: sig_ty.expect_ty(), - closure_kind_ty: closure_kind_ty.expect_ty(), - tupled_upvars_ty: tupled_upvars_ty.expect_ty(), - } - } - _ => { - unreachable!("unexpected closure sig"); - } - } - } - pub fn types(self) -> impl Iterator> { self.iter().filter_map(|it| it.as_type()) } @@ -688,27 +660,9 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< // FIXME: should use `ClosureSubst` when possible match self.as_slice() { [parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => { - let interner = DbInterner::conjure(); - // This is stupid, but the next solver expects the first input to actually be a tuple - let sig_ty = match sig_ty.expect_ty().kind() { - TyKind::FnPtr(sig_tys, header) => Ty::new( - interner, - TyKind::FnPtr( - sig_tys.map_bound(|s| { - let inputs = Ty::new_tup(interner, s.inputs()); - let output = s.output(); - FnSigTys { - inputs_and_output: Tys::new_from_slice(&[inputs, output]), - } - }), - header, - ), - ), - _ => unreachable!("sig_ty should be last"), - }; rustc_type_ir::ClosureArgsParts { parent_args, - closure_sig_as_fn_ptr_ty: sig_ty, + closure_sig_as_fn_ptr_ty: sig_ty.expect_ty(), closure_kind_ty: closure_kind_ty.expect_ty(), tupled_upvars_ty: tupled_upvars_ty.expect_ty(), } diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs index c89831bd40..030ba37015 100644 --- a/crates/hir-ty/src/next_solver/ty.rs +++ b/crates/hir-ty/src/next_solver/ty.rs @@ -26,6 +26,7 @@ use rustc_type_ir::{ }; use crate::{ + FnAbi, db::{HirDatabase, InternedCoroutine}, lower::GenericPredicates, next_solver::{ @@ -495,10 +496,9 @@ impl<'db> Ty<'db> { Some(interner.fn_sig(callable).instantiate(interner, args)) } TyKind::FnPtr(sig, hdr) => Some(sig.with(hdr)), - TyKind::Closure(_, closure_args) => closure_args - .split_closure_args_untupled() - .closure_sig_as_fn_ptr_ty - .callable_sig(interner), + TyKind::Closure(_, closure_args) => { + Some(interner.signature_unclosure(closure_args.as_closure().sig(), Safety::Safe)) + } TyKind::CoroutineClosure(coroutine_id, args) => { Some(args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { let unit_ty = Ty::new_unit(interner); @@ -1426,3 +1426,22 @@ impl<'db> PlaceholderLike> for PlaceholderTy { Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } } } } + +impl<'db> DbInterner<'db> { + /// Given a closure signature, returns an equivalent fn signature. Detuples + /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then + /// you would get a `fn(u32, i32)`. + /// `unsafety` determines the unsafety of the fn signature. If you pass + /// `Safety::Unsafe` in the previous example, then you would get + /// an `unsafe fn (u32, i32)`. + /// It cannot convert a closure that requires unsafe. + pub fn signature_unclosure(self, sig: PolyFnSig<'db>, safety: Safety) -> PolyFnSig<'db> { + sig.map_bound(|s| { + let params = match s.inputs()[0].kind() { + TyKind::Tuple(params) => params, + _ => panic!(), + }; + self.mk_fn_sig(params, s.output(), s.c_variadic, safety, FnAbi::Rust) + }) + } +}