diff --git a/Cargo.lock b/Cargo.lock index 86936b25d8ad9..268af59aa891c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3813,6 +3813,7 @@ dependencies = [ "atty", "rustc_data_structures", "rustc_error_messages", + "rustc_hir", "rustc_lint_defs", "rustc_macros", "rustc_serialize", diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs new file mode 100644 index 0000000000000..a463fe7b970f4 --- /dev/null +++ b/compiler/rustc_const_eval/src/errors.rs @@ -0,0 +1,89 @@ +use rustc_hir::ConstContext; +use rustc_macros::SessionDiagnostic; +use rustc_span::Span; + +#[derive(SessionDiagnostic)] +#[error(const_eval::unstable_in_stable)] +pub(crate) struct UnstableInStable { + pub gate: String, + #[primary_span] + pub span: Span, + #[suggestion( + const_eval::unstable_sugg, + code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", + applicability = "has-placeholders" + )] + #[suggestion( + const_eval::bypass_sugg, + code = "#[rustc_allow_const_fn_unstable({gate})]\n", + applicability = "has-placeholders" + )] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::thread_local_access, code = "E0625")] +pub(crate) struct NonConstOpErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::static_access, code = "E0013")] +#[help] +pub(crate) struct StaticAccessErr { + #[primary_span] + pub span: Span, + pub kind: ConstContext, + #[note(const_eval::teach_note)] + #[help(const_eval::teach_help)] + pub teach: Option<()>, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::raw_ptr_to_int)] +#[note] +#[note(const_eval::note2)] +pub(crate) struct RawPtrToIntErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::raw_ptr_comparison)] +#[note] +pub(crate) struct RawPtrComparisonErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::panic_non_str)] +pub(crate) struct PanicNonStrErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::mut_deref, code = "E0658")] +pub(crate) struct MutDerefErr { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::transient_mut_borrow, code = "E0658")] +pub(crate) struct TransientMutBorrowErr { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::transient_mut_borrow_raw, code = "E0658")] +pub(crate) struct TransientMutBorrowErrRaw { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 2d42ae236ad9d..d65d4f7eb720e 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -31,6 +31,7 @@ extern crate tracing; extern crate rustc_middle; pub mod const_eval; +mod errors; pub mod interpret; pub mod transform; pub mod util; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index f87de4f6a08a4..3dcd96df33cf5 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -1,6 +1,6 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. -use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed}; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; @@ -24,6 +24,7 @@ use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDro use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; use crate::const_eval::is_unstable_const_fn; +use crate::errors::UnstableInStable; type QualifResults<'mir, 'tcx, Q> = rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; @@ -1026,23 +1027,5 @@ fn is_int_bool_or_char(ty: Ty<'_>) -> bool { fn emit_unstable_in_stable_error(ccx: &ConstCx<'_, '_>, span: Span, gate: Symbol) { let attr_span = ccx.tcx.def_span(ccx.def_id()).shrink_to_lo(); - ccx.tcx - .sess - .struct_span_err( - span, - &format!("const-stable function cannot use `#[feature({})]`", gate.as_str()), - ) - .span_suggestion( - attr_span, - "if it is not part of the public API, make this function unstably const", - concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n'), - Applicability::HasPlaceholders, - ) - .span_suggestion( - attr_span, - "otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks", - format!("#[rustc_allow_const_fn_unstable({})]\n", gate), - Applicability::MaybeIncorrect, - ) - .emit(); + ccx.tcx.sess.emit_err(UnstableInStable { gate: gate.to_string(), span, attr_span }); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 9574661282ba1..17376e59e09cc 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -1,7 +1,9 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use hir::def_id::LocalDefId; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{ + error_code, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, +}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; @@ -20,6 +22,10 @@ use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::traits::SelectionContext; use super::ConstCx; +use crate::errors::{ + MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrComparisonErr, RawPtrToIntErr, + StaticAccessErr, TransientMutBorrowErr, TransientMutBorrowErrRaw, +}; use crate::util::{call_kind, CallDesugaringKind, CallKind}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -590,17 +596,17 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let raw = match self.0 { - hir::BorrowKind::Raw => "raw ", - hir::BorrowKind::Ref => "", - }; - - feature_err( - &ccx.tcx.sess.parse_sess, - sym::const_mut_refs, - span, - &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()), - ) + let kind = ccx.const_kind(); + match self.0 { + hir::BorrowKind::Raw => ccx + .tcx + .sess + .create_feature_err(TransientMutBorrowErrRaw { span, kind }, sym::const_mut_refs), + hir::BorrowKind::Ref => ccx + .tcx + .sess + .create_feature_err(TransientMutBorrowErr { span, kind }, sym::const_mut_refs), + } } } @@ -621,12 +627,9 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - feature_err( - &ccx.tcx.sess.parse_sess, - sym::const_mut_refs, - span, - &format!("mutation through a reference is not allowed in {}s", ccx.const_kind()), - ) + ccx.tcx + .sess + .create_feature_err(MutDerefErr { span, kind: ccx.const_kind() }, sym::const_mut_refs) } } @@ -639,10 +642,7 @@ impl<'tcx> NonConstOp<'tcx> for PanicNonStr { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - ccx.tcx.sess.struct_span_err( - span, - "argument to `panic!()` in a const context must have type `&str`", - ) + ccx.tcx.sess.create_err(PanicNonStrErr { span }) } } @@ -657,15 +657,7 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = ccx - .tcx - .sess - .struct_span_err(span, "pointers cannot be reliably compared during const eval"); - err.note( - "see issue #53020 \ - for more information", - ); - err + ccx.tcx.sess.create_err(RawPtrComparisonErr { span }) } } @@ -701,15 +693,7 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = ccx - .tcx - .sess - .struct_span_err(span, "pointers cannot be cast to integers during const eval"); - err.note("at compile-time, pointers do not have an integer value"); - err.note( - "avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior", - ); - err + ccx.tcx.sess.create_err(RawPtrToIntErr { span }) } } @@ -730,24 +714,11 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = struct_span_err!( - ccx.tcx.sess, + ccx.tcx.sess.create_err(StaticAccessErr { span, - E0013, - "{}s cannot refer to statics", - ccx.const_kind() - ); - err.help( - "consider extracting the value of the `static` to a `const`, and referring to that", - ); - 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.", - ); - err.help("To fix this, the value can be extracted to a `const` and then used."); - } - err + kind: ccx.const_kind(), + teach: ccx.tcx.sess.teach(&error_code!(E0013)).then_some(()), + }) } } @@ -760,13 +731,7 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - struct_span_err!( - ccx.tcx.sess, - span, - E0625, - "thread-local statics cannot be \ - accessed at compile-time" - ) + ccx.tcx.sess.create_err(NonConstOpErr { span }) } } diff --git a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl b/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl index 4a42d52f71004..1d3e33c81851f 100644 --- a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl +++ b/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl @@ -1,5 +1,5 @@ -builtin_macros-requires-cfg-pattern = +builtin-macros-requires-cfg-pattern = macro requires a cfg-pattern as an argument .label = cfg-pattern required -builtin_macros-expected-one-cfg-pattern = expected 1 cfg-pattern +builtin-macros-expected-one-cfg-pattern = expected 1 cfg-pattern diff --git a/compiler/rustc_error_messages/locales/en-US/const_eval.ftl b/compiler/rustc_error_messages/locales/en-US/const_eval.ftl new file mode 100644 index 0000000000000..3f2ff86100160 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/const_eval.ftl @@ -0,0 +1,31 @@ +const-eval-unstable-in-stable = + const-stable function cannot use `#[feature({$gate})]` + .unstable-sugg = if it is not part of the public API, make this function unstably const + .bypass-sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks + +const-eval-thread-local-access = + thread-local statics cannot be accessed at compile-time + +const-eval-static-access = + {$kind}s cannot refer to statics + .help = consider extracting the value of the `static` to a `const`, and referring to that + .teach-note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. + .teach-help = To fix this, the value can be extracted to a `const` and then used. + +const-eval-raw-ptr-to-int = + pointers cannot be cast to integers during const eval + .note = at compile-time, pointers do not have an integer value + .note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior + +const-eval-raw-ptr-comparison = + pointers cannot be reliably compared during const eval + .note = see issue #53020 for more information + +const-eval-panic-non-str = argument to `panic!()` in a const context must have type `&str` + +const-eval-mut-deref = + mutation through a reference is not allowed in {$kind}s + +const-eval-transient-mut-borrow = mutable references are not allowed in {$kind}s + +const-eval-transient-mut-borrow-raw = raw mutable references are not allowed in {$kind}s diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 563d0534d8f38..5a482bc5b2c39 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -37,6 +37,7 @@ fluent_messages! { parser => "../locales/en-US/parser.ftl", privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", + const_eval => "../locales/en-US/const_eval.ftl", } pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES}; diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 8955762605713..7d7e92c522922 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -13,6 +13,7 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_hir = { path = "../rustc_hir" } rustc_lint_defs = { path = "../rustc_lint_defs" } unicode-width = "0.1.4" atty = "0.2" diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 0d1d017d87458..9429ad1a89745 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -5,6 +5,7 @@ use crate::{ }; use rustc_data_structures::stable_map::FxHashMap; use rustc_error_messages::FluentValue; +use rustc_hir as hir; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::symbol::{Ident, Symbol}; @@ -160,6 +161,16 @@ impl<'source> Into> for DiagnosticArgValue<'source> { } } +impl IntoDiagnosticArg for hir::ConstContext { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Borrowed(match self { + hir::ConstContext::ConstFn => "constant function", + hir::ConstContext::Static(_) => "static", + hir::ConstContext::Const => "constant", + })) + } +} + /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(SessionSubdiagnostic)]` -- see [rustc_macros::SessionSubdiagnostic]. #[rustc_diagnostic_item = "AddSubdiagnostic"] diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a2ef158ce8d32..acd77f5d2ee91 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1595,6 +1595,9 @@ impl fmt::Display for ConstContext { } } +// NOTE: `IntoDiagnosticArg` impl for `ConstContext` lives in `rustc_errors` +// due to a cyclical dependency between hir that crate. + /// A literal. pub type Lit = Spanned; diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 2317186e65502..2758fcd1310fe 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -189,9 +189,13 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok if let Entry::Message(Message { id: Identifier { name }, attributes, .. }) = entry { let _ = previous_defns.entry(name.to_string()).or_insert(ident_span); - // `typeck-foo-bar` => `foo_bar` + // `typeck-foo-bar` => `foo_bar` (in `typeck.ftl`) + // `const-eval-baz` => `baz` (in `const_eval.ftl`) let snake_name = Ident::new( - &name.replace(&format!("{}-", res.ident), "").replace("-", "_"), + // FIXME: should probably trim prefix, not replace all occurrences + &name + .replace(&format!("{}-", res.ident).replace("_", "-"), "") + .replace("-", "_"), span, ); constants.extend(quote! { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 52b9ee9471802..1eee0c3163d40 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -2,7 +2,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; use crate::config::{self, CrateType, OutputType, SwitchWithOptPath}; -use crate::parse::ParseSess; +use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; use crate::{filesearch, lint}; @@ -458,6 +458,15 @@ impl Session { ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { self.parse_sess.create_err(err) } + pub fn create_feature_err<'a>( + &'a self, + err: impl SessionDiagnostic<'a>, + feature: Symbol, + ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + let mut err = self.parse_sess.create_err(err); + add_feature_diagnostics(&mut err, &self.parse_sess, feature); + err + } pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { self.parse_sess.emit_err(err) }