Merge pull request #19536 from jackh726/closure-infer

Port closure inference from rustc
This commit is contained in:
David Barsky 2025-04-08 14:17:30 +00:00 committed by GitHub
commit f3f6b5f4f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 405 additions and 157 deletions

View File

@ -1,24 +1,26 @@
//! Inference of closure parameter types based on the closure's expected type.
use std::{cmp, convert::Infallible, mem};
use std::{cmp, convert::Infallible, mem, ops::ControlFlow};
use chalk_ir::{
BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind,
cast::Cast,
fold::{FallibleTypeFolder, TypeFoldable},
fold::{FallibleTypeFolder, Shift, TypeFoldable},
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
};
use either::Either;
use hir_def::{
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
data::adt::VariantData,
hir::{
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
Statement, UnaryOp,
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, ClosureKind, Expr, ExprId, ExprOrPatId,
Pat, PatId, Statement, UnaryOp,
},
lang_item::LangItem,
path::Path,
resolver::ValueNs,
};
use hir_def::{Lookup, type_ref::TypeRefId};
use hir_expand::name::Name;
use intern::sym;
use rustc_hash::FxHashMap;
@ -28,12 +30,12 @@ use syntax::utils::is_raw_identifier;
use crate::{
Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy,
DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTyExt, Substitution, Ty,
TyExt, WhereClause,
db::{HirDatabase, InternedClosure},
DynTyExt, FnAbi, FnPointer, FnSig, GenericArg, Interner, OpaqueTy, ProjectionTy,
ProjectionTyExt, Substitution, Ty, TyBuilder, TyExt, WhereClause,
db::{HirDatabase, InternedClosure, InternedCoroutine},
error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx,
generics::Generics,
infer::coerce::CoerceNever,
infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever},
make_binders,
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
to_chalk_trait_id,
@ -43,7 +45,106 @@ use crate::{
use super::{Expectation, InferenceContext};
#[derive(Debug)]
pub(super) struct ClosureSignature {
pub(super) ret_ty: Ty,
pub(super) expected_sig: FnPointer,
}
impl InferenceContext<'_> {
pub(super) fn infer_closure(
&mut self,
body: &ExprId,
args: &[PatId],
ret_type: &Option<TypeRefId>,
arg_types: &[Option<TypeRefId>],
closure_kind: ClosureKind,
tgt_expr: ExprId,
expected: &Expectation,
) -> Ty {
assert_eq!(args.len(), arg_types.len());
let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) {
Some(expected_ty) => self.deduce_closure_signature(&expected_ty, closure_kind),
None => (None, None),
};
let ClosureSignature { expected_sig: bound_sig, ret_ty: body_ret_ty } =
self.sig_of_closure(body, ret_type, arg_types, closure_kind, expected_sig);
let bound_sig = self.normalize_associated_types_in(bound_sig);
let sig_ty = TyKind::Function(bound_sig.clone()).intern(Interner);
let (id, ty, resume_yield_tys) = match closure_kind {
ClosureKind::Coroutine(_) => {
let sig_tys = bound_sig.substitution.0.as_slice(Interner);
// FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() {
// When `sig_tys.len() == 1` the first type is the return type, not the
// first parameter type.
Some(ty) if sig_tys.len() > 1 => ty.assert_ty_ref(Interner).clone(),
_ => self.result.standard_types.unit.clone(),
};
let yield_ty = self.table.new_type_var();
let subst = TyBuilder::subst_for_coroutine(self.db, self.owner)
.push(resume_ty.clone())
.push(yield_ty.clone())
.push(body_ret_ty.clone())
.build();
let coroutine_id =
self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into();
let coroutine_ty = TyKind::Coroutine(coroutine_id, subst).intern(Interner);
(None, coroutine_ty, Some((resume_ty, yield_ty)))
}
ClosureKind::Closure | ClosureKind::Async => {
let closure_id =
self.db.intern_closure(InternedClosure(self.owner, tgt_expr)).into();
let closure_ty = TyKind::Closure(
closure_id,
TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
)
.intern(Interner);
self.deferred_closures.entry(closure_id).or_default();
if let Some(c) = self.current_closure {
self.closure_dependencies.entry(c).or_default().push(closure_id);
}
(Some(closure_id), closure_ty, None)
}
};
// Eagerly try to relate the closure type with the expected
// type, otherwise we often won't have enough information to
// infer the body.
self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected, expected_kind);
// Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(bound_sig.substitution.0.as_slice(Interner).iter())
{
self.infer_top_pat(*arg_pat, arg_ty.assert_ty_ref(Interner), None);
}
// FIXME: lift these out into a struct
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_closure = mem::replace(&mut self.current_closure, id);
let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty.clone());
let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty.clone()));
let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
this.infer_return(*body);
});
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
self.return_coercion = prev_ret_coercion;
self.current_closure = prev_closure;
self.resume_yield_tys = prev_resume_yield_tys;
self.table.normalize_associated_types_in(ty)
}
// This function handles both closures and coroutines.
pub(super) fn deduce_closure_type_from_expectations(
&mut self,
@ -51,19 +152,21 @@ impl InferenceContext<'_> {
closure_ty: &Ty,
sig_ty: &Ty,
expectation: &Expectation,
expected_kind: Option<FnTrait>,
) {
let expected_ty = match expectation.to_option(&mut self.table) {
Some(ty) => ty,
None => return,
};
if let TyKind::Closure(closure_id, _) = closure_ty.kind(Interner) {
if let Some(closure_kind) = self.deduce_closure_kind_from_expectations(&expected_ty) {
match (closure_ty.kind(Interner), expected_kind) {
(TyKind::Closure(closure_id, _), Some(closure_kind)) => {
self.result
.closure_info
.entry(*closure_id)
.or_insert_with(|| (Vec::new(), closure_kind));
}
_ => {}
}
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
@ -86,56 +189,146 @@ impl InferenceContext<'_> {
// Closure kind deductions are mostly from `rustc_hir_typeck/src/closure.rs`.
// Might need to port closure sig deductions too.
fn deduce_closure_kind_from_expectations(&mut self, expected_ty: &Ty) -> Option<FnTrait> {
pub(super) fn deduce_closure_signature(
&mut self,
expected_ty: &Ty,
closure_kind: ClosureKind,
) -> (Option<FnSubst<Interner>>, Option<FnTrait>) {
match expected_ty.kind(Interner) {
TyKind::Alias(AliasTy::Opaque(OpaqueTy { .. })) | TyKind::OpaqueType(..) => {
let clauses = expected_ty
.impl_trait_bounds(self.db)
.into_iter()
.flatten()
.map(|b| b.into_value_and_skipped_binders().0);
self.deduce_closure_kind_from_predicate_clauses(clauses)
let clauses = expected_ty.impl_trait_bounds(self.db).into_iter().flatten().map(
|b: chalk_ir::Binders<chalk_ir::WhereClause<Interner>>| {
b.into_value_and_skipped_binders().0
},
);
self.deduce_closure_kind_from_predicate_clauses(expected_ty, clauses, closure_kind)
}
TyKind::Dyn(dyn_ty) => {
let sig =
dyn_ty.bounds.skip_binders().as_slice(Interner).iter().find_map(|bound| {
if let WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(projection_ty),
ty: projected_ty,
}) = bound.skip_binders()
{
if let Some(sig) = self.deduce_sig_from_projection(
closure_kind,
projection_ty,
projected_ty,
) {
return Some(sig);
}
}
None
});
let kind = dyn_ty.principal().and_then(|principal_trait_ref| {
self.fn_trait_kind_from_trait_id(from_chalk_trait_id(
principal_trait_ref.skip_binders().skip_binders().trait_id,
))
});
(sig, kind)
}
TyKind::Dyn(dyn_ty) => dyn_ty.principal_id().and_then(|trait_id| {
self.fn_trait_kind_from_trait_id(from_chalk_trait_id(trait_id))
}),
TyKind::InferenceVar(ty, chalk_ir::TyVariableKind::General) => {
let clauses = self.clauses_for_self_ty(*ty);
self.deduce_closure_kind_from_predicate_clauses(clauses.into_iter())
self.deduce_closure_kind_from_predicate_clauses(
expected_ty,
clauses.into_iter(),
closure_kind,
)
}
TyKind::Function(_) => Some(FnTrait::Fn),
_ => None,
TyKind::Function(fn_ptr) => match closure_kind {
ClosureKind::Closure => (Some(fn_ptr.substitution.clone()), Some(FnTrait::Fn)),
ClosureKind::Async | ClosureKind::Coroutine(_) => (None, None),
},
_ => (None, None),
}
}
fn deduce_closure_kind_from_predicate_clauses(
&self,
expected_ty: &Ty,
clauses: impl DoubleEndedIterator<Item = WhereClause>,
) -> Option<FnTrait> {
closure_kind: ClosureKind,
) -> (Option<FnSubst<Interner>>, Option<FnTrait>) {
let mut expected_sig = None;
let mut expected_kind = None;
for clause in elaborate_clause_supertraits(self.db, clauses.rev()) {
if expected_sig.is_none() {
if let WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(projection),
ty,
}) = &clause
{
let inferred_sig =
self.deduce_sig_from_projection(closure_kind, projection, ty);
// Make sure that we didn't infer a signature that mentions itself.
// This can happen when we elaborate certain supertrait bounds that
// mention projections containing the `Self` type. See rust-lang/rust#105401.
struct MentionsTy<'a> {
expected_ty: &'a Ty,
}
impl TypeVisitor<Interner> for MentionsTy<'_> {
type BreakTy = ();
fn interner(&self) -> Interner {
Interner
}
fn as_dyn(
&mut self,
) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy>
{
self
}
fn visit_ty(
&mut self,
t: &Ty,
db: chalk_ir::DebruijnIndex,
) -> ControlFlow<()> {
if t == self.expected_ty {
ControlFlow::Break(())
} else {
t.super_visit_with(self, db)
}
}
}
if inferred_sig
.visit_with(
&mut MentionsTy { expected_ty },
chalk_ir::DebruijnIndex::INNERMOST,
)
.is_continue()
{
expected_sig = inferred_sig;
}
}
}
let trait_id = match clause {
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(projection), ..
}) => Some(projection.trait_(self.db)),
WhereClause::Implemented(trait_ref) => {
Some(from_chalk_trait_id(trait_ref.trait_id))
}
_ => None,
}) => projection.trait_(self.db),
WhereClause::Implemented(trait_ref) => from_chalk_trait_id(trait_ref.trait_id),
_ => continue,
};
if let Some(closure_kind) =
trait_id.and_then(|trait_id| self.fn_trait_kind_from_trait_id(trait_id))
{
// `FnX`'s variants order is opposite from rustc, so use `cmp::max` instead of `cmp::min`
expected_kind = Some(
expected_kind
.map_or_else(|| closure_kind, |current| cmp::max(current, closure_kind)),
);
if let Some(closure_kind) = self.fn_trait_kind_from_trait_id(trait_id) {
// always use the closure kind that is more permissive.
match (expected_kind, closure_kind) {
(None, _) => expected_kind = Some(closure_kind),
(Some(FnTrait::FnMut), FnTrait::Fn) => expected_kind = Some(FnTrait::Fn),
(Some(FnTrait::FnOnce), FnTrait::Fn | FnTrait::FnMut) => {
expected_kind = Some(closure_kind)
}
_ => {}
}
}
}
expected_kind
(expected_sig, expected_kind)
}
fn deduce_sig_from_dyn_ty(&self, dyn_ty: &DynTy) -> Option<FnPointer> {
@ -186,9 +379,174 @@ impl InferenceContext<'_> {
None
}
fn deduce_sig_from_projection(
&self,
closure_kind: ClosureKind,
projection_ty: &ProjectionTy,
projected_ty: &Ty,
) -> Option<FnSubst<Interner>> {
let container =
from_assoc_type_id(projection_ty.associated_ty_id).lookup(self.db.upcast()).container;
let trait_ = match container {
hir_def::ItemContainerId::TraitId(trait_) => trait_,
_ => return None,
};
// For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
// for closures and async closures, respectively.
match closure_kind {
ClosureKind::Closure | ClosureKind::Async
if self.fn_trait_kind_from_trait_id(trait_).is_some() =>
{
self.extract_sig_from_projection(projection_ty, projected_ty)
}
_ => None,
}
}
fn extract_sig_from_projection(
&self,
projection_ty: &ProjectionTy,
projected_ty: &Ty,
) -> Option<FnSubst<Interner>> {
let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner);
let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else {
return None;
};
let ret_param_ty = projected_ty;
Some(FnSubst(Substitution::from_iter(
Interner,
input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new(
Interner,
chalk_ir::GenericArgData::Ty(ret_param_ty.clone()),
))),
)))
}
fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option<FnTrait> {
FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?)
}
fn supplied_sig_of_closure(
&mut self,
body: &ExprId,
ret_type: &Option<TypeRefId>,
arg_types: &[Option<TypeRefId>],
closure_kind: ClosureKind,
) -> ClosureSignature {
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
// collect explicitly written argument types
for arg_type in arg_types.iter() {
let arg_ty = match arg_type {
Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
sig_tys.push(arg_ty);
}
// add return type
let ret_ty = match ret_type {
Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
if let ClosureKind::Async = closure_kind {
sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body));
} else {
sig_tys.push(ret_ty.clone());
}
let expected_sig = FnPointer {
num_binders: 0,
sig: FnSig { abi: FnAbi::RustCall, safety: chalk_ir::Safety::Safe, variadic: false },
substitution: FnSubst(
Substitution::from_iter(Interner, sig_tys.iter().cloned()).shifted_in(Interner),
),
};
ClosureSignature { ret_ty, expected_sig }
}
/// The return type is the signature of the closure, and the return type
/// *as represented inside the body* (so, for async closures, the `Output` ty)
pub(super) fn sig_of_closure(
&mut self,
body: &ExprId,
ret_type: &Option<TypeRefId>,
arg_types: &[Option<TypeRefId>],
closure_kind: ClosureKind,
expected_sig: Option<FnSubst<Interner>>,
) -> ClosureSignature {
if let Some(e) = expected_sig {
self.sig_of_closure_with_expectation(body, ret_type, arg_types, closure_kind, e)
} else {
self.sig_of_closure_no_expectation(body, ret_type, arg_types, closure_kind)
}
}
fn sig_of_closure_no_expectation(
&mut self,
body: &ExprId,
ret_type: &Option<TypeRefId>,
arg_types: &[Option<TypeRefId>],
closure_kind: ClosureKind,
) -> ClosureSignature {
self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind)
}
fn sig_of_closure_with_expectation(
&mut self,
body: &ExprId,
ret_type: &Option<TypeRefId>,
arg_types: &[Option<TypeRefId>],
closure_kind: ClosureKind,
expected_sig: FnSubst<Interner>,
) -> ClosureSignature {
let expected_sig = FnPointer {
num_binders: 0,
sig: FnSig { abi: FnAbi::RustCall, safety: chalk_ir::Safety::Safe, variadic: false },
substitution: expected_sig,
};
// If the expected signature does not match the actual arg types,
// then just return the expected signature
if expected_sig.substitution.0.len(Interner) != arg_types.len() + 1 {
let ret_ty = match ret_type {
Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
return ClosureSignature { expected_sig, ret_ty };
}
self.merge_supplied_sig_with_expectation(
body,
ret_type,
arg_types,
closure_kind,
expected_sig,
)
}
fn merge_supplied_sig_with_expectation(
&mut self,
body: &ExprId,
ret_type: &Option<TypeRefId>,
arg_types: &[Option<TypeRefId>],
closure_kind: ClosureKind,
expected_sig: FnPointer,
) -> ClosureSignature {
let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind);
let snapshot = self.table.snapshot();
if !self.table.unify(&expected_sig.substitution, &supplied_sig.expected_sig.substitution) {
self.table.rollback_to(snapshot);
}
supplied_sig
}
}
// The below functions handle capture and closure kind (Fn, FnMut, ..)

