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 structurally_resolved_type
,
24 use middle
::ty
::{self, Ty, HasTypeFlags}
;
27 use syntax
::parse
::token
;
29 /// Check a `a <op>= b`
30 pub fn check_binop_assign
<'a
,'tcx
>(fcx
: &FnCtxt
<'a
,'tcx
>,
31 expr
: &'tcx ast
::Expr
,
33 lhs_expr
: &'tcx ast
::Expr
,
34 rhs_expr
: &'tcx ast
::Expr
)
36 let tcx
= fcx
.ccx
.tcx
;
38 check_expr_with_lvalue_pref(fcx
, lhs_expr
, PreferMutLvalue
);
39 check_expr(fcx
, rhs_expr
);
41 let lhs_ty
= structurally_resolved_type(fcx
, lhs_expr
.span
, fcx
.expr_ty(lhs_expr
));
42 let rhs_ty
= structurally_resolved_type(fcx
, rhs_expr
.span
, fcx
.expr_ty(rhs_expr
));
44 if is_builtin_binop(fcx
.tcx(), lhs_ty
, rhs_ty
, op
) {
45 enforce_builtin_binop_types(fcx
, lhs_expr
, lhs_ty
, rhs_expr
, rhs_ty
, op
);
46 fcx
.write_nil(expr
.id
);
48 // error types are considered "builtin"
49 assert
!(!lhs_ty
.references_error() || !rhs_ty
.references_error());
50 span_err
!(tcx
.sess
, lhs_expr
.span
, E0368
,
51 "binary assignment operation `{}=` cannot be applied to types `{}` and `{}`",
52 ast_util
::binop_to_string(op
.node
),
55 fcx
.write_error(expr
.id
);
59 if !tcx
.expr_is_lval(lhs_expr
) {
60 span_err
!(tcx
.sess
, lhs_expr
.span
, E0067
, "invalid left-hand side expression");
63 fcx
.require_expr_have_sized_type(lhs_expr
, traits
::AssignmentLhsSized
);
66 /// Check a potentially overloaded binary operator.
67 pub fn check_binop
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
68 expr
: &'tcx ast
::Expr
,
70 lhs_expr
: &'tcx ast
::Expr
,
71 rhs_expr
: &'tcx ast
::Expr
)
73 let tcx
= fcx
.ccx
.tcx
;
75 debug
!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
82 check_expr(fcx
, lhs_expr
);
83 let lhs_ty
= fcx
.resolve_type_vars_if_possible(fcx
.expr_ty(lhs_expr
));
85 // Annoyingly, SIMD ops don't fit into the PartialEq/PartialOrd
86 // traits, because their return type is not bool. Perhaps this
87 // should change, but for now if LHS is SIMD we go down a
88 // different path that bypassess all traits.
89 if lhs_ty
.is_simd(fcx
.tcx()) {
90 check_expr_coercable_to_type(fcx
, rhs_expr
, lhs_ty
);
91 let rhs_ty
= fcx
.resolve_type_vars_if_possible(fcx
.expr_ty(lhs_expr
));
92 let return_ty
= enforce_builtin_binop_types(fcx
, lhs_expr
, lhs_ty
, rhs_expr
, rhs_ty
, op
);
93 fcx
.write_ty(expr
.id
, return_ty
);
97 match BinOpCategory
::from(op
) {
98 BinOpCategory
::Shortcircuit
=> {
99 // && and || are a simple case.
100 demand
::suptype(fcx
, lhs_expr
.span
, tcx
.mk_bool(), lhs_ty
);
101 check_expr_coercable_to_type(fcx
, rhs_expr
, tcx
.mk_bool());
102 fcx
.write_ty(expr
.id
, tcx
.mk_bool());
105 // Otherwise, we always treat operators as if they are
106 // overloaded. This is the way to be most flexible w/r/t
107 // types that get inferred.
108 let (rhs_ty
, return_ty
) =
109 check_overloaded_binop(fcx
, expr
, lhs_expr
, lhs_ty
, rhs_expr
, op
);
111 // Supply type inference hints if relevant. Probably these
112 // hints should be enforced during select as part of the
113 // `consider_unification_despite_ambiguity` routine, but this
114 // more convenient for now.
116 // The basic idea is to help type inference by taking
117 // advantage of things we know about how the impls for
118 // scalar types are arranged. This is important in a
119 // scenario like `1_u32 << 2`, because it lets us quickly
120 // deduce that the result type should be `u32`, even
121 // though we don't know yet what type 2 has and hence
122 // can't pin this down to a specific impl.
123 let rhs_ty
= fcx
.resolve_type_vars_if_possible(rhs_ty
);
125 !lhs_ty
.is_ty_var() && !rhs_ty
.is_ty_var() &&
126 is_builtin_binop(fcx
.tcx(), lhs_ty
, rhs_ty
, op
)
128 let builtin_return_ty
=
129 enforce_builtin_binop_types(fcx
, lhs_expr
, lhs_ty
, rhs_expr
, rhs_ty
, op
);
130 demand
::suptype(fcx
, expr
.span
, builtin_return_ty
, return_ty
);
133 fcx
.write_ty(expr
.id
, return_ty
);
138 fn enforce_builtin_binop_types
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
139 lhs_expr
: &'tcx ast
::Expr
,
141 rhs_expr
: &'tcx ast
::Expr
,
146 debug_assert
!(is_builtin_binop(fcx
.tcx(), lhs_ty
, rhs_ty
, op
));
149 match BinOpCategory
::from(op
) {
150 BinOpCategory
::Shortcircuit
=> {
151 demand
::suptype(fcx
, lhs_expr
.span
, tcx
.mk_bool(), lhs_ty
);
152 demand
::suptype(fcx
, rhs_expr
.span
, tcx
.mk_bool(), rhs_ty
);
156 BinOpCategory
::Shift
=> {
157 // For integers, the shift amount can be of any integral
158 // type. For simd, the type must match exactly.
159 if lhs_ty
.is_simd(tcx
) {
160 demand
::suptype(fcx
, rhs_expr
.span
, lhs_ty
, rhs_ty
);
163 // result type is same as LHS always
167 BinOpCategory
::Math
|
168 BinOpCategory
::Bitwise
=> {
169 // both LHS and RHS and result will have the same type
170 demand
::suptype(fcx
, rhs_expr
.span
, lhs_ty
, rhs_ty
);
174 BinOpCategory
::Comparison
=> {
175 // both LHS and RHS and result will have the same type
176 demand
::suptype(fcx
, rhs_expr
.span
, lhs_ty
, rhs_ty
);
178 // if this is simd, result is same as lhs, else bool
179 if lhs_ty
.is_simd(tcx
) {
180 let unit_ty
= lhs_ty
.simd_type(tcx
);
181 debug
!("enforce_builtin_binop_types: lhs_ty={:?} unit_ty={:?}",
184 if !unit_ty
.is_integral() {
187 &format
!("binary comparison operation `{}` not supported \
188 for floating point SIMD vector `{}`",
189 ast_util
::binop_to_string(op
.node
),
202 fn check_overloaded_binop
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
203 expr
: &'tcx ast
::Expr
,
204 lhs_expr
: &'tcx ast
::Expr
,
206 rhs_expr
: &'tcx ast
::Expr
,
208 -> (Ty
<'tcx
>, Ty
<'tcx
>)
210 debug
!("check_overloaded_binop(expr.id={}, lhs_ty={:?})",
214 let (name
, trait_def_id
) = name_and_trait_def_id(fcx
, op
);
216 // NB: As we have not yet type-checked the RHS, we don't have the
217 // type at hand. Make a variable to represent it. The whole reason
218 // for this indirection is so that, below, we can check the expr
219 // using this variable as the expected type, which sometimes lets
220 // us do better coercions than we would be able to do otherwise,
221 // particularly for things like `String + &String`.
222 let rhs_ty_var
= fcx
.infcx().next_ty_var();
224 let return_ty
= match lookup_op_method(fcx
, expr
, lhs_ty
, vec
![rhs_ty_var
],
225 token
::intern(name
), trait_def_id
,
227 Ok(return_ty
) => return_ty
,
229 // error types are considered "builtin"
230 if !lhs_ty
.references_error() {
231 span_err
!(fcx
.tcx().sess
, lhs_expr
.span
, E0369
,
232 "binary operation `{}` cannot be applied to type `{}`",
233 ast_util
::binop_to_string(op
.node
),
241 check_expr_coercable_to_type(fcx
, rhs_expr
, rhs_ty_var
);
243 (rhs_ty_var
, return_ty
)
246 pub fn check_user_unop
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
249 trait_did
: Option
<ast
::DefId
>,
251 operand_expr
: &'tcx ast
::Expr
,
252 operand_ty
: Ty
<'tcx
>,
256 assert
!(ast_util
::is_by_value_unop(op
));
257 match lookup_op_method(fcx
, ex
, operand_ty
, vec
![],
258 token
::intern(mname
), trait_did
,
262 fcx
.type_error_message(ex
.span
, |actual
| {
263 format
!("cannot apply unary operator `{}` to type `{}`",
265 }, operand_ty
, None
);
271 fn name_and_trait_def_id(fcx
: &FnCtxt
, op
: ast
::BinOp
) -> (&'
static str, Option
<ast
::DefId
>) {
272 let lang
= &fcx
.tcx().lang_items
;
274 ast
::BiAdd
=> ("add", lang
.add_trait()),
275 ast
::BiSub
=> ("sub", lang
.sub_trait()),
276 ast
::BiMul
=> ("mul", lang
.mul_trait()),
277 ast
::BiDiv
=> ("div", lang
.div_trait()),
278 ast
::BiRem
=> ("rem", lang
.rem_trait()),
279 ast
::BiBitXor
=> ("bitxor", lang
.bitxor_trait()),
280 ast
::BiBitAnd
=> ("bitand", lang
.bitand_trait()),
281 ast
::BiBitOr
=> ("bitor", lang
.bitor_trait()),
282 ast
::BiShl
=> ("shl", lang
.shl_trait()),
283 ast
::BiShr
=> ("shr", lang
.shr_trait()),
284 ast
::BiLt
=> ("lt", lang
.ord_trait()),
285 ast
::BiLe
=> ("le", lang
.ord_trait()),
286 ast
::BiGe
=> ("ge", lang
.ord_trait()),
287 ast
::BiGt
=> ("gt", lang
.ord_trait()),
288 ast
::BiEq
=> ("eq", lang
.eq_trait()),
289 ast
::BiNe
=> ("ne", lang
.eq_trait()),
290 ast
::BiAnd
| ast
::BiOr
=> {
291 fcx
.tcx().sess
.span_bug(op
.span
, "&& and || are not overloadable")
296 fn lookup_op_method
<'a
, 'tcx
>(fcx
: &'a FnCtxt
<'a
, 'tcx
>,
297 expr
: &'tcx ast
::Expr
,
299 other_tys
: Vec
<Ty
<'tcx
>>,
301 trait_did
: Option
<ast
::DefId
>,
302 lhs_expr
: &'a ast
::Expr
)
303 -> Result
<Ty
<'tcx
>,()>
305 debug
!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, trait_did={:?}, lhs_expr={:?})",
312 let method
= match trait_did
{
314 method
::lookup_in_trait_adjusted(fcx
,
329 let method_ty
= method
.ty
;
331 // HACK(eddyb) Fully qualified path to work around a resolve bug.
332 let method_call
= ::middle
::ty
::MethodCall
::expr(expr
.id
);
333 fcx
.inh
.tables
.borrow_mut().method_map
.insert(method_call
, method
);
335 // extract return type for method; all late bound regions
336 // should have been instantiated by now
337 let ret_ty
= method_ty
.fn_ret();
338 Ok(fcx
.tcx().no_late_bound_regions(&ret_ty
).unwrap().unwrap())
346 // Binary operator categories. These categories summarize the behavior
347 // with respect to the builtin operationrs supported.
349 /// &&, || -- cannot be overridden
352 /// <<, >> -- when shifting a single integer, rhs can be any
353 /// integer type. For simd, types must match.
356 /// +, -, etc -- takes equal types, produces same type as input,
357 /// applicable to ints/floats/simd
360 /// &, |, ^ -- takes equal types, produces same type as input,
361 /// applicable to ints/floats/simd/bool
364 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
365 /// which produce the input type
370 fn from(op
: ast
::BinOp
) -> BinOpCategory
{
372 ast
::BiShl
| ast
::BiShr
=>
373 BinOpCategory
::Shift
,
385 BinOpCategory
::Bitwise
,
393 BinOpCategory
::Comparison
,
397 BinOpCategory
::Shortcircuit
,
402 /// Returns true if this is a built-in arithmetic operation (e.g. u32
403 /// + u32, i16x4 == i16x4) and false if these types would have to be
404 /// overloaded to be legal. There are two reasons that we distinguish
405 /// builtin operations from overloaded ones (vs trying to drive
406 /// everything uniformly through the trait system and intrinsics or
407 /// something like that):
409 /// 1. Builtin operations can trivially be evaluated in constants.
410 /// 2. For comparison operators applied to SIMD types the result is
411 /// not of type `bool`. For example, `i16x4==i16x4` yields a
412 /// type like `i16x4`. This means that the overloaded trait
413 /// `PartialEq` is not applicable.
415 /// Reason #2 is the killer. I tried for a while to always use
416 /// overloaded logic and just check the types in constants/trans after
417 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
418 fn is_builtin_binop
<'tcx
>(cx
: &ty
::ctxt
<'tcx
>,
424 match BinOpCategory
::from(op
) {
425 BinOpCategory
::Shortcircuit
=> {
429 BinOpCategory
::Shift
=> {
430 lhs
.references_error() || rhs
.references_error() ||
431 lhs
.is_integral() && rhs
.is_integral() ||
432 lhs
.is_simd(cx
) && rhs
.is_simd(cx
)
435 BinOpCategory
::Math
=> {
436 lhs
.references_error() || rhs
.references_error() ||
437 lhs
.is_integral() && rhs
.is_integral() ||
438 lhs
.is_floating_point() && rhs
.is_floating_point() ||
439 lhs
.is_simd(cx
) && rhs
.is_simd(cx
)
442 BinOpCategory
::Bitwise
=> {
443 lhs
.references_error() || rhs
.references_error() ||
444 lhs
.is_integral() && rhs
.is_integral() ||
445 lhs
.is_floating_point() && rhs
.is_floating_point() ||
446 lhs
.is_simd(cx
) && rhs
.is_simd(cx
) ||
447 lhs
.is_bool() && rhs
.is_bool()
450 BinOpCategory
::Comparison
=> {
451 lhs
.references_error() || rhs
.references_error() ||
452 lhs
.is_scalar() && rhs
.is_scalar() ||
453 lhs
.is_simd(cx
) && rhs
.is_simd(cx
)