From c2e849c0221bbaf1106884cd776e375168a95323 Mon Sep 17 00:00:00 2001 From: Henry Boisdequin <65845077+henryboisdequin@users.noreply.github.com> Date: Fri, 29 Jan 2021 11:54:19 +0530 Subject: [PATCH] added a suggestion to create a `const` item if the `fn` in the array repeat expression is a `const fn` --- compiler/rustc_middle/src/traits/mod.rs | 5 ++- .../src/borrow_check/type_check/mod.rs | 13 +++++-- .../rustc_mir/src/transform/promote_consts.rs | 35 +++++++++++++++++++ .../src/traits/error_reporting/suggestions.rs | 18 +++++++++- .../const-blocks/fn-call-in-non-const.stderr | 1 + src/test/ui/consts/const-fn-in-vec.rs | 7 ++++ src/test/ui/consts/const-fn-in-vec.stderr | 12 +++++++ 7 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/consts/const-fn-in-vec.rs create mode 100644 src/test/ui/consts/const-fn-in-vec.stderr diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 163b400973b..f34ee228602 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -228,7 +228,10 @@ pub enum ObligationCauseCode<'tcx> { /// Inline asm operand type must be `Sized`. InlineAsmSized, /// `[T, ..n]` implies that `T` must be `Copy`. - RepeatVec, + /// If the function in the array repeat expression is a `const fn`, + /// display a help message suggesting to move the function call to a + /// new `const` item while saying that `T` doesn't implement `Copy`. + RepeatVec(bool), /// Types of fields (other than the last, except for packed structs) in a struct must be sized. FieldSized { diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 3ba06bdd6e0..44fa1970717 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -43,6 +43,9 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations} use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::move_paths::MoveData; use crate::dataflow::ResultsCursor; +use crate::transform::{ + check_consts::ConstCx, promote_consts::is_const_fn_in_array_repeat_expression, +}; use crate::borrow_check::{ borrow_set::BorrowSet, @@ -1988,18 +1991,24 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Operand::Copy(..) | Operand::Constant(..) => { // These are always okay: direct use of a const, or a value that can evidently be copied. } - Operand::Move(_) => { + Operand::Move(place) => { // Make sure that repeated elements implement `Copy`. let span = body.source_info(location).span; let ty = operand.ty(body, tcx); if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { + let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env); + let is_const_fn = + is_const_fn_in_array_repeat_expression(&ccx, &place, &body); + + debug!("check_rvalue: is_const_fn={:?}", is_const_fn); + let def_id = body.source.def_id().expect_local(); self.infcx.report_selection_error( &traits::Obligation::new( ObligationCause::new( span, self.tcx().hir().local_def_id_to_hir_id(def_id), - traits::ObligationCauseCode::RepeatVec, + traits::ObligationCauseCode::RepeatVec(is_const_fn), ), self.param_env, ty::Binder::bind(ty::TraitRef::new( diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index b4504a0e223..1d4438d80c9 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -1231,3 +1231,38 @@ pub fn promote_candidates<'tcx>( promotions } + +/// This function returns `true` if the function being called in the array +/// repeat expression is a `const` function. +crate fn is_const_fn_in_array_repeat_expression<'tcx>( + ccx: &ConstCx<'_, 'tcx>, + place: &Place<'tcx>, + body: &Body<'tcx>, +) -> bool { + match place.as_local() { + // rule out cases such as: `let my_var = some_fn(); [my_var; N]` + Some(local) if body.local_decls[local].is_user_variable() => return false, + None => return false, + _ => {} + } + + for block in body.basic_blocks() { + if let Some(Terminator { kind: TerminatorKind::Call { func, destination, .. }, .. }) = + &block.terminator + { + if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { + if let ty::FnDef(def_id, _) = *ty.kind() { + if let Some((destination_place, _)) = destination { + if destination_place == place { + if is_const_fn(ccx.tcx, def_id) { + return true; + } + } + } + } + } + } + } + + false +} 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 21828006164..859e2fb7162 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1881,10 +1881,26 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::Coercion { source: _, target } => { err.note(&format!("required by cast to type `{}`", self.ty_to_string(target))); } - ObligationCauseCode::RepeatVec => { + ObligationCauseCode::RepeatVec(is_const_fn) => { err.note( "the `Copy` trait is required because the repeated element will be copied", ); + if is_const_fn && !self.tcx.sess.is_nightly_build() { + err.help( + "consider creating a new `const` item and initializing with the result \ + of the function call to be used in the repeat position, like \ + `const VAL: Type = const_fn();` and `let x = [VAL; 42];`", + ); + } else if self.tcx.sess.is_nightly_build() && is_const_fn { + err.help( + "create an inline `const` block, see PR \ + #2920 \ + for more information", + ); + } else { + // Don't suggest anything to the user as suggesting the user to make the function `const` + // could lead them down the wrong path. + } } ObligationCauseCode::VariableType(hir_id) => { let parent_node = self.tcx.hir().get_parent_node(hir_id); diff --git a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr index b75452cd217..661f279e6be 100644 --- a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr +++ b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr @@ -7,6 +7,7 @@ LL | let _: [Option; 2] = [no_copy(); 2]; = help: the following implementations were found: as Copy> = note: the `Copy` trait is required because the repeated element will be copied + = help: create an inline `const` block, see PR #2920 for more information error: aborting due to previous error diff --git a/src/test/ui/consts/const-fn-in-vec.rs b/src/test/ui/consts/const-fn-in-vec.rs new file mode 100644 index 00000000000..edf29015200 --- /dev/null +++ b/src/test/ui/consts/const-fn-in-vec.rs @@ -0,0 +1,7 @@ +fn main() { + // should hint to create an inline const block + // as all tests are on "nightly" + let strings: [String; 5] = [String::new(); 5]; + //~^ ERROR the trait bound `String: Copy` is not satisfied + println!("{:?}", strings); +} diff --git a/src/test/ui/consts/const-fn-in-vec.stderr b/src/test/ui/consts/const-fn-in-vec.stderr new file mode 100644 index 00000000000..c477605842d --- /dev/null +++ b/src/test/ui/consts/const-fn-in-vec.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/const-fn-in-vec.rs:4:32 + | +LL | let strings: [String; 5] = [String::new(); 5]; + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | + = note: the `Copy` trait is required because the repeated element will be copied + = help: create an inline `const` block, see PR #2920 for more information + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.