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