]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/check/op.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / op.rs
CommitLineData
c34b1796
AL
1//! Code related to processing overloaded binary and unary operators.
2
7cac9316 3use super::method::MethodCallee;
17df50a5 4use super::{has_expected_num_generic_args, FnCtxt};
064997fb 5use crate::check::Expectation;
29967ef6 6use rustc_ast as ast;
5e7ed085 7use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
dfeec247 8use rustc_hir as hir;
74b04a01 9use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
064997fb 10use rustc_infer::traits::ObligationCauseCode;
ba9703b0
XL
11use rustc_middle::ty::adjustment::{
12 Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
13};
f2b60f7d
FG
14use rustc_middle::ty::print::with_no_trimmed_paths;
15use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
29967ef6 16use rustc_span::source_map::Spanned;
3dfed10e 17use rustc_span::symbol::{sym, Ident};
dfeec247 18use rustc_span::Span;
ba9703b0 19use rustc_trait_selection::infer::InferCtxtExt;
04454e1e 20use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _;
c295e0f8 21use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
923072b8 22use rustc_type_ir::sty::TyKind::*;
60c5eb7d 23
dc9dc135 24impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9fa01778 25 /// Checks a `a <op>= b`
dc9dc135
XL
26 pub fn check_binop_assign(
27 &self,
dfeec247 28 expr: &'tcx hir::Expr<'tcx>,
dc9dc135 29 op: hir::BinOp,
dfeec247
XL
30 lhs: &'tcx hir::Expr<'tcx>,
31 rhs: &'tcx hir::Expr<'tcx>,
064997fb 32 expected: Expectation<'tcx>,
dc9dc135 33 ) -> Ty<'tcx> {
abe05a73 34 let (lhs_ty, rhs_ty, return_ty) =
064997fb 35 self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
a7813a04 36
dfeec247
XL
37 let ty =
38 if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
5e7ed085 39 self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
dfeec247
XL
40 self.tcx.mk_unit()
41 } else {
42 return_ty
43 };
44
923072b8
FG
45 self.check_lhs_assignable(lhs, "E0067", op.span, |err| {
46 if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
47 if self
48 .lookup_op_method(
49 lhs_deref_ty,
50 Some(rhs_ty),
51 Some(rhs),
52 Op::Binary(op, IsAssign::Yes),
064997fb 53 expected,
923072b8
FG
54 )
55 .is_ok()
56 {
f2b60f7d
FG
57 // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have
58 // emitted a better suggestion during error handling in check_overloaded_binop.
59 if self
60 .lookup_op_method(
61 lhs_ty,
62 Some(rhs_ty),
63 Some(rhs),
64 Op::Binary(op, IsAssign::Yes),
65 expected,
66 )
67 .is_err()
68 {
69 err.downgrade_to_delayed_bug();
70 } else {
71 // Otherwise, it's valid to suggest dereferencing the LHS here.
72 err.span_suggestion_verbose(
73 lhs.span.shrink_to_lo(),
74 "consider dereferencing the left-hand side of this operation",
75 "*",
76 Applicability::MaybeIncorrect,
77 );
78 }
923072b8
FG
79 }
80 }
81 });
c34b1796 82
9e0c209e 83 ty
c34b1796 84 }
c34b1796 85
9fa01778 86 /// Checks a potentially overloaded binary operator.
dc9dc135
XL
87 pub fn check_binop(
88 &self,
dfeec247 89 expr: &'tcx hir::Expr<'tcx>,
dc9dc135 90 op: hir::BinOp,
dfeec247
XL
91 lhs_expr: &'tcx hir::Expr<'tcx>,
92 rhs_expr: &'tcx hir::Expr<'tcx>,
064997fb 93 expected: Expectation<'tcx>,
dc9dc135 94 ) -> Ty<'tcx> {
a7813a04
XL
95 let tcx = self.tcx;
96
dfeec247
XL
97 debug!(
98 "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
99 expr.hir_id, expr, op, lhs_expr, rhs_expr
100 );
a7813a04 101
a7813a04
XL
102 match BinOpCategory::from(op) {
103 BinOpCategory::Shortcircuit => {
104 // && and || are a simple case.
f035d41b 105 self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None);
476ff2be 106 let lhs_diverges = self.diverges.get();
f035d41b 107 self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None);
476ff2be
SL
108
109 // Depending on the LHS' value, the RHS can never execute.
110 self.diverges.set(lhs_diverges);
111
abe05a73 112 tcx.types.bool
c34b1796 113 }
a7813a04
XL
114 _ => {
115 // Otherwise, we always treat operators as if they are
116 // overloaded. This is the way to be most flexible w/r/t
117 // types that get inferred.
064997fb
FG
118 let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
119 expr,
120 lhs_expr,
121 rhs_expr,
122 op,
123 IsAssign::No,
124 expected,
125 );
a7813a04
XL
126
127 // Supply type inference hints if relevant. Probably these
128 // hints should be enforced during select as part of the
129 // `consider_unification_despite_ambiguity` routine, but this
130 // more convenient for now.
131 //
132 // The basic idea is to help type inference by taking
133 // advantage of things we know about how the impls for
134 // scalar types are arranged. This is important in a
135 // scenario like `1_u32 << 2`, because it lets us quickly
136 // deduce that the result type should be `u32`, even
137 // though we don't know yet what type 2 has and hence
138 // can't pin this down to a specific impl.
dfeec247
XL
139 if !lhs_ty.is_ty_var()
140 && !rhs_ty.is_ty_var()
141 && is_builtin_binop(lhs_ty, rhs_ty, op)
a7813a04 142 {
74b04a01 143 let builtin_return_ty = self.enforce_builtin_binop_types(
5e7ed085 144 lhs_expr.span,
74b04a01 145 lhs_ty,
5e7ed085 146 rhs_expr.span,
74b04a01
XL
147 rhs_ty,
148 op,
149 );
a7813a04
XL
150 self.demand_suptype(expr.span, builtin_return_ty, return_ty);
151 }
c34b1796 152
9e0c209e 153 return_ty
a7813a04 154 }
c34b1796
AL
155 }
156 }
c34b1796 157
dc9dc135
XL
158 fn enforce_builtin_binop_types(
159 &self,
5e7ed085 160 lhs_span: Span,
dc9dc135 161 lhs_ty: Ty<'tcx>,
5e7ed085 162 rhs_span: Span,
dc9dc135
XL
163 rhs_ty: Ty<'tcx>,
164 op: hir::BinOp,
165 ) -> Ty<'tcx> {
a7813a04
XL
166 debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
167
74b04a01
XL
168 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
169 // (See https://github.com/rust-lang/rust/issues/57447.)
170 let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
171
a7813a04
XL
172 let tcx = self.tcx;
173 match BinOpCategory::from(op) {
174 BinOpCategory::Shortcircuit => {
5e7ed085
FG
175 self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
176 self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
f9f354fc 177 tcx.types.bool
a7813a04 178 }
c34b1796 179
a7813a04
XL
180 BinOpCategory::Shift => {
181 // result type is same as LHS always
182 lhs_ty
183 }
c34b1796 184
dfeec247 185 BinOpCategory::Math | BinOpCategory::Bitwise => {
a7813a04 186 // both LHS and RHS and result will have the same type
5e7ed085 187 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
a7813a04
XL
188 lhs_ty
189 }
c34b1796 190
a7813a04
XL
191 BinOpCategory::Comparison => {
192 // both LHS and RHS and result will have the same type
5e7ed085 193 self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
f9f354fc 194 tcx.types.bool
a7813a04 195 }
c34b1796
AL
196 }
197 }
c34b1796 198
dc9dc135
XL
199 fn check_overloaded_binop(
200 &self,
dfeec247
XL
201 expr: &'tcx hir::Expr<'tcx>,
202 lhs_expr: &'tcx hir::Expr<'tcx>,
203 rhs_expr: &'tcx hir::Expr<'tcx>,
dc9dc135
XL
204 op: hir::BinOp,
205 is_assign: IsAssign,
064997fb 206 expected: Expectation<'tcx>,
dc9dc135 207 ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
dfeec247
XL
208 debug!(
209 "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
210 expr.hir_id, op, is_assign
211 );
a7813a04 212
74d20737
XL
213 let lhs_ty = match is_assign {
214 IsAssign::No => {
215 // Find a suitable supertype of the LHS expression's type, by coercing to
216 // a type variable, to pass as the `Self` to the trait, avoiding invariant
217 // trait matching creating lifetime constraints that are too strict.
0731742a 218 // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
74d20737 219 // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
f035d41b 220 let lhs_ty = self.check_expr(lhs_expr);
dc9dc135
XL
221 let fresh_var = self.next_ty_var(TypeVariableOrigin {
222 kind: TypeVariableOriginKind::MiscVariable,
223 span: lhs_expr.span,
224 });
f035d41b 225 self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
74d20737
XL
226 }
227 IsAssign::Yes => {
228 // rust-lang/rust#52126: We have to use strict
229 // equivalence on the LHS of an assign-op like `+=`;
230 // overwritten or mutably-borrowed places cannot be
231 // coerced to a supertype.
f035d41b 232 self.check_expr(lhs_expr)
74d20737 233 }
abe05a73 234 };
e74abb32 235 let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
abe05a73 236
0731742a 237 // N.B., as we have not yet type-checked the RHS, we don't have the
a7813a04
XL
238 // type at hand. Make a variable to represent it. The whole reason
239 // for this indirection is so that, below, we can check the expr
240 // using this variable as the expected type, which sometimes lets
241 // us do better coercions than we would be able to do otherwise,
242 // particularly for things like `String + &String`.
dc9dc135
XL
243 let rhs_ty_var = self.next_ty_var(TypeVariableOrigin {
244 kind: TypeVariableOriginKind::MiscVariable,
245 span: rhs_expr.span,
246 });
a7813a04 247
5e7ed085
FG
248 let result = self.lookup_op_method(
249 lhs_ty,
250 Some(rhs_ty_var),
251 Some(rhs_expr),
252 Op::Binary(op, is_assign),
064997fb 253 expected,
5e7ed085 254 );
8bb4bdeb
XL
255
256 // see `NB` above
f035d41b 257 let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
e74abb32 258 let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
8bb4bdeb 259
7cac9316
XL
260 let return_ty = match result {
261 Ok(method) => {
262 let by_ref_binop = !op.node.is_by_value();
263 if is_assign == IsAssign::Yes || by_ref_binop {
1b1a35ee 264 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() {
94b46f34 265 let mutbl = match mutbl {
dfeec247
XL
266 hir::Mutability::Not => AutoBorrowMutability::Not,
267 hir::Mutability::Mut => AutoBorrowMutability::Mut {
0531ce1d
XL
268 // Allow two-phase borrows for binops in initial deployment
269 // since they desugar to methods
83c7162d 270 allow_two_phase_borrow: AllowTwoPhase::Yes,
dfeec247 271 },
2c00a5a8 272 };
7cac9316 273 let autoref = Adjustment {
5099ac24 274 kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
dfeec247 275 target: method.sig.inputs()[0],
7cac9316
XL
276 };
277 self.apply_adjustments(lhs_expr, vec![autoref]);
278 }
279 }
280 if by_ref_binop {
1b1a35ee 281 if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() {
94b46f34 282 let mutbl = match mutbl {
dfeec247
XL
283 hir::Mutability::Not => AutoBorrowMutability::Not,
284 hir::Mutability::Mut => AutoBorrowMutability::Mut {
0531ce1d
XL
285 // Allow two-phase borrows for binops in initial deployment
286 // since they desugar to methods
83c7162d 287 allow_two_phase_borrow: AllowTwoPhase::Yes,
dfeec247 288 },
2c00a5a8 289 };
7cac9316 290 let autoref = Adjustment {
5099ac24 291 kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
dfeec247 292 target: method.sig.inputs()[1],
7cac9316
XL
293 };
294 // HACK(eddyb) Bypass checks due to reborrows being in
295 // some cases applied on the RHS, on top of which we need
296 // to autoref, which is not allowed by apply_adjustments.
297 // self.apply_adjustments(rhs_expr, vec![autoref]);
3dfed10e 298 self.typeck_results
3b2f2976
XL
299 .borrow_mut()
300 .adjustments_mut()
301 .entry(rhs_expr.hir_id)
b7449926 302 .or_default()
3b2f2976 303 .push(autoref);
7cac9316
XL
304 }
305 }
3b2f2976 306 self.write_method_call(expr.hir_id, method);
7cac9316
XL
307
308 method.sig.output()
309 }
f035d41b 310 // error types are considered "builtin"
c295e0f8
XL
311 Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
312 Err(errors) => {
f2b60f7d
FG
313 let (_, trait_def_id) =
314 lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
315 let missing_trait = trait_def_id
316 .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
317 let (mut err, output_def_id) = match is_assign {
f035d41b
XL
318 IsAssign::Yes => {
319 let mut err = struct_span_err!(
320 self.tcx.sess,
321 expr.span,
322 E0368,
323 "binary assignment operation `{}=` cannot be applied to type `{}`",
324 op.node.as_str(),
325 lhs_ty,
326 );
327 err.span_label(
328 lhs_expr.span,
329 format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
330 );
c295e0f8 331 self.note_unmet_impls_on_type(&mut err, errors);
f2b60f7d 332 (err, None)
f035d41b
XL
333 }
334 IsAssign::No => {
f2b60f7d
FG
335 let message = match op.node {
336 hir::BinOpKind::Add => {
337 format!("cannot add `{rhs_ty}` to `{lhs_ty}`")
338 }
339 hir::BinOpKind::Sub => {
340 format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`")
341 }
342 hir::BinOpKind::Mul => {
343 format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`")
344 }
345 hir::BinOpKind::Div => {
346 format!("cannot divide `{lhs_ty}` by `{rhs_ty}`")
347 }
348 hir::BinOpKind::Rem => {
349 format!("cannot mod `{lhs_ty}` by `{rhs_ty}`")
350 }
351 hir::BinOpKind::BitAnd => {
352 format!("no implementation for `{lhs_ty} & {rhs_ty}`")
353 }
354 hir::BinOpKind::BitXor => {
355 format!("no implementation for `{lhs_ty} ^ {rhs_ty}`")
356 }
357 hir::BinOpKind::BitOr => {
358 format!("no implementation for `{lhs_ty} | {rhs_ty}`")
359 }
360 hir::BinOpKind::Shl => {
361 format!("no implementation for `{lhs_ty} << {rhs_ty}`")
362 }
363 hir::BinOpKind::Shr => {
364 format!("no implementation for `{lhs_ty} >> {rhs_ty}`")
365 }
366 _ => format!(
367 "binary operation `{}` cannot be applied to type `{}`",
368 op.node.as_str(),
369 lhs_ty
f035d41b
XL
370 ),
371 };
f2b60f7d
FG
372 let output_def_id = trait_def_id.and_then(|def_id| {
373 self.tcx
374 .associated_item_def_ids(def_id)
375 .iter()
376 .find(|item_def_id| {
377 self.tcx.associated_item(*item_def_id).name == sym::Output
378 })
379 .cloned()
380 });
064997fb 381 let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
f035d41b 382 if !lhs_expr.span.eq(&rhs_expr.span) {
f2b60f7d
FG
383 err.span_label(lhs_expr.span, lhs_ty.to_string());
384 err.span_label(rhs_expr.span, rhs_ty.to_string());
32a655c1 385 }
c295e0f8 386 self.note_unmet_impls_on_type(&mut err, errors);
f2b60f7d 387 (err, output_def_id)
f035d41b
XL
388 }
389 };
923072b8
FG
390
391 let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| {
392 if self
393 .lookup_op_method(
394 lhs_deref_ty,
395 Some(rhs_ty),
396 Some(rhs_expr),
397 Op::Binary(op, is_assign),
064997fb 398 expected,
923072b8
FG
399 )
400 .is_ok()
3c0e092e 401 {
f2b60f7d
FG
402 let msg = &format!(
403 "`{}{}` can be used on `{}` if you dereference the left-hand side",
404 op.node.as_str(),
405 match is_assign {
406 IsAssign::Yes => "=",
407 IsAssign::No => "",
408 },
409 lhs_deref_ty,
410 );
411 err.span_suggestion_verbose(
412 lhs_expr.span.shrink_to_lo(),
413 msg,
414 "*",
415 rustc_errors::Applicability::MachineApplicable,
416 );
f035d41b 417 }
923072b8
FG
418 };
419
f2b60f7d
FG
420 let is_compatible = |lhs_ty, rhs_ty| {
421 self.lookup_op_method(
422 lhs_ty,
423 Some(rhs_ty),
424 Some(rhs_expr),
425 Op::Binary(op, is_assign),
426 expected,
427 )
428 .is_ok()
429 };
430
923072b8
FG
431 // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
432 // `a += b` => `*a += b` if a is a mut ref.
f2b60f7d
FG
433 if !op.span.can_be_used_for_suggestions() {
434 // Suppress suggestions when lhs and rhs are not in the same span as the error
435 } else if is_assign == IsAssign::Yes
436 && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
437 {
438 suggest_deref_binop(lhs_deref_ty);
923072b8 439 } else if is_assign == IsAssign::No
f2b60f7d
FG
440 && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
441 {
442 if self.type_is_copy_modulo_regions(
443 self.param_env,
444 *lhs_deref_ty,
445 lhs_expr.span,
446 ) {
923072b8
FG
447 suggest_deref_binop(*lhs_deref_ty);
448 }
f2b60f7d
FG
449 } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
450 is_compatible(lhs_ty, rhs_ty)
451 }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
452 is_compatible(lhs_ty, rhs_ty)
453 }) || self.suggest_two_fn_call(
454 &mut err,
455 rhs_expr,
456 rhs_ty,
457 lhs_expr,
458 lhs_ty,
459 |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
460 ) {
461 // Cool
f035d41b 462 }
f035d41b 463
f2b60f7d 464 if let Some(missing_trait) = missing_trait {
f035d41b
XL
465 if op.node == hir::BinOpKind::Add
466 && self.check_str_addition(
467 lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
468 )
469 {
470 // This has nothing here because it means we did string
471 // concatenation (e.g., "Hello " + "World!"). This means
472 // we don't want the note in the else clause to be emitted
f2b60f7d 473 } else if lhs_ty.has_param_types_or_consts() {
04454e1e
FG
474 // Look for a TraitPredicate in the Fulfillment errors,
475 // and use it to generate a suggestion.
476 //
477 // Note that lookup_op_method must be called again but
478 // with a specific rhs_ty instead of a placeholder so
479 // the resulting predicate generates a more specific
480 // suggestion for the user.
481 let errors = self
482 .lookup_op_method(
483 lhs_ty,
484 Some(rhs_ty),
485 Some(rhs_expr),
486 Op::Binary(op, is_assign),
064997fb 487 expected,
04454e1e
FG
488 )
489 .unwrap_err();
064997fb
FG
490 if !errors.is_empty() {
491 for error in errors {
492 if let Some(trait_pred) =
493 error.obligation.predicate.to_opt_poly_trait_pred()
494 {
f2b60f7d
FG
495 let output_associated_item = match error.obligation.cause.code()
496 {
064997fb 497 ObligationCauseCode::BinOp {
f2b60f7d 498 output_ty: Some(output_ty),
064997fb 499 ..
f2b60f7d
FG
500 } => {
501 // Make sure that we're attaching `Output = ..` to the right trait predicate
502 if let Some(output_def_id) = output_def_id
503 && let Some(trait_def_id) = trait_def_id
504 && self.tcx.parent(output_def_id) == trait_def_id
505 {
506 Some(("Output", *output_ty))
507 } else {
508 None
509 }
064997fb
FG
510 }
511 _ => None,
512 };
513
514 self.suggest_restricting_param_bound(
515 &mut err,
516 trait_pred,
f2b60f7d 517 output_associated_item,
064997fb
FG
518 self.body_id,
519 );
520 }
532ac7d7 521 }
f2b60f7d 522 } else {
04454e1e
FG
523 // When we know that a missing bound is responsible, we don't show
524 // this note as it is redundant.
525 err.note(&format!(
526 "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
527 ));
a7813a04 528 }
b039eaaf
SL
529 }
530 }
f035d41b
XL
531 err.emit();
532 self.tcx.ty_error()
c34b1796 533 }
a7813a04 534 };
c34b1796 535
abe05a73 536 (lhs_ty, rhs_ty, return_ty)
a7813a04 537 }
c34b1796 538
48663c56
XL
539 /// Provide actionable suggestions when trying to add two strings with incorrect types,
540 /// like `&str + &str`, `String + String` and `&str + &String`.
541 ///
542 /// If this function returns `true` it means a note was printed, so we don't need
543 /// to print the normal "implementation of `std::ops::Add` might be missing" note
8faf50e0
XL
544 fn check_str_addition(
545 &self,
dfeec247
XL
546 lhs_expr: &'tcx hir::Expr<'tcx>,
547 rhs_expr: &'tcx hir::Expr<'tcx>,
8faf50e0
XL
548 lhs_ty: Ty<'tcx>,
549 rhs_ty: Ty<'tcx>,
5e7ed085 550 err: &mut Diagnostic,
f035d41b 551 is_assign: IsAssign,
532ac7d7 552 op: hir::BinOp,
8faf50e0 553 ) -> bool {
5099ac24
FG
554 let str_concat_note = "string concatenation requires an owned `String` on the left";
555 let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
556 let to_owned_msg = "create an owned `String` from a string reference";
48663c56 557
064997fb
FG
558 let is_std_string = |ty: Ty<'tcx>| {
559 ty.ty_adt_def()
560 .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did()))
3dfed10e 561 };
48663c56 562
1b1a35ee 563 match (lhs_ty.kind(), rhs_ty.kind()) {
48663c56 564 (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
064997fb
FG
565 if (*l_ty.kind() == Str || is_std_string(l_ty))
566 && (*r_ty.kind() == Str
567 || is_std_string(r_ty)
568 || matches!(
569 r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str
570 )) =>
48663c56 571 {
f035d41b 572 if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
5099ac24
FG
573 err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
574 err.note(str_concat_note);
575 if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
576 err.span_suggestion_verbose(
577 lhs_expr.span.until(lhs_inner_expr.span),
578 rm_borrow_msg,
923072b8 579 "",
5099ac24
FG
580 Applicability::MachineApplicable
581 );
582 } else {
583 err.span_suggestion_verbose(
584 lhs_expr.span.shrink_to_hi(),
585 to_owned_msg,
923072b8 586 ".to_owned()",
5099ac24
FG
587 Applicability::MachineApplicable
588 );
589 }
8faf50e0 590 }
0531ce1d 591 true
8bb4bdeb 592 }
48663c56 593 (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
1b1a35ee 594 if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
48663c56
XL
595 {
596 err.span_label(
597 op.span,
598 "`+` cannot be used to concatenate a `&str` with a `String`",
599 );
5099ac24
FG
600 match is_assign {
601 IsAssign::No => {
602 let sugg_msg;
603 let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
604 sugg_msg = "remove the borrow on the left and add one on the right";
605 (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned())
48663c56 606 } else {
5099ac24
FG
607 sugg_msg = "create an owned `String` on the left and add a borrow on the right";
608 (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned())
48663c56 609 };
5099ac24
FG
610 let suggestions = vec![
611 lhs_sugg,
612 (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
613 ];
614 err.multipart_suggestion_verbose(
615 sugg_msg,
616 suggestions,
0bf4aa26
XL
617 Applicability::MachineApplicable,
618 );
8faf50e0 619 }
5099ac24
FG
620 IsAssign::Yes => {
621 err.note(str_concat_note);
0531ce1d 622 }
5099ac24 623 }
0531ce1d
XL
624 true
625 }
626 _ => false,
8bb4bdeb 627 }
8bb4bdeb
XL
628 }
629
dc9dc135
XL
630 pub fn check_user_unop(
631 &self,
dfeec247 632 ex: &'tcx hir::Expr<'tcx>,
dc9dc135
XL
633 operand_ty: Ty<'tcx>,
634 op: hir::UnOp,
064997fb 635 expected: Expectation<'tcx>,
dc9dc135 636 ) -> Ty<'tcx> {
a7813a04 637 assert!(op.is_by_value());
064997fb 638 match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) {
7cac9316 639 Ok(method) => {
3b2f2976 640 self.write_method_call(ex.hir_id, method);
7cac9316
XL
641 method.sig.output()
642 }
c295e0f8 643 Err(errors) => {
fc512014 644 let actual = self.resolve_vars_if_possible(operand_ty);
7cac9316 645 if !actual.references_error() {
dfeec247
XL
646 let mut err = struct_span_err!(
647 self.tcx.sess,
648 ex.span,
649 E0600,
650 "cannot apply unary operator `{}` to type `{}`",
651 op.as_str(),
652 actual
653 );
654 err.span_label(
655 ex.span,
f035d41b 656 format!("cannot apply unary operator `{}`", op.as_str()),
dfeec247 657 );
04454e1e 658
f2b60f7d
FG
659 if operand_ty.has_param_types_or_consts() {
660 let predicates = errors.iter().filter_map(|error| {
661 error.obligation.predicate.to_opt_poly_trait_pred()
662 });
04454e1e 663 for pred in predicates {
064997fb 664 self.suggest_restricting_param_bound(
04454e1e
FG
665 &mut err,
666 pred,
064997fb 667 None,
04454e1e
FG
668 self.body_id,
669 );
670 }
5e7ed085 671 }
c295e0f8
XL
672
673 let sp = self.tcx.sess.source_map().start_point(ex.span);
674 if let Some(sp) =
675 self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
676 {
677 // If the previous expression was a block expression, suggest parentheses
678 // (turning this into a binary subtraction operation instead.)
679 // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs)
680 self.tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp);
681 } else {
682 match actual.kind() {
683 Uint(_) if op == hir::UnOp::Neg => {
684 err.note("unsigned values cannot be negated");
685
686 if let hir::ExprKind::Unary(
687 _,
688 hir::Expr {
689 kind:
690 hir::ExprKind::Lit(Spanned {
691 node: ast::LitKind::Int(1, _),
692 ..
693 }),
694 ..
695 },
696 ) = ex.kind
697 {
698 err.span_suggestion(
699 ex.span,
700 &format!(
5e7ed085 701 "you may have meant the maximum value of `{actual}`",
c295e0f8 702 ),
5e7ed085 703 format!("{actual}::MAX"),
c295e0f8
XL
704 Applicability::MaybeIncorrect,
705 );
706 }
707 }
708 Str | Never | Char | Tuple(_) | Array(_, _) => {}
709 Ref(_, lty, _) if *lty.kind() == Str => {}
710 _ => {
711 self.note_unmet_impls_on_type(&mut err, errors);
29967ef6 712 }
94b46f34
XL
713 }
714 }
715 err.emit();
7cac9316 716 }
f035d41b 717 self.tcx.ty_error()
a7813a04 718 }
c34b1796
AL
719 }
720 }
c34b1796 721
dfeec247
XL
722 fn lookup_op_method(
723 &self,
724 lhs_ty: Ty<'tcx>,
5e7ed085
FG
725 other_ty: Option<Ty<'tcx>>,
726 other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
dfeec247 727 op: Op,
064997fb 728 expected: Expectation<'tcx>,
c295e0f8 729 ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
7cac9316
XL
730 let span = match op {
731 Op::Binary(op, _) => op.span,
dfeec247 732 Op::Unary(_, span) => span,
7cac9316 733 };
f2b60f7d 734 let (opname, trait_did) = lang_item_for_op(self.tcx, op, span);
c34b1796 735
dfeec247
XL
736 debug!(
737 "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
738 lhs_ty, op, opname, trait_did
739 );
7cac9316 740
17df50a5
XL
741 // Catches cases like #83893, where a lang item is declared with the
742 // wrong number of generic arguments. Should have yielded an error
743 // elsewhere by now, but we have to catch it here so that we do not
744 // index `other_tys` out of bounds (if the lang item has too many
745 // generic arguments, `other_tys` is too short).
746 if !has_expected_num_generic_args(
747 self.tcx,
748 trait_did,
749 match op {
750 // Binary ops have a generic right-hand side, unary ops don't
751 Op::Binary(..) => 1,
752 Op::Unary(..) => 0,
753 },
754 ) {
c295e0f8 755 return Err(vec![]);
17df50a5
XL
756 }
757
c295e0f8 758 let opname = Ident::with_dummy_span(opname);
7cac9316 759 let method = trait_did.and_then(|trait_did| {
064997fb
FG
760 self.lookup_op_method_in_trait(
761 span,
762 opname,
763 trait_did,
764 lhs_ty,
765 other_ty,
766 other_ty_expr,
767 expected,
768 )
7cac9316 769 });
c34b1796 770
c295e0f8
XL
771 match (method, trait_did) {
772 (Some(ok), _) => {
cc61c64b 773 let method = self.register_infer_ok_obligations(ok);
e1599b0c 774 self.select_obligations_where_possible(false, |_| {});
7cac9316 775 Ok(method)
a7813a04 776 }
c295e0f8
XL
777 (None, None) => Err(vec![]),
778 (None, Some(trait_did)) => {
064997fb
FG
779 let (obligation, _) = self.obligation_for_op_method(
780 span,
781 trait_did,
782 lhs_ty,
783 other_ty,
784 other_ty_expr,
785 expected,
786 );
c295e0f8
XL
787 let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
788 fulfill.register_predicate_obligation(self, obligation);
3c0e092e 789 Err(fulfill.select_where_possible(&self.infcx))
c295e0f8 790 }
c34b1796
AL
791 }
792 }
793}
794
f2b60f7d
FG
795fn lang_item_for_op(
796 tcx: TyCtxt<'_>,
797 op: Op,
798 span: Span,
799) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
800 let lang = tcx.lang_items();
801 if let Op::Binary(op, IsAssign::Yes) = op {
802 match op.node {
803 hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
804 hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
805 hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
806 hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
807 hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
808 hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
809 hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
810 hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
811 hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
812 hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
813 hir::BinOpKind::Lt
814 | hir::BinOpKind::Le
815 | hir::BinOpKind::Ge
816 | hir::BinOpKind::Gt
817 | hir::BinOpKind::Eq
818 | hir::BinOpKind::Ne
819 | hir::BinOpKind::And
820 | hir::BinOpKind::Or => {
821 span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
822 }
823 }
824 } else if let Op::Binary(op, IsAssign::No) = op {
825 match op.node {
826 hir::BinOpKind::Add => (sym::add, lang.add_trait()),
827 hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
828 hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
829 hir::BinOpKind::Div => (sym::div, lang.div_trait()),
830 hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
831 hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
832 hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
833 hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
834 hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
835 hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
836 hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
837 hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
838 hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
839 hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
840 hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
841 hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
842 hir::BinOpKind::And | hir::BinOpKind::Or => {
843 span_bug!(span, "&& and || are not overloadable")
844 }
845 }
846 } else if let Op::Unary(hir::UnOp::Not, _) = op {
847 (sym::not, lang.not_trait())
848 } else if let Op::Unary(hir::UnOp::Neg, _) = op {
849 (sym::neg, lang.neg_trait())
850 } else {
851 bug!("lookup_op_method: op not supported: {:?}", op)
852 }
853}
854
c34b1796 855// Binary operator categories. These categories summarize the behavior
5e7ed085 856// with respect to the builtin operations supported.
c34b1796
AL
857enum BinOpCategory {
858 /// &&, || -- cannot be overridden
859 Shortcircuit,
860
861 /// <<, >> -- when shifting a single integer, rhs can be any
862 /// integer type. For simd, types must match.
863 Shift,
864
865 /// +, -, etc -- takes equal types, produces same type as input,
866 /// applicable to ints/floats/simd
867 Math,
868
869 /// &, |, ^ -- takes equal types, produces same type as input,
870 /// applicable to ints/floats/simd/bool
871 Bitwise,
872
873 /// ==, !=, etc -- takes equal types, produces bools, except for simd,
874 /// which produce the input type
875 Comparison,
876}
877
878impl BinOpCategory {
e9174d1e 879 fn from(op: hir::BinOp) -> BinOpCategory {
c34b1796 880 match op.node {
dfeec247
XL
881 hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
882
883 hir::BinOpKind::Add
884 | hir::BinOpKind::Sub
885 | hir::BinOpKind::Mul
886 | hir::BinOpKind::Div
887 | hir::BinOpKind::Rem => BinOpCategory::Math,
888
889 hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
890 BinOpCategory::Bitwise
891 }
892
893 hir::BinOpKind::Eq
894 | hir::BinOpKind::Ne
895 | hir::BinOpKind::Lt
896 | hir::BinOpKind::Le
897 | hir::BinOpKind::Ge
898 | hir::BinOpKind::Gt => BinOpCategory::Comparison,
899
900 hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
c34b1796
AL
901 }
902 }
903}
904
b039eaaf 905/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
7cac9316 906#[derive(Clone, Copy, Debug, PartialEq)]
b039eaaf
SL
907enum IsAssign {
908 No,
909 Yes,
910}
911
7cac9316
XL
912#[derive(Clone, Copy, Debug)]
913enum Op {
914 Binary(hir::BinOp, IsAssign),
915 Unary(hir::UnOp, Span),
916}
917
74b04a01 918/// Dereferences a single level of immutable referencing.
a2a8927a 919fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
1b1a35ee 920 match ty.kind() {
5099ac24 921 ty::Ref(_, ty, hir::Mutability::Not) => *ty,
74b04a01
XL
922 _ => ty,
923 }
924}
925
9fa01778 926/// Returns `true` if this is a built-in arithmetic operation (e.g., u32
c34b1796
AL
927/// + u32, i16x4 == i16x4) and false if these types would have to be
928/// overloaded to be legal. There are two reasons that we distinguish
929/// builtin operations from overloaded ones (vs trying to drive
930/// everything uniformly through the trait system and intrinsics or
931/// something like that):
932///
933/// 1. Builtin operations can trivially be evaluated in constants.
934/// 2. For comparison operators applied to SIMD types the result is
9fa01778 935/// not of type `bool`. For example, `i16x4 == i16x4` yields a
c34b1796
AL
936/// type like `i16x4`. This means that the overloaded trait
937/// `PartialEq` is not applicable.
938///
939/// Reason #2 is the killer. I tried for a while to always use
94b46f34 940/// overloaded logic and just check the types in constants/codegen after
c34b1796 941/// the fact, and it worked fine, except for SIMD types. -nmatsakis
74b04a01
XL
942fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
943 // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
944 // (See https://github.com/rust-lang/rust/issues/57447.)
945 let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
946
c34b1796 947 match BinOpCategory::from(op) {
dfeec247 948 BinOpCategory::Shortcircuit => true,
c34b1796
AL
949
950 BinOpCategory::Shift => {
dfeec247
XL
951 lhs.references_error()
952 || rhs.references_error()
953 || lhs.is_integral() && rhs.is_integral()
c34b1796
AL
954 }
955
956 BinOpCategory::Math => {
dfeec247
XL
957 lhs.references_error()
958 || rhs.references_error()
959 || lhs.is_integral() && rhs.is_integral()
960 || lhs.is_floating_point() && rhs.is_floating_point()
c34b1796
AL
961 }
962
963 BinOpCategory::Bitwise => {
dfeec247
XL
964 lhs.references_error()
965 || rhs.references_error()
966 || lhs.is_integral() && rhs.is_integral()
967 || lhs.is_floating_point() && rhs.is_floating_point()
968 || lhs.is_bool() && rhs.is_bool()
c34b1796
AL
969 }
970
971 BinOpCategory::Comparison => {
dfeec247 972 lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
c34b1796
AL
973 }
974 }
975}
74b04a01 976
f035d41b
XL
977struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
978
a2a8927a 979impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
f035d41b
XL
980 fn tcx(&self) -> TyCtxt<'tcx> {
981 self.0.tcx
982 }
983
984 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
1b1a35ee 985 match ty.kind() {
f035d41b
XL
986 ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin {
987 kind: TypeVariableOriginKind::MiscVariable,
988 span: self.1,
989 }),
990 _ => ty.super_fold_with(self),
991 }
992 }
993}