Skip to content

Commit

Permalink
added a suggestion to create a const item if the fn in the array …
Browse files Browse the repository at this point in the history
…repeat expression is a `const fn`
  • Loading branch information
henryboisdequin committed Feb 3, 2021
1 parent 3682750 commit c2e849c
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 4 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_mir/src/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_mir/src/transform/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/consts/const-fn-in-vec.rs
Original file line number Diff line number Diff line change
@@ -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);
}
12 changes: 12 additions & 0 deletions src/test/ui/consts/const-fn-in-vec.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit c2e849c

Please sign in to comment.