Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
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
1 change: 1 addition & 0 deletions compiler/rustc_mir/src/transform/check_consts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Symbol;

pub use self::qualifs::Qualif;
pub use self::validation::non_const_fn_could_be_made_stable_const_fn;

mod ops;
pub mod post_drop_elaboration;
Expand Down
45 changes: 1 addition & 44 deletions compiler/rustc_mir/src/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Concrete error types for all operations which may be invalid in a certain const context.

use rustc_errors::{struct_span_err, Applicability};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::config::nightly_options;
Expand All @@ -10,49 +10,6 @@ use rustc_span::{Span, Symbol};

use super::ConstCx;

/// Emits an error and returns `true` if `op` is not allowed in the given const context.
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) -> bool {
debug!("illegal_op: op={:?}", op);

let gate = match op.status_in_item(ccx) {
Status::Allowed => return false,

Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => {
let unstable_in_stable = ccx.is_const_stable_const_fn()
&& !super::allow_internal_unstable(ccx.tcx, ccx.def_id.to_def_id(), gate);

if unstable_in_stable {
ccx.tcx.sess
.struct_span_err(
span,
&format!("const-stable function cannot use `#[feature({})]`", gate.as_str()),
)
.span_suggestion(
ccx.body.span,
"if it is not part of the public API, make this function unstably const",
concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(),
Applicability::HasPlaceholders,
)
.note("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
.emit();
}

return unstable_in_stable;
}

Status::Unstable(gate) => Some(gate),
Status::Forbidden => None,
};

if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
ccx.tcx.sess.miri_unleashed_feature(span, gate);
return false;
}

op.emit_error(ccx, span);
true
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Status {
Allowed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc_middle::mir::{self, BasicBlock, Location};
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;

use super::ops;
use super::ops::{self, NonConstOp};
use super::qualifs::{NeedsDrop, Qualif};
use super::validation::Qualifs;
use super::ConstCx;
Expand Down Expand Up @@ -56,7 +56,7 @@ impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {

impl CheckLiveDrops<'mir, 'tcx> {
fn check_live_drop(&self, span: Span) {
ops::non_const(self.ccx, ops::LiveDrop { dropped_at: None }, span);
ops::LiveDrop { dropped_at: None }.emit_error(self.ccx, span)
}
}

Expand Down
138 changes: 113 additions & 25 deletions compiler/rustc_mir/src/transform/check_consts/validation.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.

use rustc_errors::struct_span_err;
use rustc_hir::{self as hir, LangItem};
use rustc_hir::{def_id::DefId, HirId};
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, HirId, LangItem};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
Expand All @@ -11,20 +11,48 @@ use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{
self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt, TypeAndMut,
};
use rustc_span::{sym, Span};
use rustc_span::{sym, Span, Symbol};
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::{self, TraitEngine};

use std::ops::Deref;

use super::ops::{self, NonConstOp};
use super::ops::{self, NonConstOp, Status};
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
use super::resolver::FlowSensitiveAnalysis;
use super::{is_lang_panic_fn, ConstCx, Qualif};
use crate::const_eval::is_unstable_const_fn;
use crate::dataflow::impls::MaybeMutBorrowedLocals;
use crate::dataflow::{self, Analysis};

/// Returns `true` if the given `fn` could be made into a `const fn` without depending on any
/// unstable features.
///
/// This is used by clippy. Do not use it for const-checking.
pub fn non_const_fn_could_be_made_stable_const_fn(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
body: &Body<'tcx>,
) -> bool {
let const_kind = tcx.hir().body_const_context(def_id);

// Only run this on non-const `fn`s.
assert!(const_kind.is_none());

let ccx = ConstCx {
body,
tcx,
def_id,
const_kind: Some(hir::ConstContext::ConstFn),
param_env: tcx.param_env(def_id),
};

let mut checker = Validator::new(&ccx);
checker.silence_errors = true;
checker.check_body();
checker.passes_checks_without_unstable_features
}

// We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated
// through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals`
// kills locals upon `StorageDead` because a local will never be used after a `StorageDead`.
Expand Down Expand Up @@ -179,7 +207,12 @@ pub struct Validator<'mir, 'tcx> {
/// The span of the current statement.
span: Span,

const_checking_stopped: bool,
/// True if we shouldn't emit errors when we find them.
///
/// This allows items to be speculatively const-checked.
silence_errors: bool,

passes_checks_without_unstable_features: bool,
}

impl Deref for Validator<'mir, 'tcx> {
Expand All @@ -196,7 +229,8 @@ impl Validator<'mir, 'tcx> {
span: ccx.body.span,
ccx,
qualifs: Default::default(),
const_checking_stopped: false,
passes_checks_without_unstable_features: true,
silence_errors: false,
}
}

Expand Down Expand Up @@ -266,15 +300,48 @@ impl Validator<'mir, 'tcx> {
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
/// context.
pub fn check_op_spanned<O: NonConstOp>(&mut self, op: O, span: Span) {
// HACK: This is for strict equivalence with the old `qualify_min_const_fn` pass, which
// only emitted one error per function. It should be removed and the test output updated.
if self.const_checking_stopped {
debug!("illegal_op: op={:?}", op);

let ccx = self.ccx;

let gate = match op.status_in_item(ccx) {
Status::Allowed => return,
Status::Unstable(gate) => Some(gate),
Status::Forbidden => None,
};

self.passes_checks_without_unstable_features = false;

if self.silence_errors {
return;
}

// Unless we are const-checking a const-stable function, return before emitting an error if
// the user has enabled the requisite feature gate.
if let Some(gate) = gate {
if ccx.tcx.features().enabled(gate) {
let unstable_in_stable = ccx.is_const_stable_const_fn()
&& !super::allow_internal_unstable(ccx.tcx, ccx.def_id.to_def_id(), gate);

if unstable_in_stable {
error_unstable_in_stable(ccx, gate, span);
}

return;
}
}

if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
ccx.tcx.sess.miri_unleashed_feature(span, gate);
return;
}

let err_emitted = ops::non_const(self.ccx, op, span);
if err_emitted && O::STOPS_CONST_CHECKING {
self.const_checking_stopped = true;
op.emit_error(ccx, span);

// HACK: This is for strict equivalence with the old `qualify_min_const_fn` pass, which
// only emitted one error per function. It should be removed and the test output updated.
if O::STOPS_CONST_CHECKING {
self.silence_errors = true;
}
}

Expand Down Expand Up @@ -774,12 +841,6 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
// projections that cannot be `NeedsDrop`.
TerminatorKind::Drop { place: dropped_place, .. }
| TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
// If we are checking live drops after drop-elaboration, don't emit duplicate
// errors here.
if super::post_drop_elaboration::checking_enabled(self.ccx) {
return;
}

let mut err_span = self.span;

// Check to see if the type of this place can ever have a drop impl. If not, this
Expand All @@ -791,20 +852,30 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
return;
}

let needs_drop = if let Some(local) = dropped_place.as_local() {
let local_needs_drop = if let Some(local) = dropped_place.as_local() {
// Use the span where the local was declared as the span of the drop error.
err_span = self.body.local_decls[local].source_info.span;
self.qualifs.needs_drop(self.ccx, local, location)
} else {
true
};

if needs_drop {
self.check_op_spanned(
ops::LiveDrop { dropped_at: Some(terminator.source_info.span) },
err_span,
);
if !local_needs_drop {
return;
}

self.passes_checks_without_unstable_features = false;

// If we are checking live drops after drop-elaboration, don't emit duplicate
// errors here.
if super::post_drop_elaboration::checking_enabled(self.ccx) {
return;
}

self.check_op_spanned(
ops::LiveDrop { dropped_at: Some(terminator.source_info.span) },
err_span,
);
}

TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
Expand Down Expand Up @@ -866,3 +937,20 @@ fn place_as_reborrow(
}
})
}

fn error_unstable_in_stable(ccx: &ConstCx<'_, '_>, gate: Symbol, span: Span) {
ccx.tcx
.sess
.struct_span_err(
span,
&format!("const-stable function cannot use `#[feature({})]`", gate.as_str()),
)
.span_suggestion(
ccx.body.span,
"if it is not part of the public API, make this function unstably const",
concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(),
Applicability::HasPlaceholders,
)
.note("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
.emit();
}
1 change: 0 additions & 1 deletion compiler/rustc_mir/src/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub mod match_branches;
pub mod no_landing_pads;
pub mod nrvo;
pub mod promote_consts;
pub mod qualify_min_const_fn;
pub mod remove_noop_landing_pads;
pub mod required_consts;
pub mod rustc_peek;
Expand Down
Loading