1use rustc_data_structures::packed::Pu128;
4use rustc_errors::codes::*;
5use rustc_errors::{Applicability, Diag, struct_span_code_err};
6use rustc_infer::traits::ObligationCauseCode;
7use rustc_middle::ty::adjustment::{
8 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
9};
10use rustc_middle::ty::print::with_no_trimmed_paths;
11use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
12use rustc_middle::{bug, span_bug};
13use rustc_session::errors::ExprParenthesesNeeded;
14use rustc_span::source_map::Spanned;
15use rustc_span::{Ident, Span, sym};
16use rustc_trait_selection::infer::InferCtxtExt;
17use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
18use tracing::debug;
19use {rustc_ast as ast, rustc_hir as hir};
20
21use super::FnCtxt;
22use super::method::MethodCallee;
23use crate::Expectation;
24
25impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26 pub(crate) fn check_expr_binop_assign(
28 &self,
29 expr: &'tcx hir::Expr<'tcx>,
30 op: hir::BinOp,
31 lhs: &'tcx hir::Expr<'tcx>,
32 rhs: &'tcx hir::Expr<'tcx>,
33 expected: Expectation<'tcx>,
34 ) -> Ty<'tcx> {
35 let (lhs_ty, rhs_ty, return_ty) =
36 self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
37
38 let ty =
39 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
40 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
41 self.tcx.types.unit
42 } else {
43 return_ty
44 };
45
46 self.check_lhs_assignable(lhs, E0067, op.span, |err| {
47 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
48 if self
49 .lookup_op_method(
50 (lhs, lhs_deref_ty),
51 Some((rhs, rhs_ty)),
52 Op::Binary(op, IsAssign::Yes),
53 expected,
54 )
55 .is_ok()
56 {
57 if self
60 .lookup_op_method(
61 (lhs, lhs_ty),
62 Some((rhs, rhs_ty)),
63 Op::Binary(op, IsAssign::Yes),
64 expected,
65 )
66 .is_err()
67 {
68 err.downgrade_to_delayed_bug();
69 } else {
70 err.span_suggestion_verbose(
72 lhs.span.shrink_to_lo(),
73 "consider dereferencing the left-hand side of this operation",
74 "*",
75 Applicability::MaybeIncorrect,
76 );
77 }
78 }
79 }
80 });
81
82 ty
83 }
84
85 pub(crate) fn check_expr_binop(
87 &self,
88 expr: &'tcx hir::Expr<'tcx>,
89 op: hir::BinOp,
90 lhs_expr: &'tcx hir::Expr<'tcx>,
91 rhs_expr: &'tcx hir::Expr<'tcx>,
92 expected: Expectation<'tcx>,
93 ) -> Ty<'tcx> {
94 let tcx = self.tcx;
95
96 debug!(
97 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
98 expr.hir_id, expr, op, lhs_expr, rhs_expr
99 );
100
101 match BinOpCategory::from(op) {
102 BinOpCategory::Shortcircuit => {
103 self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
105 let lhs_diverges = self.diverges.get();
106 self.check_expr_coercible_to_type(rhs_expr, tcx.types.bool, None);
107
108 self.diverges.set(lhs_diverges);
110
111 tcx.types.bool
112 }
113 _ => {
114 let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
118 expr,
119 lhs_expr,
120 rhs_expr,
121 op,
122 IsAssign::No,
123 expected,
124 );
125
126 if !lhs_ty.is_ty_var()
139 && !rhs_ty.is_ty_var()
140 && is_builtin_binop(lhs_ty, rhs_ty, op)
141 {
142 let builtin_return_ty = self.enforce_builtin_binop_types(
143 lhs_expr.span,
144 lhs_ty,
145 rhs_expr.span,
146 rhs_ty,
147 op,
148 );
149 self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
150 builtin_return_ty
151 } else {
152 return_ty
153 }
154 }
155 }
156 }
157
158 fn enforce_builtin_binop_types(
159 &self,
160 lhs_span: Span,
161 lhs_ty: Ty<'tcx>,
162 rhs_span: Span,
163 rhs_ty: Ty<'tcx>,
164 op: hir::BinOp,
165 ) -> Ty<'tcx> {
166 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
167
168 let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
171
172 let tcx = self.tcx;
173 match BinOpCategory::from(op) {
174 BinOpCategory::Shortcircuit => {
175 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
176 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
177 tcx.types.bool
178 }
179
180 BinOpCategory::Shift => {
181 lhs_ty
183 }
184
185 BinOpCategory::Math | BinOpCategory::Bitwise => {
186 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
188 lhs_ty
189 }
190
191 BinOpCategory::Comparison => {
192 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
194 tcx.types.bool
195 }
196 }
197 }
198
199 fn check_overloaded_binop(
200 &self,
201 expr: &'tcx hir::Expr<'tcx>,
202 lhs_expr: &'tcx hir::Expr<'tcx>,
203 rhs_expr: &'tcx hir::Expr<'tcx>,
204 op: hir::BinOp,
205 is_assign: IsAssign,
206 expected: Expectation<'tcx>,
207 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
208 debug!(
209 "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
210 expr.hir_id, op, is_assign
211 );
212
213 let lhs_ty = match is_assign {
214 IsAssign::No => {
215 let lhs_ty = self.check_expr(lhs_expr);
221 let fresh_var = self.next_ty_var(lhs_expr.span);
222 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
223 }
224 IsAssign::Yes => {
225 self.check_expr(lhs_expr)
230 }
231 };
232 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
233
234 let rhs_ty_var = self.next_ty_var(rhs_expr.span);
241
242 let result = self.lookup_op_method(
243 (lhs_expr, lhs_ty),
244 Some((rhs_expr, rhs_ty_var)),
245 Op::Binary(op, is_assign),
246 expected,
247 );
248
249 let rhs_ty = self.check_expr_coercible_to_type_or_error(
251 rhs_expr,
252 rhs_ty_var,
253 Some(lhs_expr),
254 |err, ty| {
255 self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr, op);
256 },
257 );
258 let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
259
260 let return_ty = match result {
261 Ok(method) => {
262 let by_ref_binop = !op.node.is_by_value();
263 if is_assign == IsAssign::Yes || by_ref_binop {
264 if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
265 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
266 let autoref = Adjustment {
267 kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
268 target: method.sig.inputs()[0],
269 };
270 self.apply_adjustments(lhs_expr, vec![autoref]);
271 }
272 }
273 if by_ref_binop {
274 if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() {
275 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
278
279 let autoref = Adjustment {
280 kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
281 target: method.sig.inputs()[1],
282 };
283 self.typeck_results
288 .borrow_mut()
289 .adjustments_mut()
290 .entry(rhs_expr.hir_id)
291 .or_default()
292 .push(autoref);
293 }
294 }
295 self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
296
297 method.sig.output()
298 }
299 Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => {
301 Ty::new_misc_error(self.tcx)
302 }
303 Err(errors) => {
304 let (_, trait_def_id) =
305 lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
306 let missing_trait = trait_def_id
307 .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
308 let mut path = None;
309 let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
310 let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
311 let (mut err, output_def_id) = match is_assign {
312 IsAssign::Yes => {
313 let mut err = struct_span_code_err!(
314 self.dcx(),
315 expr.span,
316 E0368,
317 "binary assignment operation `{}=` cannot be applied to type `{}`",
318 op.node.as_str(),
319 lhs_ty_str,
320 );
321 err.span_label(
322 lhs_expr.span,
323 format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str),
324 );
325 self.note_unmet_impls_on_type(&mut err, errors, false);
326 (err, None)
327 }
328 IsAssign::No => {
329 let message = match op.node {
330 hir::BinOpKind::Add => {
331 format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
332 }
333 hir::BinOpKind::Sub => {
334 format!("cannot subtract `{rhs_ty_str}` from `{lhs_ty_str}`")
335 }
336 hir::BinOpKind::Mul => {
337 format!("cannot multiply `{lhs_ty_str}` by `{rhs_ty_str}`")
338 }
339 hir::BinOpKind::Div => {
340 format!("cannot divide `{lhs_ty_str}` by `{rhs_ty_str}`")
341 }
342 hir::BinOpKind::Rem => {
343 format!(
344 "cannot calculate the remainder of `{lhs_ty_str}` divided by \
345 `{rhs_ty_str}`"
346 )
347 }
348 hir::BinOpKind::BitAnd => {
349 format!("no implementation for `{lhs_ty_str} & {rhs_ty_str}`")
350 }
351 hir::BinOpKind::BitXor => {
352 format!("no implementation for `{lhs_ty_str} ^ {rhs_ty_str}`")
353 }
354 hir::BinOpKind::BitOr => {
355 format!("no implementation for `{lhs_ty_str} | {rhs_ty_str}`")
356 }
357 hir::BinOpKind::Shl => {
358 format!("no implementation for `{lhs_ty_str} << {rhs_ty_str}`")
359 }
360 hir::BinOpKind::Shr => {
361 format!("no implementation for `{lhs_ty_str} >> {rhs_ty_str}`")
362 }
363 _ => format!(
364 "binary operation `{}` cannot be applied to type `{}`",
365 op.node.as_str(),
366 lhs_ty_str,
367 ),
368 };
369 let output_def_id = trait_def_id.and_then(|def_id| {
370 self.tcx
371 .associated_item_def_ids(def_id)
372 .iter()
373 .find(|item_def_id| {
374 self.tcx.associated_item(*item_def_id).name == sym::Output
375 })
376 .cloned()
377 });
378 let mut err =
379 struct_span_code_err!(self.dcx(), op.span, E0369, "{message}");
380 if !lhs_expr.span.eq(&rhs_expr.span) {
381 err.span_label(lhs_expr.span, lhs_ty_str.clone());
382 err.span_label(rhs_expr.span, rhs_ty_str);
383 }
384 let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty);
385 self.note_unmet_impls_on_type(&mut err, errors, suggest_derive);
386 (err, output_def_id)
387 }
388 };
389 *err.long_ty_path() = path;
390
391 let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err);
393
394 if maybe_missing_semi
398 && let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id)
399 && let hir::ExprKind::Assign(lhs, _, _) = parent.kind
400 && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(parent.hir_id)
401 && let hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_) = stmt.kind
402 && lhs.hir_id == expr.hir_id
403 {
404 err.downgrade_to_delayed_bug();
405 }
406
407 let suggest_deref_binop = |err: &mut Diag<'_, _>, lhs_deref_ty: Ty<'tcx>| {
408 if self
409 .lookup_op_method(
410 (lhs_expr, lhs_deref_ty),
411 Some((rhs_expr, rhs_ty)),
412 Op::Binary(op, is_assign),
413 expected,
414 )
415 .is_ok()
416 {
417 let msg = format!(
418 "`{}{}` can be used on `{}` if you dereference the left-hand side",
419 op.node.as_str(),
420 match is_assign {
421 IsAssign::Yes => "=",
422 IsAssign::No => "",
423 },
424 self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
425 );
426 err.span_suggestion_verbose(
427 lhs_expr.span.shrink_to_lo(),
428 msg,
429 "*",
430 rustc_errors::Applicability::MachineApplicable,
431 );
432 }
433 };
434
435 let suggest_different_borrow =
436 |err: &mut Diag<'_, _>,
437 lhs_adjusted_ty,
438 lhs_new_mutbl: Option<ast::Mutability>,
439 rhs_adjusted_ty,
440 rhs_new_mutbl: Option<ast::Mutability>| {
441 if self
442 .lookup_op_method(
443 (lhs_expr, lhs_adjusted_ty),
444 Some((rhs_expr, rhs_adjusted_ty)),
445 Op::Binary(op, is_assign),
446 expected,
447 )
448 .is_ok()
449 {
450 let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
451 let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
452 let op = op.node.as_str();
453 err.note(format!("an implementation for `{lhs} {op} {rhs}` exists"));
454
455 if let Some(lhs_new_mutbl) = lhs_new_mutbl
456 && let Some(rhs_new_mutbl) = rhs_new_mutbl
457 && lhs_new_mutbl.is_not()
458 && rhs_new_mutbl.is_not()
459 {
460 err.multipart_suggestion_verbose(
461 "consider reborrowing both sides",
462 vec![
463 (lhs_expr.span.shrink_to_lo(), "&*".to_string()),
464 (rhs_expr.span.shrink_to_lo(), "&*".to_string()),
465 ],
466 rustc_errors::Applicability::MachineApplicable,
467 );
468 } else {
469 let mut suggest_new_borrow =
470 |new_mutbl: ast::Mutability, sp: Span| {
471 if new_mutbl.is_not() {
473 err.span_suggestion_verbose(
474 sp.shrink_to_lo(),
475 "consider reborrowing this side",
476 "&*",
477 rustc_errors::Applicability::MachineApplicable,
478 );
479 } else {
481 err.span_help(
482 sp,
483 "consider making this expression a mutable borrow",
484 );
485 }
486 };
487
488 if let Some(lhs_new_mutbl) = lhs_new_mutbl {
489 suggest_new_borrow(lhs_new_mutbl, lhs_expr.span);
490 }
491 if let Some(rhs_new_mutbl) = rhs_new_mutbl {
492 suggest_new_borrow(rhs_new_mutbl, rhs_expr.span);
493 }
494 }
495 }
496 };
497
498 let is_compatible_after_call = |lhs_ty, rhs_ty| {
499 self.lookup_op_method(
500 (lhs_expr, lhs_ty),
501 Some((rhs_expr, rhs_ty)),
502 Op::Binary(op, is_assign),
503 expected,
504 )
505 .is_ok()
506 || self.can_eq(self.param_env, lhs_ty, rhs_ty)
510 };
511
512 if !op.span.can_be_used_for_suggestions() {
515 } else if is_assign == IsAssign::Yes
517 && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
518 {
519 suggest_deref_binop(&mut err, lhs_deref_ty);
520 } else if is_assign == IsAssign::No
521 && let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
522 {
523 if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
524 suggest_deref_binop(&mut err, *lhs_deref_ty);
525 } else {
526 let lhs_inv_mutbl = mutbl.invert();
527 let lhs_inv_mutbl_ty =
528 Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl);
529
530 suggest_different_borrow(
531 &mut err,
532 lhs_inv_mutbl_ty,
533 Some(lhs_inv_mutbl),
534 rhs_ty,
535 None,
536 );
537
538 if let ty::Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() {
539 let rhs_inv_mutbl = mutbl.invert();
540 let rhs_inv_mutbl_ty =
541 Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl);
542
543 suggest_different_borrow(
544 &mut err,
545 lhs_ty,
546 None,
547 rhs_inv_mutbl_ty,
548 Some(rhs_inv_mutbl),
549 );
550 suggest_different_borrow(
551 &mut err,
552 lhs_inv_mutbl_ty,
553 Some(lhs_inv_mutbl),
554 rhs_inv_mutbl_ty,
555 Some(rhs_inv_mutbl),
556 );
557 }
558 }
559 } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
560 is_compatible_after_call(lhs_ty, rhs_ty)
561 }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
562 is_compatible_after_call(lhs_ty, rhs_ty)
563 }) || self.suggest_two_fn_call(
564 &mut err,
565 rhs_expr,
566 rhs_ty,
567 lhs_expr,
568 lhs_ty,
569 |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty),
570 ) {
571 }
573
574 if let Some(missing_trait) = missing_trait {
575 if op.node == hir::BinOpKind::Add
576 && self.check_str_addition(
577 lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
578 )
579 {
580 } else if lhs_ty.has_non_region_param() {
584 let errors = self
592 .lookup_op_method(
593 (lhs_expr, lhs_ty),
594 Some((rhs_expr, rhs_ty)),
595 Op::Binary(op, is_assign),
596 expected,
597 )
598 .unwrap_err();
599 if !errors.is_empty() {
600 for error in errors {
601 if let Some(trait_pred) =
602 error.obligation.predicate.as_trait_clause()
603 {
604 let output_associated_item = match error.obligation.cause.code()
605 {
606 ObligationCauseCode::BinOp {
607 output_ty: Some(output_ty),
608 ..
609 } => {
610 if let Some(output_def_id) = output_def_id
612 && let Some(trait_def_id) = trait_def_id
613 && self.tcx.parent(output_def_id) == trait_def_id
614 && let Some(output_ty) = output_ty
615 .make_suggestable(self.tcx, false, None)
616 {
617 Some(("Output", output_ty))
618 } else {
619 None
620 }
621 }
622 _ => None,
623 };
624
625 self.err_ctxt().suggest_restricting_param_bound(
626 &mut err,
627 trait_pred,
628 output_associated_item,
629 self.body_id,
630 );
631 }
632 }
633 } else {
634 err.note(format!(
637 "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`"
638 ));
639 }
640 }
641 }
642
643 if op.span.can_be_used_for_suggestions() {
646 match op.node {
647 hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => {
648 err.multipart_suggestion(
649 "consider using `wrapping_add` or `add` for pointer + {integer}",
650 vec![
651 (
652 lhs_expr.span.between(rhs_expr.span),
653 ".wrapping_add(".to_owned(),
654 ),
655 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
656 ],
657 Applicability::MaybeIncorrect,
658 );
659 }
660 hir::BinOpKind::Sub => {
661 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
662 err.multipart_suggestion(
663 "consider using `wrapping_sub` or `sub` for \
664 pointer - {integer}",
665 vec![
666 (
667 lhs_expr.span.between(rhs_expr.span),
668 ".wrapping_sub(".to_owned(),
669 ),
670 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
671 ],
672 Applicability::MaybeIncorrect,
673 );
674 }
675
676 if lhs_ty.is_raw_ptr() && rhs_ty.is_raw_ptr() {
677 err.multipart_suggestion(
678 "consider using `offset_from` for pointer - pointer if the \
679 pointers point to the same allocation",
680 vec![
681 (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
682 (
683 lhs_expr.span.between(rhs_expr.span),
684 ".offset_from(".to_owned(),
685 ),
686 (rhs_expr.span.shrink_to_hi(), ") }".to_owned()),
687 ],
688 Applicability::MaybeIncorrect,
689 );
690 }
691 }
692 _ => {}
693 }
694 }
695
696 let reported = err.emit();
697 Ty::new_error(self.tcx, reported)
698 }
699 };
700
701 (lhs_ty, rhs_ty, return_ty)
702 }
703
704 fn check_str_addition(
710 &self,
711 lhs_expr: &'tcx hir::Expr<'tcx>,
712 rhs_expr: &'tcx hir::Expr<'tcx>,
713 lhs_ty: Ty<'tcx>,
714 rhs_ty: Ty<'tcx>,
715 err: &mut Diag<'_>,
716 is_assign: IsAssign,
717 op: hir::BinOp,
718 ) -> bool {
719 let str_concat_note = "string concatenation requires an owned `String` on the left";
720 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
721 let to_owned_msg = "create an owned `String` from a string reference";
722
723 let string_type = self.tcx.lang_items().string();
724 let is_std_string =
725 |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|ty_def| Some(ty_def.did()) == string_type);
726
727 match (lhs_ty.kind(), rhs_ty.kind()) {
728 (&ty::Ref(_, l_ty, _), &ty::Ref(_, r_ty, _)) if (*l_ty.kind() == ty::Str || is_std_string(l_ty))
730 && (*r_ty.kind() == ty::Str
731 || is_std_string(r_ty)
732 || matches!(
733 r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str
734 )) =>
735 {
736 if let IsAssign::No = is_assign { err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
738 err.note(str_concat_note);
739 if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
740 err.span_suggestion_verbose(
741 lhs_expr.span.until(lhs_inner_expr.span),
742 rm_borrow_msg,
743 "",
744 Applicability::MachineApplicable
745 );
746 } else {
747 err.span_suggestion_verbose(
748 lhs_expr.span.shrink_to_hi(),
749 to_owned_msg,
750 ".to_owned()",
751 Applicability::MachineApplicable
752 );
753 }
754 }
755 true
756 }
757 (&ty::Ref(_, l_ty, _), &ty::Adt(..)) if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
759 {
760 err.span_label(
761 op.span,
762 "`+` cannot be used to concatenate a `&str` with a `String`",
763 );
764 match is_assign {
765 IsAssign::No => {
766 let sugg_msg;
767 let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
768 sugg_msg = "remove the borrow on the left and add one on the right";
769 (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned())
770 } else {
771 sugg_msg = "create an owned `String` on the left and add a borrow on the right";
772 (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned())
773 };
774 let suggestions = vec![
775 lhs_sugg,
776 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
777 ];
778 err.multipart_suggestion_verbose(
779 sugg_msg,
780 suggestions,
781 Applicability::MachineApplicable,
782 );
783 }
784 IsAssign::Yes => {
785 err.note(str_concat_note);
786 }
787 }
788 true
789 }
790 _ => false,
791 }
792 }
793
794 pub(crate) fn check_user_unop(
795 &self,
796 ex: &'tcx hir::Expr<'tcx>,
797 operand_ty: Ty<'tcx>,
798 op: hir::UnOp,
799 expected: Expectation<'tcx>,
800 ) -> Ty<'tcx> {
801 assert!(op.is_by_value());
802 match self.lookup_op_method((ex, operand_ty), None, Op::Unary(op, ex.span), expected) {
803 Ok(method) => {
804 self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
805 method.sig.output()
806 }
807 Err(errors) => {
808 let actual = self.resolve_vars_if_possible(operand_ty);
809 let guar = actual.error_reported().err().unwrap_or_else(|| {
810 let mut file = None;
811 let ty_str = self.tcx.short_string(actual, &mut file);
812 let mut err = struct_span_code_err!(
813 self.dcx(),
814 ex.span,
815 E0600,
816 "cannot apply unary operator `{}` to type `{ty_str}`",
817 op.as_str(),
818 );
819 *err.long_ty_path() = file;
820 err.span_label(
821 ex.span,
822 format!("cannot apply unary operator `{}`", op.as_str()),
823 );
824
825 if operand_ty.has_non_region_param() {
826 let predicates = errors
827 .iter()
828 .filter_map(|error| error.obligation.predicate.as_trait_clause());
829 for pred in predicates {
830 self.err_ctxt().suggest_restricting_param_bound(
831 &mut err,
832 pred,
833 None,
834 self.body_id,
835 );
836 }
837 }
838
839 let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
840 if let Some(sp) =
841 self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp)
842 {
843 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
847 } else {
848 match actual.kind() {
849 ty::Uint(_) if op == hir::UnOp::Neg => {
850 err.note("unsigned values cannot be negated");
851
852 if let hir::ExprKind::Unary(
853 _,
854 hir::Expr {
855 kind:
856 hir::ExprKind::Lit(Spanned {
857 node: ast::LitKind::Int(Pu128(1), _),
858 ..
859 }),
860 ..
861 },
862 ) = ex.kind
863 {
864 let span = if let hir::Node::Expr(parent) =
865 self.tcx.parent_hir_node(ex.hir_id)
866 && let hir::ExprKind::Cast(..) = parent.kind
867 {
868 parent.span
870 } else {
871 ex.span
872 };
873 err.span_suggestion_verbose(
874 span,
875 format!(
876 "you may have meant the maximum value of `{actual}`",
877 ),
878 format!("{actual}::MAX"),
879 Applicability::MaybeIncorrect,
880 );
881 }
882 }
883 ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {}
884 ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {}
885 _ => {
886 self.note_unmet_impls_on_type(&mut err, errors, true);
887 }
888 }
889 }
890 err.emit()
891 });
892 Ty::new_error(self.tcx, guar)
893 }
894 }
895 }
896
897 fn lookup_op_method(
898 &self,
899 (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
900 opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
901 op: Op,
902 expected: Expectation<'tcx>,
903 ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
904 let span = match op {
905 Op::Binary(op, _) => op.span,
906 Op::Unary(_, span) => span,
907 };
908 let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else {
909 return Err(vec![]);
911 };
912
913 debug!(
914 "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
915 lhs_ty, op, opname, trait_did
916 );
917
918 let opname = Ident::with_dummy_span(opname);
919 let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
920 let cause = self.cause(
921 span,
922 ObligationCauseCode::BinOp {
923 lhs_hir_id: lhs_expr.hir_id,
924 rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id),
925 rhs_span: opt_rhs_expr.map(|expr| expr.span),
926 rhs_is_lit: opt_rhs_expr
927 .is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
928 output_ty: expected.only_has_type(self),
929 },
930 );
931
932 let method =
933 self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty);
934 match method {
935 Some(ok) => {
936 let method = self.register_infer_ok_obligations(ok);
937 self.select_obligations_where_possible(|_| {});
938 Ok(method)
939 }
940 None => {
941 self.dcx().span_delayed_bug(span, "this path really should be doomed...");
945 if let Some((rhs_expr, rhs_ty)) = opt_rhs
949 && rhs_ty.is_ty_var()
950 {
951 self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None);
952 }
953
954 let args =
956 ty::GenericArgs::for_item(self.tcx, trait_did, |param, _| match param.kind {
957 ty::GenericParamDefKind::Lifetime
958 | ty::GenericParamDefKind::Const { .. } => {
959 unreachable!("did not expect operand trait to have lifetime/const args")
960 }
961 ty::GenericParamDefKind::Type { .. } => {
962 if param.index == 0 {
963 lhs_ty.into()
964 } else {
965 opt_rhs_ty.expect("expected RHS for binop").into()
966 }
967 }
968 });
969 let obligation = Obligation::new(
970 self.tcx,
971 cause,
972 self.param_env,
973 ty::TraitRef::new_from_args(self.tcx, trait_did, args),
974 );
975 let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
976 ocx.register_obligation(obligation);
977 Err(ocx.select_all_or_error())
978 }
979 }
980 }
981}
982
983fn lang_item_for_op(
984 tcx: TyCtxt<'_>,
985 op: Op,
986 span: Span,
987) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
988 let lang = tcx.lang_items();
989 if let Op::Binary(op, IsAssign::Yes) = op {
990 match op.node {
991 hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
992 hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
993 hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
994 hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
995 hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
996 hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
997 hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
998 hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
999 hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
1000 hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
1001 hir::BinOpKind::Lt
1002 | hir::BinOpKind::Le
1003 | hir::BinOpKind::Ge
1004 | hir::BinOpKind::Gt
1005 | hir::BinOpKind::Eq
1006 | hir::BinOpKind::Ne
1007 | hir::BinOpKind::And
1008 | hir::BinOpKind::Or => {
1009 span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
1010 }
1011 }
1012 } else if let Op::Binary(op, IsAssign::No) = op {
1013 match op.node {
1014 hir::BinOpKind::Add => (sym::add, lang.add_trait()),
1015 hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
1016 hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
1017 hir::BinOpKind::Div => (sym::div, lang.div_trait()),
1018 hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
1019 hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
1020 hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
1021 hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
1022 hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
1023 hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
1024 hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
1025 hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
1026 hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
1027 hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
1028 hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
1029 hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
1030 hir::BinOpKind::And | hir::BinOpKind::Or => {
1031 span_bug!(span, "&& and || are not overloadable")
1032 }
1033 }
1034 } else if let Op::Unary(hir::UnOp::Not, _) = op {
1035 (sym::not, lang.not_trait())
1036 } else if let Op::Unary(hir::UnOp::Neg, _) = op {
1037 (sym::neg, lang.neg_trait())
1038 } else {
1039 bug!("lookup_op_method: op not supported: {:?}", op)
1040 }
1041}
1042
1043enum BinOpCategory {
1046 Shortcircuit,
1048
1049 Shift,
1052
1053 Math,
1056
1057 Bitwise,
1060
1061 Comparison,
1064}
1065
1066impl BinOpCategory {
1067 fn from(op: hir::BinOp) -> BinOpCategory {
1068 match op.node {
1069 hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
1070
1071 hir::BinOpKind::Add
1072 | hir::BinOpKind::Sub
1073 | hir::BinOpKind::Mul
1074 | hir::BinOpKind::Div
1075 | hir::BinOpKind::Rem => BinOpCategory::Math,
1076
1077 hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
1078 BinOpCategory::Bitwise
1079 }
1080
1081 hir::BinOpKind::Eq
1082 | hir::BinOpKind::Ne
1083 | hir::BinOpKind::Lt
1084 | hir::BinOpKind::Le
1085 | hir::BinOpKind::Ge
1086 | hir::BinOpKind::Gt => BinOpCategory::Comparison,
1087
1088 hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
1089 }
1090 }
1091}
1092
1093#[derive(Clone, Copy, Debug, PartialEq)]
1095enum IsAssign {
1096 No,
1097 Yes,
1098}
1099
1100#[derive(Clone, Copy, Debug)]
1101enum Op {
1102 Binary(hir::BinOp, IsAssign),
1103 Unary(hir::UnOp, Span),
1104}
1105
1106fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1108 match ty.kind() {
1109 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
1110 _ => ty,
1111 }
1112}
1113
1114fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
1131 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
1134
1135 match BinOpCategory::from(op) {
1136 BinOpCategory::Shortcircuit => true,
1137
1138 BinOpCategory::Shift => {
1139 lhs.references_error()
1140 || rhs.references_error()
1141 || lhs.is_integral() && rhs.is_integral()
1142 }
1143
1144 BinOpCategory::Math => {
1145 lhs.references_error()
1146 || rhs.references_error()
1147 || lhs.is_integral() && rhs.is_integral()
1148 || lhs.is_floating_point() && rhs.is_floating_point()
1149 }
1150
1151 BinOpCategory::Bitwise => {
1152 lhs.references_error()
1153 || rhs.references_error()
1154 || lhs.is_integral() && rhs.is_integral()
1155 || lhs.is_floating_point() && rhs.is_floating_point()
1156 || lhs.is_bool() && rhs.is_bool()
1157 }
1158
1159 BinOpCategory::Comparison => {
1160 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
1161 }
1162 }
1163}