Auto merge of #99054 - Dylan-DPC:rollup-0zuhhds, r=Dylan-DPC
Rollup of 4 pull requests Successful merges: - #98533 (Add a `-Zdump-drop-tracking-cfg` debugging flag) - #98654 (An optimization for `pest-2.1.3`) - #98657 (Migrate some diagnostics from `rustc_const_eval` to `SessionDiagnostic`) - #98794 (Highlight conflicting param-env candidates) Failed merges: - #98957 ( don't allow ZST in ScalarInt ) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
45263fc66d
28 changed files with 372 additions and 145 deletions
89
compiler/rustc_const_eval/src/errors.rs
Normal file
89
compiler/rustc_const_eval/src/errors.rs
Normal file
|
@ -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,
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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 <https://github.com/rust-lang/rust/issues/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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
31
compiler/rustc_error_messages/locales/en-US/const_eval.ftl
Normal file
31
compiler/rustc_error_messages/locales/en-US/const_eval.ftl
Normal file
|
@ -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 <https://github.com/rust-lang/rust/issues/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
|
|
@ -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};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<FluentValue<'source>> 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"]
|
||||
|
|
|
@ -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<LitKind>;
|
||||
|
||||
|
|
|
@ -649,6 +649,7 @@ fn test_debugging_options_tracking_hash() {
|
|||
untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe")));
|
||||
untracked!(dont_buffer_diagnostics, true);
|
||||
untracked!(dump_dep_graph, true);
|
||||
untracked!(dump_drop_tracking_cfg, Some("cfg.dot".to_string()));
|
||||
untracked!(dump_mir, Some(String::from("abc")));
|
||||
untracked!(dump_mir_dataflow, true);
|
||||
untracked!(dump_mir_dir, String::from("abc"));
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -546,9 +546,17 @@ pub enum SelectionError<'tcx> {
|
|||
ErrorReporting,
|
||||
/// Multiple applicable `impl`s where found. The `DefId`s correspond to
|
||||
/// all the `impl`s' Items.
|
||||
Ambiguous(Vec<DefId>),
|
||||
Ambiguous(Vec<AmbiguousSelection>),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum AmbiguousSelection {
|
||||
Impl(DefId),
|
||||
ParamEnv(Span),
|
||||
}
|
||||
|
||||
TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, }
|
||||
|
||||
/// When performing resolution, it is typically the case that there
|
||||
/// can be one of three outcomes:
|
||||
///
|
||||
|
|
|
@ -1246,6 +1246,8 @@ options! {
|
|||
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
|
||||
"dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \
|
||||
(default: no)"),
|
||||
dump_drop_tracking_cfg: Option<String> = (None, parse_opt_string, [UNTRACKED],
|
||||
"dump drop-tracking control-flow graph as a `.dot` file (default: no)"),
|
||||
dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
|
||||
"dump MIR state to file.
|
||||
`val` is used to select which passes and functions to dump. For example:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use rustc_hir::GenericParam;
|
|||
use rustc_hir::Item;
|
||||
use rustc_hir::Node;
|
||||
use rustc_infer::infer::error_reporting::same_type_modulo_infer;
|
||||
use rustc_infer::traits::TraitEngine;
|
||||
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
|
||||
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::traits::select::OverflowError;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
|
@ -1404,7 +1404,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
|
|||
fn annotate_source_of_ambiguity(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
impls: &[DefId],
|
||||
impls: &[AmbiguousSelection],
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
);
|
||||
|
||||
|
@ -2020,6 +2020,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
);
|
||||
match selcx.select_from_obligation(&obligation) {
|
||||
Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => {
|
||||
if self.is_tainted_by_errors() && subst.is_none() {
|
||||
// If `subst.is_none()`, then this is probably two param-env
|
||||
// candidates or impl candidates that are equal modulo lifetimes.
|
||||
// Therefore, if we've already emitted an error, just skip this
|
||||
// one, since it's not particularly actionable.
|
||||
err.cancel();
|
||||
return;
|
||||
}
|
||||
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
|
||||
}
|
||||
_ => {
|
||||
|
@ -2170,24 +2178,35 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
fn annotate_source_of_ambiguity(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
impls: &[DefId],
|
||||
impls: &[AmbiguousSelection],
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
) {
|
||||
let mut spans = vec![];
|
||||
let mut crates = vec![];
|
||||
let mut post = vec![];
|
||||
for def_id in impls {
|
||||
match self.tcx.span_of_impl(*def_id) {
|
||||
Ok(span) => spans.push(span),
|
||||
Err(name) => {
|
||||
crates.push(name);
|
||||
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
|
||||
post.push(header);
|
||||
let mut or_where_clause = false;
|
||||
for ambig in impls {
|
||||
match ambig {
|
||||
AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) {
|
||||
Ok(span) => spans.push(span),
|
||||
Err(name) => {
|
||||
crates.push(name);
|
||||
if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
|
||||
post.push(header);
|
||||
}
|
||||
}
|
||||
},
|
||||
AmbiguousSelection::ParamEnv(span) => {
|
||||
or_where_clause = true;
|
||||
spans.push(*span);
|
||||
}
|
||||
}
|
||||
}
|
||||
let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
|
||||
let msg = format!(
|
||||
"multiple `impl`s{} satisfying `{}` found",
|
||||
if or_where_clause { " or `where` clauses" } else { "" },
|
||||
predicate
|
||||
);
|
||||
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
|
||||
crate_names.sort();
|
||||
crate_names.dedup();
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
//!
|
||||
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
||||
use hir::LangItem;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::traits::TraitEngine;
|
||||
use rustc_infer::traits::util::elaborate_predicates_with_span;
|
||||
use rustc_infer::traits::{AmbiguousSelection, TraitEngine};
|
||||
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
||||
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
@ -199,11 +201,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// and report ambiguity.
|
||||
if i > 1 {
|
||||
debug!("multiple matches, ambig");
|
||||
|
||||
// Collect a list of (probable) spans that point to a param-env candidate
|
||||
let tcx = self.infcx.tcx;
|
||||
let owner = stack.obligation.cause.body_id.owner.to_def_id();
|
||||
let predicates = tcx.predicates_of(owner).instantiate_identity(tcx);
|
||||
let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span(
|
||||
tcx,
|
||||
std::iter::zip(predicates.predicates, predicates.spans),
|
||||
)
|
||||
.filter_map(|obligation| {
|
||||
let kind = obligation.predicate.kind();
|
||||
if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() {
|
||||
if trait_pred.trait_ref
|
||||
== ty::TraitRef::identity(tcx, trait_pred.def_id())
|
||||
.skip_binder()
|
||||
{
|
||||
// HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span
|
||||
Some((
|
||||
kind.rebind(trait_pred),
|
||||
tcx.def_span(trait_pred.def_id()),
|
||||
))
|
||||
} else {
|
||||
Some((kind.rebind(trait_pred), obligation.cause.span))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Err(Ambiguous(
|
||||
candidates
|
||||
.into_iter()
|
||||
.filter_map(|c| match c.candidate {
|
||||
SelectionCandidate::ImplCandidate(def_id) => Some(def_id),
|
||||
SelectionCandidate::ImplCandidate(def_id) => {
|
||||
Some(AmbiguousSelection::Impl(def_id))
|
||||
}
|
||||
SelectionCandidate::ParamCandidate(predicate) => {
|
||||
Some(AmbiguousSelection::ParamEnv(
|
||||
*param_env_spans.get(&predicate)?,
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
|
|
|
@ -58,10 +58,7 @@ pub fn search_for_structural_match_violation<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<NonStructuralMatchTy<'tcx>> {
|
||||
// FIXME: we should instead pass in an `infcx` from the outside.
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
ty.visit_with(&mut Search { infcx, span, seen: FxHashSet::default() }).break_value()
|
||||
})
|
||||
ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value()
|
||||
}
|
||||
|
||||
/// This method returns true if and only if `adt_ty` itself has been marked as
|
||||
|
@ -114,27 +111,23 @@ fn type_marked_structural<'tcx>(
|
|||
/// This implements the traversal over the structure of a given type to try to
|
||||
/// find instances of ADTs (specifically structs or enums) that do not implement
|
||||
/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`).
|
||||
struct Search<'a, 'tcx> {
|
||||
struct Search<'tcx> {
|
||||
span: Span,
|
||||
|
||||
infcx: InferCtxt<'a, 'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
||||
/// Tracks ADTs previously encountered during search, so that
|
||||
/// we will not recur on them again.
|
||||
seen: FxHashSet<hir::def_id::DefId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Search<'a, 'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
impl<'tcx> Search<'tcx> {
|
||||
fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool {
|
||||
adt_ty.is_structural_eq_shallow(self.tcx())
|
||||
adt_ty.is_structural_eq_shallow(self.tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
||||
impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
|
||||
type BreakTy = NonStructuralMatchTy<'tcx>;
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
|
@ -193,7 +186,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
|||
return ControlFlow::CONTINUE;
|
||||
}
|
||||
ty::Array(_, n)
|
||||
if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } =>
|
||||
if { n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } =>
|
||||
{
|
||||
// rust-lang/rust#62336: ignore type of contents
|
||||
// for empty array.
|
||||
|
@ -214,7 +207,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
|||
bug!("unexpected type during structural-match checking: {:?}", ty);
|
||||
}
|
||||
ty::Error(_) => {
|
||||
self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check");
|
||||
self.tcx.sess.delay_span_bug(self.span, "ty::Error in structural-match check");
|
||||
// We still want to check other types after encountering an error,
|
||||
// as this may still emit relevant errors.
|
||||
return ControlFlow::CONTINUE;
|
||||
|
@ -244,9 +237,9 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
|
|||
|
||||
// even though we skip super_visit_with, we must recur on
|
||||
// fields of ADT.
|
||||
let tcx = self.tcx();
|
||||
let tcx = self.tcx;
|
||||
adt_def.all_fields().map(|field| field.ty(tcx, substs)).try_for_each(|field_ty| {
|
||||
let ty = self.tcx().normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
|
||||
let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
|
||||
debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty);
|
||||
ty.visit_with(self)
|
||||
})
|
||||
|
|
|
@ -109,7 +109,7 @@ rustc_index::newtype_index! {
|
|||
}
|
||||
|
||||
/// Identifies a value whose drop state we need to track.
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||
enum TrackedValue {
|
||||
/// Represents a named variable, such as a let binding, parameter, or upvar.
|
||||
///
|
||||
|
@ -121,6 +121,21 @@ enum TrackedValue {
|
|||
Temporary(HirId),
|
||||
}
|
||||
|
||||
impl Debug for TrackedValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
ty::tls::with_opt(|opt_tcx| {
|
||||
if let Some(tcx) = opt_tcx {
|
||||
write!(f, "{}", tcx.hir().node_to_string(self.hir_id()))
|
||||
} else {
|
||||
match self {
|
||||
Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id),
|
||||
Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TrackedValue {
|
||||
fn hir_id(&self) -> HirId {
|
||||
match self {
|
||||
|
@ -148,7 +163,7 @@ enum TrackedValueConversionError {
|
|||
/// Place projects are not currently supported.
|
||||
///
|
||||
/// The reasoning around these is kind of subtle, so we choose to be more
|
||||
/// conservative around these for now. There is not reason in theory we
|
||||
/// conservative around these for now. There is no reason in theory we
|
||||
/// cannot support these, we just have not implemented it yet.
|
||||
PlaceProjectionsNotSupported,
|
||||
}
|
||||
|
|
|
@ -33,6 +33,9 @@ pub(super) fn build_control_flow_graph<'tcx>(
|
|||
intravisit::walk_body(&mut drop_range_visitor, body);
|
||||
|
||||
drop_range_visitor.drop_ranges.process_deferred_edges();
|
||||
if let Some(filename) = &tcx.sess.opts.debugging_opts.dump_drop_tracking_cfg {
|
||||
super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx);
|
||||
}
|
||||
|
||||
(drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries)
|
||||
}
|
||||
|
@ -126,13 +129,14 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
|
|||
/// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all
|
||||
/// expressions. This method consumes a little deeper into the expression when needed.
|
||||
fn consume_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
debug!("consuming expr {:?}, count={:?}", expr.hir_id, self.expr_index);
|
||||
debug!("consuming expr {:?}, count={:?}", expr.kind, self.expr_index);
|
||||
let places = self
|
||||
.places
|
||||
.consumed
|
||||
.get(&expr.hir_id)
|
||||
.map_or(vec![], |places| places.iter().cloned().collect());
|
||||
for place in places {
|
||||
trace!(?place, "consuming place");
|
||||
for_each_consumable(self.hir, place, |value| self.record_drop(value));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//! flow graph when needed for debugging.
|
||||
|
||||
use rustc_graphviz as dot;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use super::{DropRangesBuilder, PostOrderId};
|
||||
|
||||
|
@ -9,22 +10,35 @@ use super::{DropRangesBuilder, PostOrderId};
|
|||
///
|
||||
/// It is not normally called, but is kept around to easily add debugging
|
||||
/// code when needed.
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn write_graph_to_file(drop_ranges: &DropRangesBuilder, filename: &str) {
|
||||
dot::render(drop_ranges, &mut std::fs::File::create(filename).unwrap()).unwrap();
|
||||
pub(super) fn write_graph_to_file(
|
||||
drop_ranges: &DropRangesBuilder,
|
||||
filename: &str,
|
||||
tcx: TyCtxt<'_>,
|
||||
) {
|
||||
dot::render(
|
||||
&DropRangesGraph { drop_ranges, tcx },
|
||||
&mut std::fs::File::create(filename).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
impl<'a> dot::GraphWalk<'a> for DropRangesBuilder {
|
||||
struct DropRangesGraph<'a, 'tcx> {
|
||||
drop_ranges: &'a DropRangesBuilder,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a> dot::GraphWalk<'a> for DropRangesGraph<'_, '_> {
|
||||
type Node = PostOrderId;
|
||||
|
||||
type Edge = (PostOrderId, PostOrderId);
|
||||
|
||||
fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> {
|
||||
self.nodes.iter_enumerated().map(|(i, _)| i).collect()
|
||||
self.drop_ranges.nodes.iter_enumerated().map(|(i, _)| i).collect()
|
||||
}
|
||||
|
||||
fn edges(&'a self) -> dot::Edges<'a, Self::Edge> {
|
||||
self.nodes
|
||||
self.drop_ranges
|
||||
.nodes
|
||||
.iter_enumerated()
|
||||
.flat_map(|(i, node)| {
|
||||
if node.successors.len() == 0 {
|
||||
|
@ -45,7 +59,7 @@ impl<'a> dot::GraphWalk<'a> for DropRangesBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> dot::Labeller<'a> for DropRangesBuilder {
|
||||
impl<'a> dot::Labeller<'a> for DropRangesGraph<'_, '_> {
|
||||
type Node = PostOrderId;
|
||||
|
||||
type Edge = (PostOrderId, PostOrderId);
|
||||
|
@ -61,15 +75,15 @@ impl<'a> dot::Labeller<'a> for DropRangesBuilder {
|
|||
fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> {
|
||||
dot::LabelText::LabelStr(
|
||||
format!(
|
||||
"{:?}, local_id: {}",
|
||||
n,
|
||||
self.post_order_map
|
||||
"{n:?}: {}",
|
||||
self.drop_ranges
|
||||
.post_order_map
|
||||
.iter()
|
||||
.find(|(_hir_id, &post_order_id)| post_order_id == *n)
|
||||
.map_or("<unknown>".into(), |(hir_id, _)| format!(
|
||||
"{}",
|
||||
hir_id.local_id.index()
|
||||
))
|
||||
.map_or("<unknown>".into(), |(hir_id, _)| self
|
||||
.tcx
|
||||
.hir()
|
||||
.node_to_string(*hir_id))
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
|
|
|
@ -75,6 +75,7 @@ impl<'tcx> ExprUseDelegate<'tcx> {
|
|||
if !self.places.consumed.contains_key(&consumer) {
|
||||
self.places.consumed.insert(consumer, <_>::default());
|
||||
}
|
||||
debug!(?consumer, ?target, "mark_consumed");
|
||||
self.places.consumed.get_mut(&consumer).map(|places| places.insert(target));
|
||||
}
|
||||
|
||||
|
@ -136,13 +137,16 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
|
|||
place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: HirId,
|
||||
) {
|
||||
let parent = match self.tcx.hir().find_parent_node(place_with_id.hir_id) {
|
||||
let hir = self.tcx.hir();
|
||||
let parent = match hir.find_parent_node(place_with_id.hir_id) {
|
||||
Some(parent) => parent,
|
||||
None => place_with_id.hir_id,
|
||||
};
|
||||
debug!(
|
||||
"consume {:?}; diag_expr_id={:?}, using parent {:?}",
|
||||
place_with_id, diag_expr_id, parent
|
||||
"consume {:?}; diag_expr_id={}, using parent {}",
|
||||
place_with_id,
|
||||
hir.node_to_string(diag_expr_id),
|
||||
hir.node_to_string(parent)
|
||||
);
|
||||
place_with_id
|
||||
.try_into()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue