Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/expr.rs')
-rw-r--r--crates/hir-ty/src/infer/expr.rs364
1 files changed, 99 insertions, 265 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index ee34a30eba..d80ea71674 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -11,23 +11,20 @@ use hir_def::{
InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp,
},
resolver::ValueNs,
- signatures::{FunctionSignature, VariantFields},
+ signatures::VariantFields,
};
use hir_def::{FunctionId, hir::ClosureKind};
use hir_expand::name::Name;
use rustc_ast_ir::Mutability;
use rustc_type_ir::{
- CoroutineArgs, CoroutineArgsParts, InferTy, Interner,
+ InferTy, Interner,
inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _},
};
use syntax::ast::RangeOp;
use tracing::debug;
use crate::{
- Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness,
- autoderef::InferenceContextAutoderef,
- consteval,
- db::InternedCoroutine,
+ Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, consteval,
generics::generics,
infer::{
AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable,
@@ -36,7 +33,7 @@ use crate::{
lower::{GenericPredicates, lower_mutability},
method_resolution::{self, CandidateId, MethodCallee, MethodError},
next_solver::{
- ErrorGuaranteed, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError,
+ ClauseKind, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError,
infer::{
BoundRegionConversionTime, InferOk,
traits::{Obligation, ObligationCause},
@@ -44,7 +41,6 @@ use crate::{
obligation_ctxt::ObligationCtxt,
util::clauses_as_obligations,
},
- traits::FnTrait,
};
use super::{
@@ -244,7 +240,6 @@ impl<'db> InferenceContext<'_, 'db> {
| Expr::Assignment { .. }
| Expr::Yield { .. }
| Expr::Cast { .. }
- | Expr::Async { .. }
| Expr::Unsafe { .. }
| Expr::Await { .. }
| Expr::Ref { .. }
@@ -390,9 +385,6 @@ impl<'db> InferenceContext<'_, 'db> {
})
.1
}
- Expr::Async { id: _, statements, tail } => {
- self.infer_async_block(tgt_expr, statements, tail)
- }
&Expr::Loop { body, label } => {
// FIXME: should be:
// let ty = expected.coercion_target_type(&mut self.table);
@@ -1184,134 +1176,6 @@ impl<'db> InferenceContext<'_, 'db> {
}
oprnd_t
}
-
- fn infer_async_block(
- &mut self,
- tgt_expr: ExprId,
- statements: &[Statement],
- tail: &Option<ExprId>,
- ) -> Ty<'db> {
- let ret_ty = self.table.next_ty_var();
- let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty);
- let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty));
-
- // FIXME: We should handle async blocks like we handle closures
- let expected = &Expectation::has_type(ret_ty);
- let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
- let ty = this.infer_block(tgt_expr, statements, *tail, None, expected);
- if let Some(target) = expected.only_has_type(&mut this.table) {
- match this.coerce(tgt_expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes) {
- Ok(res) => res,
- Err(_) => {
- this.result.type_mismatches.get_or_insert_default().insert(
- tgt_expr.into(),
- TypeMismatch { expected: target.store(), actual: ty.store() },
- );
- target
- }
- }
- } else {
- ty
- }
- });
-
- self.diverges = prev_diverges;
- self.return_ty = prev_ret_ty;
- self.return_coercion = prev_ret_coercion;
-
- self.lower_async_block_type_impl_trait(inner_ty, tgt_expr)
- }
-
- pub(crate) fn lower_async_block_type_impl_trait(
- &mut self,
- inner_ty: Ty<'db>,
- tgt_expr: ExprId,
- ) -> Ty<'db> {
- let coroutine_id = InternedCoroutine(self.owner, tgt_expr);
- let coroutine_id = self.db.intern_coroutine(coroutine_id).into();
- let parent_args = GenericArgs::identity_for_item(self.interner(), self.generic_def.into());
- Ty::new_coroutine(
- self.interner(),
- coroutine_id,
- CoroutineArgs::new(
- self.interner(),
- CoroutineArgsParts {
- parent_args: parent_args.as_slice(),
- kind_ty: self.types.types.unit,
- // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
- resume_ty: self.types.types.unit,
- yield_ty: self.types.types.unit,
- return_ty: inner_ty,
- // FIXME: Infer upvars.
- tupled_upvars_ty: self.types.types.unit,
- },
- )
- .args,
- )
- }
-
- pub(crate) fn write_fn_trait_method_resolution(
- &mut self,
- fn_x: FnTrait,
- derefed_callee: Ty<'db>,
- adjustments: &mut Vec<Adjustment>,
- callee_ty: Ty<'db>,
- params: &[Ty<'db>],
- tgt_expr: ExprId,
- ) {
- match fn_x {
- FnTrait::FnOnce | FnTrait::AsyncFnOnce => (),
- FnTrait::FnMut | FnTrait::AsyncFnMut => {
- if let TyKind::Ref(lt, inner, Mutability::Mut) = derefed_callee.kind() {
- if adjustments
- .last()
- .map(|it| matches!(it.kind, Adjust::Borrow(_)))
- .unwrap_or(true)
- {
- // prefer reborrow to move
- adjustments
- .push(Adjustment { kind: Adjust::Deref(None), target: inner.store() });
- adjustments.push(Adjustment::borrow(
- self.interner(),
- Mutability::Mut,
- inner,
- lt,
- ))
- }
- } else {
- adjustments.push(Adjustment::borrow(
- self.interner(),
- Mutability::Mut,
- derefed_callee,
- self.table.next_region_var(),
- ));
- }
- }
- FnTrait::Fn | FnTrait::AsyncFn => {
- if !matches!(derefed_callee.kind(), TyKind::Ref(_, _, Mutability::Not)) {
- adjustments.push(Adjustment::borrow(
- self.interner(),
- Mutability::Not,
- derefed_callee,
- self.table.next_region_var(),
- ));
- }
- }
- }
- let Some(trait_) = fn_x.get_id(self.lang_items) else {
- return;
- };
- let trait_data = trait_.trait_items(self.db);
- if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
- let subst = GenericArgs::new_from_slice(&[
- callee_ty.into(),
- Ty::new_tup(self.interner(), params).into(),
- ]);
- self.write_method_resolution(tgt_expr, func, subst);
- }
- }
-
fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> {
let elem_ty = match expected
.to_option(&mut self.table)
@@ -1728,76 +1592,6 @@ impl<'db> InferenceContext<'_, 'db> {
MethodCallee { def_id, args, sig }
}
- fn infer_call(
- &mut self,
- tgt_expr: ExprId,
- callee: ExprId,
- args: &[ExprId],
- expected: &Expectation<'db>,
- ) -> Ty<'db> {
- let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes);
- let callee_ty = self.table.try_structurally_resolve_type(callee_ty);
- let interner = self.interner();
- let mut derefs = InferenceContextAutoderef::new_from_inference_context(self, callee_ty);
- let (res, derefed_callee) = loop {
- let Some((callee_deref_ty, _)) = derefs.next() else {
- break (None, callee_ty);
- };
- if let Some(res) = derefs.ctx().table.callable_sig(callee_deref_ty, args.len()) {
- break (Some(res), callee_deref_ty);
- }
- };
- // if the function is unresolved, we use is_varargs=true to
- // suppress the arg count diagnostic here
- let is_varargs = derefed_callee.callable_sig(interner).is_some_and(|sig| sig.c_variadic())
- || res.is_none();
- let (param_tys, ret_ty) = match res {
- Some((func, params, ret_ty)) => {
- let infer_ok = derefs.adjust_steps_as_infer_ok();
- let mut adjustments = self.table.register_infer_ok(infer_ok);
- if let Some(fn_x) = func {
- self.write_fn_trait_method_resolution(
- fn_x,
- derefed_callee,
- &mut adjustments,
- callee_ty,
- &params,
- tgt_expr,
- );
- }
- if let TyKind::Closure(c, _) = self.table.resolve_completely(callee_ty).kind() {
- self.add_current_closure_dependency(c.into());
- self.deferred_closures.entry(c.into()).or_default().push((
- derefed_callee,
- callee_ty,
- params.clone(),
- tgt_expr,
- ));
- }
- self.write_expr_adj(callee, adjustments.into_boxed_slice());
- (params, ret_ty)
- }
- None => {
- self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
- call_expr: tgt_expr,
- found: callee_ty.store(),
- });
- (Vec::new(), Ty::new_error(interner, ErrorGuaranteed))
- }
- };
- let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
- self.check_call(
- tgt_expr,
- args,
- callee_ty,
- &param_tys,
- ret_ty,
- &indices_to_skip,
- is_varargs,
- expected,
- )
- }
-
fn check_call(
&mut self,
tgt_expr: ExprId,
@@ -1819,6 +1613,7 @@ impl<'db> InferenceContext<'_, 'db> {
args,
indices_to_skip,
is_varargs,
+ TupleArgumentsFlag::DontTupleArguments,
);
ret_ty
}
@@ -1949,13 +1744,22 @@ impl<'db> InferenceContext<'_, 'db> {
};
let ret_ty = sig.output();
- self.check_call_arguments(tgt_expr, param_tys, ret_ty, expected, args, &[], sig.c_variadic);
+ self.check_call_arguments(
+ tgt_expr,
+ param_tys,
+ ret_ty,
+ expected,
+ args,
+ &[],
+ sig.c_variadic,
+ TupleArgumentsFlag::DontTupleArguments,
+ );
ret_ty
}
/// Generic function that factors out common logic from function calls,
/// method calls and overloaded operators.
- pub(in super::super) fn check_call_arguments(
+ pub(super) fn check_call_arguments(
&mut self,
call_expr: ExprId,
// Types (as defined in the *signature* of the target function)
@@ -1968,7 +1772,18 @@ impl<'db> InferenceContext<'_, 'db> {
skip_indices: &[u32],
// Whether the function is variadic, for example when imported from C
c_variadic: bool,
+ // Whether the arguments have been bundled in a tuple (ex: closures)
+ tuple_arguments: TupleArgumentsFlag,
) {
+ let formal_input_tys: Vec<_> = formal_input_tys
+ .iter()
+ .map(|&ty| {
+ let generalized_ty = self.table.next_ty_var();
+ let _ = self.demand_eqtype(call_expr.into(), ty, generalized_ty);
+ generalized_ty
+ })
+ .collect();
+
// First, let's unify the formal method signature with the expectation eagerly.
// We use this to guide coercion inference; it's output is "fudged" which means
// any remaining type variables are assigned to new, unrelated variables. This
@@ -1988,29 +1803,68 @@ impl<'db> InferenceContext<'_, 'db> {
// No argument expectations are produced if unification fails.
let origin = ObligationCause::new();
ocx.sup(&origin, self.table.param_env, expected_output, formal_output)?;
+
+ for &ty in &formal_input_tys {
+ ocx.register_obligation(Obligation::new(
+ self.interner(),
+ ObligationCause::new(),
+ self.table.param_env,
+ ClauseKind::WellFormed(ty.into()),
+ ));
+ }
+
if !ocx.try_evaluate_obligations().is_empty() {
return Err(TypeError::Mismatch);
}
// Record all the argument types, with the args
// produced from the above subtyping unification.
- Ok(Some(
- formal_input_tys
- .iter()
- .map(|&ty| self.table.infer_ctxt.resolve_vars_if_possible(ty))
- .collect(),
- ))
+ Ok(Some(formal_input_tys.clone()))
})
.ok()
})
.unwrap_or_default();
+ // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
+ let (formal_input_tys, expected_input_tys) =
+ if tuple_arguments == TupleArgumentsFlag::TupleArguments {
+ let tuple_type = self.table.structurally_resolve_type(formal_input_tys[0]);
+ match tuple_type.kind() {
+ // We expected a tuple and got a tuple
+ TyKind::Tuple(arg_types) => {
+ // Argument length differs
+ if arg_types.len() != provided_args.len() {
+ // FIXME: Emit an error.
+ }
+ let expected_input_tys = match expected_input_tys {
+ Some(expected_input_tys) => match expected_input_tys.first() {
+ Some(ty) => match ty.kind() {
+ TyKind::Tuple(tys) => Some(tys.iter().collect()),
+ _ => None,
+ },
+ None => None,
+ },
+ None => None,
+ };
+ (arg_types.iter().collect(), expected_input_tys)
+ }
+ _ => {
+ // Otherwise, there's a mismatch, so clear out what we're expecting, and set
+ // our input types to err_args so we don't blow up the error messages
+ // FIXME: Emit an error.
+ (vec![self.types.types.error; provided_args.len()], None)
+ }
+ }
+ } else {
+ (formal_input_tys.to_vec(), expected_input_tys)
+ };
+
// If there are no external expectations at the call site, just use the types from the function defn
- let expected_input_tys = if let Some(expected_input_tys) = &expected_input_tys {
+ let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys {
assert_eq!(expected_input_tys.len(), formal_input_tys.len());
expected_input_tys
} else {
- formal_input_tys
+ formal_input_tys.clone()
};
let minimum_input_count = expected_input_tys.len();
@@ -2183,51 +2037,6 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
- /// Returns the argument indices to skip.
- fn check_legacy_const_generics(&mut self, callee: Ty<'db>, args: &[ExprId]) -> Box<[u32]> {
- let (func, _subst) = match callee.kind() {
- TyKind::FnDef(callable, subst) => {
- let func = match callable.0 {
- CallableDefId::FunctionId(f) => f,
- _ => return Default::default(),
- };
- (func, subst)
- }
- _ => return Default::default(),
- };
-
- let data = FunctionSignature::of(self.db, func);
- let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func)
- else {
- return Default::default();
- };
- let mut legacy_const_generics_indices = Box::<[u32]>::from(legacy_const_generics_indices);
-
- // only use legacy const generics if the param count matches with them
- if data.params.len() + legacy_const_generics_indices.len() != args.len() {
- if args.len() <= data.params.len() {
- return Default::default();
- } else {
- // there are more parameters than there should be without legacy
- // const params; use them
- legacy_const_generics_indices.sort_unstable();
- return legacy_const_generics_indices;
- }
- }
-
- // check legacy const parameters
- for arg_idx in legacy_const_generics_indices.iter().copied() {
- if arg_idx >= args.len() as u32 {
- continue;
- }
- let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
- self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
- // FIXME: evaluate and unify with the const
- }
- legacy_const_generics_indices.sort_unstable();
- legacy_const_generics_indices
- }
-
pub(super) fn with_breakable_ctx<T>(
&mut self,
kind: BreakableKind,
@@ -2243,3 +2052,28 @@ impl<'db> InferenceContext<'_, 'db> {
(if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res)
}
}
+
+/// Controls whether the arguments are tupled. This is used for the call
+/// operator.
+///
+/// Tupling means that all call-side arguments are packed into a tuple and
+/// passed as a single parameter. For example, if tupling is enabled, this
+/// function:
+/// ```
+/// fn f(x: (isize, isize)) {}
+/// ```
+/// Can be called as:
+/// ```ignore UNSOLVED (can this be done in user code?)
+/// # fn f(x: (isize, isize)) {}
+/// f(1, 2);
+/// ```
+/// Instead of:
+/// ```
+/// # fn f(x: (isize, isize)) {}
+/// f((1, 2));
+/// ```
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub(super) enum TupleArgumentsFlag {
+ DontTupleArguments,
+ TupleArguments,
+}