let lhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
let (rhs_ty, return_ty) =
- check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, true);
+ check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes);
let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
// overloaded. This is the way to be most flexible w/r/t
// types that get inferred.
let (rhs_ty, return_ty) =
- check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, false);
+ check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::No);
// Supply type inference hints if relevant. Probably these
// hints should be enforced during select as part of the
lhs_ty: Ty<'tcx>,
rhs_expr: &'tcx hir::Expr,
op: hir::BinOp,
- assign: bool)
+ is_assign: IsAssign)
-> (Ty<'tcx>, Ty<'tcx>)
{
- debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, assign={})",
+ debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
expr.id,
lhs_ty,
- assign);
+ is_assign);
- let (name, trait_def_id) = name_and_trait_def_id(fcx, op, assign);
+ let (name, trait_def_id) = name_and_trait_def_id(fcx, op, is_assign);
// NB: As we have not yet type-checked the RHS, we don't have the
// type at hand. Make a variable to represent it. The whole reason
Err(()) => {
// error types are considered "builtin"
if !lhs_ty.references_error() {
- if assign {
+ if let IsAssign::Yes = is_assign {
span_err!(fcx.tcx().sess, lhs_expr.span, E0368,
"binary assignment operation `{}=` cannot be applied to type `{}`",
hir_util::binop_to_string(op.node),
lhs_ty);
} else {
- span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
- "binary operation `{}` cannot be applied to type `{}`",
- hir_util::binop_to_string(op.node),
- lhs_ty);
+ let mut err = struct_span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
+ "binary operation `{}` cannot be applied to type `{}`",
+ hir_util::binop_to_string(op.node),
+ lhs_ty);
+ let missing_trait = match op.node {
+ hir::BiAdd => Some("std::ops::Add"),
+ hir::BiSub => Some("std::ops::Sub"),
+ hir::BiMul => Some("std::ops::Mul"),
+ hir::BiDiv => Some("std::ops::Div"),
+ hir::BiRem => Some("std::ops::Rem"),
+ hir::BiBitAnd => Some("std::ops::BitAnd"),
+ hir::BiBitOr => Some("std::ops::BitOr"),
+ hir::BiShl => Some("std::ops::Shl"),
+ hir::BiShr => Some("std::ops::Shr"),
+ hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
+ hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
+ Some("std::cmp::PartialOrd"),
+ _ => None
+ };
+
+ if let Some(missing_trait) = missing_trait {
+ span_note!(&mut err, lhs_expr.span,
+ "an implementation of `{}` might be missing for `{}`",
+ missing_trait, lhs_ty);
+ }
+ err.emit();
}
}
fcx.tcx().types.err
fn name_and_trait_def_id(fcx: &FnCtxt,
op: hir::BinOp,
- assign: bool)
+ is_assign: IsAssign)
-> (&'static str, Option<DefId>) {
let lang = &fcx.tcx().lang_items;
- if assign {
+ if let IsAssign::Yes = is_assign {
match op.node {
hir::BiAdd => ("add_assign", lang.add_assign_trait()),
hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
}
}
+/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
+#[derive(Clone, Copy, Debug)]
+enum IsAssign {
+ No,
+ Yes,
+}
+
/// Returns true if this is a built-in arithmetic operation (e.g. u32
/// + u32, i16x4 == i16x4) and false if these types would have to be
/// overloaded to be legal. There are two reasons that we distinguish