1use rustc_ast::{self as ast, AssignOp, BinOp};
4use rustc_data_structures::packed::Pu128;
5use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
6use rustc_errors::codes::*;
7use rustc_errors::{Applicability, Diag, struct_span_code_err};
8use rustc_hir::def_id::DefId;
9use rustc_hir::{self as hir, AssignOpKind, BinOpKind, Expr, ExprKind};
10use rustc_infer::traits::ObligationCauseCode;
11use rustc_middle::bug;
12use rustc_middle::ty::adjustment::{
13 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
14};
15use rustc_middle::ty::print::with_no_trimmed_paths;
16use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
17use rustc_span::{Span, Spanned, Symbol, sym};
18use rustc_trait_selection::infer::InferCtxtExt;
19use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
20use tracing::debug;
21
22use super::FnCtxt;
23use super::method::MethodCallee;
24use crate::diagnostics::ExprParenthesesNeeded;
25use crate::method::TreatNotYetDefinedOpaques;
26use crate::{Expectation, diagnostics};
27
28impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
29 pub(crate) fn check_expr_assign_op(
31 &self,
32 expr: &'tcx Expr<'tcx>,
33 op: hir::AssignOp,
34 lhs: &'tcx Expr<'tcx>,
35 rhs: &'tcx Expr<'tcx>,
36 expected: Expectation<'tcx>,
37 ) -> Ty<'tcx> {
38 let (lhs_ty, rhs_ty, _return_ty) =
39 self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected);
40
41 let category = BinOpCategory::from(op.node);
42 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && 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 }
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 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
54 op.span,
55 expected,
56 )
57 .is_ok()
58 {
59 if self
62 .lookup_op_method(
63 (lhs, lhs_ty),
64 Some((rhs, rhs_ty)),
65 lang_item_for_binop(self.tcx, Op::AssignOp(op)),
66 op.span,
67 expected,
68 )
69 .is_err()
70 {
71 err.downgrade_to_delayed_bug();
72 } else {
73 err.span_suggestion_verbose(
75 lhs.span.shrink_to_lo(),
76 "consider dereferencing the left-hand side of this operation",
77 "*",
78 Applicability::MaybeIncorrect,
79 );
80 }
81 }
82 }
83 });
84
85 self.tcx.types.unit
86 }
87
88 pub(crate) fn check_expr_binop(
90 &self,
91 expr: &'tcx Expr<'tcx>,
92 op: hir::BinOp,
93 lhs_expr: &'tcx Expr<'tcx>,
94 rhs_expr: &'tcx Expr<'tcx>,
95 expected: Expectation<'tcx>,
96 ) -> Ty<'tcx> {
97 let tcx = self.tcx;
98
99 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/op.rs:99",
"rustc_hir_typeck::op", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/op.rs"),
::tracing_core::__macro_support::Option::Some(99u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::op"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("check_binop(expr.hir_id={0}, expr={1:?}, op={2:?}, lhs_expr={3:?}, rhs_expr={4:?})",
expr.hir_id, expr, op, lhs_expr, rhs_expr) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
100 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
101 expr.hir_id, expr, op, lhs_expr, rhs_expr
102 );
103
104 match BinOpCategory::from(op.node) {
105 BinOpCategory::Shortcircuit => {
106 self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
108 let lhs_diverges = self.diverges.get();
109 self.check_expr_coercible_to_type(rhs_expr, tcx.types.bool, None);
110
111 self.diverges.set(lhs_diverges);
113
114 tcx.types.bool
115 }
116 _ => {
117 let (lhs_ty, rhs_ty, return_ty) =
121 self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
122
123 let category = BinOpCategory::from(op.node);
136 if !lhs_ty.is_ty_var()
137 && !rhs_ty.is_ty_var()
138 && is_builtin_binop(lhs_ty, rhs_ty, category)
139 {
140 let builtin_return_ty = self.enforce_builtin_binop_types(
141 lhs_expr.span,
142 lhs_ty,
143 rhs_expr.span,
144 rhs_ty,
145 category,
146 );
147 self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
148 builtin_return_ty
149 } else {
150 return_ty
151 }
152 }
153 }
154 }
155
156 fn enforce_builtin_binop_types(
157 &self,
158 lhs_span: Span,
159 lhs_ty: Ty<'tcx>,
160 rhs_span: Span,
161 rhs_ty: Ty<'tcx>,
162 category: BinOpCategory,
163 ) -> Ty<'tcx> {
164 if true {
if !is_builtin_binop(lhs_ty, rhs_ty, category) {
::core::panicking::panic("assertion failed: is_builtin_binop(lhs_ty, rhs_ty, category)")
};
};debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
165
166 let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
169
170 let tcx = self.tcx;
171 match category {
172 BinOpCategory::Shortcircuit => {
173 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
174 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
175 tcx.types.bool
176 }
177
178 BinOpCategory::Shift => lhs_ty,
180
181 BinOpCategory::Math | BinOpCategory::Bitwise => {
182 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
184 lhs_ty
185 }
186
187 BinOpCategory::Comparison => {
188 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
190 tcx.types.bool
191 }
192 }
193 }
194
195 fn check_overloaded_binop(
196 &self,
197 expr: &'tcx Expr<'tcx>,
198 lhs_expr: &'tcx Expr<'tcx>,
199 rhs_expr: &'tcx Expr<'tcx>,
200 op: Op,
201 expected: Expectation<'tcx>,
202 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
203 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/op.rs:203",
"rustc_hir_typeck::op", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/op.rs"),
::tracing_core::__macro_support::Option::Some(203u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::op"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("check_overloaded_binop(expr.hir_id={0}, op={1:?})",
expr.hir_id, op) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
204
205 let lhs_ty = match op {
206 Op::BinOp(_) => {
207 let lhs_ty = self.check_expr(lhs_expr);
213 let fresh_var = self.next_ty_var(lhs_expr.span);
214 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
215 }
216 Op::AssignOp(_) => {
217 self.check_expr(lhs_expr)
222 }
223 };
224 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
225
226 let rhs_ty_var = self.next_ty_var(rhs_expr.span);
233 let result = self.lookup_op_method(
234 (lhs_expr, lhs_ty),
235 Some((rhs_expr, rhs_ty_var)),
236 lang_item_for_binop(self.tcx, op),
237 op.span(),
238 expected,
239 );
240
241 let rhs_ty = self.check_expr_coercible_to_type_or_error(
243 rhs_expr,
244 rhs_ty_var,
245 Some(lhs_expr),
246 |err, ty| {
247 self.err_ctxt().note_field_shadowed_by_private_candidate(
248 err,
249 rhs_expr.hir_id,
250 self.param_env,
251 );
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 = self.overloaded_binop_ret_ty(
262 expr, lhs_expr, rhs_expr, op, expected, lhs_ty, result, rhs_ty,
263 );
264
265 (lhs_ty, rhs_ty, return_ty)
266 }
267
268 fn overloaded_binop_ret_ty(
269 &self,
270 expr: &'tcx Expr<'tcx>,
271 lhs_expr: &'tcx Expr<'tcx>,
272 rhs_expr: &'tcx Expr<'tcx>,
273 op: Op,
274 expected: Expectation<'tcx>,
275 lhs_ty: Ty<'tcx>,
276 result: Result<MethodCallee<'tcx>, ThinVec<FulfillmentError<'tcx>>>,
277 rhs_ty: Ty<'tcx>,
278 ) -> Ty<'tcx> {
279 match result {
280 Ok(method) => {
281 let by_ref_binop = !op.is_by_value();
282
283 if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_)) || by_ref_binop {
284 if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
285 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
286 let autoref = Adjustment {
287 kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
288 target: method.sig.inputs()[0],
289 };
290 self.apply_adjustments(lhs_expr, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[autoref]))vec![autoref]);
291 }
292
293 if by_ref_binop {
294 if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() {
295 let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
298 let autoref = Adjustment {
299 kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
300 target: method.sig.inputs()[1],
301 };
302 self.typeck_results
307 .borrow_mut()
308 .adjustments_mut()
309 .entry(rhs_expr.hir_id)
310 .or_default()
311 .push(autoref);
312 }
313 }
314 }
315
316 self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
317
318 method.sig.output()
319 }
320 Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => {
322 Ty::new_misc_error(self.tcx)
323 }
324 Err(errors) => self.report_binop_fulfillment_errors(
325 expr, lhs_expr, rhs_expr, op, expected, lhs_ty, rhs_ty, errors,
326 ),
327 }
328 }
329
330 fn report_binop_fulfillment_errors(
331 &self,
332 expr: &'tcx Expr<'tcx>,
333 lhs_expr: &'tcx Expr<'tcx>,
334 rhs_expr: &'tcx Expr<'tcx>,
335 op: Op,
336 expected: Expectation<'tcx>,
337 lhs_ty: Ty<'tcx>,
338 rhs_ty: Ty<'tcx>,
339 errors: ThinVec<FulfillmentError<'tcx>>,
340 ) -> Ty<'tcx> {
341 let (_, trait_def_id) = lang_item_for_binop(self.tcx, op);
342
343 let mut path = None;
344 let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
345 let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
346
347 let (mut err, output_def_id) = match op {
348 Op::AssignOp(assign_op) => {
351 if let Err(e) =
352 diagnostics::maybe_emit_plus_equals_diagnostic(&self, assign_op, lhs_expr)
353 {
354 (e, None)
355 } else {
356 let s = assign_op.node.as_str();
357 let mut err = {
self.dcx().struct_span_err(expr.span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("binary assignment operation `{0}` cannot be applied to type `{1}`",
s, lhs_ty_str))
})).with_code(E0368)
}struct_span_code_err!(
358 self.dcx(),
359 expr.span,
360 E0368,
361 "binary assignment operation `{}` cannot be applied to type `{}`",
362 s,
363 lhs_ty_str,
364 );
365 err.span_label(
366 lhs_expr.span,
367 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot use `{0}` on type `{1}`", s,
lhs_ty_str))
})format!("cannot use `{}` on type `{}`", s, lhs_ty_str),
368 );
369 let err_ctxt = self.err_ctxt();
370 err_ctxt.note_field_shadowed_by_private_candidate(
371 &mut err,
372 lhs_expr.hir_id,
373 self.param_env,
374 );
375 err_ctxt.note_field_shadowed_by_private_candidate(
376 &mut err,
377 rhs_expr.hir_id,
378 self.param_env,
379 );
380 self.note_unmet_impls_on_type(&mut err, &errors, false);
381 (err, None)
382 }
383 }
384 Op::BinOp(bin_op) => {
385 use hir::BinOpKind;
386 let message = match bin_op.node {
387 BinOpKind::Add => {
388 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot add `{0}` to `{1}`",
rhs_ty_str, lhs_ty_str))
})format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
389 }
390 BinOpKind::Sub => {
391 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot subtract `{0}` from `{1}`",
rhs_ty_str, lhs_ty_str))
})format!("cannot subtract `{rhs_ty_str}` from `{lhs_ty_str}`")
392 }
393 BinOpKind::Mul => {
394 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot multiply `{0}` by `{1}`",
lhs_ty_str, rhs_ty_str))
})format!("cannot multiply `{lhs_ty_str}` by `{rhs_ty_str}`")
395 }
396 BinOpKind::Div => {
397 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot divide `{0}` by `{1}`",
lhs_ty_str, rhs_ty_str))
})format!("cannot divide `{lhs_ty_str}` by `{rhs_ty_str}`")
398 }
399 BinOpKind::Rem => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot calculate the remainder of `{0}` divided by `{1}`",
lhs_ty_str, rhs_ty_str))
})format!(
400 "cannot calculate the remainder of `{lhs_ty_str}` divided by `{rhs_ty_str}`"
401 ),
402 BinOpKind::BitAnd
403 | BinOpKind::BitXor
404 | BinOpKind::BitOr
405 | BinOpKind::Shl
406 | BinOpKind::Shr => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("no implementation for `{1} {0} {2}`",
bin_op.node.as_str(), lhs_ty_str, rhs_ty_str))
})format!(
407 "no implementation for `{lhs_ty_str} {} {rhs_ty_str}`",
408 bin_op.node.as_str()
409 ),
410 _ => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("binary operation `{0}` cannot be applied to type `{1}`",
bin_op.node.as_str(), lhs_ty_str))
})format!(
411 "binary operation `{}` cannot be applied to type `{lhs_ty_str}`",
412 bin_op.node.as_str()
413 ),
414 };
415
416 let output_def_id = trait_def_id.and_then(|def_id| {
417 self.tcx
418 .associated_item_def_ids(def_id)
419 .iter()
420 .find(|&&item_def_id| {
421 self.tcx.associated_item(item_def_id).name() == sym::Output
422 })
423 .cloned()
424 });
425 let mut err = {
self.dcx().struct_span_err(bin_op.span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", message))
})).with_code(E0369)
}struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}");
426 if !lhs_expr.span.eq(&rhs_expr.span) {
427 err.span_label(lhs_expr.span, lhs_ty_str.clone());
428 err.span_label(rhs_expr.span, rhs_ty_str);
429 }
430 let err_ctxt = self.err_ctxt();
431 err_ctxt.note_field_shadowed_by_private_candidate(
432 &mut err,
433 lhs_expr.hir_id,
434 self.param_env,
435 );
436 err_ctxt.note_field_shadowed_by_private_candidate(
437 &mut err,
438 rhs_expr.hir_id,
439 self.param_env,
440 );
441 let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty);
442 self.note_unmet_impls_on_type(&mut err, &errors, suggest_derive);
443 (err, output_def_id)
444 }
445 };
446 *err.long_ty_path() = path;
447
448 let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err);
450
451 if maybe_missing_semi && self.is_lhs_of_assign_stmt(expr) {
455 err.downgrade_to_delayed_bug();
456 }
457
458 let is_compatible_after_call = |lhs_ty, rhs_ty| {
459 let op_ok = self
460 .lookup_op_method(
461 (lhs_expr, lhs_ty),
462 Some((rhs_expr, rhs_ty)),
463 lang_item_for_binop(self.tcx, op),
464 op.span(),
465 expected,
466 )
467 .is_ok();
468
469 op_ok || self.can_eq(self.param_env, lhs_ty, rhs_ty)
470 };
471
472 self.suggest_deref_or_call_for_binop_error(
475 lhs_expr,
476 rhs_expr,
477 op,
478 expected,
479 lhs_ty,
480 rhs_ty,
481 &mut err,
482 is_compatible_after_call,
483 );
484
485 if let Some(missing_trait) =
486 trait_def_id.map(|def_id| { let _guard = NoTrimmedGuard::new(); self.tcx.def_path_str(def_id) }with_no_trimmed_paths!(self.tcx.def_path_str(def_id)))
487 {
488 if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::BinOp(BinOp { node: BinOpKind::Add, .. }) |
Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. }) => true,
_ => false,
}matches!(
489 op,
490 Op::BinOp(BinOp { node: BinOpKind::Add, .. })
491 | Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. })
492 ) && self.check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op)
493 {
494 } else if lhs_ty.has_non_region_param() {
498 if !errors.is_empty() {
499 for error in errors {
500 if let Some(trait_pred) = error.obligation.predicate.as_trait_clause() {
501 let output_associated_item = if let ObligationCauseCode::BinOp {
502 output_ty: Some(output_ty),
503 ..
504 } = error.obligation.cause.code()
505 {
506 output_def_id
507 .zip(trait_def_id)
508 .filter(|(output_def_id, trait_def_id)| {
509 self.tcx.parent(*output_def_id) == *trait_def_id
510 })
511 .and_then(|_| output_ty.make_suggestable(self.tcx, false, None))
512 .map(|output_ty| ("Output", output_ty))
513 } else {
514 None
515 };
516
517 self.err_ctxt().suggest_restricting_param_bound(
518 &mut err,
519 trait_pred,
520 output_associated_item,
521 self.body_def_id,
522 );
523 }
524 }
525 } else {
526 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the trait `{0}` is not implemented for `{1}`",
missing_trait, lhs_ty_str))
})format!(
529 "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`"
530 ));
531 }
532 }
533 }
534
535 self.suggest_raw_ptr_binop_arithmetic(lhs_expr, rhs_expr, op, lhs_ty, rhs_ty, &mut err);
538
539 let lhs_name_str = match lhs_expr.kind {
540 ExprKind::Path(hir::QPath::Resolved(_, path)) => {
541 path.segments.last().map_or("_".to_string(), |s| s.ident.to_string())
542 }
543 _ => self
544 .tcx
545 .sess
546 .source_map()
547 .span_to_snippet(lhs_expr.span)
548 .unwrap_or_else(|_| "_".to_string()),
549 };
550
551 self.suggest_raw_ptr_assign_arithmetic(
552 lhs_expr,
553 rhs_expr,
554 op,
555 lhs_ty,
556 rhs_ty,
557 &lhs_name_str,
558 &mut err,
559 );
560
561 Ty::new_error(self.tcx, err.emit())
562 }
563
564 fn suggest_deref_or_call_for_binop_error(
565 &self,
566 lhs_expr: &'tcx Expr<'tcx>,
567 rhs_expr: &'tcx Expr<'tcx>,
568 op: Op,
569 expected: Expectation<'tcx>,
570 lhs_ty: Ty<'tcx>,
571 rhs_ty: Ty<'tcx>,
572 err: &mut Diag<'_>,
573 is_compatible_after_call: impl Fn(Ty<'tcx>, Ty<'tcx>) -> bool,
574 ) {
575 if !op.span().can_be_used_for_suggestions() {
577 return;
578 }
579
580 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
581 && #[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_))
582 {
583 self.suggest_deref_binop(lhs_expr, rhs_expr, op, expected, rhs_ty, err, lhs_deref_ty);
584 } else if let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
585 && #[allow(non_exhaustive_omitted_patterns)] match op {
Op::BinOp(_) => true,
_ => false,
}matches!(op, Op::BinOp(_))
586 {
587 if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
588 self.suggest_deref_binop(
589 lhs_expr,
590 rhs_expr,
591 op,
592 expected,
593 rhs_ty,
594 err,
595 *lhs_deref_ty,
596 );
597 } else {
598 let lhs_inv_mutbl = mutbl.invert();
599 let lhs_inv_mutbl_ty = Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl);
600
601 self.suggest_different_borrow(
602 lhs_expr,
603 rhs_expr,
604 op,
605 expected,
606 err,
607 lhs_inv_mutbl_ty,
608 Some(lhs_inv_mutbl),
609 rhs_ty,
610 None,
611 );
612
613 if let ty::Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() {
614 let rhs_inv_mutbl = mutbl.invert();
615 let rhs_inv_mutbl_ty =
616 Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl);
617
618 self.suggest_different_borrow(
619 lhs_expr,
620 rhs_expr,
621 op,
622 expected,
623 err,
624 lhs_ty,
625 None,
626 rhs_inv_mutbl_ty,
627 Some(rhs_inv_mutbl),
628 );
629 self.suggest_different_borrow(
630 lhs_expr,
631 rhs_expr,
632 op,
633 expected,
634 err,
635 lhs_inv_mutbl_ty,
636 Some(lhs_inv_mutbl),
637 rhs_inv_mutbl_ty,
638 Some(rhs_inv_mutbl),
639 );
640 }
641 }
642 } else {
643 let suggested = self.suggest_fn_call(err, lhs_expr, lhs_ty, |lhs_ty| {
644 is_compatible_after_call(lhs_ty, rhs_ty)
645 }) || self.suggest_fn_call(err, rhs_expr, rhs_ty, |rhs_ty| {
646 is_compatible_after_call(lhs_ty, rhs_ty)
647 });
648
649 if !suggested {
650 self.suggest_two_fn_call(
651 err,
652 rhs_expr,
653 rhs_ty,
654 lhs_expr,
655 lhs_ty,
656 is_compatible_after_call,
657 );
658 }
659 }
660 }
661
662 fn suggest_raw_ptr_binop_arithmetic(
663 &self,
664 lhs_expr: &'tcx Expr<'tcx>,
665 rhs_expr: &'tcx Expr<'tcx>,
666 op: Op,
667 lhs_ty: Ty<'tcx>,
668 rhs_ty: Ty<'tcx>,
669 err: &mut Diag<'_>,
670 ) {
671 if !op.span().can_be_used_for_suggestions() {
672 return;
673 }
674
675 match op {
676 Op::BinOp(BinOp { node: BinOpKind::Add, .. })
677 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
678 {
679 err.multipart_suggestion(
680 "consider using `wrapping_add` or `add` for pointer + {integer}",
681 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_expr.span.between(rhs_expr.span), ".wrapping_add(".to_owned()),
(rhs_expr.span.shrink_to_hi(), ")".to_owned())]))vec![
682 (lhs_expr.span.between(rhs_expr.span), ".wrapping_add(".to_owned()),
683 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
684 ],
685 Applicability::MaybeIncorrect,
686 );
687 }
688 Op::BinOp(BinOp { node: BinOpKind::Sub, .. }) => {
689 if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
690 err.multipart_suggestion(
691 "consider using `wrapping_sub` or `sub` for pointer - {integer}",
692 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()),
(rhs_expr.span.shrink_to_hi(), ")".to_owned())]))vec![
693 (lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()),
694 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
695 ],
696 Applicability::MaybeIncorrect,
697 );
698 }
699 if lhs_ty.is_raw_ptr() && rhs_ty.is_raw_ptr() {
700 err.multipart_suggestion(
701 "consider using `offset_from` for pointer - pointer if the \
702 pointers point to the same allocation",
703 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
(lhs_expr.span.between(rhs_expr.span),
".offset_from(".to_owned()),
(rhs_expr.span.shrink_to_hi(), ") }".to_owned())]))vec![
704 (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
705 (lhs_expr.span.between(rhs_expr.span), ".offset_from(".to_owned()),
706 (rhs_expr.span.shrink_to_hi(), ") }".to_owned()),
707 ],
708 Applicability::MaybeIncorrect,
709 );
710 }
711 }
712 _ => {}
713 }
714 }
715
716 fn suggest_raw_ptr_assign_arithmetic(
717 &self,
718 lhs_expr: &'tcx Expr<'tcx>,
719 rhs_expr: &'tcx Expr<'tcx>,
720 op: Op,
721 lhs_ty: Ty<'tcx>,
722 rhs_ty: Ty<'tcx>,
723 lhs_name_str: &str,
724 err: &mut Diag<'_>,
725 ) {
726 if !op.span().can_be_used_for_suggestions()
727 || !#[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_))
728 || !lhs_ty.is_raw_ptr()
729 || !rhs_ty.is_integral()
730 {
731 return;
732 }
733
734 let (msg, method) = match op {
735 Op::AssignOp(AssignOp { node: AssignOpKind::AddAssign, .. }) => {
736 ("consider using `add` or `wrapping_add` to do pointer arithmetic", "wrapping_add")
737 }
738 Op::AssignOp(AssignOp { node: AssignOpKind::SubAssign, .. }) => {
739 ("consider using `sub` or `wrapping_sub` to do pointer arithmetic", "wrapping_sub")
740 }
741 _ => return,
742 };
743
744 err.multipart_suggestion(
745 msg,
746 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_expr.span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} = ", lhs_name_str))
})),
(lhs_expr.span.between(rhs_expr.span),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".{0}(", method))
})), (rhs_expr.span.shrink_to_hi(), ")".to_owned())]))vec![
747 (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
748 (lhs_expr.span.between(rhs_expr.span), format!(".{method}(")),
749 (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
750 ],
751 Applicability::MaybeIncorrect,
752 );
753 }
754
755 fn suggest_different_borrow(
756 &self,
757 lhs_expr: &'tcx Expr<'tcx>,
758 rhs_expr: &'tcx Expr<'tcx>,
759 op: Op,
760 expected: Expectation<'tcx>,
761 err: &mut Diag<'_>,
762 lhs_adjusted_ty: Ty<'tcx>,
763 lhs_new_mutbl: Option<ty::Mutability>,
764 rhs_adjusted_ty: Ty<'tcx>,
765 rhs_new_mutbl: Option<ty::Mutability>,
766 ) {
767 if self
768 .lookup_op_method(
769 (lhs_expr, lhs_adjusted_ty),
770 Some((rhs_expr, rhs_adjusted_ty)),
771 lang_item_for_binop(self.tcx, op),
772 op.span(),
773 expected,
774 )
775 .is_ok()
776 {
777 let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
778 let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path());
779 let op = op.as_str();
780 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("an implementation for `{0} {1} {2}` exists",
lhs, op, rhs))
})format!("an implementation for `{lhs} {op} {rhs}` exists"));
781
782 if lhs_new_mutbl.is_some_and(|lhs_mutbl| lhs_mutbl.is_not())
783 && rhs_new_mutbl.is_some_and(|rhs_mutbl| rhs_mutbl.is_not())
784 {
785 err.multipart_suggestion(
786 "consider reborrowing both sides",
787 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_expr.span.shrink_to_lo(), "&*".to_string()),
(rhs_expr.span.shrink_to_lo(), "&*".to_string())]))vec![
788 (lhs_expr.span.shrink_to_lo(), "&*".to_string()),
789 (rhs_expr.span.shrink_to_lo(), "&*".to_string()),
790 ],
791 rustc_errors::Applicability::MachineApplicable,
792 );
793 } else {
794 let mut suggest_new_borrow = |new_mutbl: ast::Mutability, sp: Span| {
795 if new_mutbl.is_not() {
797 err.span_suggestion_verbose(
798 sp.shrink_to_lo(),
799 "consider reborrowing this side",
800 "&*",
801 rustc_errors::Applicability::MachineApplicable,
802 );
803 } else {
805 err.span_help(sp, "consider making this expression a mutable borrow");
806 }
807 };
808
809 if let Some(lhs_new_mutbl) = lhs_new_mutbl {
810 suggest_new_borrow(lhs_new_mutbl, lhs_expr.span);
811 }
812 if let Some(rhs_new_mutbl) = rhs_new_mutbl {
813 suggest_new_borrow(rhs_new_mutbl, rhs_expr.span);
814 }
815 }
816 }
817 }
818
819 fn is_lhs_of_assign_stmt(&self, expr: &Expr<'_>) -> bool {
820 let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id) else { return false };
821 let ExprKind::Assign(lhs, _, _) = parent.kind else { return false };
822 let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(parent.hir_id) else { return false };
823 #[allow(non_exhaustive_omitted_patterns)] match stmt.kind {
hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_) => true,
_ => false,
}matches!(stmt.kind, hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_))
824 && lhs.hir_id == expr.hir_id
825 }
826
827 fn suggest_deref_binop(
828 &self,
829 lhs_expr: &'tcx Expr<'tcx>,
830 rhs_expr: &'tcx Expr<'tcx>,
831 op: Op,
832 expected: Expectation<'tcx>,
833 rhs_ty: Ty<'tcx>,
834 err: &mut Diag<'_>,
835 lhs_deref_ty: Ty<'tcx>,
836 ) {
837 if self
838 .lookup_op_method(
839 (lhs_expr, lhs_deref_ty),
840 Some((rhs_expr, rhs_ty)),
841 lang_item_for_binop(self.tcx, op),
842 op.span(),
843 expected,
844 )
845 .is_ok()
846 {
847 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` can be used on `{1}` if you dereference the left-hand side",
op.as_str(),
self.tcx.short_string(lhs_deref_ty, err.long_ty_path())))
})format!(
848 "`{}` can be used on `{}` if you dereference the left-hand side",
849 op.as_str(),
850 self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
851 );
852 err.span_suggestion_verbose(
853 lhs_expr.span.shrink_to_lo(),
854 msg,
855 "*",
856 rustc_errors::Applicability::MachineApplicable,
857 );
858 }
859 }
860
861 fn check_str_addition(
867 &self,
868 lhs_expr: &'tcx Expr<'tcx>,
869 rhs_expr: &'tcx Expr<'tcx>,
870 lhs_ty: Ty<'tcx>,
871 rhs_ty: Ty<'tcx>,
872 err: &mut Diag<'_>,
873 op: Op,
874 ) -> bool {
875 let str_concat_note = "string concatenation requires an owned `String` on the left";
876 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
877 let to_owned_msg = "create an owned `String` from a string reference";
878
879 let string_type = self.tcx.lang_items().string();
880 let is_std_string =
881 |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|def| Some(def.did()) == string_type);
882 let is_str_like = |ty: Ty<'tcx>| *ty.kind() == ty::Str || is_std_string(ty);
883
884 let lhs_owned_sugg = |lhs_expr: &Expr<'_>| {
886 if let ExprKind::AddrOf(_, _, inner) = lhs_expr.kind {
887 (lhs_expr.span.until(inner.span), None)
888 } else {
889 (lhs_expr.span.shrink_to_hi(), Some(".to_owned()".to_owned()))
890 }
891 };
892
893 let (&ty::Ref(_, l_ty, _), rhs_kind) = (lhs_ty.kind(), rhs_ty.kind()) else {
894 return false;
895 };
896 if !is_str_like(l_ty) {
897 return false;
898 }
899
900 match rhs_kind {
901 &ty::Ref(_, r_ty, _)
903 if is_str_like(r_ty)
904 || #[allow(non_exhaustive_omitted_patterns)] match r_ty.kind() {
ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
_ => false,
}matches!(r_ty.kind(), ty::Ref(_, inner, _) if *inner.kind() == ty::Str) =>
905 {
906 if let Op::BinOp(_) = op {
908 err.span_label(
909 op.span(),
910 "`+` cannot be used to concatenate two `&str` strings",
911 );
912 err.note(str_concat_note);
913 let (span, replacement) = lhs_owned_sugg(lhs_expr);
914 let (msg, replacement) = match replacement {
915 None => (rm_borrow_msg, "".to_owned()),
916 Some(r) => (to_owned_msg, r),
917 };
918 err.span_suggestion_verbose(
919 span,
920 msg,
921 replacement,
922 Applicability::MachineApplicable,
923 );
924 }
925 true
926 }
927 ty::Adt(..) if is_std_string(rhs_ty) => {
929 err.span_label(
930 op.span(),
931 "`+` cannot be used to concatenate a `&str` with a `String`",
932 );
933 if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::BinOp(_) => true,
_ => false,
}matches!(op, Op::BinOp(_)) {
934 let (lhs_span, lhs_replacement) = lhs_owned_sugg(lhs_expr);
935 let (sugg_msg, lhs_replacement) = match lhs_replacement {
936 None => (
937 "remove the borrow on the left and add one on the right",
938 "".to_owned(),
939 ),
940 Some(r) => (
941 "create an owned `String` on the left and add a borrow on the right",
942 r,
943 ),
944 };
945 err.multipart_suggestion(
946 sugg_msg,
947 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_span, lhs_replacement),
(rhs_expr.span.shrink_to_lo(), "&".to_owned())]))vec![
948 (lhs_span, lhs_replacement),
949 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
950 ],
951 Applicability::MachineApplicable,
952 );
953 } else if #[allow(non_exhaustive_omitted_patterns)] match op {
Op::AssignOp(_) => true,
_ => false,
}matches!(op, Op::AssignOp(_)) {
954 err.note(str_concat_note);
955 }
956 true
957 }
958 _ => false,
959 }
960 }
961
962 pub(crate) fn check_user_unop(
963 &self,
964 ex: &'tcx Expr<'tcx>,
965 operand_ty: Ty<'tcx>,
966 op: hir::UnOp,
967 expected: Expectation<'tcx>,
968 ) -> Ty<'tcx> {
969 if !op.is_by_value() {
::core::panicking::panic("assertion failed: op.is_by_value()")
};assert!(op.is_by_value());
970 match self.lookup_op_method(
971 (ex, operand_ty),
972 None,
973 lang_item_for_unop(self.tcx, op),
974 ex.span,
975 expected,
976 ) {
977 Ok(method) => {
978 self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
979 method.sig.output()
980 }
981 Err(errors) => {
982 let actual = self.resolve_vars_if_possible(operand_ty);
983 let guar = actual.error_reported().err().unwrap_or_else(|| {
984 let mut file = None;
985 let ty_str = self.tcx.short_string(actual, &mut file);
986 let mut err = {
self.dcx().struct_span_err(ex.span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot apply unary operator `{0}` to type `{1}`",
op.as_str(), ty_str))
})).with_code(E0600)
}struct_span_code_err!(
987 self.dcx(),
988 ex.span,
989 E0600,
990 "cannot apply unary operator `{}` to type `{ty_str}`",
991 op.as_str(),
992 );
993 *err.long_ty_path() = file;
994 err.span_label(
995 ex.span,
996 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot apply unary operator `{0}`",
op.as_str()))
})format!("cannot apply unary operator `{}`", op.as_str()),
997 );
998
999 if operand_ty.has_non_region_param() {
1000 let predicates = errors
1001 .iter()
1002 .filter_map(|error| error.obligation.predicate.as_trait_clause());
1003 for pred in predicates {
1004 self.err_ctxt().suggest_restricting_param_bound(
1005 &mut err,
1006 pred,
1007 None,
1008 self.body_def_id,
1009 );
1010 }
1011 }
1012
1013 let sp = self.tcx.sess.source_map().start_point(ex.span).with_parent(None);
1014 if let Some(sp) =
1015 self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp)
1016 {
1017 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1021 } else {
1022 match actual.kind() {
1023 ty::Uint(_) if op == hir::UnOp::Neg => {
1024 err.note("unsigned values cannot be negated");
1025
1026 if let ExprKind::Unary(
1027 _,
1028 Expr {
1029 kind:
1030 ExprKind::Lit(Spanned {
1031 node: ast::LitKind::Int(Pu128(1), _),
1032 ..
1033 }),
1034 ..
1035 },
1036 ) = ex.kind
1037 {
1038 let span = if let hir::Node::Expr(parent) =
1039 self.tcx.parent_hir_node(ex.hir_id)
1040 && let ExprKind::Cast(..) = parent.kind
1041 {
1042 parent.span
1044 } else {
1045 ex.span
1046 };
1047 err.span_suggestion_verbose(
1048 span,
1049 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you may have meant the maximum value of `{0}`",
actual))
})format!(
1050 "you may have meant the maximum value of `{actual}`",
1051 ),
1052 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::MAX", actual))
})format!("{actual}::MAX"),
1053 Applicability::MaybeIncorrect,
1054 );
1055 }
1056 }
1057 ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {}
1058 ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {}
1059 _ => {
1060 self.note_unmet_impls_on_type(&mut err, &errors, true);
1061 }
1062 }
1063 }
1064 err.emit()
1065 });
1066 Ty::new_error(self.tcx, guar)
1067 }
1068 }
1069 }
1070
1071 fn lookup_op_method(
1072 &self,
1073 (lhs_expr, lhs_ty): (&'tcx Expr<'tcx>, Ty<'tcx>),
1074 opt_rhs: Option<(&'tcx Expr<'tcx>, Ty<'tcx>)>,
1075 (opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
1076 span: Span,
1077 expected: Expectation<'tcx>,
1078 ) -> Result<MethodCallee<'tcx>, ThinVec<FulfillmentError<'tcx>>> {
1079 let Some(trait_did) = trait_did else {
1080 return Err(::thin_vec::ThinVec::new()thin_vec![]);
1082 };
1083
1084 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/op.rs:1084",
"rustc_hir_typeck::op", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/op.rs"),
::tracing_core::__macro_support::Option::Some(1084u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::op"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("lookup_op_method(lhs_ty={0:?}, opname={1:?}, trait_did={2:?})",
lhs_ty, opname, trait_did) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
1085 "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
1086 lhs_ty, opname, trait_did
1087 );
1088
1089 let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
1090 let cause = self.cause(
1091 span,
1092 match opt_rhs_expr {
1093 Some(rhs) => ObligationCauseCode::BinOp {
1094 lhs_hir_id: lhs_expr.hir_id,
1095 rhs_hir_id: rhs.hir_id,
1096 rhs_span: rhs.span,
1097 rhs_is_lit: #[allow(non_exhaustive_omitted_patterns)] match rhs.kind {
ExprKind::Lit(_) => true,
_ => false,
}matches!(rhs.kind, ExprKind::Lit(_)),
1098 output_ty: expected.only_has_type(self),
1099 },
1100 None => ObligationCauseCode::UnOp { hir_id: lhs_expr.hir_id },
1101 },
1102 );
1103
1104 let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
1108 let method = self.lookup_method_for_operator(
1109 cause.clone(),
1110 opname,
1111 trait_did,
1112 lhs_ty,
1113 opt_rhs_ty,
1114 treat_opaques,
1115 );
1116 match method {
1117 Some(ok) => {
1118 let method = self.register_infer_ok_obligations(ok);
1119 self.select_obligations_where_possible(|_| {});
1120 Ok(method)
1121 }
1122 None => {
1123 self.dcx().span_delayed_bug(span, "this path really should be doomed...");
1127 if let Some((rhs_expr, rhs_ty)) = opt_rhs
1131 && rhs_ty.is_ty_var()
1132 {
1133 self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None);
1134 }
1135
1136 let args =
1138 ty::GenericArgs::for_item(self.tcx, trait_did, |param, _| match param.kind {
1139 ty::GenericParamDefKind::Lifetime
1140 | ty::GenericParamDefKind::Const { .. } => {
1141 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("did not expect operand trait to have lifetime/const args")));
}unreachable!("did not expect operand trait to have lifetime/const args")
1142 }
1143 ty::GenericParamDefKind::Type { .. } => {
1144 if param.index == 0 {
1145 lhs_ty.into()
1146 } else {
1147 opt_rhs_ty.expect("expected RHS for binop").into()
1148 }
1149 }
1150 });
1151 let obligation = Obligation::new(
1152 self.tcx,
1153 cause,
1154 self.param_env,
1155 ty::TraitRef::new_from_args(self.tcx, trait_did, args),
1156 );
1157 let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
1158 ocx.register_obligation(obligation);
1159 Err(ocx.evaluate_obligations_error_on_ambiguity().into_thin_vec())
1160 }
1161 }
1162 }
1163}
1164
1165fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<DefId>) {
1166 let lang = tcx.lang_items();
1167 match op {
1168 Op::AssignOp(op) => match op.node {
1169 AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
1170 AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
1171 AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
1172 AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
1173 AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
1174 AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
1175 AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
1176 AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
1177 AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
1178 AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
1179 },
1180 Op::BinOp(op) => match op.node {
1181 BinOpKind::Add => (sym::add, lang.add_trait()),
1182 BinOpKind::Sub => (sym::sub, lang.sub_trait()),
1183 BinOpKind::Mul => (sym::mul, lang.mul_trait()),
1184 BinOpKind::Div => (sym::div, lang.div_trait()),
1185 BinOpKind::Rem => (sym::rem, lang.rem_trait()),
1186 BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
1187 BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
1188 BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
1189 BinOpKind::Shl => (sym::shl, lang.shl_trait()),
1190 BinOpKind::Shr => (sym::shr, lang.shr_trait()),
1191 BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
1192 BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
1193 BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
1194 BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
1195 BinOpKind::Eq => (sym::eq, lang.eq_trait()),
1196 BinOpKind::Ne => (sym::ne, lang.eq_trait()),
1197 BinOpKind::And | BinOpKind::Or => {
1198 ::rustc_middle::util::bug::bug_fmt(format_args!("&& and || are not overloadable"))bug!("&& and || are not overloadable")
1199 }
1200 },
1201 }
1202}
1203
1204fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
1205 let lang = tcx.lang_items();
1206 match op {
1207 hir::UnOp::Not => (sym::not, lang.not_trait()),
1208 hir::UnOp::Neg => (sym::neg, lang.neg_trait()),
1209 hir::UnOp::Deref => ::rustc_middle::util::bug::bug_fmt(format_args!("Deref is not overloadable"))bug!("Deref is not overloadable"),
1210 }
1211}
1212
1213pub(crate) fn contains_let_in_chain(expr: &Expr<'_>) -> bool {
1215 match &expr.kind {
1216 ExprKind::Let(..) => true,
1217 ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, left, right) => {
1218 contains_let_in_chain(left) || contains_let_in_chain(right)
1219 }
1220 _ => false,
1221 }
1222}
1223
1224#[derive(#[automatically_derived]
impl ::core::clone::Clone for BinOpCategory {
#[inline]
fn clone(&self) -> BinOpCategory { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BinOpCategory { }Copy)]
1227enum BinOpCategory {
1228 Shortcircuit,
1230
1231 Shift,
1234
1235 Math,
1238
1239 Bitwise,
1242
1243 Comparison,
1246}
1247
1248impl From<BinOpKind> for BinOpCategory {
1249 fn from(op: BinOpKind) -> BinOpCategory {
1250 use hir::BinOpKind::*;
1251 match op {
1252 Shl | Shr => BinOpCategory::Shift,
1253 Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
1254 BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
1255 Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
1256 And | Or => BinOpCategory::Shortcircuit,
1257 }
1258 }
1259}
1260
1261impl From<AssignOpKind> for BinOpCategory {
1262 fn from(op: AssignOpKind) -> BinOpCategory {
1263 use hir::AssignOpKind::*;
1264 match op {
1265 ShlAssign | ShrAssign => BinOpCategory::Shift,
1266 AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
1267 BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
1268 }
1269 }
1270}
1271
1272#[derive(#[automatically_derived]
impl ::core::clone::Clone for Op {
#[inline]
fn clone(&self) -> Op {
let _: ::core::clone::AssertParamIsClone<hir::BinOp>;
let _: ::core::clone::AssertParamIsClone<hir::AssignOp>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Op { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for Op {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Op::BinOp(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "BinOp",
&__self_0),
Op::AssignOp(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AssignOp", &__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for Op {
#[inline]
fn eq(&self, other: &Op) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(Op::BinOp(__self_0), Op::BinOp(__arg1_0)) =>
__self_0 == __arg1_0,
(Op::AssignOp(__self_0), Op::AssignOp(__arg1_0)) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq)]
1274enum Op {
1275 BinOp(hir::BinOp),
1276 AssignOp(hir::AssignOp),
1277}
1278
1279impl Op {
1280 fn span(&self) -> Span {
1281 match self {
1282 Op::BinOp(op) => op.span,
1283 Op::AssignOp(op) => op.span,
1284 }
1285 }
1286
1287 fn as_str(&self) -> &'static str {
1288 match self {
1289 Op::BinOp(op) => op.node.as_str(),
1290 Op::AssignOp(op) => op.node.as_str(),
1291 }
1292 }
1293
1294 fn is_by_value(&self) -> bool {
1295 match self {
1296 Op::BinOp(op) => op.node.is_by_value(),
1297 Op::AssignOp(op) => op.node.is_by_value(),
1298 }
1299 }
1300}
1301
1302fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
1304 match ty.kind() {
1305 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
1306 _ => ty,
1307 }
1308}
1309
1310fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool {
1322 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
1325
1326 match category {
1327 BinOpCategory::Shortcircuit => true,
1328 BinOpCategory::Shift => {
1329 lhs.references_error()
1330 || rhs.references_error()
1331 || lhs.is_integral() && rhs.is_integral()
1332 }
1333 BinOpCategory::Math => {
1334 lhs.references_error()
1335 || rhs.references_error()
1336 || lhs.is_integral() && rhs.is_integral()
1337 || lhs.is_floating_point() && rhs.is_floating_point()
1338 }
1339 BinOpCategory::Bitwise => {
1340 lhs.references_error()
1341 || rhs.references_error()
1342 || lhs.is_integral() && rhs.is_integral()
1343 || lhs.is_floating_point() && rhs.is_floating_point()
1344 || lhs.is_bool() && rhs.is_bool()
1345 }
1346 BinOpCategory::Comparison => {
1347 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
1348 }
1349 }
1350}