summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs')
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs378
1 files changed, 222 insertions, 156 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 8f9cdac37..175fded8c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -10,15 +10,15 @@ use chalk_ir::{
};
use hir_def::{
expr::{
- ArithOp, Array, BinaryOp, ClosureKind, CmpOp, Expr, ExprId, LabelId, Literal, Statement,
- UnaryOp,
+ ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
},
generics::TypeOrConstParamData,
+ lang_item::LangItem,
path::{GenericArg, GenericArgs},
resolver::resolver_for_expr,
ConstParamId, FieldId, ItemContainerId, Lookup,
};
-use hir_expand::name::Name;
+use hir_expand::name::{name, Name};
use stdx::always;
use syntax::ast::RangeOp;
@@ -30,7 +30,7 @@ use crate::{
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
},
mapping::{from_chalk, ToChalk},
- method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
+ method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
utils::{generics, Generics},
@@ -87,16 +87,15 @@ impl<'a> InferenceContext<'a> {
let expected = &expected.adjust_for_branches(&mut self.table);
self.infer_expr(
condition,
- &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
+ &Expectation::HasType(self.result.standard_types.bool_.clone()),
);
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let mut both_arms_diverge = Diverges::Always;
- let result_ty = self.table.new_type_var();
let then_ty = self.infer_expr_inner(then_branch, expected);
both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
- let mut coerce = CoerceMany::new(result_ty);
+ let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
coerce.coerce(self, Some(then_branch), &then_ty);
let else_ty = match else_branch {
Some(else_branch) => self.infer_expr_inner(else_branch, expected),
@@ -113,7 +112,7 @@ impl<'a> InferenceContext<'a> {
&Expr::Let { pat, expr } => {
let input_ty = self.infer_expr(expr, &Expectation::none());
self.infer_pat(pat, &input_ty, BindingMode::default());
- TyKind::Scalar(Scalar::Bool).intern(Interner)
+ self.result.standard_types.bool_.clone()
}
Expr::Block { statements, tail, label, id: _ } => {
let old_resolver = mem::replace(
@@ -158,7 +157,8 @@ impl<'a> InferenceContext<'a> {
}
// The ok-ish type that is expected from the last expression
- let ok_ty = self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_ok());
+ let ok_ty =
+ self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
self.with_breakable_ctx(BreakableKind::Block, ok_ty.clone(), None, |this| {
this.infer_expr(*body, &Expectation::has_type(ok_ty));
@@ -187,10 +187,12 @@ impl<'a> InferenceContext<'a> {
.intern(Interner)
}
&Expr::Loop { body, label } => {
+ // FIXME: should be:
+ // let ty = expected.coercion_target_type(&mut self.table);
let ty = self.table.new_type_var();
let (breaks, ()) =
self.with_breakable_ctx(BreakableKind::Loop, ty, label, |this| {
- this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
+ this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
});
match breaks {
@@ -198,16 +200,16 @@ impl<'a> InferenceContext<'a> {
self.diverges = Diverges::Maybe;
breaks
}
- None => TyKind::Never.intern(Interner),
+ None => self.result.standard_types.never.clone(),
}
}
&Expr::While { condition, body, label } => {
self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
this.infer_expr(
condition,
- &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
+ &Expectation::HasType(this.result.standard_types.bool_.clone()),
);
- this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
+ this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
});
// the body may not run, so it diverging doesn't mean we diverge
@@ -223,7 +225,7 @@ impl<'a> InferenceContext<'a> {
self.infer_pat(pat, &pat_ty, BindingMode::default());
self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
- this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
+ this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
});
// the body may not run, so it diverging doesn't mean we diverge
@@ -233,7 +235,7 @@ impl<'a> InferenceContext<'a> {
Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
assert_eq!(args.len(), arg_types.len());
- let mut sig_tys = Vec::new();
+ let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
// collect explicitly written argument types
for arg_type in arg_types.iter() {
@@ -254,7 +256,8 @@ impl<'a> InferenceContext<'a> {
num_binders: 0,
sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
substitution: FnSubst(
- Substitution::from_iter(Interner, sig_tys.clone()).shifted_in(Interner),
+ Substitution::from_iter(Interner, sig_tys.iter().cloned())
+ .shifted_in(Interner),
),
})
.intern(Interner);
@@ -316,27 +319,34 @@ impl<'a> InferenceContext<'a> {
Expr::Call { callee, args, .. } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none());
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
- let mut res = None;
- let mut derefed_callee = callee_ty.clone();
- // manual loop to be able to access `derefs.table`
- while let Some((callee_deref_ty, _)) = derefs.next() {
- res = derefs.table.callable_sig(&callee_deref_ty, args.len());
- if res.is_some() {
- derefed_callee = callee_deref_ty;
- break;
+ let (res, derefed_callee) = 'b: {
+ // manual loop to be able to access `derefs.table`
+ while let Some((callee_deref_ty, _)) = derefs.next() {
+ let res = derefs.table.callable_sig(&callee_deref_ty, args.len());
+ if res.is_some() {
+ break 'b (res, callee_deref_ty);
+ }
}
- }
+ (None, callee_ty.clone())
+ };
// if the function is unresolved, we use is_varargs=true to
// suppress the arg count diagnostic here
let is_varargs =
derefed_callee.callable_sig(self.db).map_or(false, |sig| sig.is_varargs)
|| res.is_none();
let (param_tys, ret_ty) = match res {
- Some(res) => {
+ Some((func, params, ret_ty)) => {
let adjustments = auto_deref_adjust_steps(&derefs);
// FIXME: Handle call adjustments for Fn/FnMut
self.write_expr_adj(*callee, adjustments);
- res
+ if let Some((trait_, func)) = func {
+ let subst = TyBuilder::subst_for_def(self.db, trait_, None)
+ .push(callee_ty.clone())
+ .push(TyBuilder::tuple_with(params.iter().cloned()))
+ .build();
+ self.write_method_resolution(tgt_expr, func, subst.clone());
+ }
+ (params, ret_ty)
}
None => (Vec::new(), self.err_ty()), // FIXME diagnostic
};
@@ -374,12 +384,9 @@ impl<'a> InferenceContext<'a> {
let expected = expected.adjust_for_branches(&mut self.table);
let result_ty = if arms.is_empty() {
- TyKind::Never.intern(Interner)
+ self.result.standard_types.never.clone()
} else {
- match &expected {
- Expectation::HasType(ty) => ty.clone(),
- _ => self.table.new_type_var(),
- }
+ expected.coercion_target_type(&mut self.table)
};
let mut coerce = CoerceMany::new(result_ty);
@@ -392,7 +399,7 @@ impl<'a> InferenceContext<'a> {
if let Some(guard_expr) = arm.guard {
self.infer_expr(
guard_expr,
- &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
+ &Expectation::HasType(self.result.standard_types.bool_.clone()),
);
}
@@ -417,7 +424,7 @@ impl<'a> InferenceContext<'a> {
is_break: false,
});
};
- TyKind::Never.intern(Interner)
+ self.result.standard_types.never.clone()
}
Expr::Break { expr, label } => {
let val_ty = if let Some(expr) = *expr {
@@ -431,7 +438,7 @@ impl<'a> InferenceContext<'a> {
// avoiding the borrowck
let mut coerce = mem::replace(
&mut ctxt.coerce,
- CoerceMany::new(self.result.standard_types.unknown.clone()),
+ CoerceMany::new(expected.coercion_target_type(&mut self.table)),
);
// FIXME: create a synthetic `()` during lowering so we have something to refer to here?
@@ -449,7 +456,7 @@ impl<'a> InferenceContext<'a> {
});
}
}
- TyKind::Never.intern(Interner)
+ self.result.standard_types.never.clone()
}
Expr::Return { expr } => {
if let Some(expr) = expr {
@@ -458,7 +465,7 @@ impl<'a> InferenceContext<'a> {
let unit = TyBuilder::unit();
let _ = self.coerce(Some(tgt_expr), &unit, &self.return_ty.clone());
}
- TyKind::Never.intern(Interner)
+ self.result.standard_types.never.clone()
}
Expr::Yield { expr } => {
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
@@ -471,14 +478,14 @@ impl<'a> InferenceContext<'a> {
resume_ty
} else {
// FIXME: report error (yield expr in non-generator)
- TyKind::Error.intern(Interner)
+ self.result.standard_types.unknown.clone()
}
}
Expr::Yeet { expr } => {
if let &Some(expr) = expr {
self.infer_expr_inner(expr, &Expectation::None);
}
- TyKind::Never.intern(Interner)
+ self.result.standard_types.never.clone()
}
Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
@@ -588,12 +595,23 @@ impl<'a> InferenceContext<'a> {
}
Expr::Try { expr } => {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
- self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
+ if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) {
+ if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) {
+ let subst = TyBuilder::subst_for_def(self.db, trait_, None)
+ .push(inner_ty.clone())
+ .build();
+ self.write_method_resolution(tgt_expr, func, subst.clone());
+ }
+ let try_output = self.resolve_output_on(trait_);
+ self.resolve_associated_type(inner_ty, try_output)
+ } else {
+ self.err_ty()
+ }
}
Expr::Cast { expr, type_ref } => {
- // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
- let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
let cast_ty = self.make_ty(type_ref);
+ // FIXME: propagate the "castable to" expectation
+ let _inner_ty = self.infer_expr_inner(*expr, &Expectation::None);
// FIXME check the cast...
cast_ty
}
@@ -627,6 +645,7 @@ impl<'a> InferenceContext<'a> {
Expr::UnaryOp { expr, op } => {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
let inner_ty = self.resolve_ty_shallow(&inner_ty);
+ // FIXME: Note down method resolution her
match op {
UnaryOp::Deref => {
autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
@@ -736,7 +755,7 @@ impl<'a> InferenceContext<'a> {
let base_ty = self.infer_expr_inner(*base, &Expectation::none());
let index_ty = self.infer_expr(*index, &Expectation::none());
- if let Some(index_trait) = self.resolve_ops_index() {
+ if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
let canonicalized = self.canonicalize(base_ty.clone());
let receiver_adjustments = method_resolution::resolve_indexing_op(
self.db,
@@ -749,6 +768,15 @@ impl<'a> InferenceContext<'a> {
adj.apply(&mut self.table, base_ty)
});
self.write_expr_adj(*base, adj);
+ if let Some(func) =
+ self.db.trait_data(index_trait).method_by_name(&name!(index))
+ {
+ let substs = TyBuilder::subst_for_def(self.db, index_trait, None)
+ .push(self_ty.clone())
+ .push(index_ty.clone())
+ .build();
+ self.write_method_resolution(tgt_expr, func, substs.clone());
+ }
self.resolve_associated_type_with_params(
self_ty,
self.resolve_ops_index_output(),
@@ -800,7 +828,7 @@ impl<'a> InferenceContext<'a> {
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
self.infer_expr(
repeat,
- &Expectation::has_type(
+ &Expectation::HasType(
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
),
);
@@ -823,7 +851,7 @@ impl<'a> InferenceContext<'a> {
TyKind::Array(coerce.complete(), len).intern(Interner)
}
Expr::Literal(lit) => match lit {
- Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(Interner),
+ Literal::Bool(..) => self.result.standard_types.bool_.clone(),
Literal::String(..) => {
TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(Interner))
.intern(Interner)
@@ -1009,7 +1037,7 @@ impl<'a> InferenceContext<'a> {
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
let rhs_ty = self.table.new_type_var();
- let trait_func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
+ let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| {
let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?;
let func = self.db.trait_data(trait_id).method_by_name(&name)?;
Some((trait_id, func))
@@ -1017,11 +1045,21 @@ impl<'a> InferenceContext<'a> {
let (trait_, func) = match trait_func {
Some(it) => it,
None => {
- let rhs_ty = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone());
- let rhs_ty = self.infer_expr_coerce(rhs, &Expectation::from_option(rhs_ty));
- return self
- .builtin_binary_op_return_ty(op, lhs_ty, rhs_ty)
- .unwrap_or_else(|| self.err_ty());
+ // HACK: `rhs_ty` is a general inference variable with no clue at all at this
+ // point. Passing `lhs_ty` as both operands just to check if `lhs_ty` is a builtin
+ // type applicable to `op`.
+ let ret_ty = if self.is_builtin_binop(&lhs_ty, &lhs_ty, op) {
+ // Assume both operands are builtin so we can continue inference. No guarantee
+ // on the correctness, rustc would complain as necessary lang items don't seem
+ // to exist anyway.
+ self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op)
+ } else {
+ self.err_ty()
+ };
+
+ self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty));
+
+ return ret_ty;
}
};
@@ -1071,11 +1109,9 @@ impl<'a> InferenceContext<'a> {
let ret_ty = self.normalize_associated_types_in(ret_ty);
- // use knowledge of built-in binary ops, which can sometimes help inference
- if let Some(builtin_rhs) = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone()) {
- self.unify(&builtin_rhs, &rhs_ty);
- }
- if let Some(builtin_ret) = self.builtin_binary_op_return_ty(op, lhs_ty, rhs_ty) {
+ if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) {
+ // use knowledge of built-in binary ops, which can sometimes help inference
+ let builtin_ret = self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op);
self.unify(&builtin_ret, &ret_ty);
}
@@ -1111,7 +1147,7 @@ impl<'a> InferenceContext<'a> {
if let Some(expr) = else_branch {
self.infer_expr_coerce(
*expr,
- &Expectation::has_type(Ty::new(Interner, TyKind::Never)),
+ &Expectation::HasType(self.result.standard_types.never.clone()),
);
}
@@ -1136,18 +1172,16 @@ impl<'a> InferenceContext<'a> {
if self.diverges.is_always() {
// we don't even make an attempt at coercion
self.table.new_maybe_never_var()
- } else {
- if let Some(t) = expected.only_has_type(&mut self.table) {
- if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
- self.result.type_mismatches.insert(
- expr.into(),
- TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
- );
- }
- t
- } else {
- TyBuilder::unit()
+ } else if let Some(t) = expected.only_has_type(&mut self.table) {
+ if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
+ self.result.type_mismatches.insert(
+ expr.into(),
+ TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
+ );
}
+ t
+ } else {
+ TyBuilder::unit()
}
}
}
@@ -1271,7 +1305,7 @@ impl<'a> InferenceContext<'a> {
// that are not closures, then we type-check the closures. This is so
// that we have more information about the types of arguments when we
// type-check the functions. This isn't really the right way to do this.
- for &check_closures in &[false, true] {
+ for check_closures in [false, true] {
let mut skip_indices = skip_indices.into_iter().copied().fuse().peekable();
let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty()));
let expected_iter = expected_inputs
@@ -1314,13 +1348,13 @@ impl<'a> InferenceContext<'a> {
} else {
param_ty
};
- if !coercion_target.is_unknown() {
- if self.coerce(Some(arg), &ty, &coercion_target).is_err() {
- self.result.type_mismatches.insert(
- arg.into(),
- TypeMismatch { expected: coercion_target, actual: ty.clone() },
- );
- }
+ if !coercion_target.is_unknown()
+ && self.coerce(Some(arg), &ty, &coercion_target).is_err()
+ {
+ self.result.type_mismatches.insert(
+ arg.into(),
+ TypeMismatch { expected: coercion_target, actual: ty.clone() },
+ );
}
}
}
@@ -1479,92 +1513,124 @@ impl<'a> InferenceContext<'a> {
indices
}
- fn builtin_binary_op_return_ty(&mut self, op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Option<Ty> {
- let lhs_ty = self.resolve_ty_shallow(&lhs_ty);
- let rhs_ty = self.resolve_ty_shallow(&rhs_ty);
- match op {
- BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => {
- Some(TyKind::Scalar(Scalar::Bool).intern(Interner))
+ /// Dereferences a single level of immutable referencing.
+ fn deref_ty_if_possible(&mut self, ty: &Ty) -> Ty {
+ let ty = self.resolve_ty_shallow(ty);
+ match ty.kind(Interner) {
+ TyKind::Ref(Mutability::Not, _, inner) => self.resolve_ty_shallow(inner),
+ _ => ty,
+ }
+ }
+
+ /// Enforces expectations on lhs type and rhs type depending on the operator and returns the
+ /// output type of the binary op.
+ fn enforce_builtin_binop_types(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> Ty {
+ // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
+ let lhs = self.deref_ty_if_possible(lhs);
+ let rhs = self.deref_ty_if_possible(rhs);
+
+ let (op, is_assign) = match op {
+ BinaryOp::Assignment { op: Some(inner) } => (BinaryOp::ArithOp(inner), true),
+ _ => (op, false),
+ };
+
+ let output_ty = match op {
+ BinaryOp::LogicOp(_) => {
+ let bool_ = self.result.standard_types.bool_.clone();
+ self.unify(&lhs, &bool_);
+ self.unify(&rhs, &bool_);
+ bool_
}
- BinaryOp::Assignment { .. } => Some(TyBuilder::unit()),
+
BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
- // all integer combinations are valid here
- if matches!(
- lhs_ty.kind(Interner),
- TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
- | TyKind::InferenceVar(_, TyVariableKind::Integer)
- ) && matches!(
- rhs_ty.kind(Interner),
- TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
- | TyKind::InferenceVar(_, TyVariableKind::Integer)
- ) {
- Some(lhs_ty)
- } else {
- None
- }
+ // result type is same as LHS always
+ lhs
}
- BinaryOp::ArithOp(_) => match (lhs_ty.kind(Interner), rhs_ty.kind(Interner)) {
- // (int, int) | (uint, uint) | (float, float)
- (TyKind::Scalar(Scalar::Int(_)), TyKind::Scalar(Scalar::Int(_)))
- | (TyKind::Scalar(Scalar::Uint(_)), TyKind::Scalar(Scalar::Uint(_)))
- | (TyKind::Scalar(Scalar::Float(_)), TyKind::Scalar(Scalar::Float(_))) => {
- Some(rhs_ty)
- }
- // ({int}, int) | ({int}, uint)
- (
- TyKind::InferenceVar(_, TyVariableKind::Integer),
- TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)),
- ) => Some(rhs_ty),
- // (int, {int}) | (uint, {int})
- (
- TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)),
- TyKind::InferenceVar(_, TyVariableKind::Integer),
- ) => Some(lhs_ty),
- // ({float} | float)
- (
- TyKind::InferenceVar(_, TyVariableKind::Float),
- TyKind::Scalar(Scalar::Float(_)),
- ) => Some(rhs_ty),
- // (float, {float})
- (
- TyKind::Scalar(Scalar::Float(_)),
- TyKind::InferenceVar(_, TyVariableKind::Float),
- ) => Some(lhs_ty),
- // ({int}, {int}) | ({float}, {float})
- (
- TyKind::InferenceVar(_, TyVariableKind::Integer),
- TyKind::InferenceVar(_, TyVariableKind::Integer),
- )
- | (
- TyKind::InferenceVar(_, TyVariableKind::Float),
- TyKind::InferenceVar(_, TyVariableKind::Float),
- ) => Some(rhs_ty),
- _ => None,
- },
+
+ BinaryOp::ArithOp(_) => {
+ // LHS, RHS, and result will have the same type
+ self.unify(&lhs, &rhs);
+ lhs
+ }
+
+ BinaryOp::CmpOp(_) => {
+ // LHS and RHS will have the same type
+ self.unify(&lhs, &rhs);
+ self.result.standard_types.bool_.clone()
+ }
+
+ BinaryOp::Assignment { op: None } => {
+ stdx::never!("Simple assignment operator is not binary op.");
+ lhs
+ }
+
+ BinaryOp::Assignment { .. } => unreachable!("handled above"),
+ };
+
+ if is_assign {
+ self.result.standard_types.unit.clone()
+ } else {
+ output_ty
}
}
- fn builtin_binary_op_rhs_expectation(&mut self, op: BinaryOp, lhs_ty: Ty) -> Option<Ty> {
- Some(match op {
- BinaryOp::LogicOp(..) => TyKind::Scalar(Scalar::Bool).intern(Interner),
- BinaryOp::Assignment { op: None } => lhs_ty,
- BinaryOp::CmpOp(CmpOp::Eq { .. }) => match self
- .resolve_ty_shallow(&lhs_ty)
- .kind(Interner)
- {
- TyKind::Scalar(_) | TyKind::Str => lhs_ty,
- TyKind::InferenceVar(_, TyVariableKind::Integer | TyVariableKind::Float) => lhs_ty,
- _ => return None,
- },
- BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => return None,
- BinaryOp::CmpOp(CmpOp::Ord { .. })
- | BinaryOp::Assignment { op: Some(_) }
- | BinaryOp::ArithOp(_) => match self.resolve_ty_shallow(&lhs_ty).kind(Interner) {
- TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) => lhs_ty,
- TyKind::InferenceVar(_, TyVariableKind::Integer | TyVariableKind::Float) => lhs_ty,
- _ => return None,
- },
- })
+ fn is_builtin_binop(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> bool {
+ // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
+ let lhs = self.deref_ty_if_possible(lhs);
+ let rhs = self.deref_ty_if_possible(rhs);
+
+ let op = match op {
+ BinaryOp::Assignment { op: Some(inner) } => BinaryOp::ArithOp(inner),
+ _ => op,
+ };
+
+ match op {
+ BinaryOp::LogicOp(_) => true,
+
+ BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
+ lhs.is_integral() && rhs.is_integral()
+ }
+
+ BinaryOp::ArithOp(
+ ArithOp::Add | ArithOp::Sub | ArithOp::Mul | ArithOp::Div | ArithOp::Rem,
+ ) => {
+ lhs.is_integral() && rhs.is_integral()
+ || lhs.is_floating_point() && rhs.is_floating_point()
+ }
+
+ BinaryOp::ArithOp(ArithOp::BitAnd | ArithOp::BitOr | ArithOp::BitXor) => {
+ lhs.is_integral() && rhs.is_integral()
+ || lhs.is_floating_point() && rhs.is_floating_point()
+ || matches!(
+ (lhs.kind(Interner), rhs.kind(Interner)),
+ (TyKind::Scalar(Scalar::Bool), TyKind::Scalar(Scalar::Bool))
+ )
+ }
+
+ BinaryOp::CmpOp(_) => {
+ let is_scalar = |kind| {
+ matches!(
+ kind,
+ &TyKind::Scalar(_)
+ | TyKind::FnDef(..)
+ | TyKind::Function(_)
+ | TyKind::Raw(..)
+ | TyKind::InferenceVar(
+ _,
+ TyVariableKind::Integer | TyVariableKind::Float
+ )
+ )
+ };
+ is_scalar(lhs.kind(Interner)) && is_scalar(rhs.kind(Interner))
+ }
+
+ BinaryOp::Assignment { op: None } => {
+ stdx::never!("Simple assignment operator is not binary op.");
+ false
+ }
+
+ BinaryOp::Assignment { .. } => unreachable!("handled above"),
+ }
}
fn with_breakable_ctx<T>(