rust/compiler/rustc_const_eval/src/check_consts/ops.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

654 lines
24 KiB
Rust
Raw Normal View History

//! Concrete error types for all operations which may be invalid in a certain const context.
2022-05-02 09:31:56 +02:00
use hir::def_id::LocalDefId;
use hir::{ConstContext, LangItem};
use rustc_errors::{codes::*, Diag};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
2021-12-09 22:42:17 +08:00
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::{self, CallSource};
use rustc_middle::span_bug;
2024-05-10 14:59:56 -04:00
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
use rustc_middle::ty::{
self, suggest_constraining_type_param, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef,
Param, TraitRef, Ty,
};
use rustc_middle::util::{call_kind, CallDesugaringKind, CallKind};
use rustc_session::parse::feature_err;
2020-01-01 19:30:57 +01:00
use rustc_span::symbol::sym;
2021-12-09 22:42:17 +08:00
use rustc_span::{BytePos, Pos, Span, Symbol};
use rustc_trait_selection::traits::SelectionContext;
use tracing::debug;
use super::ConstCx;
use crate::errors;
2020-09-17 11:10:36 -07:00
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2020-09-02 13:25:19 -07:00
pub enum Status {
Allowed,
Unstable(Symbol),
Forbidden,
}
2020-09-29 17:52:12 -07:00
#[derive(Clone, Copy)]
pub enum DiagImportance {
2020-09-29 17:52:12 -07:00
/// An operation that must be removed for const-checking to pass.
Primary,
/// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
Secondary,
}
/// An operation that is not *always* allowed in a const context.
2021-12-09 22:42:17 +08:00
pub trait NonConstOp<'tcx>: std::fmt::Debug {
2020-09-02 13:25:19 -07:00
/// Returns an enum indicating whether this operation is allowed within the given item.
2021-12-09 22:42:17 +08:00
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
2020-09-02 13:25:19 -07:00
Status::Forbidden
}
fn importance(&self) -> DiagImportance {
DiagImportance::Primary
2020-09-29 17:52:12 -07:00
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx>;
}
#[derive(Debug)]
pub struct FloatingPointOp;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() == hir::ConstContext::ConstFn {
Status::Unstable(sym::const_fn_floating_point_arithmetic)
} else {
Status::Allowed
}
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
feature_err(
&ccx.tcx.sess,
sym::const_fn_floating_point_arithmetic,
span,
format!("floating point arithmetic is not allowed in {}s", ccx.const_kind()),
)
}
}
/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::UnallowedFnPointerCall { span, kind: ccx.const_kind() })
}
}
/// A function call where the callee is not marked as `const`.
2021-12-09 22:42:17 +08:00
#[derive(Debug, Clone, Copy)]
pub struct FnCallNonConst<'tcx> {
2022-05-02 09:31:56 +02:00
pub caller: LocalDefId,
2021-12-09 22:42:17 +08:00
pub callee: DefId,
pub args: GenericArgsRef<'tcx>,
2021-12-09 22:42:17 +08:00
pub span: Span,
pub call_source: CallSource,
pub feature: Option<Symbol>,
2021-12-09 22:42:17 +08:00
}
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
Reduce capabilities of `Diagnostic`. Currently many diagnostic modifier methods are available on both `Diagnostic` and `DiagnosticBuilder`. This commit removes most of them from `Diagnostic`. To minimize the diff size, it keeps them within `diagnostic.rs` but changes the surrounding `impl Diagnostic` block to `impl DiagnosticBuilder`. (I intend to move things around later, to give a more sensible code layout.) `Diagnostic` keeps a few methods that it still needs, like `sub`, `arg`, and `replace_args`. The `forward!` macro, which defined two additional methods per call (e.g. `note` and `with_note`), is replaced by the `with_fn!` macro, which defines one additional method per call (e.g. `with_note`). It's now also only used when necessary -- not all modifier methods currently need a `with_*` form. (New ones can be easily added as necessary.) All this also requires changing `trait AddToDiagnostic` so its methods take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`. There are three subdiagnostics -- `DelayedAtWithoutNewline`, `DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` -- that are created within the diagnostics machinery and appended to external diagnostics. These are handled at the `Diagnostic` level, which means it's now hard to construct them via `derive(Diagnostic)`, so instead we construct them by hand. This has no effect on what they look like when printed. There are lots of new `allow` markers for `untranslatable_diagnostics` and `diagnostics_outside_of_impl`. This is because `#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic` modifier methods, but missing from the `DiagnosticBuilder` modifier methods. They're now present.
2024-02-06 16:44:30 +11:00
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
let ConstCx { tcx, param_env, body, .. } = *ccx;
2021-12-09 22:42:17 +08:00
let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
let trait_ref = TraitRef::from_method(tcx, trait_id, args);
2021-12-09 22:42:17 +08:00
match self_ty.kind() {
Param(param_ty) => {
debug!(?param_ty);
if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() {
let constraint = with_no_trimmed_paths!(format!(
"~const {}",
trait_ref.print_trait_sugared(),
));
2021-12-09 22:42:17 +08:00
suggest_constraining_type_param(
tcx,
generics,
err,
param_ty.name.as_str(),
2021-12-09 22:42:17 +08:00
&constraint,
None,
2023-02-04 17:09:19 -08:00
None,
2021-12-09 22:42:17 +08:00
);
}
}
ty::Adt(..) => {
let obligation =
Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
2021-12-09 22:42:17 +08:00
let infcx = tcx.infer_ctxt().build();
let mut selcx = SelectionContext::new(&infcx);
let implsrc = selcx.select(&obligation);
if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
// FIXME(effects) revisit this
if !tcx.is_const_trait_impl_raw(data.impl_def_id) {
let span = tcx.def_span(data.impl_def_id);
err.subdiagnostic(errors::NonConstImplNote { span });
}
2021-12-09 22:42:17 +08:00
}
}
_ => {}
}
};
let call_kind =
call_kind(tcx, ccx.param_env, callee, args, span, call_source.from_hir_call(), None);
2021-12-09 22:42:17 +08:00
debug!(?call_kind);
let mut err = match call_kind {
CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
macro_rules! error {
($err:ident) => {
tcx.dcx().create_err(errors::$err {
span,
ty: self_ty,
kind: ccx.const_kind(),
})
2021-12-09 22:42:17 +08:00
};
}
let mut err = match kind {
2021-12-09 22:42:17 +08:00
CallDesugaringKind::ForLoopIntoIter => {
error!(NonConstForLoopIntoIter)
2021-12-09 22:42:17 +08:00
}
CallDesugaringKind::QuestionBranch => {
error!(NonConstQuestionBranch)
2021-12-09 22:42:17 +08:00
}
CallDesugaringKind::QuestionFromResidual => {
error!(NonConstQuestionFromResidual)
2021-12-09 22:42:17 +08:00
}
CallDesugaringKind::TryBlockFromOutput => {
error!(NonConstTryBlockFromOutput)
2021-12-09 22:42:17 +08:00
}
2023-04-25 19:48:59 +00:00
CallDesugaringKind::Await => {
error!(NonConstAwait)
2023-04-25 19:48:59 +00:00
}
2021-12-09 22:42:17 +08:00
};
diag_trait(&mut err, self_ty, kind.trait_def_id(tcx));
err
2021-12-09 22:42:17 +08:00
}
2021-12-29 17:05:54 +08:00
CallKind::FnCall { fn_trait_id, self_ty } => {
let note = match self_ty.kind() {
2021-12-29 17:05:54 +08:00
FnDef(def_id, ..) => {
let span = tcx.def_span(*def_id);
2021-12-29 17:05:54 +08:00
if ccx.tcx.is_const_fn_raw(*def_id) {
span_bug!(span, "calling const FnDef errored when it shouldn't");
}
Some(errors::NonConstClosureNote::FnDef { span })
2021-12-29 17:05:54 +08:00
}
FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
Closure(..) => Some(errors::NonConstClosureNote::Closure),
_ => None,
};
let mut err = tcx.dcx().create_err(errors::NonConstClosure {
span,
kind: ccx.const_kind(),
note,
});
2021-12-29 17:05:54 +08:00
diag_trait(&mut err, self_ty, fn_trait_id);
err
2021-12-29 17:05:54 +08:00
}
2021-12-09 22:42:17 +08:00
CallKind::Operator { trait_id, self_ty, .. } => {
let mut err = if let CallSource::MatchCmp = call_source {
tcx.dcx().create_err(errors::NonConstMatchEq {
span,
kind: ccx.const_kind(),
ty: self_ty,
})
} else {
let mut sugg = None;
2021-12-09 22:42:17 +08:00
2024-06-14 14:46:32 -04:00
if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) {
match (args[0].unpack(), args[1].unpack()) {
(GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
if self_ty == rhs_ty
&& self_ty.is_ref()
&& self_ty.peel_refs().is_primitive() =>
{
let mut num_refs = 0;
let mut tmp_ty = self_ty;
while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
num_refs += 1;
tmp_ty = *inner_ty;
}
let deref = "*".repeat(num_refs);
if let Ok(call_str) =
ccx.tcx.sess.source_map().span_to_snippet(span)
{
if let Some(eq_idx) = call_str.find("==") {
if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
.find(|c: char| !c.is_whitespace())
{
let rhs_pos = span.lo()
+ BytePos::from_usize(eq_idx + 2 + rhs_idx);
let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
sugg = Some(errors::ConsiderDereferencing {
deref,
span: span.shrink_to_lo(),
rhs_span,
});
}
}
}
}
_ => {}
}
}
tcx.dcx().create_err(errors::NonConstOperator {
span,
kind: ccx.const_kind(),
sugg,
})
};
diag_trait(&mut err, self_ty, trait_id);
err
}
2021-12-09 22:42:17 +08:00
CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
// Check first whether the source is accessible (issue #87060)
let target = if tcx.sess.source_map().is_span_accessible(deref_target) {
Some(deref_target)
} else {
None
};
let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion {
span,
ty: self_ty,
kind: ccx.const_kind(),
target_ty: deref_target_ty,
deref_target: target,
});
2021-12-09 22:42:17 +08:00
diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
err
2021-12-09 22:42:17 +08:00
}
_ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => {
ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() })
}
_ => ccx.dcx().create_err(errors::NonConstFnCall {
2021-12-09 22:42:17 +08:00
span,
def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
kind: ccx.const_kind(),
}),
2021-12-09 22:42:17 +08:00
};
err.note(format!(
2021-12-09 22:42:17 +08:00
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
ccx.const_kind(),
));
if let Some(feature) = feature {
ccx.tcx.disabled_nightly_features(
&mut err,
body.source.def_id().as_local().map(|local| ccx.tcx.local_def_id_to_hir_id(local)),
[(String::new(), feature)],
);
}
if let ConstContext::Static(_) = ccx.const_kind() {
err.note("consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`");
}
err
}
}
2021-08-22 17:27:18 +02:00
/// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let FnCallUnstable(def_id, feature) = *self;
let mut err = ccx
.dcx()
.create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
Reduce capabilities of `Diagnostic`. Currently many diagnostic modifier methods are available on both `Diagnostic` and `DiagnosticBuilder`. This commit removes most of them from `Diagnostic`. To minimize the diff size, it keeps them within `diagnostic.rs` but changes the surrounding `impl Diagnostic` block to `impl DiagnosticBuilder`. (I intend to move things around later, to give a more sensible code layout.) `Diagnostic` keeps a few methods that it still needs, like `sub`, `arg`, and `replace_args`. The `forward!` macro, which defined two additional methods per call (e.g. `note` and `with_note`), is replaced by the `with_fn!` macro, which defines one additional method per call (e.g. `with_note`). It's now also only used when necessary -- not all modifier methods currently need a `with_*` form. (New ones can be easily added as necessary.) All this also requires changing `trait AddToDiagnostic` so its methods take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`. There are three subdiagnostics -- `DelayedAtWithoutNewline`, `DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` -- that are created within the diagnostics machinery and appended to external diagnostics. These are handled at the `Diagnostic` level, which means it's now hard to construct them via `derive(Diagnostic)`, so instead we construct them by hand. This has no effect on what they look like when printed. There are lots of new `allow` markers for `untranslatable_diagnostics` and `diagnostics_outside_of_impl`. This is because `#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic` modifier methods, but missing from the `DiagnosticBuilder` modifier methods. They're now present.
2024-02-06 16:44:30 +11:00
// FIXME: make this translatable
#[allow(rustc::untranslatable_diagnostic)]
if ccx.is_const_stable_const_fn() {
err.help("const-stable functions can only call other const-stable functions");
2020-10-10 14:27:52 -04:00
} else if ccx.tcx.sess.is_nightly_build() {
if let Some(feature) = feature {
err.help(format!("add `#![feature({feature})]` to the crate attributes to enable"));
}
}
err
}
}
#[derive(Debug)]
2023-10-19 16:06:43 +00:00
pub struct Coroutine(pub hir::CoroutineKind);
impl<'tcx> NonConstOp<'tcx> for Coroutine {
2021-12-09 22:42:17 +08:00
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
if let hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Block,
) = self.0
{
Status::Unstable(sym::const_async_blocks)
} else {
Status::Forbidden
}
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
if let hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Block,
) = self.0
{
ccx.tcx.sess.create_feature_err(
errors::UnallowedOpInConstContext { span, msg },
sym::const_async_blocks,
)
} else {
ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
}
}
}
#[derive(Debug)]
pub struct HeapAllocation;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::UnallowedHeapAllocations {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0010).then_some(()),
})
}
}
#[derive(Debug)]
pub struct InlineAsm;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for InlineAsm {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
}
}
#[derive(Debug)]
pub struct LiveDrop<'tcx> {
pub dropped_at: Option<Span>,
pub dropped_ty: Ty<'tcx>,
}
impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::LiveDrop {
span,
dropped_ty: self.dropped_ty,
kind: ccx.const_kind(),
dropped_at: self.dropped_at,
})
}
}
#[derive(Debug)]
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
/// the final value of the constant.
pub struct TransientCellBorrow;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_refs_to_cell)
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.tcx
.sess
.create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
}
}
#[derive(Debug)]
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
/// the final value of the constant, and thus we cannot allow this (for now). We may allow
/// it in the future for static items.
2019-11-26 10:00:41 -05:00
pub struct CellBorrow;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for CellBorrow {
fn importance(&self) -> DiagImportance {
// Most likely the code will try to do mutation with these borrows, which
// triggers its own errors. Only show this one if that does not happen.
DiagImportance::Secondary
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
// FIXME: Maybe a more elegant solution to this if else case
if let hir::ConstContext::Static(_) = ccx.const_kind() {
ccx.dcx().create_err(errors::InteriorMutableDataRefer {
span,
opt_help: Some(()),
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0492).then_some(()),
})
} else {
ccx.dcx().create_err(errors::InteriorMutableDataRefer {
span,
opt_help: None,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0492).then_some(()),
})
}
2019-11-26 10:00:41 -05:00
}
}
#[derive(Debug)]
/// This op is for `&mut` borrows in the trailing expression of a constant
/// which uses the "enclosing scopes rule" to leak its locals into anonymous
/// static or const items.
pub struct MutBorrow(pub hir::BorrowKind);
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for MutBorrow {
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Forbidden
}
fn importance(&self) -> DiagImportance {
// Most likely the code will try to do mutation with these borrows, which
// triggers its own errors. Only show this one if that does not happen.
DiagImportance::Secondary
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
match self.0 {
2023-12-17 11:24:34 +01:00
hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::UnallowedMutableRaw {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764).then_some(()),
}),
hir::BorrowKind::Ref => ccx.dcx().create_err(errors::UnallowedMutableRefs {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764).then_some(()),
}),
}
}
}
#[derive(Debug)]
pub struct TransientMutBorrow(pub hir::BorrowKind);
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let kind = ccx.const_kind();
match self.0 {
2023-12-17 11:24:34 +01:00
hir::BorrowKind::Raw => ccx
.tcx
.sess
.create_feature_err(errors::TransientMutRawErr { span, kind }, sym::const_mut_refs),
hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
errors::TransientMutBorrowErr { span, kind },
sym::const_mut_refs,
),
}
}
}
#[derive(Debug)]
pub struct MutDeref;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for MutDeref {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
2020-09-02 13:25:19 -07:00
Status::Unstable(sym::const_mut_refs)
2019-11-22 19:59:34 -05:00
}
2020-09-29 14:40:14 -07:00
fn importance(&self) -> DiagImportance {
// Usually a side-effect of a `TransientMutBorrow` somewhere.
DiagImportance::Secondary
2020-09-29 17:52:12 -07:00
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.tcx.sess.create_feature_err(
errors::MutDerefErr { span, kind: ccx.const_kind() },
sym::const_mut_refs,
)
2020-09-29 14:40:14 -07:00
}
2019-11-22 19:59:34 -05:00
}
/// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
#[derive(Debug)]
pub struct PanicNonStr;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::PanicNonStrErr { span })
}
}
/// Comparing raw pointers for equality.
/// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
/// allocation base addresses that are not known at compile-time.
#[derive(Debug)]
pub struct RawPtrComparison;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
2023-04-16 09:25:48 +00:00
// FIXME(const_trait_impl): revert to span_bug?
ccx.dcx().create_err(errors::RawPtrComparisonErr { span })
}
}
#[derive(Debug)]
pub struct RawMutPtrDeref;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
2020-09-02 13:25:19 -07:00
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
feature_err(
&ccx.tcx.sess,
sym::const_mut_refs,
span,
format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
)
}
}
/// Casting raw pointer or function pointer to an integer.
/// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
/// allocation base addresses that are not known at compile-time.
#[derive(Debug)]
pub struct RawPtrToIntCast;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::RawPtrToIntErr { span })
}
}
/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for StaticAccess {
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
2020-09-02 13:25:19 -07:00
if let hir::ConstContext::Static(_) = ccx.const_kind() {
Status::Allowed
} else {
Status::Unstable(sym::const_refs_to_static)
2020-09-02 13:25:19 -07:00
}
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let mut err = feature_err(
&ccx.tcx.sess,
sym::const_refs_to_static,
span,
format!("referencing statics in {}s is unstable", ccx.const_kind(),),
);
Reduce capabilities of `Diagnostic`. Currently many diagnostic modifier methods are available on both `Diagnostic` and `DiagnosticBuilder`. This commit removes most of them from `Diagnostic`. To minimize the diff size, it keeps them within `diagnostic.rs` but changes the surrounding `impl Diagnostic` block to `impl DiagnosticBuilder`. (I intend to move things around later, to give a more sensible code layout.) `Diagnostic` keeps a few methods that it still needs, like `sub`, `arg`, and `replace_args`. The `forward!` macro, which defined two additional methods per call (e.g. `note` and `with_note`), is replaced by the `with_fn!` macro, which defines one additional method per call (e.g. `with_note`). It's now also only used when necessary -- not all modifier methods currently need a `with_*` form. (New ones can be easily added as necessary.) All this also requires changing `trait AddToDiagnostic` so its methods take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`. There are three subdiagnostics -- `DelayedAtWithoutNewline`, `DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` -- that are created within the diagnostics machinery and appended to external diagnostics. These are handled at the `Diagnostic` level, which means it's now hard to construct them via `derive(Diagnostic)`, so instead we construct them by hand. This has no effect on what they look like when printed. There are lots of new `allow` markers for `untranslatable_diagnostics` and `diagnostics_outside_of_impl`. This is because `#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic` modifier methods, but missing from the `DiagnosticBuilder` modifier methods. They're now present.
2024-02-06 16:44:30 +11:00
// FIXME: make this translatable
#[allow(rustc::untranslatable_diagnostic)]
err
.note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.")
.help("to fix this, the value can be extracted to a `const` and then used.");
err
}
}
/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
}
}
/// Types that cannot appear in the signature or locals of a `const fn`.
pub mod mut_ref {
use super::*;
#[derive(Debug)]
2020-09-29 17:52:12 -07:00
pub struct MutRef(pub mir::LocalKind);
2021-12-09 22:42:17 +08:00
impl<'tcx> NonConstOp<'tcx> for MutRef {
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
fn importance(&self) -> DiagImportance {
2020-09-29 17:52:12 -07:00
match self.0 {
mir::LocalKind::Temp => DiagImportance::Secondary,
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => DiagImportance::Primary,
2020-09-29 17:52:12 -07:00
}
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
feature_err(
&ccx.tcx.sess,
sym::const_mut_refs,
span,
format!("mutable references are not allowed in {}s", ccx.const_kind()),
)
}
}
}