diff options
Diffstat (limited to 'compiler/rustc_hir_typeck/src/op.rs')
-rw-r--r-- | compiler/rustc_hir_typeck/src/op.rs | 62 |
1 files changed, 41 insertions, 21 deletions
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index a52c94cb0..b8bf2b691 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -390,7 +390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .is_ok() { - let msg = &format!( + let msg = format!( "`{}{}` can be used on `{}` if you dereference the left-hand side", op.node.as_str(), match is_assign { @@ -408,7 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - let is_compatible = |lhs_ty, rhs_ty| { + let is_compatible_after_call = |lhs_ty, rhs_ty| { self.lookup_op_method( lhs_ty, Some((rhs_expr, rhs_ty)), @@ -416,6 +416,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, ) .is_ok() + // Suggest calling even if, after calling, the types don't + // implement the operator, since it'll lead to better + // diagnostics later. + || self.can_eq(self.param_env, lhs_ty, rhs_ty) }; // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest @@ -436,16 +440,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggest_deref_binop(*lhs_deref_ty); } } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { - is_compatible(lhs_ty, rhs_ty) + is_compatible_after_call(lhs_ty, rhs_ty) }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| { - is_compatible(lhs_ty, rhs_ty) + is_compatible_after_call(lhs_ty, rhs_ty) }) || self.suggest_two_fn_call( &mut err, rhs_expr, rhs_ty, lhs_expr, lhs_ty, - |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty), + |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty), ) { // Cool } @@ -511,7 +515,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // When we know that a missing bound is responsible, we don't show // this note as it is redundant. - err.note(&format!( + err.note(format!( "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" )); } @@ -545,9 +549,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let to_owned_msg = "create an owned `String` from a string reference"; let string_type = self.tcx.lang_items().string(); - let is_std_string = |ty: Ty<'tcx>| { - ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type) - }; + let is_std_string = + |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|ty_def| Some(ty_def.did()) == string_type); match (lhs_ty.kind(), rhs_ty.kind()) { (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str @@ -686,7 +689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.span_suggestion( ex.span, - &format!( + format!( "you may have meant the maximum value of `{actual}`", ), format!("{actual}::MAX"), @@ -719,7 +722,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Op::Binary(op, _) => op.span, Op::Unary(_, span) => span, }; - let (opname, trait_did) = lang_item_for_op(self.tcx, op, span); + let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else { + // Bail if the operator trait is not defined. + return Err(vec![]); + }; debug!( "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", @@ -753,24 +759,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, traits::BinOp { rhs_span: opt_rhs_expr.map(|expr| expr.span), - is_lit: opt_rhs_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + is_lit: opt_rhs_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), output_ty: expected.only_has_type(self), }, ); - let method = trait_did.and_then(|trait_did| { - self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, Some(input_types)) - }); - - match (method, trait_did) { - (Some(ok), _) => { + let method = self.lookup_method_in_trait( + cause.clone(), + opname, + trait_did, + lhs_ty, + Some(input_types), + ); + match method { + Some(ok) => { let method = self.register_infer_ok_obligations(ok); self.select_obligations_where_possible(|_| {}); Ok(method) } - (None, None) => Err(vec![]), - (None, Some(trait_did)) => { + None => { + // This path may do some inference, so make sure we've really + // doomed compilation so as to not accidentally stabilize new + // inference or something here... + self.tcx.sess.delay_span_bug(span, "this path really should be doomed..."); + // Guide inference for the RHS expression if it's provided -- + // this will allow us to better error reporting, at the expense + // of making some error messages a bit more specific. + if let Some((rhs_expr, rhs_ty)) = opt_rhs + && rhs_ty.is_ty_var() + { + self.check_expr_coercible_to_type(rhs_expr, rhs_ty, None); + } + let (obligation, _) = self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types)); // FIXME: This should potentially just add the obligation to the `FnCtxt` |