demand,
method,
FnCtxt,
- PreferMutLvalue,
- structurally_resolved_type,
};
use middle::def_id::DefId;
-use middle::traits;
-use middle::ty::{Ty, HasTypeFlags};
+use middle::ty::{Ty, HasTypeFlags, PreferMutLvalue};
use syntax::ast;
-use syntax::ast_util;
use syntax::parse::token;
+use rustc_front::hir;
+use rustc_front::util as hir_util;
/// Check a `a <op>= b`
pub fn check_binop_assign<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
- expr: &'tcx ast::Expr,
- op: ast::BinOp,
- lhs_expr: &'tcx ast::Expr,
- rhs_expr: &'tcx ast::Expr)
+ expr: &'tcx hir::Expr,
+ op: hir::BinOp,
+ lhs_expr: &'tcx hir::Expr,
+ rhs_expr: &'tcx hir::Expr)
{
- let tcx = fcx.ccx.tcx;
-
check_expr_with_lvalue_pref(fcx, lhs_expr, PreferMutLvalue);
- check_expr(fcx, rhs_expr);
- let lhs_ty = structurally_resolved_type(fcx, lhs_expr.span, fcx.expr_ty(lhs_expr));
- let rhs_ty = structurally_resolved_type(fcx, rhs_expr.span, fcx.expr_ty(rhs_expr));
+ 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, IsAssign::Yes);
+ let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
- if is_builtin_binop(lhs_ty, rhs_ty, op) {
+ if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
fcx.write_nil(expr.id);
} else {
- // error types are considered "builtin"
- assert!(!lhs_ty.references_error() || !rhs_ty.references_error());
- span_err!(tcx.sess, lhs_expr.span, E0368,
- "binary assignment operation `{}=` cannot be applied to types `{}` and `{}`",
- ast_util::binop_to_string(op.node),
- lhs_ty,
- rhs_ty);
- fcx.write_error(expr.id);
+ fcx.write_ty(expr.id, return_ty);
}
let tcx = fcx.tcx();
if !tcx.expr_is_lval(lhs_expr) {
span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression");
}
-
- fcx.require_expr_have_sized_type(lhs_expr, traits::AssignmentLhsSized);
}
/// Check a potentially overloaded binary operator.
pub fn check_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
- expr: &'tcx ast::Expr,
- op: ast::BinOp,
- lhs_expr: &'tcx ast::Expr,
- rhs_expr: &'tcx ast::Expr)
+ expr: &'tcx hir::Expr,
+ op: hir::BinOp,
+ lhs_expr: &'tcx hir::Expr,
+ rhs_expr: &'tcx hir::Expr)
{
let tcx = fcx.ccx.tcx;
// 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);
+ 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
}
fn enforce_builtin_binop_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
- lhs_expr: &'tcx ast::Expr,
+ lhs_expr: &'tcx hir::Expr,
lhs_ty: Ty<'tcx>,
- rhs_expr: &'tcx ast::Expr,
+ rhs_expr: &'tcx hir::Expr,
rhs_ty: Ty<'tcx>,
- op: ast::BinOp)
+ op: hir::BinOp)
-> Ty<'tcx>
{
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
}
fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
- expr: &'tcx ast::Expr,
- lhs_expr: &'tcx ast::Expr,
+ expr: &'tcx hir::Expr,
+ lhs_expr: &'tcx hir::Expr,
lhs_ty: Ty<'tcx>,
- rhs_expr: &'tcx ast::Expr,
- op: ast::BinOp)
+ rhs_expr: &'tcx hir::Expr,
+ op: hir::BinOp,
+ is_assign: IsAssign)
-> (Ty<'tcx>, Ty<'tcx>)
{
- debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?})",
+ debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
expr.id,
- lhs_ty);
+ lhs_ty,
+ is_assign);
- let (name, trait_def_id) = name_and_trait_def_id(fcx, op);
+ 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() {
- span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
- "binary operation `{}` cannot be applied to type `{}`",
- ast_util::binop_to_string(op.node),
- lhs_ty);
+ 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 {
+ 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
}
op_str: &str,
mname: &str,
trait_did: Option<DefId>,
- ex: &'tcx ast::Expr,
- operand_expr: &'tcx ast::Expr,
+ ex: &'tcx hir::Expr,
+ operand_expr: &'tcx hir::Expr,
operand_ty: Ty<'tcx>,
- op: ast::UnOp)
+ op: hir::UnOp)
-> Ty<'tcx>
{
- assert!(ast_util::is_by_value_unop(op));
+ assert!(hir_util::is_by_value_unop(op));
match lookup_op_method(fcx, ex, operand_ty, vec![],
token::intern(mname), trait_did,
operand_expr) {
}
}
-fn name_and_trait_def_id(fcx: &FnCtxt, op: ast::BinOp) -> (&'static str, Option<DefId>) {
+fn name_and_trait_def_id(fcx: &FnCtxt,
+ op: hir::BinOp,
+ is_assign: IsAssign)
+ -> (&'static str, Option<DefId>) {
let lang = &fcx.tcx().lang_items;
- match op.node {
- ast::BiAdd => ("add", lang.add_trait()),
- ast::BiSub => ("sub", lang.sub_trait()),
- ast::BiMul => ("mul", lang.mul_trait()),
- ast::BiDiv => ("div", lang.div_trait()),
- ast::BiRem => ("rem", lang.rem_trait()),
- ast::BiBitXor => ("bitxor", lang.bitxor_trait()),
- ast::BiBitAnd => ("bitand", lang.bitand_trait()),
- ast::BiBitOr => ("bitor", lang.bitor_trait()),
- ast::BiShl => ("shl", lang.shl_trait()),
- ast::BiShr => ("shr", lang.shr_trait()),
- ast::BiLt => ("lt", lang.ord_trait()),
- ast::BiLe => ("le", lang.ord_trait()),
- ast::BiGe => ("ge", lang.ord_trait()),
- ast::BiGt => ("gt", lang.ord_trait()),
- ast::BiEq => ("eq", lang.eq_trait()),
- ast::BiNe => ("ne", lang.eq_trait()),
- ast::BiAnd | ast::BiOr => {
- fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
+
+ 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()),
+ hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
+ hir::BiDiv => ("div_assign", lang.div_assign_trait()),
+ hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
+ hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
+ hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
+ hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
+ hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
+ hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
+ hir::BiLt | hir::BiLe | hir::BiGe | hir::BiGt | hir::BiEq | hir::BiNe | hir::BiAnd |
+ hir::BiOr => {
+ fcx.tcx().sess.span_bug(op.span, &format!("impossible assignment operation: {}=",
+ hir_util::binop_to_string(op.node)))
+ }
+ }
+ } else {
+ match op.node {
+ hir::BiAdd => ("add", lang.add_trait()),
+ hir::BiSub => ("sub", lang.sub_trait()),
+ hir::BiMul => ("mul", lang.mul_trait()),
+ hir::BiDiv => ("div", lang.div_trait()),
+ hir::BiRem => ("rem", lang.rem_trait()),
+ hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
+ hir::BiBitAnd => ("bitand", lang.bitand_trait()),
+ hir::BiBitOr => ("bitor", lang.bitor_trait()),
+ hir::BiShl => ("shl", lang.shl_trait()),
+ hir::BiShr => ("shr", lang.shr_trait()),
+ hir::BiLt => ("lt", lang.ord_trait()),
+ hir::BiLe => ("le", lang.ord_trait()),
+ hir::BiGe => ("ge", lang.ord_trait()),
+ hir::BiGt => ("gt", lang.ord_trait()),
+ hir::BiEq => ("eq", lang.eq_trait()),
+ hir::BiNe => ("ne", lang.eq_trait()),
+ hir::BiAnd | hir::BiOr => {
+ fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
+ }
}
}
}
fn lookup_op_method<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
- expr: &'tcx ast::Expr,
+ expr: &'tcx hir::Expr,
lhs_ty: Ty<'tcx>,
other_tys: Vec<Ty<'tcx>>,
opname: ast::Name,
trait_did: Option<DefId>,
- lhs_expr: &'a ast::Expr)
+ lhs_expr: &'a hir::Expr)
-> Result<Ty<'tcx>,()>
{
debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, trait_did={:?}, lhs_expr={:?})",
}
impl BinOpCategory {
- fn from(op: ast::BinOp) -> BinOpCategory {
+ fn from(op: hir::BinOp) -> BinOpCategory {
match op.node {
- ast::BiShl | ast::BiShr =>
+ hir::BiShl | hir::BiShr =>
BinOpCategory::Shift,
- ast::BiAdd |
- ast::BiSub |
- ast::BiMul |
- ast::BiDiv |
- ast::BiRem =>
+ hir::BiAdd |
+ hir::BiSub |
+ hir::BiMul |
+ hir::BiDiv |
+ hir::BiRem =>
BinOpCategory::Math,
- ast::BiBitXor |
- ast::BiBitAnd |
- ast::BiBitOr =>
+ hir::BiBitXor |
+ hir::BiBitAnd |
+ hir::BiBitOr =>
BinOpCategory::Bitwise,
- ast::BiEq |
- ast::BiNe |
- ast::BiLt |
- ast::BiLe |
- ast::BiGe |
- ast::BiGt =>
+ hir::BiEq |
+ hir::BiNe |
+ hir::BiLt |
+ hir::BiLe |
+ hir::BiGe |
+ hir::BiGt =>
BinOpCategory::Comparison,
- ast::BiAnd |
- ast::BiOr =>
+ hir::BiAnd |
+ hir::BiOr =>
BinOpCategory::Shortcircuit,
}
}
}
+/// 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
/// the fact, and it worked fine, except for SIMD types. -nmatsakis
fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>,
rhs: Ty<'tcx>,
- op: ast::BinOp)
+ op: hir::BinOp)
-> bool
{
match BinOpCategory::from(op) {