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

Use explicit promotion for constants in repeat expressions #70042

Closed
wants to merge 9 commits into from
16 changes: 11 additions & 5 deletions src/librustc_mir/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations}
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::MaybeInitializedPlaces;
use crate::dataflow::ResultsCursor;
use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute;
use crate::transform::{
check_consts::ConstCx,
promote_consts::should_suggest_const_in_array_repeat_expressions_attribute,
};

use crate::borrow_check::{
borrow_set::BorrowSet,
Expand Down Expand Up @@ -1997,14 +2000,17 @@ 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) {
let ccx = ConstCx::new_with_param_env(
tcx,
self.mir_def_id,
body,
self.param_env,
);
// To determine if `const_in_array_repeat_expressions` 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,
&ccx, operand,
);
debug!("check_rvalue: should_suggest={:?}", should_suggest);

Expand Down
15 changes: 12 additions & 3 deletions src/librustc_mir/transform/check_consts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,33 @@ pub mod validation;

/// Information about the item currently being const-checked, as well as a reference to the global
/// context.
pub struct Item<'mir, 'tcx> {
pub struct ConstCx<'mir, 'tcx> {
pub body: mir::ReadOnlyBodyAndCache<'mir, 'tcx>,
pub tcx: TyCtxt<'tcx>,
pub def_id: DefId,
pub param_env: ty::ParamEnv<'tcx>,
pub const_kind: Option<ConstKind>,
}

impl Item<'mir, 'tcx> {
impl ConstCx<'mir, 'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
def_id: DefId,
body: mir::ReadOnlyBodyAndCache<'mir, 'tcx>,
) -> Self {
let param_env = tcx.param_env(def_id);
Self::new_with_param_env(tcx, def_id, body, param_env)
}

pub fn new_with_param_env(
tcx: TyCtxt<'tcx>,
def_id: DefId,
body: mir::ReadOnlyBodyAndCache<'mir, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
let const_kind = ConstKind::for_item(tcx, def_id);

Item { body, tcx, def_id, param_env, const_kind }
ConstCx { body, tcx, def_id, param_env, const_kind }
}

/// Returns the kind of const context this `Item` represents (`const`, `static`, etc.).
Expand Down
124 changes: 62 additions & 62 deletions src/librustc_mir/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};

use super::{ConstKind, Item};
use super::{ConstCx, ConstKind};

/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
Expand All @@ -27,19 +27,19 @@ pub trait NonConstOp: std::fmt::Debug {
///
/// By default, it returns `true` if and only if this operation has a corresponding feature
/// gate and that gate is enabled.
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
Self::feature_gate().map_or(false, |gate| item.tcx.features().enabled(gate))
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate))
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let mut err = struct_span_err!(
item.tcx.sess,
ccx.tcx.sess,
span,
E0019,
"{} contains unimplemented expression type",
item.const_kind()
ccx.const_kind()
);
if item.tcx.sess.teach(&err.get_code().unwrap()) {
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"A function call isn't allowed in the const's initialization expression \
because the expression's value must be known at compile-time.",
Expand All @@ -66,9 +66,9 @@ impl NonConstOp for Downcast {
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let mut err =
item.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
err.emit();
}
}
Expand All @@ -77,14 +77,14 @@ impl NonConstOp for FnCallIndirect {
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let mut err = struct_span_err!(
item.tcx.sess,
ccx.tcx.sess,
span,
E0015,
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
item.const_kind(),
ccx.const_kind(),
);
err.emit();
}
Expand All @@ -106,12 +106,12 @@ impl NonConstOp for FnCallOther {
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let FnCallUnstable(def_id, feature) = *self;

let mut err = item.tcx.sess.struct_span_err(
let mut err = ccx.tcx.sess.struct_span_err(
span,
&format!("`{}` is not yet stable as a const fn", item.tcx.def_path_str(def_id)),
&format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
);
if nightly_options::is_nightly_build() {
err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
Expand All @@ -125,16 +125,16 @@ pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
const IS_SUPPORTED_IN_MIRI: bool = false;

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let mut err = struct_span_err!(
item.tcx.sess,
ccx.tcx.sess,
span,
E0010,
"allocations are not allowed in {}s",
item.const_kind()
ccx.const_kind()
);
err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"The value of statics and constants must be known at compile time, \
and they live for the entire lifetime of a program. Creating a boxed \
Expand All @@ -153,23 +153,23 @@ impl NonConstOp for IfOrMatch {
Some(sym::const_if_match)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
// This should be caught by the HIR const-checker.
item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
}
}

#[derive(Debug)]
pub struct LiveDrop;
impl NonConstOp for LiveDrop {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
struct_span_err!(
item.tcx.sess,
ccx.tcx.sess,
span,
E0493,
"destructors cannot be evaluated at compile-time"
)
.span_label(span, format!("{}s cannot evaluate destructors", item.const_kind()))
.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()))
.emit();
}
}
Expand All @@ -181,18 +181,18 @@ impl NonConstOp for Loop {
Some(sym::const_loop)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
// This should be caught by the HIR const-checker.
item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
ccx.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
}
}

