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::Expectation;
24
25impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26 pub(crate) fn check_expr_assign_op(
28 &self,
29 expr: &'tcx hir::Expr<'tcx>,
30 op: hir::AssignOp,
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::AssignOp(op), expected);
37
38 let category = BinOpCategory::from(op.node);
39 let ty = if !lhs_ty.is_ty_var()
40 && !rhs_ty.is_ty_var()
41 && is_builtin_binop(lhs_ty, rhs_ty, category)
42 {
43 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category);
44 self.tcx.types.unit
45 } else {
46 return_ty
47 };
48
49 self.check_lhs_assignable(lhs, E0067, op.span, |err| {
50 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
51 if self
52 .lookup_op_method(
53 (lhs, lhs_deref_ty),
54 Some((rhs, rhs_ty)),
55 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
56 op.span,
57 expected,
58 )
59 .is_ok()
60 {
61 if self
64 .lookup_op_method(
65 (lhs, lhs_ty),
66 Some((rhs, rhs_ty)),
67 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
68 op.span,
69 expected,
70 )
71 .is_err()
72 {
73 err.downgrade_to_delayed_bug();
74 } else {
75 err.span_suggestion_verbose(
77 lhs.span.shrink_to_lo(),
78 "consider dereferencing the left-hand side of this operation",
79 "*",
80 Applicability::MaybeIncorrect,
81 );
82 }
83 }
84 }
85 });
86
87 ty
88 }
89
90 pub(crate) fn check_expr_binop(
92 &self,
93 expr: &'tcx hir::Expr<'tcx>,
94 op: hir::BinOp,
95 lhs_expr: &'tcx hir::Expr<'tcx>,
96 rhs_expr: &'tcx hir::Expr<'tcx>,
97 expected: Expectation<'tcx>,
98 ) -> Ty<'tcx> {
99 let tcx = self.tcx;
100
101 debug!(
102 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
103 expr.hir_id, expr, op, lhs_expr, rhs_expr
104 );
105
106 match BinOpCategory::from(op.node) {
107 BinOpCategory::Shortcircuit => {
108 self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
110 let lhs_diverges = self.diverges.get();
111 self.check_expr_coercible_to_type(rhs_expr, tcx.types.bool, None);
112
113 self.diverges.set(lhs_diverges);
115
116 tcx.types.bool
117 }
118 _ => {
119 let (lhs_ty, rhs_ty, return_ty) =
123 self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
124
125 let category = BinOpCategory::from(op.node);
138 if !lhs_ty.is_ty_var()
139 && !rhs_ty.is_ty_var()
140 && is_builtin_binop(lhs_ty, rhs_ty, category)
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 category,
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 category: BinOpCategory,
165 ) -> Ty<'tcx> {
166 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
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 category {
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: Op,
205 expected: Expectation<'tcx>,
206 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
207 debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
208
209 let lhs_ty = match op {
210 Op::BinOp(_) => {
211 let lhs_ty = self.check_expr(lhs_expr);
217 let fresh_var = self.next_ty_var(lhs_expr.span);
218 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
219 }
220 Op::AssignOp(_) => {
221 self.check_expr(lhs_expr)
226 }
227 };
228 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
229
230 let rhs_ty_var = self.next_ty_var(rhs_expr.span);
237 let result = self.lookup_op_method(
238 (lhs_expr, lhs_ty),
239 Some((rhs_expr, rhs_ty_var)),
240 lang_item_for_binop(self.tcx, op),
241 op.span(),
242 expected,
243 );
244
245 let rhs_ty = self.check_expr_coercible_to_type_or_error(
247 rhs_expr,
248 rhs_ty_var,
249 Some(lhs_expr),
250 |err, ty| {
251 if let Op::BinOp(binop) = op
252 && binop.node == hir::BinOpKind::Eq
253 {
254 self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr);
255 }
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.is_by_value();
263 if matches!(op, Op::AssignOp(_)) || 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) = lang_item_for_binop(self.tcx, op);
305 let missing_trait = trait_def_id
306 .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
307 let mut path = None;
308 let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
309 let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
310 let (mut err, output_def_id) = match op {
311 Op::AssignOp(assign_op) => {
312 let s = assign_op.node.as_str();
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 s,
319 lhs_ty_str,
320 );
321 err.span_label(
322 lhs_expr.span,
323 format!("cannot use `{}` on type `{}`", s, lhs_ty_str),
324 );
325 self.note_unmet_impls_on_type(&mut err, errors, false);
326 (err, None)
327 }
328 Op::BinOp(bin_op) => {
329 let message = match bin_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 bin_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(), bin_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 lang_item_for_binop(self.tcx, op),
413 op.span(),
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.as_str(),
421 self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
422 );
423 err.span_suggestion_verbose(
424 lhs_expr.span.shrink_to_lo(),
425 msg,
426 "*",
427 rustc_errors::Applicability::MachineApplicable,
428 );
429 }
430 };
431
432 let suggest_different_borrow =
433 |err: &mut Diag<'_, _>,
434 lhs_adjusted_ty,
435 lhs_new_mutbl: Option<ast::Mutability>,
436 rhs_adjusted_ty,
437 rhs_new_mutbl: Option<ast::Mutability>| {
438 if self
439 .lookup_op_method(
440 (lhs_expr, lhs_adjusted_ty),
441 Some((rhs_expr, rhs_adjusted_ty)),
442 lang_item_for_binop(self.tcx, op),
443 op.span(),
444 expected,
445 )
446 .is_ok()
447 {
448 let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
449 let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
450 let op = op.as_str();
451 err.note(format!("an implementation for `{lhs} {op} {rhs}` exists"));
452
453 if let Some(lhs_new_mutbl) = lhs_new_mutbl
454 && let Some(rhs_new_mutbl) = rhs_new_mutbl
455 && lhs_new_mutbl.is_not()
456 && rhs_new_mutbl.is_not()
457 {
458 err.multipart_suggestion_verbose(
459 "consider reborrowing both sides",
460 vec![
461 (lhs_expr.span.shrink_to_lo(), "&*".to_string()),
462 (rhs_expr.span.shrink_to_lo(), "&*".to_string()),
463 ],
464 rustc_errors::Applicability::MachineApplicable,
465 );
466 } else {
467 let mut suggest_new_borrow =
468 |new_mutbl: ast::Mutability, sp: Span| {
469 if new_mutbl.is_not() {
471 err.span_suggestion_verbose(
472 sp.shrink_to_lo(),
473 "consider reborrowing this side",
474 "&*",
475 rustc_errors::Applicability::MachineApplicable,
476 );
477 } else {
479 err.span_help(
480 sp,
481 "consider making this expression a mutable borrow",
482 );
483 }
484 };
485
486 if let Some(lhs_new_mutbl) = lhs_new_mutbl {
487 suggest_new_borrow(lhs_new_mutbl, lhs_expr.span);
488 }
489 if let Some(rhs_new_mutbl) = rhs_new_mutbl {
490 suggest_new_borrow(rhs_new_mutbl, rhs_expr.span);
491 }
492 }
493 }
494 };
495
496 let is_compatible_after_call = |lhs_ty, rhs_ty| {
497 self.lookup_op_method(
498 (lhs_expr, lhs_ty),
499 Some((rhs_expr, rhs_ty)),
500 lang_item_for_binop(self.tcx, op),
501 op.span(),
502 expected,
503 )
504 .is_ok()
505 || self.can_eq(self.param_env, lhs_ty, rhs_ty)
509 };
510
511 if !op.span().can_be_used_for_suggestions() {
514 } else if let Op::AssignOp(_) = op
516 && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
517 {
518 suggest_deref_binop(&mut err, lhs_deref_ty);
519 } else if let Op::BinOp(_) = op
520 && let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
521 {
522 if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
523 suggest_deref_binop(&mut err, *lhs_deref_ty);
524 } else {
525 let lhs_inv_mutbl = mutbl.invert();
526 let lhs_inv_mutbl_ty =
527 Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl);
528
529 suggest_different_borrow(
530 &mut err,
531 lhs_inv_mutbl_ty,
532 Some(lhs_inv_mutbl),
533 rhs_ty,
534 None,
535 );
536
537 if let ty::Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() {
538 let rhs_inv_mutbl = mutbl.invert();
539 let rhs_inv_mutbl_ty =
540 Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl);
541
542 suggest_different_borrow(
543 &mut err,
544 lhs_ty,
545 None,
546 rhs_inv_mutbl_ty,
547 Some(rhs_inv_mutbl),
548 );
549 suggest_different_borrow(
550 &mut err,
551 lhs_inv_mutbl_ty,
552 Some(lhs_inv_mutbl),
553 rhs_inv_mutbl_ty,
554 Some(rhs_inv_mutbl),
555 );
556 }
557 }
558 } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
559 is_compatible_after_call(lhs_ty, rhs_ty)
560 }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
561 is_compatible_after_call(lhs_ty, rhs_ty)
562 }) || self.suggest_two_fn_call(
563 &mut err,
564 rhs_expr,
565 rhs_ty,
566 lhs_expr,
567 lhs_ty,
568 |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty),
569 ) {
570 }
572
573 if let Some(missing_trait) = missing_trait {
574 if matches!(
575 op,
576 Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
577 | Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
578 ) && self
579 .check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
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 lang_item_for_binop(self.tcx, op),
597 op.span(),
598 expected,
599 )
600 .unwrap_err();
601 if !errors.is_empty() {
602 for error in errors {
603 if let Some(trait_pred) =
604 error.obligation.predicate.as_trait_clause()
605 {
606 let output_associated_item = match error.obligation.cause.code()
607 {
608 ObligationCauseCode::BinOp {
609 output_ty: Some(output_ty),
610 ..
611 } => {
612 if let Some(output_def_id) = output_def_id
614 && let Some(trait_def_id) = trait_def_id
615 && self.tcx.parent(output_def_id) == trait_def_id
616 && let Some(output_ty) = output_ty
617 .make_suggestable(self.tcx, false, None)
618 {
619 Some(("Output", output_ty))
620 } else {
621 None
622 }
623 }
624 _ => None,
625 };
626
627 self.err_ctxt().suggest_restricting_param_bound(
628 &mut err,
629 trait_pred,
630 output_associated_item,
631 self.body_id,
632 );
633 }
634 }
635 } else {
636 err.note(format!(
639 "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`"
640 ));
641 }
642 }
643 }
644
645 if op.span().can_be_used_for_suggestions() {
648 match op {
649 Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
650 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
651 {
652 err.multipart_suggestion(
653 "consider using `wrapping_add` or `add` for pointer + {integer}",
654 vec![
655 (
656 lhs_expr.span.between(rhs_expr.span),
657 ".wrapping_add(".to_owned(),
658 ),
659 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
660 ],
661 Applicability::MaybeIncorrect,
662 );
663 }
664 Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => {
665 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
666 err.multipart_suggestion(
667 "consider using `wrapping_sub` or `sub` for \
668 pointer - {integer}",
669 vec![
670 (
671 lhs_expr.span.between(rhs_expr.span),
672 ".wrapping_sub(".to_owned(),
673 ),
674 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
675 ],
676 Applicability::MaybeIncorrect,
677 );
678 }
679
680 if lhs_ty.is_raw_ptr() && rhs_ty.is_raw_ptr() {
681 err.multipart_suggestion(
682 "consider using `offset_from` for pointer - pointer if the \
683 pointers point to the same allocation",
684 vec![
685 (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
686 (
687 lhs_expr.span.between(rhs_expr.span),
688 ".offset_from(".to_owned(),
689 ),
690 (rhs_expr.span.shrink_to_hi(), ") }".to_owned()),
691 ],
692 Applicability::MaybeIncorrect,
693 );
694 }
695 }
696 _ => {}
697 }
698 }
699
700 let lhs_name_str = match lhs_expr.kind {
701 hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => {
702 path.segments.last().map_or("_".to_string(), |s| s.ident.to_string())
703 }
704 _ => self
705 .tcx
706 .sess
707 .source_map()
708 .span_to_snippet(lhs_expr.span)
709 .unwrap_or("_".to_string()),
710 };
711
712 if op.span().can_be_used_for_suggestions() {
713 match op {
714 Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
715 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
716 {
717 err.multipart_suggestion(
718 "consider using `add` or `wrapping_add` to do pointer arithmetic",
719 vec![
720 (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
721 (
722 lhs_expr.span.between(rhs_expr.span),
723 ".wrapping_add(".to_owned(),
724 ),
725 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
726 ],
727 Applicability::MaybeIncorrect,
728 );
729 }
730 Op::AssignOp(Spanned { node: hir::AssignOpKind::SubAssign, .. }) => {
731 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
732 err.multipart_suggestion(
733 "consider using `sub` or `wrapping_sub` to do pointer arithmetic",
734 vec![
735 (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
736 (
737 lhs_expr.span.between(rhs_expr.span),
738 ".wrapping_sub(".to_owned(),
739
740 ),
741 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
742 ],
743 Applicability::MaybeIncorrect,
744 );
745 }
746 }
747 _ => {}
748 }
749 }
750
751 let reported = err.emit();
752 Ty::new_error(self.tcx, reported)
753 }
754 };
755
756 (lhs_ty, rhs_ty, return_ty)
757 }
758
759 fn check_str_addition(
765 &self,
766 lhs_expr: &'tcx hir::Expr<'tcx>,
767 rhs_expr: &'tcx hir::Expr<'tcx>,
768 lhs_ty: Ty<'tcx>,
769 rhs_ty: Ty<'tcx>,
770 err: &mut Diag<'_>,
771 op: Op,
772 ) -> bool {
773 let str_concat_note = "string concatenation requires an owned `String` on the left";
774 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
775 let to_owned_msg = "create an owned `String` from a string reference";
776
777 let string_type = self.tcx.lang_items().string();
778 let is_std_string =
779 |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|ty_def| Some(ty_def.did()) == string_type);
780
781 match (lhs_ty.kind(), rhs_ty.kind()) {
782 (&ty::Ref(_, l_ty, _), &ty::Ref(_, r_ty, _)) if (*l_ty.kind() == ty::Str || is_std_string(l_ty))
784 && (*r_ty.kind() == ty::Str
785 || is_std_string(r_ty)
786 || matches!(
787 r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str
788 )) =>
789 {
790 if let Op::BinOp(_) = op { err.span_label(
792 op.span(),
793 "`+` cannot be used to concatenate two `&str` strings"
794 );
795 err.note(str_concat_note);
796 if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
797 err.span_suggestion_verbose(
798 lhs_expr.span.until(lhs_inner_expr.span),
799 rm_borrow_msg,
800 "",
801 Applicability::MachineApplicable
802 );
803 } else {
804 err.span_suggestion_verbose(
805 lhs_expr.span.shrink_to_hi(),
806 to_owned_msg,
807 ".to_owned()",
808 Applicability::MachineApplicable
809 );
810 }
811 }
812 true
813 }
814 (&ty::Ref(_, l_ty, _), &ty::Adt(..)) if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
816 {
817 err.span_label(
818 op.span(),
819 "`+` cannot be used to concatenate a `&str` with a `String`",
820 );
821 match op {
822 Op::BinOp(_) => {
823 let sugg_msg;
824 let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
825 sugg_msg = "remove the borrow on the left and add one on the right";
826 (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned())
827 } else {
828 sugg_msg = "create an owned `String` on the left and add a borrow on the right";
829 (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned())
830 };
831 let suggestions = vec![
832 lhs_sugg,
833 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
834 ];
835 err.multipart_suggestion_verbose(
836 sugg_msg,
837 suggestions,
838 Applicability::MachineApplicable,
839 );
840 }
841 Op::AssignOp(_) => {
842 err.note(str_concat_note);
843 }
844 }
845 true
846 }
847 _ => false,
848 }
849 }
850
851 pub(crate) fn check_user_unop(
852 &self,
853 ex: &'tcx hir::Expr<'tcx>,
854 operand_ty: Ty<'tcx>,
855 op: hir::UnOp,
856 expected: Expectation<'tcx>,
857 ) -> Ty<'tcx> {
858 assert!(op.is_by_value());
859 match self.lookup_op_method(
860 (ex, operand_ty),
861 None,
862 lang_item_for_unop(self.tcx, op),
863 ex.span,
864 expected,
865 ) {
866 Ok(method) => {
867 self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
868 method.sig.output()
869 }
870 Err(errors) => {
871 let actual = self.resolve_vars_if_possible(operand_ty);
872 let guar = actual.error_reported().err().unwrap_or_else(|| {
873 let mut file = None;
874 let ty_str = self.tcx.short_string(actual, &mut file);
875 let mut err = struct_span_code_err!(
876 self.dcx(),
877 ex.span,
878 E0600,
879 "cannot apply unary operator `{}` to type `{ty_str}`",
880 op.as_str(),
881 );
882 *err.long_ty_path() = file;
883 err.span_label(
884 ex.span,
885 format!("cannot apply unary operator `{}`", op.as_str()),
886 );
887
888 if operand_ty.has_non_region_param() {
889 let predicates = errors
890 .iter()
891 .filter_map(|error| error.obligation.predicate.as_trait_clause());
892 for pred in predicates {
893 self.err_ctxt().suggest_restricting_param_bound(
894 &mut err,
895 pred,
896 None,
897 self.body_id,
898 );
899 }
900 }
901
902 let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
903 if let Some(sp) =
904 self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp)
905 {
906 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
910 } else {
911 match actual.kind() {
912 ty::Uint(_) if op == hir::UnOp::Neg => {
913 err.note("unsigned values cannot be negated");
914
915 if let hir::ExprKind::Unary(
916 _,
917 hir::Expr {
918 kind:
919 hir::ExprKind::Lit(Spanned {
920 node: ast::LitKind::Int(Pu128(1), _),
921 ..
922 }),
923 ..
924 },
925 ) = ex.kind
926 {
927 let span = if let hir::Node::Expr(parent) =
928 self.tcx.parent_hir_node(ex.hir_id)
929 && let hir::ExprKind::Cast(..) = parent.kind
930 {
931 parent.span
933 } else {
934 ex.span
935 };
936 err.span_suggestion_verbose(
937 span,
938 format!(
939 "you may have meant the maximum value of `{actual}`",
940 ),
941 format!("{actual}::MAX"),
942 Applicability::MaybeIncorrect,
943 );
944 }
945 }
946 ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {}
947 ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {}
948 _ => {
949 self.note_unmet_impls_on_type(&mut err, errors, true);
950 }
951 }
952 }
953 err.emit()
954 });
955 Ty::new_error(self.tcx, guar)
956 }
957 }
958 }
959
960 fn lookup_op_method(
961 &self,
962 (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
963 opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
964 (opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
965 span: Span,
966 expected: Expectation<'tcx>,
967 ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
968 let Some(trait_did) = trait_did else {
969 return Err(vec![]);
971 };
972
973 debug!(
974 "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
975 lhs_ty, opname, trait_did
976 );
977
978 let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
979 let cause = self.cause(
980 span,
981 ObligationCauseCode::BinOp {
982 lhs_hir_id: lhs_expr.hir_id,
983 rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id),
984 rhs_span: opt_rhs_expr.map(|expr| expr.span),
985 rhs_is_lit: opt_rhs_expr
986 .is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
987 output_ty: expected.only_has_type(self),
988 },
989 );
990
991 let method =
992 self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty);
993 match method {
994 Some(ok) => {
995 let method = self.register_infer_ok_obligations(ok);
996 self.select_obligations_where_possible(|_| {});
997 Ok(method)
998 }
999 None => {
1000 self.dcx().span_delayed_bug(span, "this path really should be doomed...");
1004 if let Some((rhs_expr, rhs_ty)) = opt_rhs
1008 && rhs_ty.is_ty_var()
1009 {
1010 self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None);
1011 }
1012
1013 let args =
1015 ty::GenericArgs::for_item(self.tcx, trait_did, |param, _| match param.kind {
1016 ty::GenericParamDefKind::Lifetime
1017 | ty::GenericParamDefKind::Const { .. } => {
1018 unreachable!("did not expect operand trait to have lifetime/const args")
1019 }
1020 ty::GenericParamDefKind::Type { .. } => {
1021 if param.index == 0 {
1022 lhs_ty.into()
1023 } else {
1024 opt_rhs_ty.expect("expected RHS for binop").into()
1025 }
1026 }
1027 });
1028 let obligation = Obligation::new(
1029 self.tcx,
1030 cause,
1031 self.param_env,
1032 ty::TraitRef::new_from_args(self.tcx, trait_did, args),
1033 );
1034 let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
1035 ocx.register_obligation(obligation);
1036 Err(ocx.select_all_or_error())
1037 }
1038 }
1039 }
1040}
1041
1042fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<hir::def_id::DefId>) {
1043 let lang = tcx.lang_items();
1044 match op {
1045 Op::AssignOp(op) => match op.node {
1046 hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
1047 hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
1048 hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
1049 hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
1050 hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
1051 hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
1052 hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
1053 hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
1054 hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
1055 hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
1056 },
1057 Op::BinOp(op) => match op.node {
1058 hir::BinOpKind::Add => (sym::add, lang.add_trait()),
1059 hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
1060 hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
1061 hir::BinOpKind::Div => (sym::div, lang.div_trait()),
1062 hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
1063 hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
1064 hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
1065 hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
1066 hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
1067 hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
1068 hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
1069 hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
1070 hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
1071 hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
1072 hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
1073 hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
1074 hir::BinOpKind::And | hir::BinOpKind::Or => {
1075 bug!("&& and || are not overloadable")
1076 }
1077 },
1078 }
1079}
1080
1081fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
1082 let lang = tcx.lang_items();
1083 match op {
1084 hir::UnOp::Not => (sym::not, lang.not_trait()),
1085 hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
1086 hir::UnOp::Deref => bug!("Deref is not overloadable"),
1087 }
1088}
1089
1090#[derive(Clone, Copy)]
1093enum BinOpCategory {
1094 Shortcircuit,
1096
1097 Shift,
1100
1101 Math,
1104
1105 Bitwise,
1108
1109 Comparison,
1112}
1113
1114impl From<hir::BinOpKind> for BinOpCategory {
1115 fn from(op: hir::BinOpKind) -> BinOpCategory {
1116 use hir::BinOpKind::*;
1117 match op {
1118 Shl | Shr => BinOpCategory::Shift,
1119 Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
1120 BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
1121 Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
1122 And | Or => BinOpCategory::Shortcircuit,
1123 }
1124 }
1125}
1126
1127impl From<hir::AssignOpKind> for BinOpCategory {
1128 fn from(op: hir::AssignOpKind) -> BinOpCategory {
1129 use hir::AssignOpKind::*;
1130 match op {
1131 ShlAssign | ShrAssign => BinOpCategory::Shift,
1132 AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
1133 BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
1134 }
1135 }
1136}
1137
1138#[derive(Clone, Copy, Debug, PartialEq)]
1140enum Op {
1141 BinOp(hir::BinOp),
1142 AssignOp(hir::AssignOp),
1143}
1144
1145impl Op {
1146 fn span(&self) -> Span {
1147 match self {
1148 Op::BinOp(op) => op.span,
1149 Op::AssignOp(op) => op.span,
1150 }
1151 }
1152
1153 fn as_str(&self) -> &'static str {
1154 match self {
1155 Op::BinOp(op) => op.node.as_str(),
1156 Op::AssignOp(op) => op.node.as_str(),
1157 }
1158 }
1159
1160 fn is_by_value(&self) -> bool {
1161 match self {
1162 Op::BinOp(op) => op.node.is_by_value(),
1163 Op::AssignOp(op) => op.node.is_by_value(),
1164 }
1165 }
1166}
1167
1168fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1170 match ty.kind() {
1171 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
1172 _ => ty,
1173 }
1174}
1175
1176fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
1193 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
1196
1197 match category.into() {
1198 BinOpCategory::Shortcircuit => true,
1199 BinOpCategory::Shift => {
1200 lhs.references_error()
1201 || rhs.references_error()
1202 || lhs.is_integral() && rhs.is_integral()
1203 }
1204 BinOpCategory::Math => {
1205 lhs.references_error()
1206 || rhs.references_error()
1207 || lhs.is_integral() && rhs.is_integral()
1208 || lhs.is_floating_point() && rhs.is_floating_point()
1209 }
1210 BinOpCategory::Bitwise => {
1211 lhs.references_error()
1212 || rhs.references_error()
1213 || lhs.is_integral() && rhs.is_integral()
1214 || lhs.is_floating_point() && rhs.is_floating_point()
1215 || lhs.is_bool() && rhs.is_bool()
1216 }
1217 BinOpCategory::Comparison => {
1218 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
1219 }
1220 }
1221}