From 92b151287fcda73db8e95eeca8be97d66905626d Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 26 Oct 2019 23:59:24 +0100 Subject: [PATCH] suggest `const_in_array_repeat_expression` flag This commit adds a suggestion to add the `#![feature(const_in_array_repeat_expression)]` attribute to the crate when a promotable expression is used in a repeat expression. Signed-off-by: David Wood --- src/librustc/traits/error_reporting.rs | 11 +++++++- src/librustc/traits/mod.rs | 5 ++-- src/librustc/traits/structural_impls.rs | 2 +- .../borrow_check/nll/type_check/mod.rs | 10 +++++++- src/librustc_mir/transform/promote_consts.rs | 25 +++++++++++++++++++ src/librustc_mir/transform/qualify_consts.rs | 21 ++++++++++------ ...-gate-const_in_array_repeat_expressions.rs | 7 ++++++ ...e-const_in_array_repeat_expressions.stderr | 16 ++++++++++-- 8 files changed, 83 insertions(+), 14 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index b2e5624f4760c..b55c0f29a70ec 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -2112,9 +2112,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.note(&format!("required by cast to type `{}`", self.ty_to_string(target))); } - ObligationCauseCode::RepeatVec => { + ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expression) => { err.note("the `Copy` trait is required because the \ repeated element will be copied"); + if suggest_const_in_array_repeat_expression { + err.note("this array initializer can be evaluated at compile-time, for more \ + information, see issue \ + https://github.com/rust-lang/rust/issues/49147"); + if tcx.sess.opts.unstable_features.is_nightly_build() { + err.help("add `#![feature(const_in_array_repeat_expression)]` to the \ + crate attributes to enable"); + } + } } ObligationCauseCode::VariableType(_) => { err.note("all local variables must have a statically known size"); diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 04c1b4d927a9c..b8275299562ce 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -206,8 +206,9 @@ pub enum ObligationCauseCode<'tcx> { SizedReturnType, /// Yield type must be Sized SizedYieldType, - /// [T,..n] --> T must be Copy - RepeatVec, + /// [T,..n] --> T must be Copy. If `true`, suggest `const_in_array_repeat_expression` feature + /// flag. + RepeatVec(bool), /// Types of fields (other than the last, except for packed structs) in a struct must be sized. FieldSized { adt_kind: AdtKind, last: bool }, diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index d9b796d063e26..109e884f8bd16 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -494,7 +494,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { super::SizedArgumentType => Some(super::SizedArgumentType), super::SizedReturnType => Some(super::SizedReturnType), super::SizedYieldType => Some(super::SizedYieldType), - super::RepeatVec => Some(super::RepeatVec), + super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)), super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }), super::ConstSized => Some(super::ConstSized), super::ConstPatternStructural => Some(super::ConstPatternStructural), diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index b5560fe6751bd..9f2f174553f03 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -16,6 +16,7 @@ use crate::borrow_check::nll::type_check::free_region_relations::{ }; use crate::borrow_check::nll::universal_regions::{DefiningTy, UniversalRegions}; use crate::borrow_check::nll::ToRegionVid; +use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute; use crate::dataflow::move_paths::MoveData; use crate::dataflow::FlowAtLocation; use crate::dataflow::MaybeInitializedPlaces; @@ -1983,12 +1984,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { 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) { + // To determine if `const_in_array_repeat_expression` feature gate should + // be mentioned, need to check if the rvalue is promotable. + let should_suggest = + should_suggest_const_in_array_repeat_expressions_attribute( + tcx, self.mir_def_id, body, operand); + debug!("check_rvalue: should_suggest={:?}", should_suggest); + self.infcx.report_selection_error( &traits::Obligation::new( ObligationCause::new( span, self.tcx().hir().def_index_to_hir_id(self.mir_def_id.index), - traits::ObligationCauseCode::RepeatVec, + traits::ObligationCauseCode::RepeatVec(should_suggest), ), self.param_env, ty::Predicate::Trait(ty::Binder::bind(ty::TraitPredicate { diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 3af08090853a6..8def717f1580a 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -1110,3 +1110,28 @@ pub fn promote_candidates<'tcx>( promotions } + +/// This function returns `true` if the `const_in_array_repeat_expression` feature attribute should +/// be suggested. This function is probably quite expensive, it shouldn't be run in the happy path. +/// Feature attribute should be suggested if `operand` can be promoted and the feature is not +/// enabled. +crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>( + tcx: TyCtxt<'tcx>, + mir_def_id: DefId, + body: &Body<'tcx>, + operand: &Operand<'tcx>, +) -> bool { + let mut rpo = traversal::reverse_postorder(body); + let (temps, _) = collect_temps_and_candidates(tcx, body, &mut rpo); + let validator = Validator { + item: Item::new(tcx, mir_def_id, body), + temps: &temps, + explicit: false, + }; + + let should_promote = validator.validate_operand(operand).is_ok(); + let feature_flag = tcx.features().const_in_array_repeat_expressions; + debug!("should_suggest_const_in_array_repeat_expressions_flag: mir_def_id={:?} \ + should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag); + should_promote && !feature_flag +} diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 2f77cd5ddf716..3702dd9ad493f 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -878,13 +878,11 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { } }, ValueSource::Rvalue(&Rvalue::Repeat(ref operand, _)) => { - let candidate = Candidate::Repeat(location); - let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) || - IsNotPromotable::in_operand(self, operand); - debug!("assign: self.def_id={:?} operand={:?}", self.def_id, operand); - if !not_promotable && self.tcx.features().const_in_array_repeat_expressions { - debug!("assign: candidate={:?}", candidate); - self.promotion_candidates.push(candidate); + debug!("assign: self.cx.mode={:?} self.def_id={:?} location={:?} operand={:?}", + self.cx.mode, self.def_id, location, operand); + if self.should_promote_repeat_expression(operand) && + self.tcx.features().const_in_array_repeat_expressions { + self.promotion_candidates.push(Candidate::Repeat(location)); } }, _ => {}, @@ -1149,6 +1147,15 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { candidates } + + /// Returns `true` if the operand of a repeat expression is promotable. + fn should_promote_repeat_expression(&self, operand: &Operand<'tcx>) -> bool { + let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) || + IsNotPromotable::in_operand(self, operand); + debug!("should_promote_repeat_expression: operand={:?} not_promotable={:?}", + operand, not_promotable); + !not_promotable + } } impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs index be195271c10ce..c3c554d7d27f9 100644 --- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs +++ b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs @@ -3,9 +3,16 @@ struct Bar; +// This function would compile with the feature gate, and tests that it is suggested. fn foo() { let arr: [Option; 2] = [None::; 2]; //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] } +// This function would not compile with the feature gate, and tests that it is not suggested. +fn bar() { + let arr: [Option; 2] = [Some("foo".to_string()); 2]; + //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] +} + fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr index eed69a0c28db8..cd9242de88f25 100644 --- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied - --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:7:36 + --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:8:36 | LL | let arr: [Option; 2] = [None::; 2]; | ^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` @@ -7,7 +7,19 @@ LL | let arr: [Option; 2] = [None::; 2]; = help: the following implementations were found: as std::marker::Copy> = note: the `Copy` trait is required because the repeated element will be copied + = note: this array initializer can be evaluated at compile-time, for more information, see issue https://github.com/rust-lang/rust/issues/49147 + = help: add `#![feature(const_in_array_repeat_expression)]` to the crate attributes to enable -error: aborting due to previous error +error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied + --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:14:36 + | +LL | let arr: [Option; 2] = [Some("foo".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | + = help: the following implementations were found: + as std::marker::Copy> + = note: the `Copy` trait is required because the repeated element will be copied + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`.