1 //! Code related to processing overloaded binary and unary operators.
3 use super::method::MethodCallee;
5 use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
7 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
8 use rustc_middle::ty::adjustment::{
9 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
11 use rustc_middle::ty::fold::TypeFolder;
12 use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
13 use rustc_middle::ty::{
14 self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor,
16 use rustc_span::symbol::{sym, Ident};
18 use rustc_trait_selection::infer::InferCtxtExt;
20 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21 /// Checks a `a <op>= b`
22 pub fn check_binop_assign(
24 expr: &'tcx hir::Expr<'tcx>,
26 lhs: &'tcx hir::Expr<'tcx>,
27 rhs: &'tcx hir::Expr<'tcx>,
29 let (lhs_ty, rhs_ty, return_ty) =
30 self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes);
33 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
34 self.enforce_builtin_binop_types(&lhs.span, lhs_ty, &rhs.span, rhs_ty, op);
40 self.check_lhs_assignable(lhs, "E0067", &op.span);
45 /// Checks a potentially overloaded binary operator.
48 expr: &'tcx hir::Expr<'tcx>,
50 lhs_expr: &'tcx hir::Expr<'tcx>,
51 rhs_expr: &'tcx hir::Expr<'tcx>,
56 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
57 expr.hir_id, expr, op, lhs_expr, rhs_expr
60 match BinOpCategory::from(op) {
61 BinOpCategory::Shortcircuit => {
62 // && and || are a simple case.
63 self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None);
64 let lhs_diverges = self.diverges.get();
65 self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None);
67 // Depending on the LHS' value, the RHS can never execute.
68 self.diverges.set(lhs_diverges);
73 // Otherwise, we always treat operators as if they are
74 // overloaded. This is the way to be most flexible w/r/t
75 // types that get inferred.
76 let (lhs_ty, rhs_ty, return_ty) =
77 self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No);
79 // Supply type inference hints if relevant. Probably these
80 // hints should be enforced during select as part of the
81 // `consider_unification_despite_ambiguity` routine, but this
82 // more convenient for now.
84 // The basic idea is to help type inference by taking
85 // advantage of things we know about how the impls for
86 // scalar types are arranged. This is important in a
87 // scenario like `1_u32 << 2`, because it lets us quickly
88 // deduce that the result type should be `u32`, even
89 // though we don't know yet what type 2 has and hence
90 // can't pin this down to a specific impl.
91 if !lhs_ty.is_ty_var()
92 && !rhs_ty.is_ty_var()
93 && is_builtin_binop(lhs_ty, rhs_ty, op)
95 let builtin_return_ty = self.enforce_builtin_binop_types(
102 self.demand_suptype(expr.span, builtin_return_ty, return_ty);
110 fn enforce_builtin_binop_types(
118 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
120 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
121 // (See https://github.com/rust-lang/rust/issues/57447.)
122 let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
125 match BinOpCategory::from(op) {
126 BinOpCategory::Shortcircuit => {
127 self.demand_suptype(*lhs_span, tcx.types.bool, lhs_ty);
128 self.demand_suptype(*rhs_span, tcx.types.bool, rhs_ty);
132 BinOpCategory::Shift => {
133 // result type is same as LHS always
137 BinOpCategory::Math | BinOpCategory::Bitwise => {
138 // both LHS and RHS and result will have the same type
139 self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
143 BinOpCategory::Comparison => {
144 // both LHS and RHS and result will have the same type
145 self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
151 fn check_overloaded_binop(
153 expr: &'tcx hir::Expr<'tcx>,
154 lhs_expr: &'tcx hir::Expr<'tcx>,
155 rhs_expr: &'tcx hir::Expr<'tcx>,
158 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
160 "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
161 expr.hir_id, op, is_assign
164 let lhs_ty = match is_assign {
166 // Find a suitable supertype of the LHS expression's type, by coercing to
167 // a type variable, to pass as the `Self` to the trait, avoiding invariant
168 // trait matching creating lifetime constraints that are too strict.
169 // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
170 // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
171 let lhs_ty = self.check_expr(lhs_expr);
172 let fresh_var = self.next_ty_var(TypeVariableOrigin {
173 kind: TypeVariableOriginKind::MiscVariable,
176 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
179 // rust-lang/rust#52126: We have to use strict
180 // equivalence on the LHS of an assign-op like `+=`;
181 // overwritten or mutably-borrowed places cannot be
182 // coerced to a supertype.
183 self.check_expr(lhs_expr)
186 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
188 // N.B., as we have not yet type-checked the RHS, we don't have the
189 // type at hand. Make a variable to represent it. The whole reason
190 // for this indirection is so that, below, we can check the expr
191 // using this variable as the expected type, which sometimes lets
192 // us do better coercions than we would be able to do otherwise,
193 // particularly for things like `String + &String`.
194 let rhs_ty_var = self.next_ty_var(TypeVariableOrigin {
195 kind: TypeVariableOriginKind::MiscVariable,
199 let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign));
202 let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
203 let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
205 let return_ty = match result {
207 let by_ref_binop = !op.node.is_by_value();
208 if is_assign == IsAssign::Yes || by_ref_binop {
209 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind {
210 let mutbl = match mutbl {
211 hir::Mutability::Not => AutoBorrowMutability::Not,
212 hir::Mutability::Mut => AutoBorrowMutability::Mut {
213 // Allow two-phase borrows for binops in initial deployment
214 // since they desugar to methods
215 allow_two_phase_borrow: AllowTwoPhase::Yes,
218 let autoref = Adjustment {
219 kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
220 target: method.sig.inputs()[0],
222 self.apply_adjustments(lhs_expr, vec![autoref]);
226 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind {
227 let mutbl = match mutbl {
228 hir::Mutability::Not => AutoBorrowMutability::Not,
229 hir::Mutability::Mut => AutoBorrowMutability::Mut {
230 // Allow two-phase borrows for binops in initial deployment
231 // since they desugar to methods
232 allow_two_phase_borrow: AllowTwoPhase::Yes,
235 let autoref = Adjustment {
236 kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
237 target: method.sig.inputs()[1],
239 // HACK(eddyb) Bypass checks due to reborrows being in
240 // some cases applied on the RHS, on top of which we need
241 // to autoref, which is not allowed by apply_adjustments.
242 // self.apply_adjustments(rhs_expr, vec![autoref]);
246 .entry(rhs_expr.hir_id)
251 self.write_method_call(expr.hir_id, method);
255 // error types are considered "builtin"
256 Err(()) if lhs_ty.references_error() || rhs_ty.references_error() => {
260 let source_map = self.tcx.sess.source_map();
261 let (mut err, missing_trait, use_output, involves_fn) = match is_assign {
263 let mut err = struct_span_err!(
267 "binary assignment operation `{}=` cannot be applied to type `{}`",
273 format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
275 let missing_trait = match op.node {
276 hir::BinOpKind::Add => Some("std::ops::AddAssign"),
277 hir::BinOpKind::Sub => Some("std::ops::SubAssign"),
278 hir::BinOpKind::Mul => Some("std::ops::MulAssign"),
279 hir::BinOpKind::Div => Some("std::ops::DivAssign"),
280 hir::BinOpKind::Rem => Some("std::ops::RemAssign"),
281 hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"),
282 hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"),
283 hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"),
284 hir::BinOpKind::Shl => Some("std::ops::ShlAssign"),
285 hir::BinOpKind::Shr => Some("std::ops::ShrAssign"),
288 (err, missing_trait, false, false)
291 let (message, missing_trait, use_output) = match op.node {
292 hir::BinOpKind::Add => (
293 format!("cannot add `{}` to `{}`", rhs_ty, lhs_ty),
294 Some("std::ops::Add"),
297 hir::BinOpKind::Sub => (
298 format!("cannot subtract `{}` from `{}`", rhs_ty, lhs_ty),
299 Some("std::ops::Sub"),
302 hir::BinOpKind::Mul => (
303 format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty),
304 Some("std::ops::Mul"),
307 hir::BinOpKind::Div => (
308 format!("cannot divide `{}` by `{}`", lhs_ty, rhs_ty),
309 Some("std::ops::Div"),
312 hir::BinOpKind::Rem => (
313 format!("cannot mod `{}` by `{}`", lhs_ty, rhs_ty),
314 Some("std::ops::Rem"),
317 hir::BinOpKind::BitAnd => (
318 format!("no implementation for `{} & {}`", lhs_ty, rhs_ty),
319 Some("std::ops::BitAnd"),
322 hir::BinOpKind::BitXor => (
323 format!("no implementation for `{} ^ {}`", lhs_ty, rhs_ty),
324 Some("std::ops::BitXor"),
327 hir::BinOpKind::BitOr => (
328 format!("no implementation for `{} | {}`", lhs_ty, rhs_ty),
329 Some("std::ops::BitOr"),
332 hir::BinOpKind::Shl => (
333 format!("no implementation for `{} << {}`", lhs_ty, rhs_ty),
334 Some("std::ops::Shl"),
337 hir::BinOpKind::Shr => (
338 format!("no implementation for `{} >> {}`", lhs_ty, rhs_ty),
339 Some("std::ops::Shr"),
342 hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
344 "binary operation `{}` cannot be applied to type `{}`",
348 Some("std::cmp::PartialEq"),
354 | hir::BinOpKind::Ge => (
356 "binary operation `{}` cannot be applied to type `{}`",
360 Some("std::cmp::PartialOrd"),
365 "binary operation `{}` cannot be applied to type `{}`",
374 struct_span_err!(self.tcx.sess, op.span, E0369, "{}", message.as_str());
375 let mut involves_fn = false;
376 if !lhs_expr.span.eq(&rhs_expr.span) {
377 involves_fn |= self.add_type_neq_err_label(
385 involves_fn |= self.add_type_neq_err_label(
394 (err, missing_trait, use_output, involves_fn)
397 let mut suggested_deref = false;
398 if let Ref(_, rty, _) = lhs_ty.kind {
400 self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span)
402 .lookup_op_method(rty, &[rhs_ty], Op::Binary(op, is_assign))
405 if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
407 "`{}{}` can be used on `{}`, you can dereference `{}`",
410 IsAssign::Yes => "=",
416 err.span_suggestion_verbose(
417 lhs_expr.span.shrink_to_lo(),
420 rustc_errors::Applicability::MachineApplicable,
422 suggested_deref = true;
426 if let Some(missing_trait) = missing_trait {
427 let mut visitor = TypeParamVisitor(vec![]);
428 visitor.visit_ty(lhs_ty);
430 if op.node == hir::BinOpKind::Add
431 && self.check_str_addition(
432 lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
435 // This has nothing here because it means we did string
436 // concatenation (e.g., "Hello " + "World!"). This means
437 // we don't want the note in the else clause to be emitted
438 } else if let [ty] = &visitor.0[..] {
439 if let ty::Param(p) = ty.kind {
440 // Check if the method would be found if the type param wasn't
441 // involved. If so, it means that adding a trait bound to the param is
442 // enough. Otherwise we do not give the suggestion.
443 let mut eraser = TypeParamEraser(&self, expr.span);
444 let needs_bound = self
446 eraser.fold_ty(lhs_ty),
447 &[eraser.fold_ty(rhs_ty)],
448 Op::Binary(op, is_assign),
452 suggest_constraining_param(
462 } else if *ty != lhs_ty {
463 // When we know that a missing bound is responsible, we don't show
464 // this note as it is redundant.
466 "the trait `{}` is not implemented for `{}`",
467 missing_trait, lhs_ty
471 bug!("type param visitor stored a non type param: {:?}", ty.kind);
473 } else if !suggested_deref && !involves_fn {
474 suggest_impl_missing(&mut err, lhs_ty, &missing_trait);
482 (lhs_ty, rhs_ty, return_ty)
485 /// If one of the types is an uncalled function and calling it would yield the other type,
486 /// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
487 fn add_type_neq_err_label(
489 err: &mut rustc_errors::DiagnosticBuilder<'_>,
495 ) -> bool /* did we suggest to call a function because of missing parenthesis? */ {
496 err.span_label(span, ty.to_string());
497 if let FnDef(def_id, _) = ty.kind {
498 let source_map = self.tcx.sess.source_map();
499 if !self.tcx.has_typeck_results(def_id) {
502 // We're emitting a suggestion, so we can just ignore regions
503 let fn_sig = self.tcx.fn_sig(def_id).skip_binder();
505 let other_ty = if let FnDef(def_id, _) = other_ty.kind {
506 if !self.tcx.has_typeck_results(def_id) {
509 // We're emitting a suggestion, so we can just ignore regions
510 self.tcx.fn_sig(def_id).skip_binder().output()
516 .lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign))
519 if let Ok(snippet) = source_map.span_to_snippet(span) {
520 let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
521 (format!("{}( /* arguments */ )", snippet), Applicability::HasPlaceholders)
523 (format!("{}()", snippet), Applicability::MaybeIncorrect)
528 "you might have forgotten to call this function",
539 /// Provide actionable suggestions when trying to add two strings with incorrect types,
540 /// like `&str + &str`, `String + String` and `&str + &String`.
542 /// If this function returns `true` it means a note was printed, so we don't need
543 /// to print the normal "implementation of `std::ops::Add` might be missing" note
544 fn check_str_addition(
546 lhs_expr: &'tcx hir::Expr<'tcx>,
547 rhs_expr: &'tcx hir::Expr<'tcx>,
550 err: &mut rustc_errors::DiagnosticBuilder<'_>,
554 let source_map = self.tcx.sess.source_map();
555 let remove_borrow_msg = "String concatenation appends the string on the right to the \
556 string on the left and may require reallocation. This \
557 requires ownership of the string on the left";
559 let msg = "`to_owned()` can be used to create an owned `String` \
560 from a string reference. String concatenation \
561 appends the string on the right to the string \
562 on the left and may require reallocation. This \
563 requires ownership of the string on the left";
565 let string_type = self.tcx.get_diagnostic_item(sym::string_type);
566 let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() {
567 Some(ty_def) => Some(ty_def.did) == string_type,
571 match (&lhs_ty.kind, &rhs_ty.kind) {
572 (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
573 if (l_ty.kind == Str || is_std_string(l_ty)) && (
574 r_ty.kind == Str || is_std_string(r_ty) ||
575 &format!("{:?}", rhs_ty) == "&&str"
578 if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
581 "`+` cannot be used to concatenate two `&str` strings",
583 match source_map.span_to_snippet(lhs_expr.span) {
587 if lstring.starts_with('&') {
592 if lstring.starts_with('&') {
593 // let a = String::new();
594 // let _ = &a + "bar";
595 lstring[1..].to_string()
597 format!("{}.to_owned()", lstring)
599 Applicability::MachineApplicable,
607 (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
608 if (l_ty.kind == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
612 "`+` cannot be used to concatenate a `&str` with a `String`",
615 source_map.span_to_snippet(lhs_expr.span),
616 source_map.span_to_snippet(rhs_expr.span),
619 (Ok(l), Ok(r), IsAssign::No) => {
620 let to_string = if l.starts_with('&') {
621 // let a = String::new(); let b = String::new();
625 format!("{}.to_owned()", l)
627 err.multipart_suggestion(
630 (lhs_expr.span, to_string),
631 (rhs_expr.span, format!("&{}", r)),
633 Applicability::MachineApplicable,
646 pub fn check_user_unop(
648 ex: &'tcx hir::Expr<'tcx>,
649 operand_ty: Ty<'tcx>,
652 assert!(op.is_by_value());
653 match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) {
655 self.write_method_call(ex.hir_id, method);
659 let actual = self.resolve_vars_if_possible(&operand_ty);
660 if !actual.references_error() {
661 let mut err = struct_span_err!(
665 "cannot apply unary operator `{}` to type `{}`",
671 format!("cannot apply unary operator `{}`", op.as_str()),
674 Uint(_) if op == hir::UnOp::UnNeg => {
675 err.note("unsigned values cannot be negated");
677 Str | Never | Char | Tuple(_) | Array(_, _) => {}
678 Ref(_, ref lty, _) if lty.kind == Str => {}
680 let missing_trait = match op {
681 hir::UnOp::UnNeg => "std::ops::Neg",
682 hir::UnOp::UnNot => "std::ops::Not",
683 hir::UnOp::UnDeref => "std::ops::UnDerf",
685 suggest_impl_missing(&mut err, operand_ty, &missing_trait);
698 other_tys: &[Ty<'tcx>],
700 ) -> Result<MethodCallee<'tcx>, ()> {
701 let lang = self.tcx.lang_items();
703 let span = match op {
704 Op::Binary(op, _) => op.span,
705 Op::Unary(_, span) => span,
707 let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op {
709 hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
710 hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
711 hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
712 hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
713 hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
714 hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
715 hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
716 hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
717 hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
718 hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
725 | hir::BinOpKind::And
726 | hir::BinOpKind::Or => {
727 span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
730 } else if let Op::Binary(op, IsAssign::No) = op {
732 hir::BinOpKind::Add => (sym::add, lang.add_trait()),
733 hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
734 hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
735 hir::BinOpKind::Div => (sym::div, lang.div_trait()),
736 hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
737 hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
738 hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
739 hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
740 hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
741 hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
742 hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
743 hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
744 hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
745 hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
746 hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
747 hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
748 hir::BinOpKind::And | hir::BinOpKind::Or => {
749 span_bug!(span, "&& and || are not overloadable")
752 } else if let Op::Unary(hir::UnOp::UnNot, _) = op {
753 (sym::not, lang.not_trait())
754 } else if let Op::Unary(hir::UnOp::UnNeg, _) = op {
755 (sym::neg, lang.neg_trait())
757 bug!("lookup_op_method: op not supported: {:?}", op)
761 "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
762 lhs_ty, op, opname, trait_did
765 let method = trait_did.and_then(|trait_did| {
766 let opname = Ident::with_dummy_span(opname);
767 self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))
772 let method = self.register_infer_ok_obligations(ok);
773 self.select_obligations_where_possible(false, |_| {});
782 // Binary operator categories. These categories summarize the behavior
783 // with respect to the builtin operationrs supported.
785 /// &&, || -- cannot be overridden
788 /// <<, >> -- when shifting a single integer, rhs can be any
789 /// integer type. For simd, types must match.
792 /// +, -, etc -- takes equal types, produces same type as input,
793 /// applicable to ints/floats/simd
796 /// &, |, ^ -- takes equal types, produces same type as input,
797 /// applicable to ints/floats/simd/bool
800 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
801 /// which produce the input type
806 fn from(op: hir::BinOp) -> BinOpCategory {
808 hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
811 | hir::BinOpKind::Sub
812 | hir::BinOpKind::Mul
813 | hir::BinOpKind::Div
814 | hir::BinOpKind::Rem => BinOpCategory::Math,
816 hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
817 BinOpCategory::Bitwise
825 | hir::BinOpKind::Gt => BinOpCategory::Comparison,
827 hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
832 /// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
833 #[derive(Clone, Copy, Debug, PartialEq)]
839 #[derive(Clone, Copy, Debug)]
841 Binary(hir::BinOp, IsAssign),
842 Unary(hir::UnOp, Span),
845 /// Dereferences a single level of immutable referencing.
846 fn deref_ty_if_possible(ty: Ty<'tcx>) -> Ty<'tcx> {
848 ty::Ref(_, ty, hir::Mutability::Not) => ty,
853 /// Returns `true` if this is a built-in arithmetic operation (e.g., u32
854 /// + u32, i16x4 == i16x4) and false if these types would have to be
855 /// overloaded to be legal. There are two reasons that we distinguish
856 /// builtin operations from overloaded ones (vs trying to drive
857 /// everything uniformly through the trait system and intrinsics or
858 /// something like that):
860 /// 1. Builtin operations can trivially be evaluated in constants.
861 /// 2. For comparison operators applied to SIMD types the result is
862 /// not of type `bool`. For example, `i16x4 == i16x4` yields a
863 /// type like `i16x4`. This means that the overloaded trait
864 /// `PartialEq` is not applicable.
866 /// Reason #2 is the killer. I tried for a while to always use
867 /// overloaded logic and just check the types in constants/codegen after
868 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
869 fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
870 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
871 // (See https://github.com/rust-lang/rust/issues/57447.)
872 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
874 match BinOpCategory::from(op) {
875 BinOpCategory::Shortcircuit => true,
877 BinOpCategory::Shift => {
878 lhs.references_error()
879 || rhs.references_error()
880 || lhs.is_integral() && rhs.is_integral()
883 BinOpCategory::Math => {
884 lhs.references_error()
885 || rhs.references_error()
886 || lhs.is_integral() && rhs.is_integral()
887 || lhs.is_floating_point() && rhs.is_floating_point()
890 BinOpCategory::Bitwise => {
891 lhs.references_error()
892 || rhs.references_error()
893 || lhs.is_integral() && rhs.is_integral()
894 || lhs.is_floating_point() && rhs.is_floating_point()
895 || lhs.is_bool() && rhs.is_bool()
898 BinOpCategory::Comparison => {
899 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
904 /// If applicable, note that an implementation of `trait` for `ty` may fix the error.
905 fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_trait: &str) {
906 if let Adt(def, _) = ty.peel_refs().kind {
907 if def.did.is_local() {
909 "an implementation of `{}` might be missing for `{}`",
916 fn suggest_constraining_param(
919 mut err: &mut DiagnosticBuilder<'_>,
927 let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait);
928 // Try to find the def-id and details for the parameter p. We have only the index,
929 // so we have to find the enclosing function's def-id, then look through its declared
930 // generic parameters to get the declaration.
931 let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
932 let generics = tcx.generics_of(def_id);
933 let param_def_id = generics.type_param(&p, tcx).def_id;
934 if let Some(generics) = param_def_id
936 .map(|id| hir.as_local_hir_id(id))
937 .and_then(|id| hir.find(hir.get_parent_item(id)))
939 .and_then(|node| node.generics())
941 let output = if set_output { format!("<Output = {}>", rhs_ty) } else { String::new() };
942 suggest_constraining_type_param(
946 &format!("{}", lhs_ty),
947 &format!("{}{}", missing_trait, output),
951 let span = tcx.def_span(param_def_id);
952 err.span_label(span, msg);
956 struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
958 impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
959 fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
960 if let ty::Param(_) = ty.kind {
963 ty.super_visit_with(self)
967 struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
969 impl TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
970 fn tcx(&self) -> TyCtxt<'tcx> {
974 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
976 ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin {
977 kind: TypeVariableOriginKind::MiscVariable,
980 _ => ty.super_fold_with(self),