1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Code related to processing overloaded binary and unary operators.
15 check_expr_coercable_to_type
,
16 check_expr_with_lvalue_pref
,
21 use hir
::def_id
::DefId
;
22 use rustc
::ty
::{Ty, TypeFoldable, PreferMutLvalue}
;
24 use syntax
::parse
::token
;
27 /// Check a `a <op>= b`
28 pub fn check_binop_assign
<'a
,'tcx
>(fcx
: &FnCtxt
<'a
,'tcx
>,
29 expr
: &'tcx hir
::Expr
,
31 lhs_expr
: &'tcx hir
::Expr
,
32 rhs_expr
: &'tcx hir
::Expr
)
34 check_expr_with_lvalue_pref(fcx
, lhs_expr
, PreferMutLvalue
);
36 let lhs_ty
= fcx
.resolve_type_vars_if_possible(fcx
.expr_ty(lhs_expr
));
37 let (rhs_ty
, return_ty
) =
38 check_overloaded_binop(fcx
, expr
, lhs_expr
, lhs_ty
, rhs_expr
, op
, IsAssign
::Yes
);
39 let rhs_ty
= fcx
.resolve_type_vars_if_possible(rhs_ty
);
41 if !lhs_ty
.is_ty_var() && !rhs_ty
.is_ty_var() && is_builtin_binop(lhs_ty
, rhs_ty
, op
) {
42 enforce_builtin_binop_types(fcx
, lhs_expr
, lhs_ty
, rhs_expr
, rhs_ty
, op
);
43 fcx
.write_nil(expr
.id
);
45 fcx
.write_ty(expr
.id
, return_ty
);
49 if !tcx
.expr_is_lval(lhs_expr
) {
50 span_err
!(tcx
.sess
, lhs_expr
.span
, E0067
, "invalid left-hand side expression");
54 /// Check a potentially overloaded binary operator.
55 pub fn check_binop
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
56 expr
: &'tcx hir
::Expr
,
58 lhs_expr
: &'tcx hir
::Expr
,
59 rhs_expr
: &'tcx hir
::Expr
)
61 let tcx
= fcx
.ccx
.tcx
;
63 debug
!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
70 check_expr(fcx
, lhs_expr
);
71 let lhs_ty
= fcx
.resolve_type_vars_if_possible(fcx
.expr_ty(lhs_expr
));
73 match BinOpCategory
::from(op
) {
74 BinOpCategory
::Shortcircuit
=> {
75 // && and || are a simple case.
76 demand
::suptype(fcx
, lhs_expr
.span
, tcx
.mk_bool(), lhs_ty
);
77 check_expr_coercable_to_type(fcx
, rhs_expr
, tcx
.mk_bool());
78 fcx
.write_ty(expr
.id
, tcx
.mk_bool());
81 // Otherwise, we always treat operators as if they are
82 // overloaded. This is the way to be most flexible w/r/t
83 // types that get inferred.
84 let (rhs_ty
, return_ty
) =
85 check_overloaded_binop(fcx
, expr
, lhs_expr
, lhs_ty
, rhs_expr
, op
, IsAssign
::No
);
87 // Supply type inference hints if relevant. Probably these
88 // hints should be enforced during select as part of the
89 // `consider_unification_despite_ambiguity` routine, but this
90 // more convenient for now.
92 // The basic idea is to help type inference by taking
93 // advantage of things we know about how the impls for
94 // scalar types are arranged. This is important in a
95 // scenario like `1_u32 << 2`, because it lets us quickly
96 // deduce that the result type should be `u32`, even
97 // though we don't know yet what type 2 has and hence
98 // can't pin this down to a specific impl.
99 let rhs_ty
= fcx
.resolve_type_vars_if_possible(rhs_ty
);
101 !lhs_ty
.is_ty_var() && !rhs_ty
.is_ty_var() &&
102 is_builtin_binop(lhs_ty
, rhs_ty
, op
)
104 let builtin_return_ty
=
105 enforce_builtin_binop_types(fcx
, lhs_expr
, lhs_ty
, rhs_expr
, rhs_ty
, op
);
106 demand
::suptype(fcx
, expr
.span
, builtin_return_ty
, return_ty
);
109 fcx
.write_ty(expr
.id
, return_ty
);
114 fn enforce_builtin_binop_types
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
115 lhs_expr
: &'tcx hir
::Expr
,
117 rhs_expr
: &'tcx hir
::Expr
,
122 debug_assert
!(is_builtin_binop(lhs_ty
, rhs_ty
, op
));
125 match BinOpCategory
::from(op
) {
126 BinOpCategory
::Shortcircuit
=> {
127 demand
::suptype(fcx
, lhs_expr
.span
, tcx
.mk_bool(), lhs_ty
);
128 demand
::suptype(fcx
, rhs_expr
.span
, tcx
.mk_bool(), rhs_ty
);
132 BinOpCategory
::Shift
=> {
133 // result type is same as LHS always
137 BinOpCategory
::Math
|
138 BinOpCategory
::Bitwise
=> {
139 // both LHS and RHS and result will have the same type
140 demand
::suptype(fcx
, rhs_expr
.span
, lhs_ty
, rhs_ty
);
144 BinOpCategory
::Comparison
=> {
145 // both LHS and RHS and result will have the same type
146 demand
::suptype(fcx
, rhs_expr
.span
, lhs_ty
, rhs_ty
);
152 fn check_overloaded_binop
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
153 expr
: &'tcx hir
::Expr
,
154 lhs_expr
: &'tcx hir
::Expr
,
156 rhs_expr
: &'tcx hir
::Expr
,
159 -> (Ty
<'tcx
>, Ty
<'tcx
>)
161 debug
!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
166 let (name
, trait_def_id
) = name_and_trait_def_id(fcx
, op
, is_assign
);
168 // NB: As we have not yet type-checked the RHS, we don't have the
169 // type at hand. Make a variable to represent it. The whole reason
170 // for this indirection is so that, below, we can check the expr
171 // using this variable as the expected type, which sometimes lets
172 // us do better coercions than we would be able to do otherwise,
173 // particularly for things like `String + &String`.
174 let rhs_ty_var
= fcx
.infcx().next_ty_var();
176 let return_ty
= match lookup_op_method(fcx
, expr
, lhs_ty
, vec
![rhs_ty_var
],
177 token
::intern(name
), trait_def_id
,
179 Ok(return_ty
) => return_ty
,
181 // error types are considered "builtin"
182 if !lhs_ty
.references_error() {
183 if let IsAssign
::Yes
= is_assign
{
184 span_err
!(fcx
.tcx().sess
, lhs_expr
.span
, E0368
,
185 "binary assignment operation `{}=` cannot be applied to type `{}`",
189 let mut err
= struct_span_err
!(fcx
.tcx().sess
, lhs_expr
.span
, E0369
,
190 "binary operation `{}` cannot be applied to type `{}`",
193 let missing_trait
= match op
.node
{
194 hir
::BiAdd
=> Some("std::ops::Add"),
195 hir
::BiSub
=> Some("std::ops::Sub"),
196 hir
::BiMul
=> Some("std::ops::Mul"),
197 hir
::BiDiv
=> Some("std::ops::Div"),
198 hir
::BiRem
=> Some("std::ops::Rem"),
199 hir
::BiBitAnd
=> Some("std::ops::BitAnd"),
200 hir
::BiBitOr
=> Some("std::ops::BitOr"),
201 hir
::BiShl
=> Some("std::ops::Shl"),
202 hir
::BiShr
=> Some("std::ops::Shr"),
203 hir
::BiEq
| hir
::BiNe
=> Some("std::cmp::PartialEq"),
204 hir
::BiLt
| hir
::BiLe
| hir
::BiGt
| hir
::BiGe
=>
205 Some("std::cmp::PartialOrd"),
209 if let Some(missing_trait
) = missing_trait
{
210 span_note
!(&mut err
, lhs_expr
.span
,
211 "an implementation of `{}` might be missing for `{}`",
212 missing_trait
, lhs_ty
);
222 check_expr_coercable_to_type(fcx
, rhs_expr
, rhs_ty_var
);
224 (rhs_ty_var
, return_ty
)
227 pub fn check_user_unop
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
230 trait_did
: Option
<DefId
>,
232 operand_expr
: &'tcx hir
::Expr
,
233 operand_ty
: Ty
<'tcx
>,
237 assert
!(op
.is_by_value());
238 match lookup_op_method(fcx
, ex
, operand_ty
, vec
![],
239 token
::intern(mname
), trait_did
,
243 fcx
.type_error_message(ex
.span
, |actual
| {
244 format
!("cannot apply unary operator `{}` to type `{}`",
246 }, operand_ty
, None
);
252 fn name_and_trait_def_id(fcx
: &FnCtxt
,
255 -> (&'
static str, Option
<DefId
>) {
256 let lang
= &fcx
.tcx().lang_items
;
258 if let IsAssign
::Yes
= is_assign
{
260 hir
::BiAdd
=> ("add_assign", lang
.add_assign_trait()),
261 hir
::BiSub
=> ("sub_assign", lang
.sub_assign_trait()),
262 hir
::BiMul
=> ("mul_assign", lang
.mul_assign_trait()),
263 hir
::BiDiv
=> ("div_assign", lang
.div_assign_trait()),
264 hir
::BiRem
=> ("rem_assign", lang
.rem_assign_trait()),
265 hir
::BiBitXor
=> ("bitxor_assign", lang
.bitxor_assign_trait()),
266 hir
::BiBitAnd
=> ("bitand_assign", lang
.bitand_assign_trait()),
267 hir
::BiBitOr
=> ("bitor_assign", lang
.bitor_assign_trait()),
268 hir
::BiShl
=> ("shl_assign", lang
.shl_assign_trait()),
269 hir
::BiShr
=> ("shr_assign", lang
.shr_assign_trait()),
270 hir
::BiLt
| hir
::BiLe
| hir
::BiGe
| hir
::BiGt
| hir
::BiEq
| hir
::BiNe
| hir
::BiAnd
|
273 "impossible assignment operation: {}=",
279 hir
::BiAdd
=> ("add", lang
.add_trait()),
280 hir
::BiSub
=> ("sub", lang
.sub_trait()),
281 hir
::BiMul
=> ("mul", lang
.mul_trait()),
282 hir
::BiDiv
=> ("div", lang
.div_trait()),
283 hir
::BiRem
=> ("rem", lang
.rem_trait()),
284 hir
::BiBitXor
=> ("bitxor", lang
.bitxor_trait()),
285 hir
::BiBitAnd
=> ("bitand", lang
.bitand_trait()),
286 hir
::BiBitOr
=> ("bitor", lang
.bitor_trait()),
287 hir
::BiShl
=> ("shl", lang
.shl_trait()),
288 hir
::BiShr
=> ("shr", lang
.shr_trait()),
289 hir
::BiLt
=> ("lt", lang
.ord_trait()),
290 hir
::BiLe
=> ("le", lang
.ord_trait()),
291 hir
::BiGe
=> ("ge", lang
.ord_trait()),
292 hir
::BiGt
=> ("gt", lang
.ord_trait()),
293 hir
::BiEq
=> ("eq", lang
.eq_trait()),
294 hir
::BiNe
=> ("ne", lang
.eq_trait()),
295 hir
::BiAnd
| hir
::BiOr
=> {
296 span_bug
!(op
.span
, "&& and || are not overloadable")
302 fn lookup_op_method
<'a
, 'tcx
>(fcx
: &'a FnCtxt
<'a
, 'tcx
>,
303 expr
: &'tcx hir
::Expr
,
305 other_tys
: Vec
<Ty
<'tcx
>>,
307 trait_did
: Option
<DefId
>,
308 lhs_expr
: &'a hir
::Expr
)
309 -> Result
<Ty
<'tcx
>,()>
311 debug
!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, trait_did={:?}, lhs_expr={:?})",
318 let method
= match trait_did
{
320 method
::lookup_in_trait_adjusted(fcx
,
335 let method_ty
= method
.ty
;
337 // HACK(eddyb) Fully qualified path to work around a resolve bug.
338 let method_call
= ::rustc
::ty
::MethodCall
::expr(expr
.id
);
339 fcx
.inh
.tables
.borrow_mut().method_map
.insert(method_call
, method
);
341 // extract return type for method; all late bound regions
342 // should have been instantiated by now
343 let ret_ty
= method_ty
.fn_ret();
344 Ok(fcx
.tcx().no_late_bound_regions(&ret_ty
).unwrap().unwrap())
352 // Binary operator categories. These categories summarize the behavior
353 // with respect to the builtin operationrs supported.
355 /// &&, || -- cannot be overridden
358 /// <<, >> -- when shifting a single integer, rhs can be any
359 /// integer type. For simd, types must match.
362 /// +, -, etc -- takes equal types, produces same type as input,
363 /// applicable to ints/floats/simd
366 /// &, |, ^ -- takes equal types, produces same type as input,
367 /// applicable to ints/floats/simd/bool
370 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
371 /// which produce the input type
376 fn from(op
: hir
::BinOp
) -> BinOpCategory
{
378 hir
::BiShl
| hir
::BiShr
=>
379 BinOpCategory
::Shift
,
391 BinOpCategory
::Bitwise
,
399 BinOpCategory
::Comparison
,
403 BinOpCategory
::Shortcircuit
,
408 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
409 #[derive(Clone, Copy, Debug)]
415 /// Returns true if this is a built-in arithmetic operation (e.g. u32
416 /// + u32, i16x4 == i16x4) and false if these types would have to be
417 /// overloaded to be legal. There are two reasons that we distinguish
418 /// builtin operations from overloaded ones (vs trying to drive
419 /// everything uniformly through the trait system and intrinsics or
420 /// something like that):
422 /// 1. Builtin operations can trivially be evaluated in constants.
423 /// 2. For comparison operators applied to SIMD types the result is
424 /// not of type `bool`. For example, `i16x4==i16x4` yields a
425 /// type like `i16x4`. This means that the overloaded trait
426 /// `PartialEq` is not applicable.
428 /// Reason #2 is the killer. I tried for a while to always use
429 /// overloaded logic and just check the types in constants/trans after
430 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
431 fn is_builtin_binop
<'tcx
>(lhs
: Ty
<'tcx
>,
436 match BinOpCategory
::from(op
) {
437 BinOpCategory
::Shortcircuit
=> {
441 BinOpCategory
::Shift
=> {
442 lhs
.references_error() || rhs
.references_error() ||
443 lhs
.is_integral() && rhs
.is_integral()
446 BinOpCategory
::Math
=> {
447 lhs
.references_error() || rhs
.references_error() ||
448 lhs
.is_integral() && rhs
.is_integral() ||
449 lhs
.is_floating_point() && rhs
.is_floating_point()
452 BinOpCategory
::Bitwise
=> {
453 lhs
.references_error() || rhs
.references_error() ||
454 lhs
.is_integral() && rhs
.is_integral() ||
455 lhs
.is_floating_point() && rhs
.is_floating_point() ||
456 lhs
.is_bool() && rhs
.is_bool()
459 BinOpCategory
::Comparison
=> {
460 lhs
.references_error() || rhs
.references_error() ||
461 lhs
.is_scalar() && rhs
.is_scalar()