diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 0d2ae115cb0d..00913a483db0 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -241,9 +241,19 @@ fn compress( v: PreorderIndex, ) { assert!(is_processed(v, lastlinked)); - let u = ancestor[v]; - if is_processed(u, lastlinked) { - compress(ancestor, lastlinked, semi, label, u); + // Compute the processed list of ancestors + // + // We use a heap stack here to avoid recursing too deeply, exhausting the + // stack space. + let mut stack: smallvec::SmallVec<[_; 8]> = smallvec::smallvec![v]; + let mut u = ancestor[v]; + while is_processed(u, lastlinked) { + stack.push(u); + u = ancestor[u]; + } + + // Then in reverse order, popping the stack + for &[v, u] in stack.array_windows().rev() { if semi[label[u]] < semi[label[v]] { label[v] = label[u]; } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 072e6346cdec..45610fa77d35 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -368,6 +368,11 @@ pub enum ObligationCauseCode<'tcx> { /// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against. MatchImpl(ObligationCause<'tcx>, DefId), + + BinOp { + rhs_span: Option, + is_lit: bool, + }, } /// The 'location' at which we try to perform HIR-based wf checking. diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index bb9755e4f48d..4981ab5152cd 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -10,38 +10,11 @@ use rustc_middle::mir::*; /// At present, this is used as a very limited form of alias analysis. For example, /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for /// immovable generators. -pub struct MaybeBorrowedLocals { - ignore_borrow_on_drop: bool, -} - -impl MaybeBorrowedLocals { - /// A dataflow analysis that records whether a pointer or reference exists that may alias the - /// given local. - pub fn all_borrows() -> Self { - MaybeBorrowedLocals { ignore_borrow_on_drop: false } - } -} +pub struct MaybeBorrowedLocals; impl MaybeBorrowedLocals { - /// During dataflow analysis, ignore the borrow that may occur when a place is dropped. - /// - /// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a - /// parameter. In the general case, a drop impl could launder that reference into the - /// surrounding environment through a raw pointer, thus creating a valid `*mut` pointing to the - /// dropped local. We are not yet willing to declare this particular case UB, so we must treat - /// all dropped locals as mutably borrowed for now. See discussion on [#61069]. - /// - /// In some contexts, we know that this borrow will never occur. For example, during - /// const-eval, custom drop glue cannot be run. Code that calls this should document the - /// assumptions that justify ignoring `Drop` terminators in this way. - /// - /// [#61069]: https://github.com/rust-lang/rust/pull/61069 - pub fn unsound_ignore_borrow_on_drop(self) -> Self { - MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self } - } - fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> { - TransferFunction { trans, ignore_borrow_on_drop: self.ignore_borrow_on_drop } + TransferFunction { trans } } } @@ -92,7 +65,6 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`. struct TransferFunction<'a, T> { trans: &'a mut T, - ignore_borrow_on_drop: bool, } impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> @@ -146,10 +118,15 @@ where match terminator.kind { mir::TerminatorKind::Drop { place: dropped_place, .. } | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => { - // See documentation for `unsound_ignore_borrow_on_drop` for an explanation. - if !self.ignore_borrow_on_drop { - self.trans.gen(dropped_place.local); - } + // Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut + // self` as a parameter. In the general case, a drop impl could launder that + // reference into the surrounding environment through a raw pointer, thus creating + // a valid `*mut` pointing to the dropped local. We are not yet willing to declare + // this particular case UB, so we must treat all dropped locals as mutably borrowed + // for now. See discussion on [#61069]. + // + // [#61069]: https://github.com/rust-lang/rust/pull/61069 + self.trans.gen(dropped_place.local); } TerminatorKind::Abort diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 388bb7d34364..d9a66cace52e 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -463,10 +463,8 @@ fn locals_live_across_suspend_points<'tcx>( // Calculate the MIR locals which have been previously // borrowed (even if they are still active). - let borrowed_locals_results = MaybeBorrowedLocals::all_borrows() - .into_engine(tcx, body_ref) - .pass_name("generator") - .iterate_to_fixpoint(); + let borrowed_locals_results = + MaybeBorrowedLocals.into_engine(tcx, body_ref).pass_name("generator").iterate_to_fixpoint(); let mut borrowed_locals_cursor = rustc_mir_dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index f26f32aabda1..f5beb83729f9 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -501,6 +501,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_label(enclosing_scope_span, s.as_str()); } + self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); self.suggest_dereferences(&obligation, &mut err, trait_predicate); self.suggest_fn_call(&obligation, &mut err, trait_predicate); self.suggest_remove_reference(&obligation, &mut err, trait_predicate); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index c4fbd25b8338..9969f610ac81 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -174,6 +174,13 @@ pub trait InferCtxtExt<'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, ); + + fn suggest_floating_point_literal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { @@ -1910,8 +1917,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::AwaitableExpr(_) | ObligationCauseCode::ForLoopIterator | ObligationCauseCode::QuestionMark + | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse - | ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {} + | ObligationCauseCode::BinOp { .. } => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2497,6 +2505,32 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } } + + fn suggest_floating_point_literal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let rhs_span = match obligation.cause.code() { + ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span, + _ => return, + }; + match ( + trait_ref.skip_binder().self_ty().kind(), + trait_ref.skip_binder().substs.type_at(1).kind(), + ) { + (ty::Float(_), ty::Infer(InferTy::IntVar(_))) => { + err.span_suggestion_verbose( + rhs_span.shrink_to_hi(), + "consider using a floating-point literal by writing it with `.0`", + String::from(".0"), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + } } /// Collect all the returned expressions within the input expression. diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 8d4b97571a6a..47b1ee04e777 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -8,7 +8,7 @@ use rustc_middle::traits::ChalkRustInterner as RustInterner; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, AssocItemContainer, AssocKind, Ty, TyCtxt, TypeFoldable}; use rustc_ast::ast; use rustc_attr as attr; @@ -482,21 +482,11 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t .iter() .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars)) .map(|bound| { - bound.fold_with(&mut ty::fold::BottomUpFolder { + bound.fold_with(&mut ReplaceOpaqueTyFolder { tcx: self.interner.tcx, - ty_op: |ty| { - if let ty::Opaque(def_id, substs) = *ty.kind() { - if def_id == opaque_ty_id.0 && substs == identity_substs { - return self.interner.tcx.mk_ty(ty::Bound( - ty::INNERMOST, - ty::BoundTy::from(ty::BoundVar::from_u32(0)), - )); - } - } - ty - }, - lt_op: |lt| lt, - ct_op: |ct| ct, + opaque_ty_id, + identity_substs, + binder_index: ty::INNERMOST, }) }) .filter_map(|bound| { @@ -739,3 +729,38 @@ fn binders_for<'tcx>( }), ) } + +struct ReplaceOpaqueTyFolder<'tcx> { + tcx: TyCtxt<'tcx>, + opaque_ty_id: chalk_ir::OpaqueTyId>, + identity_substs: SubstsRef<'tcx>, + binder_index: ty::DebruijnIndex, +} + +impl<'tcx> ty::TypeFolder<'tcx> for ReplaceOpaqueTyFolder<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.binder_index.shift_in(1); + let t = t.super_fold_with(self); + self.binder_index.shift_out(1); + t + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Opaque(def_id, substs) = *ty.kind() { + if def_id == self.opaque_ty_id.0 && substs == self.identity_substs { + return self.tcx.mk_ty(ty::Bound( + self.binder_index, + ty::BoundTy::from(ty::BoundVar::from_u32(0)), + )); + } + } + ty + } +} diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 2cd179526bf1..e3c865ce9e63 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -323,7 +323,10 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { ty::Closure(def_id, substs) => { chalk_ir::TyKind::Closure(chalk_ir::ClosureId(def_id), substs.lower_into(interner)) } - ty::Generator(_def_id, _substs, _) => unimplemented!(), + ty::Generator(def_id, substs, _) => chalk_ir::TyKind::Generator( + chalk_ir::GeneratorId(def_id), + substs.lower_into(interner), + ), ty::GeneratorWitness(_) => unimplemented!(), ty::Never => chalk_ir::TyKind::Never, ty::Tuple(types) => { diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index b16bf33f06ab..a06b37627e3c 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -512,61 +512,69 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { explicit_late_bound == ExplicitLateBound::Yes, ); - let mut check_types_and_consts = - |expected_min, expected_max, provided, params_offset, args_offset| { - debug!( - ?expected_min, - ?expected_max, - ?provided, - ?params_offset, - ?args_offset, - "check_types_and_consts" + let mut check_types_and_consts = |expected_min, + expected_max, + expected_max_with_synth, + provided, + params_offset, + args_offset| { + debug!( + ?expected_min, + ?expected_max, + ?provided, + ?params_offset, + ?args_offset, + "check_types_and_consts" + ); + if (expected_min..=expected_max).contains(&provided) { + return true; + } + + let num_default_params = expected_max - expected_min; + + let gen_args_info = if provided > expected_max { + invalid_args.extend( + gen_args.args[args_offset + expected_max..args_offset + provided] + .iter() + .map(|arg| arg.span()), ); - if (expected_min..=expected_max).contains(&provided) { - return true; + let num_redundant_args = provided - expected_max; + + // Provide extra note if synthetic arguments like `impl Trait` are specified. + let synth_provided = provided <= expected_max_with_synth; + + GenericArgsInfo::ExcessTypesOrConsts { + num_redundant_args, + num_default_params, + args_offset, + synth_provided, } + } else { + let num_missing_args = expected_max - provided; - let num_default_params = expected_max - expected_min; + GenericArgsInfo::MissingTypesOrConsts { + num_missing_args, + num_default_params, + args_offset, + } + }; - let gen_args_info = if provided > expected_max { - invalid_args.extend( - gen_args.args[args_offset + expected_max..args_offset + provided] - .iter() - .map(|arg| arg.span()), - ); - let num_redundant_args = provided - expected_max; + debug!(?gen_args_info); - GenericArgsInfo::ExcessTypesOrConsts { - num_redundant_args, - num_default_params, - args_offset, - } - } else { - let num_missing_args = expected_max - provided; + WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + ) + .diagnostic() + .emit_unless(gen_args.has_err()); - GenericArgsInfo::MissingTypesOrConsts { - num_missing_args, - num_default_params, - args_offset, - } - }; - - debug!(?gen_args_info); - - WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - params_offset, - gen_args, - def_id, - ) - .diagnostic() - .emit_unless(gen_args.has_err()); - - false - }; + false + }; let args_correct = { let expected_min = if infer_args { @@ -582,6 +590,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { check_types_and_consts( expected_min, param_counts.consts + named_type_param_count, + param_counts.consts + named_type_param_count + synth_type_param_count, gen_args.num_generic_params(), param_counts.lifetimes + has_self as usize, gen_args.num_lifetime_params(), diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index d88407312630..5cb1fe8cd94e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -438,6 +438,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok( + &self, + span: Span, + value: T, + opt_input_expr: Option<&hir::Expr<'_>>, + ) -> InferOk<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + self.inh.partially_normalize_associated_types_in( + ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), + }, + ), + self.param_env, + value, + ) + } + pub fn require_type_meets( &self, ty: Ty<'tcx>, diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 999aafbd5375..7bb84581a82d 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -333,15 +333,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + pub(super) fn obligation_for_op_method( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List>) + { + // Construct a trait-reference `self_ty : Trait` + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} + GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return self_ty.into(); + } else if let Some(input_type) = opt_input_type { + return input_type.into(); + } + } + } + self.var_for_def(span, param) + }); + + let trait_ref = ty::TraitRef::new(trait_def_id, substs); + + // Construct an obligation + let poly_trait_ref = ty::Binder::dummy(trait_ref); + ( + traits::Obligation::new( + traits::ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + }, + ), + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ), + substs, + ) + } + /// `lookup_method_in_trait` is used for overloaded operators. /// It does a very narrow slice of what the normal probe/confirm path does. /// In particular, it doesn't really do any probing: it simply constructs /// an obligation for a particular trait with the given self type and checks /// whether that trait is implemented. - // - // FIXME(#18741): it seems likely that we can consolidate some of this - // code with the other method-lookup code. In particular, the second half - // of this method is basically the same as confirmation. #[instrument(level = "debug", skip(self, span, opt_input_types))] pub(super) fn lookup_method_in_trait( &self, @@ -358,7 +400,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (obligation, substs) = self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + None, + false, + ) + } + pub(super) fn lookup_op_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + ) -> Option>> { + let (obligation, substs) = self.obligation_for_op_method( + span, + trait_def_id, + self_ty, + opt_input_type, + opt_input_expr, + ); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + opt_input_expr, + true, + ) + } + + // FIXME(#18741): it seems likely that we can consolidate some of this + // code with the other method-lookup code. In particular, the second half + // of this method is basically the same as confirmation. + fn construct_obligation_for_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + obligation: traits::PredicateObligation<'tcx>, + substs: &'tcx ty::List>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + is_op: bool, + ) -> Option>> { debug!(?obligation); // Now we want to know if this can be matched @@ -395,8 +487,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0; let fn_sig = fn_sig.subst(self.tcx, substs); - let InferOk { value, obligations: o } = - self.normalize_associated_types_in_as_infer_ok(span, fn_sig); + let InferOk { value, obligations: o } = if is_op { + self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr) + } else { + self.normalize_associated_types_in_as_infer_ok(span, fn_sig) + }; let fn_sig = { obligations.extend(o); value @@ -412,8 +507,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // any late-bound regions appearing in its bounds. let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - let InferOk { value, obligations: o } = - self.normalize_associated_types_in_as_infer_ok(span, bounds); + let InferOk { value, obligations: o } = if is_op { + self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr) + } else { + self.normalize_associated_types_in_as_infer_ok(span, bounds) + }; let bounds = { obligations.extend(o); value @@ -421,7 +519,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(!bounds.has_escaping_bound_vars()); - let cause = traits::ObligationCause::misc(span, self.body_id); + let cause = if is_op { + ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + }, + ) + } else { + traits::ObligationCause::misc(span, self.body_id) + }; obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); // Also add an obligation for the method type being well-formed. diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 5dd5e71656d2..cd77e6d0384d 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -201,7 +201,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: rhs_expr.span, }); - let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign)); + let result = self.lookup_op_method( + lhs_ty, + Some(rhs_ty_var), + Some(rhs_expr), + Op::Binary(op, is_assign), + ); // see `NB` above let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); @@ -382,6 +387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr.span, lhs_ty, rhs_ty, + rhs_expr, op, is_assign, ); @@ -390,6 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr.span, rhs_ty, lhs_ty, + lhs_expr, op, is_assign, ); @@ -400,7 +407,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Ref(_, rty, _) = lhs_ty.kind() { if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span) - && self.lookup_op_method(*rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok() + && self + .lookup_op_method( + *rty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + ) + .is_ok() { if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { let msg = &format!( @@ -443,7 +457,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let needs_bound = self .lookup_op_method( eraser.fold_ty(lhs_ty), - &[eraser.fold_ty(rhs_ty)], + Some(eraser.fold_ty(rhs_ty)), + Some(rhs_expr), Op::Binary(op, is_assign), ) .is_ok(); @@ -487,6 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, ty: Ty<'tcx>, other_ty: Ty<'tcx>, + other_expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, is_assign: IsAssign, ) -> bool /* did we suggest to call a function because of missing parentheses? */ { @@ -513,7 +529,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self - .lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign)) + .lookup_op_method( + fn_sig.output(), + Some(other_ty), + Some(other_expr), + Op::Binary(op, is_assign), + ) .is_ok() { let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { @@ -631,7 +652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: hir::UnOp, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) { + match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) { Ok(method) => { self.write_method_call(ex.hir_id, method); method.sig.output() @@ -705,7 +726,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn lookup_op_method( &self, lhs_ty: Ty<'tcx>, - other_tys: &[Ty<'tcx>], + other_ty: Option>, + other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, op: Op, ) -> Result, Vec>> { let lang = self.tcx.lang_items(); @@ -791,7 +813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let opname = Ident::with_dummy_span(opname); let method = trait_did.and_then(|trait_did| { - self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys)) + self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr) }); match (method, trait_did) { @@ -803,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (None, None) => Err(vec![]), (None, Some(trait_did)) => { let (obligation, _) = - self.obligation_for_method(span, trait_did, lhs_ty, Some(other_tys)); + self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr); let mut fulfill = >::new(self.tcx); fulfill.register_predicate_obligation(self, obligation); Err(fulfill.select_where_possible(&self.infcx)) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 4ab654560ea0..a42ed9eab643 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -31,6 +31,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, use std::convert::TryInto; use std::iter; +use std::lazy::Lazy; use std::ops::ControlFlow; /// Helper type of a temporary returned by `.for_item(...)`. @@ -1720,8 +1721,29 @@ fn check_variances_for_type_defn<'tcx>( identify_constrained_generic_params(tcx, ty_predicates, None, &mut constrained_parameters); + // Lazily calculated because it is only needed in case of an error. + let explicitly_bounded_params = Lazy::new(|| { + let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id()); + hir_generics + .where_clause + .predicates + .iter() + .filter_map(|predicate| match predicate { + hir::WherePredicate::BoundPredicate(predicate) => { + match icx.to_ty(predicate.bounded_ty).kind() { + ty::Param(data) => Some(Parameter(data.index)), + _ => None, + } + } + _ => None, + }) + .collect::>() + }); + for (index, _) in variances.iter().enumerate() { - if constrained_parameters.contains(&Parameter(index as u32)) { + let parameter = Parameter(index as u32); + + if constrained_parameters.contains(¶meter) { continue; } @@ -1730,13 +1752,19 @@ fn check_variances_for_type_defn<'tcx>( match param.name { hir::ParamName::Error => {} _ => { - report_bivariance(tcx, param); + let has_explicit_bounds = + !param.bounds.is_empty() || explicitly_bounded_params.contains(¶meter); + report_bivariance(tcx, param, has_explicit_bounds); } } } } -fn report_bivariance(tcx: TyCtxt<'_>, param: &rustc_hir::GenericParam<'_>) -> ErrorReported { +fn report_bivariance( + tcx: TyCtxt<'_>, + param: &rustc_hir::GenericParam<'_>, + has_explicit_bounds: bool, +) -> ErrorReported { let span = param.span; let param_name = param.name.ident().name; let mut err = error_392(tcx, span, param_name); @@ -1754,7 +1782,7 @@ fn report_bivariance(tcx: TyCtxt<'_>, param: &rustc_hir::GenericParam<'_>) -> Er }; err.help(&msg); - if matches!(param.kind, rustc_hir::GenericParamKind::Type { .. }) { + if matches!(param.kind, hir::GenericParamKind::Type { .. }) && !has_explicit_bounds { err.help(&format!( "if you intended `{0}` to be a const parameter, use `const {0}: usize` instead", param_name diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 40904c1dfd63..f0289fd505a4 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -68,6 +68,7 @@ This API is completely unstable and subject to change. #![feature(slice_partition_dedup)] #![feature(control_flow_enum)] #![feature(hash_drain_filter)] +#![feature(once_cell)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index b763b51dd014..62f89a2e6cf8 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -84,6 +84,9 @@ pub enum GenericArgsInfo { // us infer the position of type and const generic arguments // in the angle brackets args_offset: usize, + + // if synthetic type arguments (e.g. `impl Trait`) are specified + synth_provided: bool, }, } @@ -254,6 +257,13 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } + fn is_synth_provided(&self) -> bool { + match self.gen_args_info { + ExcessTypesOrConsts { synth_provided, .. } => synth_provided, + _ => false, + } + } + // Helper function to choose a quantifier word for the number of expected arguments // and to give a bound for the number of expected arguments fn get_quantifier_and_bound(&self) -> (&'static str, usize) { @@ -780,6 +790,15 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { err.span_note(spans, &msg); } + + /// Add note if `impl Trait` is explicitly specified. + fn note_synth_provided(&self, err: &mut Diagnostic) { + if !self.is_synth_provided() { + return; + } + + err.note("`impl Trait` cannot be explicitly specified as a generic argument"); + } } impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> { @@ -797,6 +816,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> { self.notify(&mut err); self.suggest(&mut err); self.show_definition(&mut err); + self.note_synth_provided(&mut err); err } diff --git a/src/test/ui/chalkify/bugs/async.rs b/src/test/ui/chalkify/bugs/async.rs new file mode 100644 index 000000000000..58fc93064edf --- /dev/null +++ b/src/test/ui/chalkify/bugs/async.rs @@ -0,0 +1,9 @@ +// check-fail +// known-bug +// compile-flags: -Z chalk --edition=2021 + +fn main() -> () {} + +async fn foo(x: u32) -> u32 { + x +} diff --git a/src/test/ui/chalkify/bugs/async.stderr b/src/test/ui/chalkify/bugs/async.stderr new file mode 100644 index 000000000000..7a86561bcb9c --- /dev/null +++ b/src/test/ui/chalkify/bugs/async.stderr @@ -0,0 +1,39 @@ +error[E0277]: the trait bound `[static generator@$DIR/async.rs:7:29: 9:2]: Generator` is not satisfied + --> $DIR/async.rs:7:29 + | +LL | async fn foo(x: u32) -> u32 { + | _____________________________^ +LL | | x +LL | | } + | |_^ the trait `Generator` is not implemented for `[static generator@$DIR/async.rs:7:29: 9:2]` + | +note: required by a bound in `from_generator` + --> $SRC_DIR/core/src/future/mod.rs:LL:COL + | +LL | T: Generator, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `from_generator` + +error[E0280]: the requirement `<[static generator@$DIR/async.rs:7:29: 9:2] as Generator>::Yield == ()` is not satisfied + --> $DIR/async.rs:7:29 + | +LL | async fn foo(x: u32) -> u32 { + | _____________________________^ +LL | | x +LL | | } + | |_^ + | +note: required by a bound in `from_generator` + --> $SRC_DIR/core/src/future/mod.rs:LL:COL + | +LL | T: Generator, + | ^^^^^^^^^^ required by this bound in `from_generator` + +error[E0280]: the requirement ` as Future>::Output == u32` is not satisfied + --> $DIR/async.rs:7:25 + | +LL | async fn foo(x: u32) -> u32 { + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr index 3add0429d2d5..2ae7745c725c 100644 --- a/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr +++ b/src/test/ui/impl-trait/explicit-generic-args-with-impl-trait/explicit-generic-args-for-impl.stderr @@ -11,6 +11,7 @@ note: function defined here, with 1 generic parameter: `T` | LL | fn foo(_f: impl AsRef) {} | ^^^ - + = note: `impl Trait` cannot be explicitly specified as a generic argument error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17904-2.stderr b/src/test/ui/issues/issue-17904-2.stderr index 259e029113d5..62b7b79538c6 100644 --- a/src/test/ui/issues/issue-17904-2.stderr +++ b/src/test/ui/issues/issue-17904-2.stderr @@ -5,7 +5,6 @@ LL | struct Foo where T: Copy; | ^ unused parameter | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: usize` instead error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24352.stderr b/src/test/ui/issues/issue-24352.stderr index 69cd7789065d..69731bfe7ee7 100644 --- a/src/test/ui/issues/issue-24352.stderr +++ b/src/test/ui/issues/issue-24352.stderr @@ -5,6 +5,10 @@ LL | 1.0f64 - 1 | ^ no implementation for `f64 - {integer}` | = help: the trait `Sub<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | 1.0f64 - 1.0 + | ++ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-37534.stderr b/src/test/ui/issues/issue-37534.stderr index 82bb51028c97..895479986f1d 100644 --- a/src/test/ui/issues/issue-37534.stderr +++ b/src/test/ui/issues/issue-37534.stderr @@ -22,7 +22,6 @@ LL | struct Foo { } | ^ unused parameter | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: usize` instead error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs new file mode 100644 index 000000000000..513d02db988e --- /dev/null +++ b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs @@ -0,0 +1,53 @@ +fn add_float_to_integer(x: u8) -> f32 { + x + 100.0 //~ ERROR cannot add `{float}` to `u8` +} + +fn add_str_to_float(x: f64) -> f64 { + x + "foo" //~ ERROR cannot add `&str` to `f64` +} + +fn add_lvar_to_float(x: f64) -> f64 { + let y = 3; + x + y //~ ERROR cannot add `{integer}` to `f64` +} + +fn subtract_float_from_integer(x: u8) -> f32 { + x - 100.0 //~ ERROR cannot subtract `{float}` from `u8` +} + +fn subtract_str_from_f64(x: f64) -> f64 { + x - "foo" //~ ERROR cannot subtract `&str` from `f64` +} + +fn subtract_lvar_from_f64(x: f64) -> f64 { + let y = 3; + x - y //~ ERROR cannot subtract `{integer}` from `f64` +} + +fn multiply_integer_by_float(x: u8) -> f32 { + x * 100.0 //~ ERROR cannot multiply `u8` by `{float}` +} + +fn multiply_f64_by_str(x: f64) -> f64 { + x * "foo" //~ ERROR cannot multiply `f64` by `&str` +} + +fn multiply_f64_by_lvar(x: f64) -> f64 { + let y = 3; + x * y //~ ERROR cannot multiply `f64` by `{integer}` +} + +fn divide_integer_by_float(x: u8) -> u8 { + x / 100.0 //~ ERROR cannot divide `u8` by `{float}` +} + +fn divide_f64_by_str(x: f64) -> f64 { + x / "foo" //~ ERROR cannot divide `f64` by `&str` +} + +fn divide_f64_by_lvar(x: f64) -> f64 { + let y = 3; + x / y //~ ERROR cannot divide `f64` by `{integer}` +} + +fn main() {} diff --git a/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr new file mode 100644 index 000000000000..ce9a08a15897 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr @@ -0,0 +1,99 @@ +error[E0277]: cannot add `{float}` to `u8` + --> $DIR/not-suggest-float-literal.rs:2:7 + | +LL | x + 100.0 + | ^ no implementation for `u8 + {float}` + | + = help: the trait `Add<{float}>` is not implemented for `u8` + +error[E0277]: cannot add `&str` to `f64` + --> $DIR/not-suggest-float-literal.rs:6:7 + | +LL | x + "foo" + | ^ no implementation for `f64 + &str` + | + = help: the trait `Add<&str>` is not implemented for `f64` + +error[E0277]: cannot add `{integer}` to `f64` + --> $DIR/not-suggest-float-literal.rs:11:7 + | +LL | x + y + | ^ no implementation for `f64 + {integer}` + | + = help: the trait `Add<{integer}>` is not implemented for `f64` + +error[E0277]: cannot subtract `{float}` from `u8` + --> $DIR/not-suggest-float-literal.rs:15:7 + | +LL | x - 100.0 + | ^ no implementation for `u8 - {float}` + | + = help: the trait `Sub<{float}>` is not implemented for `u8` + +error[E0277]: cannot subtract `&str` from `f64` + --> $DIR/not-suggest-float-literal.rs:19:7 + | +LL | x - "foo" + | ^ no implementation for `f64 - &str` + | + = help: the trait `Sub<&str>` is not implemented for `f64` + +error[E0277]: cannot subtract `{integer}` from `f64` + --> $DIR/not-suggest-float-literal.rs:24:7 + | +LL | x - y + | ^ no implementation for `f64 - {integer}` + | + = help: the trait `Sub<{integer}>` is not implemented for `f64` + +error[E0277]: cannot multiply `u8` by `{float}` + --> $DIR/not-suggest-float-literal.rs:28:7 + | +LL | x * 100.0 + | ^ no implementation for `u8 * {float}` + | + = help: the trait `Mul<{float}>` is not implemented for `u8` + +error[E0277]: cannot multiply `f64` by `&str` + --> $DIR/not-suggest-float-literal.rs:32:7 + | +LL | x * "foo" + | ^ no implementation for `f64 * &str` + | + = help: the trait `Mul<&str>` is not implemented for `f64` + +error[E0277]: cannot multiply `f64` by `{integer}` + --> $DIR/not-suggest-float-literal.rs:37:7 + | +LL | x * y + | ^ no implementation for `f64 * {integer}` + | + = help: the trait `Mul<{integer}>` is not implemented for `f64` + +error[E0277]: cannot divide `u8` by `{float}` + --> $DIR/not-suggest-float-literal.rs:41:7 + | +LL | x / 100.0 + | ^ no implementation for `u8 / {float}` + | + = help: the trait `Div<{float}>` is not implemented for `u8` + +error[E0277]: cannot divide `f64` by `&str` + --> $DIR/not-suggest-float-literal.rs:45:7 + | +LL | x / "foo" + | ^ no implementation for `f64 / &str` + | + = help: the trait `Div<&str>` is not implemented for `f64` + +error[E0277]: cannot divide `f64` by `{integer}` + --> $DIR/not-suggest-float-literal.rs:50:7 + | +LL | x / y + | ^ no implementation for `f64 / {integer}` + | + = help: the trait `Div<{integer}>` is not implemented for `f64` + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/numbers-arithmetic/suggest-float-literal.fixed b/src/test/ui/numbers-arithmetic/suggest-float-literal.fixed new file mode 100644 index 000000000000..9278262a6ffe --- /dev/null +++ b/src/test/ui/numbers-arithmetic/suggest-float-literal.fixed @@ -0,0 +1,37 @@ +// run-rustfix + +#![allow(dead_code)] + +fn add_integer_to_f32(x: f32) -> f32 { + x + 100.0 //~ ERROR cannot add `{integer}` to `f32` +} + +fn add_integer_to_f64(x: f64) -> f64 { + x + 100.0 //~ ERROR cannot add `{integer}` to `f64` +} + +fn subtract_integer_from_f32(x: f32) -> f32 { + x - 100.0 //~ ERROR cannot subtract `{integer}` from `f32` +} + +fn subtract_integer_from_f64(x: f64) -> f64 { + x - 100.0 //~ ERROR cannot subtract `{integer}` from `f64` +} + +fn multiply_f32_by_integer(x: f32) -> f32 { + x * 100.0 //~ ERROR cannot multiply `f32` by `{integer}` +} + +fn multiply_f64_by_integer(x: f64) -> f64 { + x * 100.0 //~ ERROR cannot multiply `f64` by `{integer}` +} + +fn divide_f32_by_integer(x: f32) -> f32 { + x / 100.0 //~ ERROR cannot divide `f32` by `{integer}` +} + +fn divide_f64_by_integer(x: f64) -> f64 { + x / 100.0 //~ ERROR cannot divide `f64` by `{integer}` +} + +fn main() {} diff --git a/src/test/ui/numbers-arithmetic/suggest-float-literal.rs b/src/test/ui/numbers-arithmetic/suggest-float-literal.rs new file mode 100644 index 000000000000..59e67f8d33e0 --- /dev/null +++ b/src/test/ui/numbers-arithmetic/suggest-float-literal.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![allow(dead_code)] + +fn add_integer_to_f32(x: f32) -> f32 { + x + 100 //~ ERROR cannot add `{integer}` to `f32` +} + +fn add_integer_to_f64(x: f64) -> f64 { + x + 100 //~ ERROR cannot add `{integer}` to `f64` +} + +fn subtract_integer_from_f32(x: f32) -> f32 { + x - 100 //~ ERROR cannot subtract `{integer}` from `f32` +} + +fn subtract_integer_from_f64(x: f64) -> f64 { + x - 100 //~ ERROR cannot subtract `{integer}` from `f64` +} + +fn multiply_f32_by_integer(x: f32) -> f32 { + x * 100 //~ ERROR cannot multiply `f32` by `{integer}` +} + +fn multiply_f64_by_integer(x: f64) -> f64 { + x * 100 //~ ERROR cannot multiply `f64` by `{integer}` +} + +fn divide_f32_by_integer(x: f32) -> f32 { + x / 100 //~ ERROR cannot divide `f32` by `{integer}` +} + +fn divide_f64_by_integer(x: f64) -> f64 { + x / 100 //~ ERROR cannot divide `f64` by `{integer}` +} + +fn main() {} diff --git a/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr b/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr new file mode 100644 index 000000000000..eb0be785061e --- /dev/null +++ b/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr @@ -0,0 +1,99 @@ +error[E0277]: cannot add `{integer}` to `f32` + --> $DIR/suggest-float-literal.rs:6:7 + | +LL | x + 100 + | ^ no implementation for `f32 + {integer}` + | + = help: the trait `Add<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x + 100.0 + | ++ + +error[E0277]: cannot add `{integer}` to `f64` + --> $DIR/suggest-float-literal.rs:10:7 + | +LL | x + 100 + | ^ no implementation for `f64 + {integer}` + | + = help: the trait `Add<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x + 100.0 + | ++ + +error[E0277]: cannot subtract `{integer}` from `f32` + --> $DIR/suggest-float-literal.rs:14:7 + | +LL | x - 100 + | ^ no implementation for `f32 - {integer}` + | + = help: the trait `Sub<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x - 100.0 + | ++ + +error[E0277]: cannot subtract `{integer}` from `f64` + --> $DIR/suggest-float-literal.rs:18:7 + | +LL | x - 100 + | ^ no implementation for `f64 - {integer}` + | + = help: the trait `Sub<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x - 100.0 + | ++ + +error[E0277]: cannot multiply `f32` by `{integer}` + --> $DIR/suggest-float-literal.rs:22:7 + | +LL | x * 100 + | ^ no implementation for `f32 * {integer}` + | + = help: the trait `Mul<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x * 100.0 + | ++ + +error[E0277]: cannot multiply `f64` by `{integer}` + --> $DIR/suggest-float-literal.rs:26:7 + | +LL | x * 100 + | ^ no implementation for `f64 * {integer}` + | + = help: the trait `Mul<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x * 100.0 + | ++ + +error[E0277]: cannot divide `f32` by `{integer}` + --> $DIR/suggest-float-literal.rs:30:7 + | +LL | x / 100 + | ^ no implementation for `f32 / {integer}` + | + = help: the trait `Div<{integer}>` is not implemented for `f32` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x / 100.0 + | ++ + +error[E0277]: cannot divide `f64` by `{integer}` + --> $DIR/suggest-float-literal.rs:34:7 + | +LL | x / 100 + | ^ no implementation for `f64 / {integer}` + | + = help: the trait `Div<{integer}>` is not implemented for `f64` +help: consider using a floating-point literal by writing it with `.0` + | +LL | x / 100.0 + | ++ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/variance/variance-unused-type-param.rs b/src/test/ui/variance/variance-unused-type-param.rs index 1e0e403ebcee..d11140643647 100644 --- a/src/test/ui/variance/variance-unused-type-param.rs +++ b/src/test/ui/variance/variance-unused-type-param.rs @@ -16,4 +16,13 @@ enum ListCell { Nil } +struct WithBounds {} +//~^ ERROR parameter `T` is never used + +struct WithWhereBounds where T: Sized {} +//~^ ERROR parameter `T` is never used + +struct WithOutlivesBounds {} +//~^ ERROR parameter `T` is never used + fn main() {} diff --git a/src/test/ui/variance/variance-unused-type-param.stderr b/src/test/ui/variance/variance-unused-type-param.stderr index 270233c0c977..e612da118f05 100644 --- a/src/test/ui/variance/variance-unused-type-param.stderr +++ b/src/test/ui/variance/variance-unused-type-param.stderr @@ -25,6 +25,30 @@ LL | enum ListCell { = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` = help: if you intended `T` to be a const parameter, use `const T: usize` instead -error: aborting due to 3 previous errors +error[E0392]: parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:19:19 + | +LL | struct WithBounds {} + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0392]: parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:22:24 + | +LL | struct WithWhereBounds where T: Sized {} + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0392]: parameter `T` is never used + --> $DIR/variance-unused-type-param.rs:25:27 + | +LL | struct WithOutlivesBounds {} + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0392`. diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index 2a23d72edc06..f6be550283a2 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -10,8 +10,8 @@ use regex::Regex; // A few of those error codes can't be tested but all the others can and *should* be tested! const EXEMPTED_FROM_TEST: &[&str] = &[ - "E0279", "E0280", "E0313", "E0377", "E0461", "E0462", "E0465", "E0476", "E0514", "E0519", - "E0523", "E0554", "E0640", "E0717", "E0729", + "E0279", "E0313", "E0377", "E0461", "E0462", "E0465", "E0476", "E0514", "E0519", "E0523", + "E0554", "E0640", "E0717", "E0729", ]; // Some error codes don't have any tests apparently...