Merge pull request #21335 from ChayimFriedman2/tupled-closure

internal: Store closures with "tupled" inputs
This commit is contained in:
Lukas Wirth 2025-12-26 08:18:20 +00:00 committed by GitHub
commit 856cc543a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 99 additions and 120 deletions

View file

@ -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) => {

View file

@ -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(),

View file

@ -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 {

View file

@ -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),
}
}

View file

@ -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::<Result<Vec<_>, _>>()?;

View file

@ -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)
}

View file

@ -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,

View file

@ -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)),

View file

@ -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<DbInterner<'db>> {
// 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<Item = Ty<'db>> {
self.iter().filter_map(|it| it.as_type())
}
@ -688,27 +660,9 @@ impl<'db> rustc_type_ir::inherent::GenericArgs<DbInterner<'db>> 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(),
}

View file

@ -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<DbInterner<'db>> 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)
})
}
}