Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest to create a new const item if the fn in the array is a const fn #81503

Merged
merged 2 commits into from
Feb 16, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
henryboisdequin marked this conversation as resolved.
Show resolved Hide resolved
"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];`",
);
henryboisdequin marked this conversation as resolved.
Show resolved Hide resolved
} 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 {
henryboisdequin marked this conversation as resolved.
Show resolved Hide resolved
// 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`.