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