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::bug;
8use rustc_middle::ty::adjustment::{
9 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
10};
11use rustc_middle::ty::print::with_no_trimmed_paths;
12use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
13use rustc_session::errors::ExprParenthesesNeeded;
14use rustc_span::source_map::Spanned;
15use rustc_span::{Span, Symbol, 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::method::TreatNotYetDefinedOpaques;
24use crate::{Expectation, errors};
25
26impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27 pub(crate) fn check_expr_assign_op(
29 &self,
30 expr: &'tcx hir::Expr<'tcx>,
31 op: hir::AssignOp,
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::AssignOp(op), expected);
38
39 let category = BinOpCategory::from(op.node);
40 let ty = if !lhs_ty.is_ty_var()
41 && !rhs_ty.is_ty_var()
42 && is_builtin_binop(lhs_ty, rhs_ty, category)
43 {
44 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category);
45 self.tcx.types.unit
46 } else {
47 return_ty
48 };
49
50 self.check_lhs_assignable(lhs, E0067, op.span, |err| {
51 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
52 if self
53 .lookup_op_method(
54 (lhs, lhs_deref_ty),
55 Some((rhs, rhs_ty)),
56 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
57 op.span,
58 expected,
59 )
60 .is_ok()
61 {
62 if self
65 .lookup_op_method(
66 (lhs, lhs_ty),
67 Some((rhs, rhs_ty)),
68 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
69 op.span,
70 expected,
71 )
72 .is_err()
73 {
74 err.downgrade_to_delayed_bug();
75 } else {
76 err.span_suggestion_verbose(
78 lhs.span.shrink_to_lo(),
79 "consider dereferencing the left-hand side of this operation",
80 "*",
81 Applicability::MaybeIncorrect,
82 );
83 }
84 }
85 }
86 });
87
88 ty
89 }
90
91 pub(crate) fn check_expr_binop(
93 &self,
94 expr: &'tcx hir::Expr<'tcx>,
95 op: hir::BinOp,
96 lhs_expr: &'tcx hir::Expr<'tcx>,
97 rhs_expr: &'tcx hir::Expr<'tcx>,
98 expected: Expectation<'tcx>,
99 ) -> Ty<'tcx> {
100 let tcx = self.tcx;
101
102 debug!(
103 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
104 expr.hir_id, expr, op, lhs_expr, rhs_expr
105 );
106
107 match BinOpCategory::from(op.node) {
108 BinOpCategory::Shortcircuit => {
109 self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
111 let lhs_diverges = self.diverges.get();
112 self.check_expr_coercible_to_type(rhs_expr, tcx.types.bool, None);
113
114 self.diverges.set(lhs_diverges);
116
117 tcx.types.bool
118 }
119 _ => {
120 let (lhs_ty, rhs_ty, return_ty) =
124 self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
125
126 let category = BinOpCategory::from(op.node);
139 if !lhs_ty.is_ty_var()
140 && !rhs_ty.is_ty_var()
141 && is_builtin_binop(lhs_ty, rhs_ty, category)
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 category,
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 category: BinOpCategory,
166 ) -> Ty<'tcx> {
167 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
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 category {
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: Op,
206 expected: Expectation<'tcx>,
207 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
208 debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
209
210 let lhs_ty = match op {
211 Op::BinOp(_) => {
212 let lhs_ty = self.check_expr(lhs_expr);
218 let fresh_var = self.next_ty_var(lhs_expr.span);
219 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
220 }
221 Op::AssignOp(_) => {
222 self.check_expr(lhs_expr)
227 }
228 };
229 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
230
231 let rhs_ty_var = self.next_ty_var(rhs_expr.span);
238 let result = self.lookup_op_method(
239 (lhs_expr, lhs_ty),
240 Some((rhs_expr, rhs_ty_var)),
241 lang_item_for_binop(self.tcx, op),
242 op.span(),
243 expected,
244 );
245
246 let rhs_ty = self.check_expr_coercible_to_type_or_error(
248 rhs_expr,
249 rhs_ty_var,
250 Some(lhs_expr),
251 |err, ty| {
252 if let Op::BinOp(binop) = op
253 && binop.node == hir::BinOpKind::Eq
254 {
255 self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr);
256 }
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.is_by_value();
264 if matches!(op, Op::AssignOp(_)) || 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) = lang_item_for_binop(self.tcx, op);
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
312 let (mut err, output_def_id) = match op {
313 Op::AssignOp(assign_op) => {
316 if let Err(e) =
317 errors::maybe_emit_plus_equals_diagnostic(&self, assign_op, lhs_expr)
318 {
319 (e, None)
320 } else {
321 let s = assign_op.node.as_str();
322 let mut err = struct_span_code_err!(
323 self.dcx(),
324 expr.span,
325 E0368,
326 "binary assignment operation `{}` cannot be applied to type `{}`",
327 s,
328 lhs_ty_str,
329 );
330 err.span_label(
331 lhs_expr.span,
332 format!("cannot use `{}` on type `{}`", s, lhs_ty_str),
333 );
334 self.note_unmet_impls_on_type(&mut err, &errors, false);
335 (err, None)
336 }
337 }
338 Op::BinOp(bin_op) => {
339 let message = match bin_op.node {
340 hir::BinOpKind::Add => {
341 format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
342 }
343 hir::BinOpKind::Sub => {
344 format!("cannot subtract `{rhs_ty_str}` from `{lhs_ty_str}`")
345 }
346 hir::BinOpKind::Mul => {
347 format!("cannot multiply `{lhs_ty_str}` by `{rhs_ty_str}`")
348 }
349 hir::BinOpKind::Div => {
350 format!("cannot divide `{lhs_ty_str}` by `{rhs_ty_str}`")
351 }
352 hir::BinOpKind::Rem => {
353 format!(
354 "cannot calculate the remainder of `{lhs_ty_str}` divided by \
355 `{rhs_ty_str}`"
356 )
357 }
358 hir::BinOpKind::BitAnd => {
359 format!("no implementation for `{lhs_ty_str} & {rhs_ty_str}`")
360 }
361 hir::BinOpKind::BitXor => {
362 format!("no implementation for `{lhs_ty_str} ^ {rhs_ty_str}`")
363 }
364 hir::BinOpKind::BitOr => {
365 format!("no implementation for `{lhs_ty_str} | {rhs_ty_str}`")
366 }
367 hir::BinOpKind::Shl => {
368 format!("no implementation for `{lhs_ty_str} << {rhs_ty_str}`")
369 }
370 hir::BinOpKind::Shr => {
371 format!("no implementation for `{lhs_ty_str} >> {rhs_ty_str}`")
372 }
373 _ => format!(
374 "binary operation `{}` cannot be applied to type `{}`",
375 bin_op.node.as_str(),
376 lhs_ty_str
377 ),
378 };
379 let output_def_id = trait_def_id.and_then(|def_id| {
380 self.tcx
381 .associated_item_def_ids(def_id)
382 .iter()
383 .find(|item_def_id| {
384 self.tcx.associated_item(*item_def_id).name() == sym::Output
385 })
386 .cloned()
387 });
388 let mut err =
389 struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}");
390 if !lhs_expr.span.eq(&rhs_expr.span) {
391 err.span_label(lhs_expr.span, lhs_ty_str.clone());
392 err.span_label(rhs_expr.span, rhs_ty_str);
393 }
394 let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty);
395 self.note_unmet_impls_on_type(&mut err, &errors, suggest_derive);
396 (err, output_def_id)
397 }
398 };
399 *err.long_ty_path() = path;
400
401 let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err);
403
404 if maybe_missing_semi
408 && let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id)
409 && let hir::ExprKind::Assign(lhs, _, _) = parent.kind
410 && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(parent.hir_id)
411 && let hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_) = stmt.kind
412 && lhs.hir_id == expr.hir_id
413 {
414 err.downgrade_to_delayed_bug();
415 }
416
417 let suggest_deref_binop = |err: &mut Diag<'_, _>, lhs_deref_ty: Ty<'tcx>| {
418 if self
419 .lookup_op_method(
420 (lhs_expr, lhs_deref_ty),
421 Some((rhs_expr, rhs_ty)),
422 lang_item_for_binop(self.tcx, op),
423 op.span(),
424 expected,
425 )
426 .is_ok()
427 {
428 let msg = format!(
429 "`{}` can be used on `{}` if you dereference the left-hand side",
430 op.as_str(),
431 self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
432 );
433 err.span_suggestion_verbose(
434 lhs_expr.span.shrink_to_lo(),
435 msg,
436 "*",
437 rustc_errors::Applicability::MachineApplicable,
438 );
439 }
440 };
441
442 let suggest_different_borrow =
443 |err: &mut Diag<'_, _>,
444 lhs_adjusted_ty,
445 lhs_new_mutbl: Option<ast::Mutability>,
446 rhs_adjusted_ty,
447 rhs_new_mutbl: Option<ast::Mutability>| {
448 if self
449 .lookup_op_method(
450 (lhs_expr, lhs_adjusted_ty),
451 Some((rhs_expr, rhs_adjusted_ty)),
452 lang_item_for_binop(self.tcx, op),
453 op.span(),
454 expected,
455 )
456 .is_ok()
457 {
458 let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
459 let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
460 let op = op.as_str();
461 err.note(format!("an implementation for `{lhs} {op} {rhs}` exists"));
462
463 if let Some(lhs_new_mutbl) = lhs_new_mutbl
464 && let Some(rhs_new_mutbl) = rhs_new_mutbl
465 && lhs_new_mutbl.is_not()
466 && rhs_new_mutbl.is_not()
467 {
468 err.multipart_suggestion_verbose(
469 "consider reborrowing both sides",
470 vec![
471 (lhs_expr.span.shrink_to_lo(), "&*".to_string()),
472 (rhs_expr.span.shrink_to_lo(), "&*".to_string()),
473 ],
474 rustc_errors::Applicability::MachineApplicable,
475 );
476 } else {
477 let mut suggest_new_borrow =
478 |new_mutbl: ast::Mutability, sp: Span| {
479 if new_mutbl.is_not() {
481 err.span_suggestion_verbose(
482 sp.shrink_to_lo(),
483 "consider reborrowing this side",
484 "&*",
485 rustc_errors::Applicability::MachineApplicable,
486 );
487 } else {
489 err.span_help(
490 sp,
491 "consider making this expression a mutable borrow",
492 );
493 }
494 };
495
496 if let Some(lhs_new_mutbl) = lhs_new_mutbl {
497 suggest_new_borrow(lhs_new_mutbl, lhs_expr.span);
498 }
499 if let Some(rhs_new_mutbl) = rhs_new_mutbl {
500 suggest_new_borrow(rhs_new_mutbl, rhs_expr.span);
501 }
502 }
503 }
504 };
505
506 let is_compatible_after_call = |lhs_ty, rhs_ty| {
507 self.lookup_op_method(
508 (lhs_expr, lhs_ty),
509 Some((rhs_expr, rhs_ty)),
510 lang_item_for_binop(self.tcx, op),
511 op.span(),
512 expected,
513 )
514 .is_ok()
515 || self.can_eq(self.param_env, lhs_ty, rhs_ty)
519 };
520
521 if !op.span().can_be_used_for_suggestions() {
524 } else if let Op::AssignOp(_) = op
526 && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
527 {
528 suggest_deref_binop(&mut err, lhs_deref_ty);
529 } else if let Op::BinOp(_) = op
530 && let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
531 {
532 if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
533 suggest_deref_binop(&mut err, *lhs_deref_ty);
534 } else {
535 let lhs_inv_mutbl = mutbl.invert();
536 let lhs_inv_mutbl_ty =
537 Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl);
538
539 suggest_different_borrow(
540 &mut err,
541 lhs_inv_mutbl_ty,
542 Some(lhs_inv_mutbl),
543 rhs_ty,
544 None,
545 );
546
547 if let ty::Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() {
548 let rhs_inv_mutbl = mutbl.invert();
549 let rhs_inv_mutbl_ty =
550 Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl);
551
552 suggest_different_borrow(
553 &mut err,
554 lhs_ty,
555 None,
556 rhs_inv_mutbl_ty,
557 Some(rhs_inv_mutbl),
558 );
559 suggest_different_borrow(
560 &mut err,
561 lhs_inv_mutbl_ty,
562 Some(lhs_inv_mutbl),
563 rhs_inv_mutbl_ty,
564 Some(rhs_inv_mutbl),
565 );
566 }
567 }
568 } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
569 is_compatible_after_call(lhs_ty, rhs_ty)
570 }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
571 is_compatible_after_call(lhs_ty, rhs_ty)
572 }) || self.suggest_two_fn_call(
573 &mut err,
574 rhs_expr,
575 rhs_ty,
576 lhs_expr,
577 lhs_ty,
578 is_compatible_after_call,
579 ) {
580 }
582
583 if let Some(missing_trait) = missing_trait {
584 if matches!(
585 op,
586 Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
587 | Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
588 ) && self
589 .check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
590 {
591 } else if lhs_ty.has_non_region_param() {
595 if !errors.is_empty() {
596 for error in errors {
597 if let Some(trait_pred) =
598 error.obligation.predicate.as_trait_clause()
599 {
600 let output_associated_item = match error.obligation.cause.code()
601 {
602 ObligationCauseCode::BinOp {
603 output_ty: Some(output_ty),
604 ..
605 } => {
606 if let Some(output_def_id) = output_def_id
608 && let Some(trait_def_id) = trait_def_id
609 && self.tcx.parent(output_def_id) == trait_def_id
610 && let Some(output_ty) = output_ty
611 .make_suggestable(self.tcx, false, None)
612 {
613 Some(("Output", output_ty))
614 } else {
615 None
616 }
617 }
618 _ => None,
619 };
620
621 self.err_ctxt().suggest_restricting_param_bound(
622 &mut err,
623 trait_pred,
624 output_associated_item,
625 self.body_id,
626 );
627 }
628 }
629 } else {
630 err.note(format!(
633 "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`"
634 ));
635 }
636 }
637 }
638
639 if op.span().can_be_used_for_suggestions() {
642 match op {
643 Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
644 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
645 {
646 err.multipart_suggestion(
647 "consider using `wrapping_add` or `add` for pointer + {integer}",
648 vec![
649 (
650 lhs_expr.span.between(rhs_expr.span),
651 ".wrapping_add(".to_owned(),
652 ),
653 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
654 ],
655 Applicability::MaybeIncorrect,
656 );
657 }
658 Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => {
659 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
660 err.multipart_suggestion(
661 "consider using `wrapping_sub` or `sub` for \
662 pointer - {integer}",
663 vec![
664 (
665 lhs_expr.span.between(rhs_expr.span),
666 ".wrapping_sub(".to_owned(),
667 ),
668 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
669 ],
670 Applicability::MaybeIncorrect,
671 );
672 }
673
674 if lhs_ty.is_raw_ptr() && rhs_ty.is_raw_ptr() {
675 err.multipart_suggestion(
676 "consider using `offset_from` for pointer - pointer if the \
677 pointers point to the same allocation",
678 vec![
679 (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
680 (
681 lhs_expr.span.between(rhs_expr.span),
682 ".offset_from(".to_owned(),
683 ),
684 (rhs_expr.span.shrink_to_hi(), ") }".to_owned()),
685 ],
686 Applicability::MaybeIncorrect,
687 );
688 }
689 }
690 _ => {}
691 }
692 }
693
694 let lhs_name_str = match lhs_expr.kind {
695 hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => {
696 path.segments.last().map_or("_".to_string(), |s| s.ident.to_string())
697 }
698 _ => self
699 .tcx
700 .sess
701 .source_map()
702 .span_to_snippet(lhs_expr.span)
703 .unwrap_or_else(|_| "_".to_string()),
704 };
705
706 if op.span().can_be_used_for_suggestions() {
707 match op {
708 Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
709 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
710 {
711 err.multipart_suggestion(
712 "consider using `add` or `wrapping_add` to do pointer arithmetic",
713 vec![
714 (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
715 (
716 lhs_expr.span.between(rhs_expr.span),
717 ".wrapping_add(".to_owned(),
718 ),
719 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
720 ],
721 Applicability::MaybeIncorrect,
722 );
723 }
724 Op::AssignOp(Spanned { node: hir::AssignOpKind::SubAssign, .. }) => {
725 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
726 err.multipart_suggestion(
727 "consider using `sub` or `wrapping_sub` to do pointer arithmetic",
728 vec![
729 (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
730 (
731 lhs_expr.span.between(rhs_expr.span),
732 ".wrapping_sub(".to_owned(),
733
734 ),
735 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
736 ],
737 Applicability::MaybeIncorrect,
738 );
739 }
740 }
741 _ => {}
742 }
743 }
744
745 let reported = err.emit();
746 Ty::new_error(self.tcx, reported)
747 }
748 };
749
750 (lhs_ty, rhs_ty, return_ty)
751 }
752
753 fn check_str_addition(
759 &self,
760 lhs_expr: &'tcx hir::Expr<'tcx>,
761 rhs_expr: &'tcx hir::Expr<'tcx>,
762 lhs_ty: Ty<'tcx>,
763 rhs_ty: Ty<'tcx>,
764 err: &mut Diag<'_>,
765 op: Op,
766 ) -> bool {
767 let str_concat_note = "string concatenation requires an owned `String` on the left";
768 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
769 let to_owned_msg = "create an owned `String` from a string reference";
770
771 let string_type = self.tcx.lang_items().string();
772 let is_std_string =
773 |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|ty_def| Some(ty_def.did()) == string_type);
774
775 match (lhs_ty.kind(), rhs_ty.kind()) {
776 (&ty::Ref(_, l_ty, _), &ty::Ref(_, r_ty, _)) if (*l_ty.kind() == ty::Str || is_std_string(l_ty))
778 && (*r_ty.kind() == ty::Str
779 || is_std_string(r_ty)
780 || matches!(
781 r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str
782 )) =>
783 {
784 if let Op::BinOp(_) = op { err.span_label(
786 op.span(),
787 "`+` cannot be used to concatenate two `&str` strings"
788 );
789 err.note(str_concat_note);
790 if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
791 err.span_suggestion_verbose(
792 lhs_expr.span.until(lhs_inner_expr.span),
793 rm_borrow_msg,
794 "",
795 Applicability::MachineApplicable
796 );
797 } else {
798 err.span_suggestion_verbose(
799 lhs_expr.span.shrink_to_hi(),
800 to_owned_msg,
801 ".to_owned()",
802 Applicability::MachineApplicable
803 );
804 }
805 }
806 true
807 }
808 (&ty::Ref(_, l_ty, _), &ty::Adt(..)) if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
810 {
811 err.span_label(
812 op.span(),
813 "`+` cannot be used to concatenate a `&str` with a `String`",
814 );
815 match op {
816 Op::BinOp(_) => {
817 let sugg_msg;
818 let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
819 sugg_msg = "remove the borrow on the left and add one on the right";
820 (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned())
821 } else {
822 sugg_msg = "create an owned `String` on the left and add a borrow on the right";
823 (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned())
824 };
825 let suggestions = vec![
826 lhs_sugg,
827 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
828 ];
829 err.multipart_suggestion_verbose(
830 sugg_msg,
831 suggestions,
832 Applicability::MachineApplicable,
833 );
834 }
835 Op::AssignOp(_) => {
836 err.note(str_concat_note);
837 }
838 }
839 true
840 }
841 _ => false,
842 }
843 }
844
845 pub(crate) fn check_user_unop(
846 &self,
847 ex: &'tcx hir::Expr<'tcx>,
848 operand_ty: Ty<'tcx>,
849 op: hir::UnOp,
850 expected: Expectation<'tcx>,
851 ) -> Ty<'tcx> {
852 assert!(op.is_by_value());
853 match self.lookup_op_method(
854 (ex, operand_ty),
855 None,
856 lang_item_for_unop(self.tcx, op),
857 ex.span,
858 expected,
859 ) {
860 Ok(method) => {
861 self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
862 method.sig.output()
863 }
864 Err(errors) => {
865 let actual = self.resolve_vars_if_possible(operand_ty);
866 let guar = actual.error_reported().err().unwrap_or_else(|| {
867 let mut file = None;
868 let ty_str = self.tcx.short_string(actual, &mut file);
869 let mut err = struct_span_code_err!(
870 self.dcx(),
871 ex.span,
872 E0600,
873 "cannot apply unary operator `{}` to type `{ty_str}`",
874 op.as_str(),
875 );
876 *err.long_ty_path() = file;
877 err.span_label(
878 ex.span,
879 format!("cannot apply unary operator `{}`", op.as_str()),
880 );
881
882 if operand_ty.has_non_region_param() {
883 let predicates = errors
884 .iter()
885 .filter_map(|error| error.obligation.predicate.as_trait_clause());
886 for pred in predicates {
887 self.err_ctxt().suggest_restricting_param_bound(
888 &mut err,
889 pred,
890 None,
891 self.body_id,
892 );
893 }
894 }
895
896 let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
897 if let Some(sp) =
898 self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp)
899 {
900 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
904 } else {
905 match actual.kind() {
906 ty::Uint(_) if op == hir::UnOp::Neg => {
907 err.note("unsigned values cannot be negated");
908
909 if let hir::ExprKind::Unary(
910 _,
911 hir::Expr {
912 kind:
913 hir::ExprKind::Lit(Spanned {
914 node: ast::LitKind::Int(Pu128(1), _),
915 ..
916 }),
917 ..
918 },
919 ) = ex.kind
920 {
921 let span = if let hir::Node::Expr(parent) =
922 self.tcx.parent_hir_node(ex.hir_id)
923 && let hir::ExprKind::Cast(..) = parent.kind
924 {
925 parent.span
927 } else {
928 ex.span
929 };
930 err.span_suggestion_verbose(
931 span,
932 format!(
933 "you may have meant the maximum value of `{actual}`",
934 ),
935 format!("{actual}::MAX"),
936 Applicability::MaybeIncorrect,
937 );
938 }
939 }
940 ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {}
941 ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {}
942 _ => {
943 self.note_unmet_impls_on_type(&mut err, &errors, true);
944 }
945 }
946 }
947 err.emit()
948 });
949 Ty::new_error(self.tcx, guar)
950 }
951 }
952 }
953
954 fn lookup_op_method(
955 &self,
956 (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
957 opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
958 (opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
959 span: Span,
960 expected: Expectation<'tcx>,
961 ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
962 let Some(trait_did) = trait_did else {
963 return Err(vec![]);
965 };
966
967 debug!(
968 "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
969 lhs_ty, opname, trait_did
970 );
971
972 let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
973 let cause = self.cause(
974 span,
975 match opt_rhs_expr {
976 Some(rhs) => ObligationCauseCode::BinOp {
977 lhs_hir_id: lhs_expr.hir_id,
978 rhs_hir_id: rhs.hir_id,
979 rhs_span: rhs.span,
980 rhs_is_lit: matches!(rhs.kind, hir::ExprKind::Lit(_)),
981 output_ty: expected.only_has_type(self),
982 },
983 None => ObligationCauseCode::UnOp { hir_id: lhs_expr.hir_id },
984 },
985 );
986
987 let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
991 let method = self.lookup_method_for_operator(
992 cause.clone(),
993 opname,
994 trait_did,
995 lhs_ty,
996 opt_rhs_ty,
997 treat_opaques,
998 );
999 match method {
1000 Some(ok) => {
1001 let method = self.register_infer_ok_obligations(ok);
1002 self.select_obligations_where_possible(|_| {});
1003 Ok(method)
1004 }
1005 None => {
1006 self.dcx().span_delayed_bug(span, "this path really should be doomed...");
1010 if let Some((rhs_expr, rhs_ty)) = opt_rhs
1014 && rhs_ty.is_ty_var()
1015 {
1016 self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None);
1017 }
1018
1019 let args =
1021 ty::GenericArgs::for_item(self.tcx, trait_did, |param, _| match param.kind {
1022 ty::GenericParamDefKind::Lifetime
1023 | ty::GenericParamDefKind::Const { .. } => {
1024 unreachable!("did not expect operand trait to have lifetime/const args")
1025 }
1026 ty::GenericParamDefKind::Type { .. } => {
1027 if param.index == 0 {
1028 lhs_ty.into()
1029 } else {
1030 opt_rhs_ty.expect("expected RHS for binop").into()
1031 }
1032 }
1033 });
1034 let obligation = Obligation::new(
1035 self.tcx,
1036 cause,
1037 self.param_env,
1038 ty::TraitRef::new_from_args(self.tcx, trait_did, args),
1039 );
1040 let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
1041 ocx.register_obligation(obligation);
1042 Err(ocx.evaluate_obligations_error_on_ambiguity())
1043 }
1044 }
1045 }
1046}
1047
1048fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<hir::def_id::DefId>) {
1049 let lang = tcx.lang_items();
1050 match op {
1051 Op::AssignOp(op) => match op.node {
1052 hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
1053 hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
1054 hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
1055 hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
1056 hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
1057 hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
1058 hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
1059 hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
1060 hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
1061 hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
1062 },
1063 Op::BinOp(op) => match op.node {
1064 hir::BinOpKind::Add => (sym::add, lang.add_trait()),
1065 hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
1066 hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
1067 hir::BinOpKind::Div => (sym::div, lang.div_trait()),
1068 hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
1069 hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
1070 hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
1071 hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
1072 hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
1073 hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
1074 hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
1075 hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
1076 hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
1077 hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
1078 hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
1079 hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
1080 hir::BinOpKind::And | hir::BinOpKind::Or => {
1081 bug!("&& and || are not overloadable")
1082 }
1083 },
1084 }
1085}
1086
1087fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
1088 let lang = tcx.lang_items();
1089 match op {
1090 hir::UnOp::Not => (sym::not, lang.not_trait()),
1091 hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
1092 hir::UnOp::Deref => bug!("Deref is not overloadable"),
1093 }
1094}
1095
1096pub(crate) fn contains_let_in_chain(expr: &hir::Expr<'_>) -> bool {
1098 match &expr.kind {
1099 hir::ExprKind::Let(..) => true,
1100 hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, left, right) => {
1101 contains_let_in_chain(left) || contains_let_in_chain(right)
1102 }
1103 _ => false,
1104 }
1105}
1106
1107#[derive(Clone, Copy)]
1110enum BinOpCategory {
1111 Shortcircuit,
1113
1114 Shift,
1117
1118 Math,
1121
1122 Bitwise,
1125
1126 Comparison,
1129}
1130
1131impl From<hir::BinOpKind> for BinOpCategory {
1132 fn from(op: hir::BinOpKind) -> BinOpCategory {
1133 use hir::BinOpKind::*;
1134 match op {
1135 Shl | Shr => BinOpCategory::Shift,
1136 Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
1137 BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
1138 Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
1139 And | Or => BinOpCategory::Shortcircuit,
1140 }
1141 }
1142}
1143
1144impl From<hir::AssignOpKind> for BinOpCategory {
1145 fn from(op: hir::AssignOpKind) -> BinOpCategory {
1146 use hir::AssignOpKind::*;
1147 match op {
1148 ShlAssign | ShrAssign => BinOpCategory::Shift,
1149 AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
1150 BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
1151 }
1152 }
1153}
1154
1155#[derive(Clone, Copy, Debug, PartialEq)]
1157enum Op {
1158 BinOp(hir::BinOp),
1159 AssignOp(hir::AssignOp),
1160}
1161
1162impl Op {
1163 fn span(&self) -> Span {
1164 match self {
1165 Op::BinOp(op) => op.span,
1166 Op::AssignOp(op) => op.span,
1167 }
1168 }
1169
1170 fn as_str(&self) -> &'static str {
1171 match self {
1172 Op::BinOp(op) => op.node.as_str(),
1173 Op::AssignOp(op) => op.node.as_str(),
1174 }
1175 }
1176
1177 fn is_by_value(&self) -> bool {
1178 match self {
1179 Op::BinOp(op) => op.node.is_by_value(),
1180 Op::AssignOp(op) => op.node.is_by_value(),
1181 }
1182 }
1183}
1184
1185fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1187 match ty.kind() {
1188 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
1189 _ => ty,
1190 }
1191}
1192
1193fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
1210 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
1213
1214 match category {
1215 BinOpCategory::Shortcircuit => true,
1216 BinOpCategory::Shift => {
1217 lhs.references_error()
1218 || rhs.references_error()
1219 || lhs.is_integral() && rhs.is_integral()
1220 }
1221 BinOpCategory::Math => {
1222 lhs.references_error()
1223 || rhs.references_error()
1224 || lhs.is_integral() && rhs.is_integral()
1225 || lhs.is_floating_point() && rhs.is_floating_point()
1226 }
1227 BinOpCategory::Bitwise => {
1228 lhs.references_error()
1229 || rhs.references_error()
1230 || lhs.is_integral() && rhs.is_integral()
1231 || lhs.is_floating_point() && rhs.is_floating_point()
1232 || lhs.is_bool() && rhs.is_bool()
1233 }
1234 BinOpCategory::Comparison => {
1235 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
1236 }
1237 }
1238}