Rollup merge of #101424 - compiler-errors:operator-err-sugg, r=TaKO8Ki
Adjust and slightly generalize operator error suggestion (in no particular order) * Stop passing around a whole extra `ProjectionPredicate` * Add spaces around `=` in `Trait<..., Output = Ty>` suggestion * Some code clean-ups, including * add `lang_item_for_op` to turn a `Op` into a `DefId` * avoid `SourceMap` because we don't really need to render an expr * Remove `TypeParamVisitor` in favor of just checking `ty.has_param_types_or_consts` -- this acts a bit differently, but shouldn't cause erroneous suggestions (actually might generalize them a bit) * We now suggest `Output = Ty` in the `where` clause suggestion when we fail to add `Struct<T>` and `T`. I can split this out into more PRs if needed, but they're all just miscellaneous generalizations, changes, and nitpicks I saw when messing with this operator code.
This commit is contained in:
commit
b5ffbd32d4
16 changed files with 234 additions and 254 deletions
|
@ -12,7 +12,7 @@ pub mod util;
|
||||||
use crate::infer::canonical::Canonical;
|
use crate::infer::canonical::Canonical;
|
||||||
use crate::ty::abstract_const::NotConstEvaluatable;
|
use crate::ty::abstract_const::NotConstEvaluatable;
|
||||||
use crate::ty::subst::SubstsRef;
|
use crate::ty::subst::SubstsRef;
|
||||||
use crate::ty::{self, AdtKind, Predicate, Ty, TyCtxt};
|
use crate::ty::{self, AdtKind, Ty, TyCtxt};
|
||||||
|
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::{Applicability, Diagnostic};
|
use rustc_errors::{Applicability, Diagnostic};
|
||||||
|
@ -416,7 +416,7 @@ pub enum ObligationCauseCode<'tcx> {
|
||||||
BinOp {
|
BinOp {
|
||||||
rhs_span: Option<Span>,
|
rhs_span: Option<Span>,
|
||||||
is_lit: bool,
|
is_lit: bool,
|
||||||
output_pred: Option<Predicate<'tcx>>,
|
output_ty: Option<Ty<'tcx>>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,13 +102,25 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
|
||||||
generics: &hir::Generics<'_>,
|
generics: &hir::Generics<'_>,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
trait_pred: PolyTraitPredicate<'tcx>,
|
trait_pred: PolyTraitPredicate<'tcx>,
|
||||||
|
associated_ty: Option<(&'static str, Ty<'tcx>)>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if !trait_pred.is_suggestable(tcx, false) {
|
if !trait_pred.is_suggestable(tcx, false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let param_name = trait_pred.skip_binder().self_ty().to_string();
|
let param_name = trait_pred.skip_binder().self_ty().to_string();
|
||||||
let constraint = trait_pred.print_modifiers_and_trait_path().to_string();
|
let mut constraint = trait_pred.print_modifiers_and_trait_path().to_string();
|
||||||
|
|
||||||
|
if let Some((name, term)) = associated_ty {
|
||||||
|
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
|
||||||
|
// That should be extracted into a helper function.
|
||||||
|
if constraint.ends_with('>') {
|
||||||
|
constraint = format!("{}, {} = {}>", &constraint[..constraint.len() - 1], name, term);
|
||||||
|
} else {
|
||||||
|
constraint.push_str(&format!("<{} = {}>", name, term));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
|
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
|
||||||
|
|
||||||
// Skip, there is a param named Self
|
// Skip, there is a param named Self
|
||||||
|
|
|
@ -25,8 +25,7 @@ use rustc_middle::hir::map;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
|
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
|
||||||
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable,
|
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable,
|
||||||
ProjectionPredicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
|
||||||
TypeVisitable,
|
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{TypeAndMut, TypeckResults};
|
use rustc_middle::ty::{TypeAndMut, TypeckResults};
|
||||||
use rustc_session::Limit;
|
use rustc_session::Limit;
|
||||||
|
@ -174,7 +173,7 @@ pub trait InferCtxtExt<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||||
proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>,
|
associated_item: Option<(&'static str, Ty<'tcx>)>,
|
||||||
body_id: hir::HirId,
|
body_id: hir::HirId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -467,7 +466,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
&self,
|
&self,
|
||||||
mut err: &mut Diagnostic,
|
mut err: &mut Diagnostic,
|
||||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||||
proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>,
|
associated_ty: Option<(&'static str, Ty<'tcx>)>,
|
||||||
body_id: hir::HirId,
|
body_id: hir::HirId,
|
||||||
) {
|
) {
|
||||||
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
|
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
|
||||||
|
@ -604,21 +603,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
trait_pred.print_modifiers_and_trait_path().to_string()
|
trait_pred.print_modifiers_and_trait_path().to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(proj_pred) = proj_pred {
|
if let Some((name, term)) = associated_ty {
|
||||||
let ProjectionPredicate { projection_ty, term } = proj_pred.skip_binder();
|
|
||||||
let item = self.tcx.associated_item(projection_ty.item_def_id);
|
|
||||||
|
|
||||||
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
|
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
|
||||||
// That should be extracted into a helper function.
|
// That should be extracted into a helper function.
|
||||||
if constraint.ends_with('>') {
|
if constraint.ends_with('>') {
|
||||||
constraint = format!(
|
constraint = format!(
|
||||||
"{}, {}={}>",
|
"{}, {} = {}>",
|
||||||
&constraint[..constraint.len() - 1],
|
&constraint[..constraint.len() - 1],
|
||||||
item.name,
|
name,
|
||||||
term
|
term
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
constraint.push_str(&format!("<{}={}>", item.name, term));
|
constraint.push_str(&format!("<{} = {}>", name, term));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,7 +644,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
..
|
..
|
||||||
}) if !param_ty => {
|
}) if !param_ty => {
|
||||||
// Missing generic type parameter bound.
|
// Missing generic type parameter bound.
|
||||||
if suggest_arbitrary_trait_bound(self.tcx, generics, &mut err, trait_pred) {
|
if suggest_arbitrary_trait_bound(
|
||||||
|
self.tcx,
|
||||||
|
generics,
|
||||||
|
&mut err,
|
||||||
|
trait_pred,
|
||||||
|
associated_ty,
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -409,7 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
rhs_span: opt_input_expr.map(|expr| expr.span),
|
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||||
is_lit: opt_input_expr
|
is_lit: opt_input_expr
|
||||||
.map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
|
.map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
|
||||||
output_pred: None,
|
output_ty: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
self.param_env,
|
self.param_env,
|
||||||
|
|
|
@ -20,10 +20,7 @@ use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::infer::{self, InferOk};
|
use rustc_infer::infer::{self, InferOk};
|
||||||
use rustc_middle::ty::subst::Subst;
|
use rustc_middle::ty::subst::Subst;
|
||||||
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable};
|
||||||
self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy,
|
|
||||||
ToPredicate, Ty, TypeVisitable,
|
|
||||||
};
|
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
|
@ -337,22 +334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
// Construct an obligation
|
// Construct an obligation
|
||||||
let poly_trait_ref = ty::Binder::dummy(trait_ref);
|
let poly_trait_ref = ty::Binder::dummy(trait_ref);
|
||||||
let opt_output_ty =
|
let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
|
||||||
expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
|
|
||||||
let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind(
|
|
||||||
self.tcx,
|
|
||||||
Ident::from_str("Output"),
|
|
||||||
AssocKind::Type,
|
|
||||||
trait_def_id,
|
|
||||||
);
|
|
||||||
let output_pred =
|
|
||||||
opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| {
|
|
||||||
ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate {
|
|
||||||
projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id },
|
|
||||||
term: output_ty.into(),
|
|
||||||
}))
|
|
||||||
.to_predicate(self.tcx)
|
|
||||||
});
|
|
||||||
|
|
||||||
(
|
(
|
||||||
traits::Obligation::new(
|
traits::Obligation::new(
|
||||||
|
@ -363,7 +345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
rhs_span: opt_input_expr.map(|expr| expr.span),
|
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||||
is_lit: opt_input_expr
|
is_lit: opt_input_expr
|
||||||
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
||||||
output_pred,
|
output_ty,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
self.param_env,
|
self.param_env,
|
||||||
|
@ -518,7 +500,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
rhs_span: opt_input_expr.map(|expr| expr.span),
|
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||||
is_lit: opt_input_expr
|
is_lit: opt_input_expr
|
||||||
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
||||||
output_pred: None,
|
output_ty: None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,9 +11,8 @@ use rustc_infer::traits::ObligationCauseCode;
|
||||||
use rustc_middle::ty::adjustment::{
|
use rustc_middle::ty::adjustment::{
|
||||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
|
||||||
};
|
|
||||||
use rustc_span::source_map::Spanned;
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::symbol::{sym, Ident};
|
use rustc_span::symbol::{sym, Ident};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -22,8 +21,6 @@ use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as
|
||||||
use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
|
use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
|
||||||
use rustc_type_ir::sty::TyKind::*;
|
use rustc_type_ir::sty::TyKind::*;
|
||||||
|
|
||||||
use std::ops::ControlFlow;
|
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
/// Checks a `a <op>= b`
|
/// Checks a `a <op>= b`
|
||||||
pub fn check_binop_assign(
|
pub fn check_binop_assign(
|
||||||
|
@ -313,8 +310,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// error types are considered "builtin"
|
// error types are considered "builtin"
|
||||||
Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
|
Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
let source_map = self.tcx.sess.source_map();
|
let (_, trait_def_id) =
|
||||||
let (mut err, missing_trait, use_output) = match is_assign {
|
lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
|
||||||
|
let missing_trait = trait_def_id
|
||||||
|
.map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
|
||||||
|
let (mut err, output_def_id) = match is_assign {
|
||||||
IsAssign::Yes => {
|
IsAssign::Yes => {
|
||||||
let mut err = struct_span_err!(
|
let mut err = struct_span_err!(
|
||||||
self.tcx.sess,
|
self.tcx.sess,
|
||||||
|
@ -328,112 +328,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
lhs_expr.span,
|
lhs_expr.span,
|
||||||
format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
|
format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
|
||||||
);
|
);
|
||||||
let missing_trait = match op.node {
|
|
||||||
hir::BinOpKind::Add => Some("std::ops::AddAssign"),
|
|
||||||
hir::BinOpKind::Sub => Some("std::ops::SubAssign"),
|
|
||||||
hir::BinOpKind::Mul => Some("std::ops::MulAssign"),
|
|
||||||
hir::BinOpKind::Div => Some("std::ops::DivAssign"),
|
|
||||||
hir::BinOpKind::Rem => Some("std::ops::RemAssign"),
|
|
||||||
hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"),
|
|
||||||
hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"),
|
|
||||||
hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"),
|
|
||||||
hir::BinOpKind::Shl => Some("std::ops::ShlAssign"),
|
|
||||||
hir::BinOpKind::Shr => Some("std::ops::ShrAssign"),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
self.note_unmet_impls_on_type(&mut err, errors);
|
self.note_unmet_impls_on_type(&mut err, errors);
|
||||||
(err, missing_trait, false)
|
(err, None)
|
||||||
}
|
}
|
||||||
IsAssign::No => {
|
IsAssign::No => {
|
||||||
let (message, missing_trait, use_output) = match op.node {
|
let message = match op.node {
|
||||||
hir::BinOpKind::Add => (
|
hir::BinOpKind::Add => {
|
||||||
format!("cannot add `{rhs_ty}` to `{lhs_ty}`"),
|
format!("cannot add `{rhs_ty}` to `{lhs_ty}`")
|
||||||
Some("std::ops::Add"),
|
}
|
||||||
true,
|
hir::BinOpKind::Sub => {
|
||||||
),
|
format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`")
|
||||||
hir::BinOpKind::Sub => (
|
}
|
||||||
format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`"),
|
hir::BinOpKind::Mul => {
|
||||||
Some("std::ops::Sub"),
|
format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`")
|
||||||
true,
|
}
|
||||||
),
|
hir::BinOpKind::Div => {
|
||||||
hir::BinOpKind::Mul => (
|
format!("cannot divide `{lhs_ty}` by `{rhs_ty}`")
|
||||||
format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`"),
|
}
|
||||||
Some("std::ops::Mul"),
|
hir::BinOpKind::Rem => {
|
||||||
true,
|
format!("cannot mod `{lhs_ty}` by `{rhs_ty}`")
|
||||||
),
|
}
|
||||||
hir::BinOpKind::Div => (
|
hir::BinOpKind::BitAnd => {
|
||||||
format!("cannot divide `{lhs_ty}` by `{rhs_ty}`"),
|
format!("no implementation for `{lhs_ty} & {rhs_ty}`")
|
||||||
Some("std::ops::Div"),
|
}
|
||||||
true,
|
hir::BinOpKind::BitXor => {
|
||||||
),
|
format!("no implementation for `{lhs_ty} ^ {rhs_ty}`")
|
||||||
hir::BinOpKind::Rem => (
|
}
|
||||||
format!("cannot mod `{lhs_ty}` by `{rhs_ty}`"),
|
hir::BinOpKind::BitOr => {
|
||||||
Some("std::ops::Rem"),
|
format!("no implementation for `{lhs_ty} | {rhs_ty}`")
|
||||||
true,
|
}
|
||||||
),
|
hir::BinOpKind::Shl => {
|
||||||
hir::BinOpKind::BitAnd => (
|
format!("no implementation for `{lhs_ty} << {rhs_ty}`")
|
||||||
format!("no implementation for `{lhs_ty} & {rhs_ty}`"),
|
}
|
||||||
Some("std::ops::BitAnd"),
|
hir::BinOpKind::Shr => {
|
||||||
true,
|
format!("no implementation for `{lhs_ty} >> {rhs_ty}`")
|
||||||
),
|
}
|
||||||
hir::BinOpKind::BitXor => (
|
_ => format!(
|
||||||
format!("no implementation for `{lhs_ty} ^ {rhs_ty}`"),
|
|
||||||
Some("std::ops::BitXor"),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
hir::BinOpKind::BitOr => (
|
|
||||||
format!("no implementation for `{lhs_ty} | {rhs_ty}`"),
|
|
||||||
Some("std::ops::BitOr"),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
hir::BinOpKind::Shl => (
|
|
||||||
format!("no implementation for `{lhs_ty} << {rhs_ty}`"),
|
|
||||||
Some("std::ops::Shl"),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
hir::BinOpKind::Shr => (
|
|
||||||
format!("no implementation for `{lhs_ty} >> {rhs_ty}`"),
|
|
||||||
Some("std::ops::Shr"),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
|
|
||||||
format!(
|
|
||||||
"binary operation `{}` cannot be applied to type `{}`",
|
"binary operation `{}` cannot be applied to type `{}`",
|
||||||
op.node.as_str(),
|
op.node.as_str(),
|
||||||
lhs_ty
|
lhs_ty
|
||||||
),
|
),
|
||||||
Some("std::cmp::PartialEq"),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
hir::BinOpKind::Lt
|
|
||||||
| hir::BinOpKind::Le
|
|
||||||
| hir::BinOpKind::Gt
|
|
||||||
| hir::BinOpKind::Ge => (
|
|
||||||
format!(
|
|
||||||
"binary operation `{}` cannot be applied to type `{}`",
|
|
||||||
op.node.as_str(),
|
|
||||||
lhs_ty
|
|
||||||
),
|
|
||||||
Some("std::cmp::PartialOrd"),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
_ => (
|
|
||||||
format!(
|
|
||||||
"binary operation `{}` cannot be applied to type `{}`",
|
|
||||||
op.node.as_str(),
|
|
||||||
lhs_ty
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
let output_def_id = trait_def_id.and_then(|def_id| {
|
||||||
|
self.tcx
|
||||||
|
.associated_item_def_ids(def_id)
|
||||||
|
.iter()
|
||||||
|
.find(|item_def_id| {
|
||||||
|
self.tcx.associated_item(*item_def_id).name == sym::Output
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
});
|
||||||
let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
|
let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
|
||||||
if !lhs_expr.span.eq(&rhs_expr.span) {
|
if !lhs_expr.span.eq(&rhs_expr.span) {
|
||||||
err.span_label(lhs_expr.span, lhs_ty.to_string());
|
err.span_label(lhs_expr.span, lhs_ty.to_string());
|
||||||
err.span_label(rhs_expr.span, rhs_ty.to_string());
|
err.span_label(rhs_expr.span, rhs_ty.to_string());
|
||||||
}
|
}
|
||||||
self.note_unmet_impls_on_type(&mut err, errors);
|
self.note_unmet_impls_on_type(&mut err, errors);
|
||||||
(err, missing_trait, use_output)
|
(err, output_def_id)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -448,16 +399,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
|
|
||||||
let msg = &format!(
|
let msg = &format!(
|
||||||
"`{}{}` can be used on `{}`, you can dereference `{}`",
|
"`{}{}` can be used on `{}` if you dereference the left-hand side",
|
||||||
op.node.as_str(),
|
op.node.as_str(),
|
||||||
match is_assign {
|
match is_assign {
|
||||||
IsAssign::Yes => "=",
|
IsAssign::Yes => "=",
|
||||||
IsAssign::No => "",
|
IsAssign::No => "",
|
||||||
},
|
},
|
||||||
lhs_deref_ty.peel_refs(),
|
lhs_deref_ty,
|
||||||
lstring,
|
|
||||||
);
|
);
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
lhs_expr.span.shrink_to_lo(),
|
lhs_expr.span.shrink_to_lo(),
|
||||||
|
@ -466,7 +415,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
rustc_errors::Applicability::MachineApplicable,
|
rustc_errors::Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_compatible = |lhs_ty, rhs_ty| {
|
let is_compatible = |lhs_ty, rhs_ty| {
|
||||||
|
@ -514,9 +462,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(missing_trait) = missing_trait {
|
if let Some(missing_trait) = missing_trait {
|
||||||
let mut visitor = TypeParamVisitor(vec![]);
|
|
||||||
visitor.visit_ty(lhs_ty);
|
|
||||||
|
|
||||||
if op.node == hir::BinOpKind::Add
|
if op.node == hir::BinOpKind::Add
|
||||||
&& self.check_str_addition(
|
&& self.check_str_addition(
|
||||||
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
|
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
|
||||||
|
@ -525,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// This has nothing here because it means we did string
|
// This has nothing here because it means we did string
|
||||||
// concatenation (e.g., "Hello " + "World!"). This means
|
// concatenation (e.g., "Hello " + "World!"). This means
|
||||||
// we don't want the note in the else clause to be emitted
|
// we don't want the note in the else clause to be emitted
|
||||||
} else if let [ty] = &visitor.0[..] {
|
} else if lhs_ty.has_param_types_or_consts() {
|
||||||
// Look for a TraitPredicate in the Fulfillment errors,
|
// Look for a TraitPredicate in the Fulfillment errors,
|
||||||
// and use it to generate a suggestion.
|
// and use it to generate a suggestion.
|
||||||
//
|
//
|
||||||
|
@ -547,12 +492,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
if let Some(trait_pred) =
|
if let Some(trait_pred) =
|
||||||
error.obligation.predicate.to_opt_poly_trait_pred()
|
error.obligation.predicate.to_opt_poly_trait_pred()
|
||||||
{
|
{
|
||||||
let proj_pred = match error.obligation.cause.code() {
|
let output_associated_item = match error.obligation.cause.code()
|
||||||
|
{
|
||||||
ObligationCauseCode::BinOp {
|
ObligationCauseCode::BinOp {
|
||||||
output_pred: Some(output_pred),
|
output_ty: Some(output_ty),
|
||||||
..
|
..
|
||||||
} if use_output => {
|
} => {
|
||||||
output_pred.to_opt_poly_projection_pred()
|
// Make sure that we're attaching `Output = ..` to the right trait predicate
|
||||||
|
if let Some(output_def_id) = output_def_id
|
||||||
|
&& let Some(trait_def_id) = trait_def_id
|
||||||
|
&& self.tcx.parent(output_def_id) == trait_def_id
|
||||||
|
{
|
||||||
|
Some(("Output", *output_ty))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
@ -560,12 +514,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
self.suggest_restricting_param_bound(
|
self.suggest_restricting_param_bound(
|
||||||
&mut err,
|
&mut err,
|
||||||
trait_pred,
|
trait_pred,
|
||||||
proj_pred,
|
output_associated_item,
|
||||||
self.body_id,
|
self.body_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if *ty != lhs_ty {
|
} else {
|
||||||
// When we know that a missing bound is responsible, we don't show
|
// When we know that a missing bound is responsible, we don't show
|
||||||
// this note as it is redundant.
|
// this note as it is redundant.
|
||||||
err.note(&format!(
|
err.note(&format!(
|
||||||
|
@ -702,12 +656,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
format!("cannot apply unary operator `{}`", op.as_str()),
|
format!("cannot apply unary operator `{}`", op.as_str()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut visitor = TypeParamVisitor(vec![]);
|
if operand_ty.has_param_types_or_consts() {
|
||||||
visitor.visit_ty(operand_ty);
|
let predicates = errors.iter().filter_map(|error| {
|
||||||
if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() {
|
|
||||||
let predicates = errors
|
|
||||||
.iter()
|
|
||||||
.filter_map(|error| {
|
|
||||||
error.obligation.predicate.to_opt_poly_trait_pred()
|
error.obligation.predicate.to_opt_poly_trait_pred()
|
||||||
});
|
});
|
||||||
for pred in predicates {
|
for pred in predicates {
|
||||||
|
@ -777,64 +727,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
op: Op,
|
op: Op,
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
||||||
let lang = self.tcx.lang_items();
|
|
||||||
|
|
||||||
let span = match op {
|
let span = match op {
|
||||||
Op::Binary(op, _) => op.span,
|
Op::Binary(op, _) => op.span,
|
||||||
Op::Unary(_, span) => span,
|
Op::Unary(_, span) => span,
|
||||||
};
|
};
|
||||||
let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op {
|
let (opname, trait_did) = lang_item_for_op(self.tcx, op, span);
|
||||||
match op.node {
|
|
||||||
hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
|
|
||||||
hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
|
|
||||||
hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
|
|
||||||
hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
|
|
||||||
hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
|
|
||||||
hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
|
|
||||||
hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
|
|
||||||
hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
|
|
||||||
hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
|
|
||||||
hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
|
|
||||||
hir::BinOpKind::Lt
|
|
||||||
| hir::BinOpKind::Le
|
|
||||||
| hir::BinOpKind::Ge
|
|
||||||
| hir::BinOpKind::Gt
|
|
||||||
| hir::BinOpKind::Eq
|
|
||||||
| hir::BinOpKind::Ne
|
|
||||||
| hir::BinOpKind::And
|
|
||||||
| hir::BinOpKind::Or => {
|
|
||||||
span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Op::Binary(op, IsAssign::No) = op {
|
|
||||||
match op.node {
|
|
||||||
hir::BinOpKind::Add => (sym::add, lang.add_trait()),
|
|
||||||
hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
|
|
||||||
hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
|
|
||||||
hir::BinOpKind::Div => (sym::div, lang.div_trait()),
|
|
||||||
hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
|
|
||||||
hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
|
|
||||||
hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
|
|
||||||
hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
|
|
||||||
hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
|
|
||||||
hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
|
|
||||||
hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
|
|
||||||
hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
|
|
||||||
hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
|
|
||||||
hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
|
|
||||||
hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
|
|
||||||
hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
|
|
||||||
hir::BinOpKind::And | hir::BinOpKind::Or => {
|
|
||||||
span_bug!(span, "&& and || are not overloadable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Op::Unary(hir::UnOp::Not, _) = op {
|
|
||||||
(sym::not, lang.not_trait())
|
|
||||||
} else if let Op::Unary(hir::UnOp::Neg, _) = op {
|
|
||||||
(sym::neg, lang.neg_trait())
|
|
||||||
} else {
|
|
||||||
bug!("lookup_op_method: op not supported: {:?}", op)
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
|
"lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
|
||||||
|
@ -895,6 +792,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lang_item_for_op(
|
||||||
|
tcx: TyCtxt<'_>,
|
||||||
|
op: Op,
|
||||||
|
span: Span,
|
||||||
|
) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
|
||||||
|
let lang = tcx.lang_items();
|
||||||
|
if let Op::Binary(op, IsAssign::Yes) = op {
|
||||||
|
match op.node {
|
||||||
|
hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
|
||||||
|
hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
|
||||||
|
hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
|
||||||
|
hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
|
||||||
|
hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
|
||||||
|
hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
|
||||||
|
hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
|
||||||
|
hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
|
||||||
|
hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
|
||||||
|
hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
|
||||||
|
hir::BinOpKind::Lt
|
||||||
|
| hir::BinOpKind::Le
|
||||||
|
| hir::BinOpKind::Ge
|
||||||
|
| hir::BinOpKind::Gt
|
||||||
|
| hir::BinOpKind::Eq
|
||||||
|
| hir::BinOpKind::Ne
|
||||||
|
| hir::BinOpKind::And
|
||||||
|
| hir::BinOpKind::Or => {
|
||||||
|
span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Op::Binary(op, IsAssign::No) = op {
|
||||||
|
match op.node {
|
||||||
|
hir::BinOpKind::Add => (sym::add, lang.add_trait()),
|
||||||
|
hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
|
||||||
|
hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
|
||||||
|
hir::BinOpKind::Div => (sym::div, lang.div_trait()),
|
||||||
|
hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
|
||||||
|
hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
|
||||||
|
hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
|
||||||
|
hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
|
||||||
|
hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
|
||||||
|
hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
|
||||||
|
hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
|
||||||
|
hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
|
||||||
|
hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
|
||||||
|
hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
|
||||||
|
hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
|
||||||
|
hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
|
||||||
|
hir::BinOpKind::And | hir::BinOpKind::Or => {
|
||||||
|
span_bug!(span, "&& and || are not overloadable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Op::Unary(hir::UnOp::Not, _) = op {
|
||||||
|
(sym::not, lang.not_trait())
|
||||||
|
} else if let Op::Unary(hir::UnOp::Neg, _) = op {
|
||||||
|
(sym::neg, lang.neg_trait())
|
||||||
|
} else {
|
||||||
|
bug!("lookup_op_method: op not supported: {:?}", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Binary operator categories. These categories summarize the behavior
|
// Binary operator categories. These categories summarize the behavior
|
||||||
// with respect to the builtin operations supported.
|
// with respect to the builtin operations supported.
|
||||||
enum BinOpCategory {
|
enum BinOpCategory {
|
||||||
|
@ -1017,17 +974,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
|
|
||||||
|
|
||||||
impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
|
|
||||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
|
||||||
if let ty::Param(_) = ty.kind() {
|
|
||||||
self.0.push(ty);
|
|
||||||
}
|
|
||||||
ty.super_visit_with(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
|
struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
|
||||||
|
|
||||||
impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
|
impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
|
||||||
|
|
|
@ -6,7 +6,7 @@ LL | x % 2 == 0
|
||||||
| |
|
| |
|
||||||
| &&{integer}
|
| &&{integer}
|
||||||
|
|
|
|
||||||
help: `%` can be used on `{integer}`, you can dereference `x`
|
help: `%` can be used on `&{integer}` if you dereference the left-hand side
|
||||||
|
|
|
|
||||||
LL | *x % 2 == 0
|
LL | *x % 2 == 0
|
||||||
| +
|
| +
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl<B: Add + Add<Output = B>> Add for C<B> {
|
||||||
|
|
||||||
struct D<B>(B);
|
struct D<B>(B);
|
||||||
|
|
||||||
impl<B: std::ops::Add<Output=B>> Add for D<B> {
|
impl<B: std::ops::Add<Output = B>> Add for D<B> {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self {
|
fn add(self, rhs: Self) -> Self {
|
||||||
|
|
|
@ -66,8 +66,8 @@ LL | Self(self.0 + rhs.0)
|
||||||
|
|
|
|
||||||
help: consider restricting type parameter `B`
|
help: consider restricting type parameter `B`
|
||||||
|
|
|
|
||||||
LL | impl<B: std::ops::Add<Output=B>> Add for D<B> {
|
LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
|
||||||
| +++++++++++++++++++++++++
|
| +++++++++++++++++++++++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/missing-bounds.rs:42:14
|
--> $DIR/missing-bounds.rs:42:14
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
fn add_ten<N: std::ops::Add<i32, Output=N>>(n: N) -> N {
|
fn add_ten<N: std::ops::Add<i32, Output = N>>(n: N) -> N {
|
||||||
n + 10
|
n + 10
|
||||||
//~^ ERROR cannot add `{integer}` to `N`
|
//~^ ERROR cannot add `{integer}` to `N`
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ LL | n + 10
|
||||||
|
|
|
|
||||||
help: consider restricting type parameter `N`
|
help: consider restricting type parameter `N`
|
||||||
|
|
|
|
||||||
LL | fn add_ten<N: std::ops::Add<i32, Output=N>>(n: N) -> N {
|
LL | fn add_ten<N: std::ops::Add<i32, Output = N>>(n: N) -> N {
|
||||||
| ++++++++++++++++++++++++++++++
|
| ++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
12
src/test/ui/suggestions/restrict-type-not-param.rs
Normal file
12
src/test/ui/suggestions/restrict-type-not-param.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
|
struct Wrapper<T>(T);
|
||||||
|
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
fn qux<T>(a: Wrapper<T>, b: T) -> T {
|
||||||
|
a + b
|
||||||
|
//~^ ERROR cannot add `T` to `Wrapper<T>`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
26
src/test/ui/suggestions/restrict-type-not-param.stderr
Normal file
26
src/test/ui/suggestions/restrict-type-not-param.stderr
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
error[E0369]: cannot add `T` to `Wrapper<T>`
|
||||||
|
--> $DIR/restrict-type-not-param.rs:8:7
|
||||||
|
|
|
||||||
|
LL | a + b
|
||||||
|
| - ^ - T
|
||||||
|
| |
|
||||||
|
| Wrapper<T>
|
||||||
|
|
|
||||||
|
note: an implementation of `Add<_>` might be missing for `Wrapper<T>`
|
||||||
|
--> $DIR/restrict-type-not-param.rs:3:1
|
||||||
|
|
|
||||||
|
LL | struct Wrapper<T>(T);
|
||||||
|
| ^^^^^^^^^^^^^^^^^ must implement `Add<_>`
|
||||||
|
note: the following trait must be implemented
|
||||||
|
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
|
||||||
|
|
|
||||||
|
LL | pub trait Add<Rhs = Self> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||||
|
|
|
||||||
|
LL | fn qux<T>(a: Wrapper<T>, b: T) -> T where Wrapper<T>: Add<T, Output = T> {
|
||||||
|
| ++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0369`.
|
|
@ -8,8 +8,8 @@ LL | a * b
|
||||||
|
|
|
|
||||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||||
|
|
|
|
||||||
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
|
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64, Output = f64> {
|
||||||
| ++++++++++++++++++
|
| ++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ LL | x.lock().unwrap() += 1;
|
||||||
| |
|
| |
|
||||||
| cannot use `+=` on type `MutexGuard<'_, usize>`
|
| cannot use `+=` on type `MutexGuard<'_, usize>`
|
||||||
|
|
|
|
||||||
help: `+=` can be used on `usize`, you can dereference `x.lock().unwrap()`
|
help: `+=` can be used on `usize` if you dereference the left-hand side
|
||||||
|
|
|
|
||||||
LL | *x.lock().unwrap() += 1;
|
LL | *x.lock().unwrap() += 1;
|
||||||
| +
|
| +
|
||||||
|
@ -47,7 +47,7 @@ LL | y += 1;
|
||||||
| |
|
| |
|
||||||
| cannot use `+=` on type `MutexGuard<'_, usize>`
|
| cannot use `+=` on type `MutexGuard<'_, usize>`
|
||||||
|
|
|
|
||||||
help: `+=` can be used on `usize`, you can dereference `y`
|
help: `+=` can be used on `usize` if you dereference the left-hand side
|
||||||
|
|
|
|
||||||
LL | *y += 1;
|
LL | *y += 1;
|
||||||
| +
|
| +
|
||||||
|
|
|
@ -19,7 +19,7 @@ LL | x.last_mut().unwrap() += 1;
|
||||||
| |
|
| |
|
||||||
| cannot use `+=` on type `&mut usize`
|
| cannot use `+=` on type `&mut usize`
|
||||||
|
|
|
|
||||||
help: `+=` can be used on `usize`, you can dereference `x.last_mut().unwrap()`
|
help: `+=` can be used on `usize` if you dereference the left-hand side
|
||||||
|
|
|
|
||||||
LL | *x.last_mut().unwrap() += 1;
|
LL | *x.last_mut().unwrap() += 1;
|
||||||
| +
|
| +
|
||||||
|
@ -45,7 +45,7 @@ LL | y += 1;
|
||||||
| |
|
| |
|
||||||
| cannot use `+=` on type `&mut usize`
|
| cannot use `+=` on type `&mut usize`
|
||||||
|
|
|
|
||||||
help: `+=` can be used on `usize`, you can dereference `y`
|
help: `+=` can be used on `usize` if you dereference the left-hand side
|
||||||
|
|
|
|
||||||
LL | *y += 1;
|
LL | *y += 1;
|
||||||
| +
|
| +
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue