about summary refs log tree commit diff
diff options
context:
space:
mode:
authorHenry Boisdequin <65845077+henryboisdequin@users.noreply.github.com>2021-01-29 11:54:19 +0530
committerHenry Boisdequin <65845077+henryboisdequin@users.noreply.github.com>2021-02-03 10:18:08 +0530
commitc2e849c0221bbaf1106884cd776e375168a95323 (patch)
tree2817749464cf18a0471f6333e0457576889aa433
parent368275062fb655c1f36e0398f88b15379a1f3c93 (diff)
added a suggestion to create a `const` item if the `fn` in the array repeat expression is a `const fn`
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs5
-rw-r--r--compiler/rustc_mir/src/borrow_check/type_check/mod.rs13
-rw-r--r--compiler/rustc_mir/src/transform/promote_consts.rs35
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs18
-rw-r--r--src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr1
-rw-r--r--src/test/ui/consts/const-fn-in-vec.rs7
-rw-r--r--src/test/ui/consts/const-fn-in-vec.stderr12
7 files changed, 87 insertions, 4 deletions
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 <https://github.com/rust-lang/rfcs/pull/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<Bar>; 2] = [no_copy(); 2];
    = help: the following implementations were found:
              <Option<T> as Copy>
    = note: the `Copy` trait is required because the repeated element will be copied
+   = help: create an inline `const` block, see PR #2920 <https://github.com/rust-lang/rfcs/pull/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 <https://github.com/rust-lang/rfcs/pull/2920> for more information
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.