#[derive(Debug)]
pub struct CellBorrow;
impl NonConstOp for CellBorrow {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
struct_span_err!(
item.tcx.sess,
ccx.tcx.sess,
span,
E0492,
"cannot borrow a constant which may contain \
Expand All @@ -209,19 +209,19 @@ impl NonConstOp for MutBorrow {
Some(sym::const_mut_refs)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let mut err = feature_err(
&item.tcx.sess.parse_sess,
&ccx.tcx.sess.parse_sess,
sym::const_mut_refs,
span,
&format!(
"references in {}s may only refer \
to immutable values",
item.const_kind()
ccx.const_kind()
),
);
err.span_label(span, format!("{}s require immutable values", item.const_kind()));
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.span_label(span, format!("{}s require immutable values", ccx.const_kind()));
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"References in statics and constants may only refer \
to immutable values.\n\n\
Expand All @@ -244,12 +244,12 @@ impl NonConstOp for MutAddressOf {
Some(sym::const_mut_refs)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
&ccx.tcx.sess.parse_sess,
sym::const_mut_refs,
span,
&format!("`&raw mut` is not allowed in {}s", item.const_kind()),
&format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
)
.emit();
}
Expand All @@ -270,12 +270,12 @@ impl NonConstOp for Panic {
Some(sym::const_panic)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
&ccx.tcx.sess.parse_sess,
sym::const_panic,
span,
&format!("panicking in {}s is unstable", item.const_kind()),
&format!("panicking in {}s is unstable", ccx.const_kind()),
)
.emit();
}
Expand All @@ -288,12 +288,12 @@ impl NonConstOp for RawPtrComparison {
Some(sym::const_compare_raw_pointers)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
&ccx.tcx.sess.parse_sess,
sym::const_compare_raw_pointers,
span,
&format!("comparing raw pointers inside {}", item.const_kind()),
&format!("comparing raw pointers inside {}", ccx.const_kind()),
)
.emit();
}
Expand All @@ -306,12 +306,12 @@ impl NonConstOp for RawPtrDeref {
Some(sym::const_raw_ptr_deref)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
&ccx.tcx.sess.parse_sess,
sym::const_raw_ptr_deref,
span,
&format!("dereferencing raw pointers in {}s is unstable", item.const_kind(),),
&format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
)
.emit();
}
Expand All @@ -324,12 +324,12 @@ impl NonConstOp for RawPtrToIntCast {
Some(sym::const_raw_ptr_to_usize_cast)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
&ccx.tcx.sess.parse_sess,
sym::const_raw_ptr_to_usize_cast,
span,
&format!("casting pointers to integers in {}s is unstable", item.const_kind(),),
&format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
)
.emit();
}
Expand All @@ -339,22 +339,22 @@ impl NonConstOp for RawPtrToIntCast {
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
item.const_kind().is_static()
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
ccx.const_kind().is_static()
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let mut err = struct_span_err!(
item.tcx.sess,
ccx.tcx.sess,
span,
E0013,
"{}s cannot refer to statics",
item.const_kind()
ccx.const_kind()
);
err.help(
"consider extracting the value of the `static` to a `const`, and referring to that",
);
if item.tcx.sess.teach(&err.get_code().unwrap()) {
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"`static` and `const` variables can refer to other `const` variables. \
A `const` variable, however, cannot refer to a `static` variable.",
Expand All @@ -371,9 +371,9 @@ pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
const IS_SUPPORTED_IN_MIRI: bool = false;

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
struct_span_err!(
item.tcx.sess,
ccx.tcx.sess,
span,
E0625,
"thread-local statics cannot be \
Expand All @@ -386,19 +386,19 @@ impl NonConstOp for ThreadLocalAccess {
#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
// Union accesses are stable in all contexts except `const fn`.
item.const_kind() != ConstKind::ConstFn
|| item.tcx.features().enabled(Self::feature_gate().unwrap())
ccx.const_kind() != ConstKind::ConstFn
|| ccx.tcx.features().enabled(Self::feature_gate().unwrap())
}

fn feature_gate() -> Option<Symbol> {
Some(sym::const_fn_union)
}

fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
&ccx.tcx.sess.parse_sess,
sym::const_fn_union,
span,
"unions in const fn are unstable",
Expand Down
Loading