Skip to content

Commit

Permalink
Add structured errors for qualify_min_const_fn checks
Browse files Browse the repository at this point in the history
  • Loading branch information
ecstatic-morse committed Sep 22, 2020
1 parent bfc10a8 commit 5ee5429
Showing 1 changed file with 220 additions and 3 deletions.
223 changes: 220 additions & 3 deletions compiler/rustc_mir/src/transform/check_consts/ops.rs
Expand Up @@ -98,6 +98,34 @@ pub trait NonConstOp: std::fmt::Debug {
}
}

#[derive(Debug)]
pub struct Abort;
impl NonConstOp for Abort {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
mcf_status_in_item(ccx)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "abort is not stable in const fn")
}
}

#[derive(Debug)]
pub struct NonPrimitiveOp;
impl NonConstOp for NonPrimitiveOp {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
mcf_status_in_item(ccx)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "only int, `bool` and `char` operations are stable in const fn")
}
}

/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
Expand Down Expand Up @@ -130,7 +158,8 @@ impl NonConstOp for FnCallNonConst {
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);

impl NonConstOp for FnCallUnstable {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let FnCallUnstable(def_id, feature) = *self;
Expand All @@ -139,13 +168,51 @@ impl NonConstOp for FnCallUnstable {
span,
&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));

if ccx.is_const_stable_const_fn() {
err.help("Const-stable functions can only call other const-stable functions");
} else if nightly_options::is_nightly_build() {
if let Some(feature) = feature {
err.help(&format!(
"add `#![feature({})]` to the crate attributes to enable",
feature
));
}
}
err.emit();
}
}

#[derive(Debug)]
pub struct FnPtrCast;
impl NonConstOp for FnPtrCast {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
mcf_status_in_item(ccx)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "function pointer casts are not allowed in const fn");
}
}

#[derive(Debug)]
pub struct Generator;
impl NonConstOp for Generator {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
// FIXME: This means generator-only MIR is only forbidden in const fn. This is for
// compatibility with the old code. Such MIR should be forbidden everywhere.
mcf_status_in_item(ccx)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "const fn generators are unstable");
}
}

#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
Expand Down Expand Up @@ -408,6 +475,24 @@ impl NonConstOp for ThreadLocalAccess {
}
}

#[derive(Debug)]
pub struct Transmute;
impl NonConstOp for Transmute {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn_transmute)
}
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "can only call `transmute` from const items, not `const fn`");
}
}

#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
Expand All @@ -430,3 +515,135 @@ impl NonConstOp for UnionAccess {
.emit();
}
}

/// See [#64992].
///
/// [#64992]: https://github.com/rust-lang/rust/issues/64992
#[derive(Debug)]
pub struct UnsizingCast;
impl NonConstOp for UnsizingCast {
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn_transmute)
}
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(
ccx,
span,
"unsizing casts to types besides slices are not allowed in const fn",
);
}
}

pub mod ty {
use super::*;

#[derive(Debug)]
pub struct MutRef;
impl NonConstOp for MutRef {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_mut_refs)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "mutable references in const fn are unstable");
}
}

#[derive(Debug)]
pub struct FnPtr;
impl NonConstOp for FnPtr {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
// FIXME: This attribute a hack to allow the specialization of the `futures` API. See
// #59739. We should have a proper feature gate for this.
if ccx.tcx.has_attr(ccx.def_id.to_def_id(), sym::rustc_allow_const_fn_ptr) {
Status::Allowed
} else {
mcf_status_in_item(ccx)
}
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "function pointers in const fn are unstable");
}
}

#[derive(Debug)]
pub struct ImplTrait;
impl NonConstOp for ImplTrait {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
mcf_status_in_item(ccx)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(ccx, span, "`impl Trait` in const fn is unstable");
}
}

#[derive(Debug)]
pub struct TraitBound;
impl NonConstOp for TraitBound {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
mcf_status_in_item(ccx)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
mcf_emit_error(
ccx,
span,
"trait bounds other than `Sized` on const fn parameters are unstable",
);
}
}

/// A trait bound with the `?const Trait` opt-out
#[derive(Debug)]
pub struct TraitBoundNotConst;
impl NonConstOp for TraitBoundNotConst {
const STOPS_CONST_CHECKING: bool = true;

fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_trait_bound_opt_out)
}

fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_trait_bound_opt_out,
span,
"`?const Trait` syntax is unstable",
)
.emit()
}
}
}

fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn)
}
}

fn mcf_emit_error(ccx: &ConstCx<'_, '_>, span: Span, msg: &str) {
struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg)
.note(
"see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
for more information",
)
.help("add `#![feature(const_fn)]` to the crate attributes to enable")
.emit();
}

0 comments on commit 5ee5429

Please sign in to comment.