View File

@ -5,13 +5,13 @@ use std::{
mem,
};
use chalk_ir::{DebruijnIndex, Mutability, TyVariableKind, cast::Cast, fold::Shift};
use chalk_ir::{DebruijnIndex, Mutability, TyVariableKind, cast::Cast};
use either::Either;
use hir_def::{
BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
hir::{
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId,
LabelId, Literal, Pat, PatId, Statement, UnaryOp,
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, Expr, ExprId, ExprOrPatId, LabelId,
Literal, Pat, PatId, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs, Path},
@ -24,12 +24,10 @@ use syntax::ast::RangeOp;
use crate::{
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
DeclOrigin, FnAbi, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Substitution,
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
DeclOrigin, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
TyExt, TyKind,
autoderef::{Autoderef, builtin_deref, deref_by_trait},
consteval,
db::{InternedClosure, InternedCoroutine},
error_lifetime,
consteval, error_lifetime,
generics::{Generics, generics},
infer::{
BreakableKind,
@ -378,116 +376,8 @@ impl InferenceContext<'_> {
None => self.result.standard_types.never.clone(),
}
}
Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => {
assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
// collect explicitly written argument types
for arg_type in arg_types.iter() {
let arg_ty = match arg_type {
Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
sig_tys.push(arg_ty);
}
// add return type
let ret_ty = match ret_type {
Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
if let ClosureKind::Async = closure_kind {
sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body));
} else {
sig_tys.push(ret_ty.clone());
}
let sig_ty = TyKind::Function(FnPointer {
num_binders: 0,
sig: FnSig {
abi: FnAbi::RustCall,
safety: chalk_ir::Safety::Safe,
variadic: false,
},
substitution: FnSubst(
Substitution::from_iter(Interner, sig_tys.iter().cloned())
.shifted_in(Interner),
),
})
.intern(Interner);
let (id, ty, resume_yield_tys) = match closure_kind {
ClosureKind::Coroutine(_) => {
// FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() {
// When `sig_tys.len() == 1` the first type is the return type, not the
// first parameter type.
Some(ty) if sig_tys.len() > 1 => ty.clone(),
_ => self.result.standard_types.unit.clone(),
};
let yield_ty = self.table.new_type_var();
let subst = TyBuilder::subst_for_coroutine(self.db, self.owner)
.push(resume_ty.clone())
.push(yield_ty.clone())
.push(ret_ty.clone())
.build();
let coroutine_id = self
.db
.intern_coroutine(InternedCoroutine(self.owner, tgt_expr))
.into();
let coroutine_ty = TyKind::Coroutine(coroutine_id, subst).intern(Interner);
(None, coroutine_ty, Some((resume_ty, yield_ty)))
}
ClosureKind::Closure | ClosureKind::Async => {
let closure_id =
self.db.intern_closure(InternedClosure(self.owner, tgt_expr)).into();
let closure_ty = TyKind::Closure(
closure_id,
TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
)
.intern(Interner);
self.deferred_closures.entry(closure_id).or_default();
if let Some(c) = self.current_closure {
self.closure_dependencies.entry(c).or_default().push(closure_id);
}
(Some(closure_id), closure_ty, None)
}
};
// Eagerly try to relate the closure type with the expected
// type, otherwise we often won't have enough information to
// infer the body.
self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected);
// Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) {
self.infer_top_pat(*arg_pat, arg_ty, None);
}
// FIXME: lift these out into a struct
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_closure = mem::replace(&mut self.current_closure, id);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty));
let prev_resume_yield_tys =
mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
this.infer_return(*body);
});
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
self.return_coercion = prev_ret_coercion;
self.current_closure = prev_closure;
self.resume_yield_tys = prev_resume_yield_tys;
ty
}
Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self
.infer_closure(body, args, ret_type, arg_types, *closure_kind, tgt_expr, expected),
Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected),
Expr::MethodCall { receiver, args, method_name, generic_args } => self
.infer_method_call(
@ -2458,7 +2348,7 @@ impl InferenceContext<'_> {
}
}
fn with_breakable_ctx<T>(
pub(super) fn with_breakable_ctx<T>(
&mut self,
kind: BreakableKind,
ty: Option<Ty>,