mirror of
https://github.com/rust-lang/rust
synced 2024-10-14 12:33:57 +00:00
Auto merge of #120715 - matthiaskrgr:rollup-sp1pp74, r=matthiaskrgr
Rollup of 12 pull requests Successful merges: - #120520 (Some cleanups around diagnostic levels.) - #120575 (Simplify codegen diagnostic handling) - #120597 (Suggest `[tail @ ..]` on `[..tail]` and `[...tail]` where `tail` is unresolved) - #120602 (rustc_monomorphize: fix outdated comment in partition) - #120609 (hir: Stop keeping prefixes for most of `use` list stems) - #120631 (Emit a diagnostic for invalid target options) - #120632 (For E0223, suggest associated functions that are similar to the path) - #120670 (cleanup effect var handling) - #120673 (rustc_metadata: fix typo) - #120683 (miri: fix ICE with symbolic alignment check on extern static) - #120690 (Remove b-naber from the compiler review rotation) - #120713 (Make async closures test use async bound modifier) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
0d531351e8
|
@ -498,8 +498,7 @@ fn lower_use_tree(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res =
|
let res = self.lower_import_res(id, path.span);
|
||||||
self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
|
|
||||||
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
|
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
|
||||||
hir::ItemKind::Use(path, hir::UseKind::Single)
|
hir::ItemKind::Use(path, hir::UseKind::Single)
|
||||||
}
|
}
|
||||||
|
@ -535,7 +534,8 @@ fn lower_use_tree(
|
||||||
// for that we return the `{}` import (called the
|
// for that we return the `{}` import (called the
|
||||||
// `ListStem`).
|
// `ListStem`).
|
||||||
|
|
||||||
let prefix = Path { segments, span: prefix.span.to(path.span), tokens: None };
|
let span = prefix.span.to(path.span);
|
||||||
|
let prefix = Path { segments, span, tokens: None };
|
||||||
|
|
||||||
// Add all the nested `PathListItem`s to the HIR.
|
// Add all the nested `PathListItem`s to the HIR.
|
||||||
for &(ref use_tree, id) in trees {
|
for &(ref use_tree, id) in trees {
|
||||||
|
@ -569,9 +569,16 @@ fn lower_use_tree(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let res =
|
let path = if trees.is_empty() && !prefix.segments.is_empty() {
|
||||||
self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
|
// For empty lists we need to lower the prefix so it is checked for things
|
||||||
let path = self.lower_use_path(res, &prefix, ParamMode::Explicit);
|
// like stability later.
|
||||||
|
let res = self.lower_import_res(id, span);
|
||||||
|
self.lower_use_path(res, &prefix, ParamMode::Explicit)
|
||||||
|
} else {
|
||||||
|
// For non-empty lists we can just drop all the data, the prefix is already
|
||||||
|
// present in HIR as a part of nested imports.
|
||||||
|
self.arena.alloc(hir::UsePath { res: smallvec![], segments: &[], span })
|
||||||
|
};
|
||||||
hir::ItemKind::Use(path, hir::UseKind::ListStem)
|
hir::ItemKind::Use(path, hir::UseKind::ListStem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::{DesugaringKind, Span, DUMMY_SP};
|
use rustc_span::{DesugaringKind, Span, DUMMY_SP};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use thin_vec::ThinVec;
|
use thin_vec::ThinVec;
|
||||||
|
|
||||||
|
@ -750,8 +750,14 @@ fn expect_full_res(&mut self, id: NodeId) -> Res<NodeId> {
|
||||||
self.resolver.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res())
|
self.resolver.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_full_res_from_use(&mut self, id: NodeId) -> impl Iterator<Item = Res<NodeId>> {
|
fn lower_import_res(&mut self, id: NodeId, span: Span) -> SmallVec<[Res; 3]> {
|
||||||
self.resolver.get_import_res(id).present_items()
|
let res = self.resolver.get_import_res(id).present_items();
|
||||||
|
let res: SmallVec<_> = res.map(|res| self.lower_res(res)).collect();
|
||||||
|
if res.is_empty() {
|
||||||
|
self.dcx().span_delayed_bug(span, "no resolution for an import");
|
||||||
|
return smallvec![Res::Err];
|
||||||
|
}
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_lang_item_qpath(&mut self, lang_item: hir::LangItem, span: Span) -> hir::QPath<'hir> {
|
fn make_lang_item_qpath(&mut self, lang_item: hir::LangItem, span: Span) -> hir::QPath<'hir> {
|
||||||
|
|
|
@ -196,6 +196,7 @@ pub(crate) fn lower_use_path(
|
||||||
p: &Path,
|
p: &Path,
|
||||||
param_mode: ParamMode,
|
param_mode: ParamMode,
|
||||||
) -> &'hir hir::UsePath<'hir> {
|
) -> &'hir hir::UsePath<'hir> {
|
||||||
|
assert!((1..=3).contains(&res.len()));
|
||||||
self.arena.alloc(hir::UsePath {
|
self.arena.alloc(hir::UsePath {
|
||||||
res,
|
res,
|
||||||
segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
|
segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
|
||||||
|
|
|
@ -28,6 +28,8 @@ codegen_llvm_invalid_minimum_alignment_not_power_of_two =
|
||||||
codegen_llvm_invalid_minimum_alignment_too_large =
|
codegen_llvm_invalid_minimum_alignment_too_large =
|
||||||
invalid minimum global alignment: {$align} is too large
|
invalid minimum global alignment: {$align} is too large
|
||||||
|
|
||||||
|
codegen_llvm_invalid_target_feature_prefix = target feature `{$feature}` must begin with a `+` or `-`"
|
||||||
|
|
||||||
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
|
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
|
||||||
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}
|
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}
|
||||||
|
|
||||||
|
|
|
@ -253,3 +253,9 @@ pub struct MismatchedDataLayout<'a> {
|
||||||
pub llvm_target: &'a str,
|
pub llvm_target: &'a str,
|
||||||
pub llvm_layout: &'a str,
|
pub llvm_layout: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(codegen_llvm_invalid_target_feature_prefix)]
|
||||||
|
pub(crate) struct InvalidTargetFeaturePrefix<'a> {
|
||||||
|
pub feature: &'a str,
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::back::write::create_informational_target_machine;
|
use crate::back::write::create_informational_target_machine;
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature,
|
InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable,
|
||||||
UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
||||||
};
|
};
|
||||||
use crate::llvm;
|
use crate::llvm;
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
|
@ -511,7 +511,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
|
||||||
sess.target
|
sess.target
|
||||||
.features
|
.features
|
||||||
.split(',')
|
.split(',')
|
||||||
.filter(|v| !v.is_empty() && backend_feature_name(v).is_some())
|
.filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some())
|
||||||
.map(String::from),
|
.map(String::from),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -535,7 +535,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let feature = backend_feature_name(s)?;
|
let feature = backend_feature_name(sess, s)?;
|
||||||
// Warn against use of LLVM specific feature names and unstable features on the CLI.
|
// Warn against use of LLVM specific feature names and unstable features on the CLI.
|
||||||
if diagnostics {
|
if diagnostics {
|
||||||
let feature_state = supported_features.iter().find(|&&(v, _)| v == feature);
|
let feature_state = supported_features.iter().find(|&&(v, _)| v == feature);
|
||||||
|
@ -611,11 +611,11 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
|
||||||
/// Returns a feature name for the given `+feature` or `-feature` string.
|
/// Returns a feature name for the given `+feature` or `-feature` string.
|
||||||
///
|
///
|
||||||
/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
|
/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
|
||||||
fn backend_feature_name(s: &str) -> Option<&str> {
|
fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> {
|
||||||
// features must start with a `+` or `-`.
|
// features must start with a `+` or `-`.
|
||||||
let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| {
|
let feature = s
|
||||||
bug!("target feature `{}` must begin with a `+` or `-`", s);
|
.strip_prefix(&['+', '-'][..])
|
||||||
});
|
.unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s }));
|
||||||
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
|
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
|
||||||
// are not passed down to LLVM.
|
// are not passed down to LLVM.
|
||||||
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
|
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
|
||||||
|
|
|
@ -1810,7 +1810,7 @@ fn fallback_fluent_bundle(&self) -> &FluentBundle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emitter for SharedEmitter {
|
impl Emitter for SharedEmitter {
|
||||||
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
|
fn emit_diagnostic(&mut self, diag: rustc_errors::Diagnostic) {
|
||||||
let args: FxHashMap<DiagnosticArgName, DiagnosticArgValue> =
|
let args: FxHashMap<DiagnosticArgName, DiagnosticArgValue> =
|
||||||
diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect();
|
diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect();
|
||||||
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
||||||
|
|
|
@ -993,10 +993,7 @@ fn validate_operand_internal(
|
||||||
// Complain about any other kind of error -- those are bad because we'd like to
|
// Complain about any other kind of error -- those are bad because we'd like to
|
||||||
// report them in a way that shows *where* in the value the issue lies.
|
// report them in a way that shows *where* in the value the issue lies.
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bug!(
|
bug!("Unexpected error during validation: {}", self.format_error(err));
|
||||||
"Unexpected Undefined Behavior error during validation: {}",
|
|
||||||
self.format_error(err)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,7 +378,7 @@ fn from(s: Cow<'static, str>) -> Self {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A workaround for "good path" ICEs when formatting types in disabled lints.
|
/// A workaround for good_path_delayed_bug ICEs when formatting types in disabled lints.
|
||||||
///
|
///
|
||||||
/// Delays formatting until `.into(): DiagnosticMessage` is used.
|
/// Delays formatting until `.into(): DiagnosticMessage` is used.
|
||||||
pub struct DelayDm<F>(pub F);
|
pub struct DelayDm<F>(pub F);
|
||||||
|
|
|
@ -44,15 +44,15 @@ fn fallback_fluent_bundle(&self) -> &FluentBundle {
|
||||||
|
|
||||||
impl Emitter for AnnotateSnippetEmitter {
|
impl Emitter for AnnotateSnippetEmitter {
|
||||||
/// The entry point for the diagnostics generation
|
/// The entry point for the diagnostics generation
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||||
let fluent_args = to_fluent_args(diag.args());
|
let fluent_args = to_fluent_args(diag.args());
|
||||||
|
|
||||||
let mut children = diag.children.clone();
|
let mut suggestions = diag.suggestions.unwrap_or(vec![]);
|
||||||
let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
|
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
||||||
|
|
||||||
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
|
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
|
||||||
&mut primary_span,
|
&mut diag.span,
|
||||||
&mut children,
|
&mut diag.children,
|
||||||
&diag.level,
|
&diag.level,
|
||||||
self.macro_backtrace,
|
self.macro_backtrace,
|
||||||
);
|
);
|
||||||
|
@ -62,9 +62,9 @@ fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||||
&diag.messages,
|
&diag.messages,
|
||||||
&fluent_args,
|
&fluent_args,
|
||||||
&diag.code,
|
&diag.code,
|
||||||
&primary_span,
|
&diag.span,
|
||||||
&children,
|
&diag.children,
|
||||||
suggestions,
|
&suggestions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,11 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
|
||||||
/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
|
/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
|
||||||
fn annotation_type_for_level(level: Level) -> AnnotationType {
|
fn annotation_type_for_level(level: Level) -> AnnotationType {
|
||||||
match level {
|
match level {
|
||||||
Level::Bug | Level::DelayedBug(_) | Level::Fatal | Level::Error => AnnotationType::Error,
|
Level::Bug
|
||||||
|
| Level::Fatal
|
||||||
|
| Level::Error
|
||||||
|
| Level::DelayedBug
|
||||||
|
| Level::GoodPathDelayedBug => AnnotationType::Error,
|
||||||
Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
|
Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
|
||||||
Level::Note | Level::OnceNote => AnnotationType::Note,
|
Level::Note | Level::OnceNote => AnnotationType::Note,
|
||||||
Level::Help | Level::OnceHelp => AnnotationType::Help,
|
Level::Help | Level::OnceHelp => AnnotationType::Help,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::snippet::Style;
|
use crate::snippet::Style;
|
||||||
use crate::{
|
use crate::{
|
||||||
CodeSuggestion, DelayedBugKind, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee,
|
CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, Level,
|
||||||
ErrCode, Level, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart,
|
MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
|
||||||
SuggestionStyle,
|
|
||||||
};
|
};
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
|
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
|
||||||
|
@ -235,19 +234,16 @@ pub fn level(&self) -> Level {
|
||||||
|
|
||||||
pub fn is_error(&self) -> bool {
|
pub fn is_error(&self) -> bool {
|
||||||
match self.level {
|
match self.level {
|
||||||
Level::Bug
|
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
|
||||||
| Level::DelayedBug(DelayedBugKind::Normal)
|
|
||||||
| Level::Fatal
|
|
||||||
| Level::Error
|
|
||||||
| Level::FailureNote => true,
|
|
||||||
|
|
||||||
Level::ForceWarning(_)
|
Level::GoodPathDelayedBug
|
||||||
|
| Level::ForceWarning(_)
|
||||||
| Level::Warning
|
| Level::Warning
|
||||||
| Level::DelayedBug(DelayedBugKind::GoodPath)
|
|
||||||
| Level::Note
|
| Level::Note
|
||||||
| Level::OnceNote
|
| Level::OnceNote
|
||||||
| Level::Help
|
| Level::Help
|
||||||
| Level::OnceHelp
|
| Level::OnceHelp
|
||||||
|
| Level::FailureNote
|
||||||
| Level::Allow
|
| Level::Allow
|
||||||
| Level::Expect(_) => false,
|
| Level::Expect(_) => false,
|
||||||
}
|
}
|
||||||
|
@ -306,11 +302,11 @@ pub(crate) fn is_force_warn(&self) -> bool {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn downgrade_to_delayed_bug(&mut self) {
|
pub fn downgrade_to_delayed_bug(&mut self) {
|
||||||
assert!(
|
assert!(
|
||||||
self.is_error(),
|
matches!(self.level, Level::Error | Level::DelayedBug),
|
||||||
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
|
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
|
||||||
self.level
|
self.level
|
||||||
);
|
);
|
||||||
self.level = Level::DelayedBug(DelayedBugKind::Normal);
|
self.level = Level::DelayedBug;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends a labeled span to the diagnostic.
|
/// Appends a labeled span to the diagnostic.
|
||||||
|
|
|
@ -193,7 +193,7 @@ fn right(&self, line_len: usize) -> usize {
|
||||||
/// Emitter trait for emitting errors.
|
/// Emitter trait for emitting errors.
|
||||||
pub trait Emitter: Translate {
|
pub trait Emitter: Translate {
|
||||||
/// Emit a structured diagnostic.
|
/// Emit a structured diagnostic.
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic);
|
fn emit_diagnostic(&mut self, diag: Diagnostic);
|
||||||
|
|
||||||
/// Emit a notification that an artifact has been output.
|
/// Emit a notification that an artifact has been output.
|
||||||
/// Currently only supported for the JSON format.
|
/// Currently only supported for the JSON format.
|
||||||
|
@ -230,17 +230,17 @@ fn supports_color(&self) -> bool {
|
||||||
///
|
///
|
||||||
/// * If the current `Diagnostic` has only one visible `CodeSuggestion`,
|
/// * If the current `Diagnostic` has only one visible `CodeSuggestion`,
|
||||||
/// we format the `help` suggestion depending on the content of the
|
/// we format the `help` suggestion depending on the content of the
|
||||||
/// substitutions. In that case, we return the modified span only.
|
/// substitutions. In that case, we modify the span and clear the
|
||||||
|
/// suggestions.
|
||||||
///
|
///
|
||||||
/// * If the current `Diagnostic` has multiple suggestions,
|
/// * If the current `Diagnostic` has multiple suggestions,
|
||||||
/// we return the original `primary_span` and the original suggestions.
|
/// we leave `primary_span` and the suggestions untouched.
|
||||||
fn primary_span_formatted<'a>(
|
fn primary_span_formatted(
|
||||||
&mut self,
|
&mut self,
|
||||||
diag: &'a Diagnostic,
|
primary_span: &mut MultiSpan,
|
||||||
|
suggestions: &mut Vec<CodeSuggestion>,
|
||||||
fluent_args: &FluentArgs<'_>,
|
fluent_args: &FluentArgs<'_>,
|
||||||
) -> (MultiSpan, &'a [CodeSuggestion]) {
|
) {
|
||||||
let mut primary_span = diag.span.clone();
|
|
||||||
let suggestions = diag.suggestions.as_deref().unwrap_or(&[]);
|
|
||||||
if let Some((sugg, rest)) = suggestions.split_first() {
|
if let Some((sugg, rest)) = suggestions.split_first() {
|
||||||
let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
|
let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
|
||||||
if rest.is_empty() &&
|
if rest.is_empty() &&
|
||||||
|
@ -287,16 +287,15 @@ fn primary_span_formatted<'a>(
|
||||||
primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg);
|
primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg);
|
||||||
|
|
||||||
// We return only the modified primary_span
|
// We return only the modified primary_span
|
||||||
(primary_span, &[])
|
suggestions.clear();
|
||||||
} else {
|
} else {
|
||||||
// if there are multiple suggestions, print them all in full
|
// if there are multiple suggestions, print them all in full
|
||||||
// to be consistent. We could try to figure out if we can
|
// to be consistent. We could try to figure out if we can
|
||||||
// make one (or the first one) inline, but that would give
|
// make one (or the first one) inline, but that would give
|
||||||
// undue importance to a semi-random suggestion
|
// undue importance to a semi-random suggestion
|
||||||
(primary_span, suggestions)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(primary_span, suggestions)
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,16 +517,15 @@ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
|
||||||
self.sm.as_ref()
|
self.sm.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||||
let fluent_args = to_fluent_args(diag.args());
|
let fluent_args = to_fluent_args(diag.args());
|
||||||
|
|
||||||
let mut children = diag.children.clone();
|
let mut suggestions = diag.suggestions.unwrap_or(vec![]);
|
||||||
let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
|
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
||||||
debug!("emit_diagnostic: suggestions={:?}", suggestions);
|
|
||||||
|
|
||||||
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
|
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
|
||||||
&mut primary_span,
|
&mut diag.span,
|
||||||
&mut children,
|
&mut diag.children,
|
||||||
&diag.level,
|
&diag.level,
|
||||||
self.macro_backtrace,
|
self.macro_backtrace,
|
||||||
);
|
);
|
||||||
|
@ -537,9 +535,9 @@ fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||||
&diag.messages,
|
&diag.messages,
|
||||||
&fluent_args,
|
&fluent_args,
|
||||||
&diag.code,
|
&diag.code,
|
||||||
&primary_span,
|
&diag.span,
|
||||||
&children,
|
&diag.children,
|
||||||
suggestions,
|
&suggestions,
|
||||||
self.track_diagnostics.then_some(&diag.emitted_at),
|
self.track_diagnostics.then_some(&diag.emitted_at),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -576,9 +574,8 @@ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||||
if diag.level == Level::Fatal {
|
if diag.level == Level::Fatal {
|
||||||
let mut diag = diag.clone();
|
|
||||||
diag.note(self.fatal_note.clone());
|
diag.note(self.fatal_note.clone());
|
||||||
self.fatal_dcx.emit_diagnostic(diag);
|
self.fatal_dcx.emit_diagnostic(diag);
|
||||||
}
|
}
|
||||||
|
@ -2116,6 +2113,7 @@ fn emit_messages_default(
|
||||||
}
|
}
|
||||||
if !self.short_message {
|
if !self.short_message {
|
||||||
for child in children {
|
for child in children {
|
||||||
|
assert!(child.level.can_be_top_or_sub().1);
|
||||||
let span = &child.span;
|
let span = &child.span;
|
||||||
if let Err(err) = self.emit_messages_default_inner(
|
if let Err(err) = self.emit_messages_default_inner(
|
||||||
span,
|
span,
|
||||||
|
|
|
@ -176,7 +176,7 @@ fn fallback_fluent_bundle(&self) -> &FluentBundle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emitter for JsonEmitter {
|
impl Emitter for JsonEmitter {
|
||||||
fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
|
fn emit_diagnostic(&mut self, diag: crate::Diagnostic) {
|
||||||
let data = Diagnostic::from_errors_diagnostic(diag, self);
|
let data = Diagnostic::from_errors_diagnostic(diag, self);
|
||||||
let result = self.emit(EmitTyped::Diagnostic(data));
|
let result = self.emit(EmitTyped::Diagnostic(data));
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
|
@ -201,7 +201,7 @@ fn emit_future_breakage_report(&mut self, diags: Vec<crate::Diagnostic>) {
|
||||||
}
|
}
|
||||||
FutureBreakageItem {
|
FutureBreakageItem {
|
||||||
diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(
|
diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(
|
||||||
&diag, self,
|
diag, self,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -340,7 +340,7 @@ struct UnusedExterns<'a, 'b, 'c> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diagnostic {
|
impl Diagnostic {
|
||||||
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
fn from_errors_diagnostic(diag: crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
||||||
let args = to_fluent_args(diag.args());
|
let args = to_fluent_args(diag.args());
|
||||||
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
||||||
let translated_message =
|
let translated_message =
|
||||||
|
@ -382,6 +382,28 @@ fn reset(&mut self) -> io::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let translated_message = je.translate_messages(&diag.messages, &args);
|
||||||
|
|
||||||
|
let code = if let Some(code) = diag.code {
|
||||||
|
Some(DiagnosticCode {
|
||||||
|
code: code.to_string(),
|
||||||
|
explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(),
|
||||||
|
})
|
||||||
|
} else if let Some(IsLint { name, .. }) = &diag.is_lint {
|
||||||
|
Some(DiagnosticCode { code: name.to_string(), explanation: None })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let level = diag.level.to_str();
|
||||||
|
let spans = DiagnosticSpan::from_multispan(&diag.span, &args, je);
|
||||||
|
let children = diag
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|c| Diagnostic::from_sub_diagnostic(c, &args, je))
|
||||||
|
.chain(sugg)
|
||||||
|
.collect();
|
||||||
|
|
||||||
let buf = BufWriter::default();
|
let buf = BufWriter::default();
|
||||||
let output = buf.clone();
|
let output = buf.clone();
|
||||||
je.json_rendered
|
je.json_rendered
|
||||||
|
@ -398,30 +420,12 @@ fn reset(&mut self) -> io::Result<()> {
|
||||||
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
|
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
|
||||||
let output = String::from_utf8(output).unwrap();
|
let output = String::from_utf8(output).unwrap();
|
||||||
|
|
||||||
let translated_message = je.translate_messages(&diag.messages, &args);
|
|
||||||
|
|
||||||
let code = if let Some(code) = diag.code {
|
|
||||||
Some(DiagnosticCode {
|
|
||||||
code: code.to_string(),
|
|
||||||
explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(),
|
|
||||||
})
|
|
||||||
} else if let Some(IsLint { name, .. }) = &diag.is_lint {
|
|
||||||
Some(DiagnosticCode { code: name.to_string(), explanation: None })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Diagnostic {
|
Diagnostic {
|
||||||
message: translated_message.to_string(),
|
message: translated_message.to_string(),
|
||||||
code,
|
code,
|
||||||
level: diag.level.to_str(),
|
level,
|
||||||
spans: DiagnosticSpan::from_multispan(&diag.span, &args, je),
|
spans,
|
||||||
children: diag
|
children,
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.map(|c| Diagnostic::from_sub_diagnostic(c, &args, je))
|
|
||||||
.chain(sugg)
|
|
||||||
.collect(),
|
|
||||||
rendered: Some(output),
|
rendered: Some(output),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -439,7 +439,7 @@ struct DiagCtxtInner {
|
||||||
has_printed: bool,
|
has_printed: bool,
|
||||||
|
|
||||||
emitter: Box<DynEmitter>,
|
emitter: Box<DynEmitter>,
|
||||||
span_delayed_bugs: Vec<DelayedDiagnostic>,
|
delayed_bugs: Vec<DelayedDiagnostic>,
|
||||||
good_path_delayed_bugs: Vec<DelayedDiagnostic>,
|
good_path_delayed_bugs: Vec<DelayedDiagnostic>,
|
||||||
/// This flag indicates that an expected diagnostic was emitted and suppressed.
|
/// This flag indicates that an expected diagnostic was emitted and suppressed.
|
||||||
/// This is used for the `good_path_delayed_bugs` check.
|
/// This is used for the `good_path_delayed_bugs` check.
|
||||||
|
@ -523,8 +523,7 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) {
|
||||||
pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
|
pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
|
||||||
AtomicRef::new(&(default_track_diagnostic as _));
|
AtomicRef::new(&(default_track_diagnostic as _));
|
||||||
|
|
||||||
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
|
enum DelayedBugKind {
|
||||||
pub enum DelayedBugKind {
|
|
||||||
Normal,
|
Normal,
|
||||||
GoodPath,
|
GoodPath,
|
||||||
}
|
}
|
||||||
|
@ -557,11 +556,6 @@ fn drop(&mut self) {
|
||||||
self.flush_delayed(DelayedBugKind::Normal)
|
self.flush_delayed(DelayedBugKind::Normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) this explains what `good_path_delayed_bugs` are!
|
|
||||||
// They're `span_delayed_bugs` but for "require some diagnostic happened"
|
|
||||||
// instead of "require some error happened". Sadly that isn't ideal, as
|
|
||||||
// lints can be `#[allow]`'d, potentially leading to this triggering.
|
|
||||||
// Also, "good path" should be replaced with a better naming.
|
|
||||||
if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
|
if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
|
||||||
self.flush_delayed(DelayedBugKind::GoodPath);
|
self.flush_delayed(DelayedBugKind::GoodPath);
|
||||||
}
|
}
|
||||||
|
@ -608,7 +602,7 @@ pub fn with_emitter(emitter: Box<DynEmitter>) -> Self {
|
||||||
deduplicated_warn_count: 0,
|
deduplicated_warn_count: 0,
|
||||||
has_printed: false,
|
has_printed: false,
|
||||||
emitter,
|
emitter,
|
||||||
span_delayed_bugs: Vec::new(),
|
delayed_bugs: Vec::new(),
|
||||||
good_path_delayed_bugs: Vec::new(),
|
good_path_delayed_bugs: Vec::new(),
|
||||||
suppressed_expected_diag: false,
|
suppressed_expected_diag: false,
|
||||||
taught_diagnostics: Default::default(),
|
taught_diagnostics: Default::default(),
|
||||||
|
@ -665,7 +659,7 @@ pub fn reset_err_count(&self) {
|
||||||
inner.has_printed = false;
|
inner.has_printed = false;
|
||||||
|
|
||||||
// actually free the underlying memory (which `clear` would not do)
|
// actually free the underlying memory (which `clear` would not do)
|
||||||
inner.span_delayed_bugs = Default::default();
|
inner.delayed_bugs = Default::default();
|
||||||
inner.good_path_delayed_bugs = Default::default();
|
inner.good_path_delayed_bugs = Default::default();
|
||||||
inner.taught_diagnostics = Default::default();
|
inner.taught_diagnostics = Default::default();
|
||||||
inner.emitted_diagnostic_codes = Default::default();
|
inner.emitted_diagnostic_codes = Default::default();
|
||||||
|
@ -866,8 +860,7 @@ pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMess
|
||||||
/// directly).
|
/// directly).
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn delayed_bug(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
|
pub fn delayed_bug(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
|
||||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg)
|
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).emit()
|
||||||
.emit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `delayed_bug`, but takes an additional span.
|
/// Like `delayed_bug`, but takes an additional span.
|
||||||
|
@ -880,15 +873,12 @@ pub fn span_delayed_bug(
|
||||||
sp: impl Into<MultiSpan>,
|
sp: impl Into<MultiSpan>,
|
||||||
msg: impl Into<DiagnosticMessage>,
|
msg: impl Into<DiagnosticMessage>,
|
||||||
) -> ErrorGuaranteed {
|
) -> ErrorGuaranteed {
|
||||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg)
|
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit()
|
||||||
.with_span(sp)
|
|
||||||
.emit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
|
/// Ensures that a diagnostic is printed. See `Level::GoodPathDelayedBug`.
|
||||||
// where the explanation of what "good path" is (also, it should be renamed).
|
|
||||||
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
||||||
DiagnosticBuilder::<()>::new(self, DelayedBug(DelayedBugKind::GoodPath), msg).emit()
|
DiagnosticBuilder::<()>::new(self, GoodPathDelayedBug, msg).emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
@ -962,7 +952,7 @@ pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||||
pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||||
let inner = self.inner.borrow();
|
let inner = self.inner.borrow();
|
||||||
let result =
|
let result =
|
||||||
inner.has_errors() || inner.lint_err_count > 0 || !inner.span_delayed_bugs.is_empty();
|
inner.has_errors() || inner.lint_err_count > 0 || !inner.delayed_bugs.is_empty();
|
||||||
result.then(|| {
|
result.then(|| {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||||
|
@ -991,9 +981,13 @@ pub fn print_error_count(&self, registry: &Registry) {
|
||||||
|
|
||||||
match (errors.len(), warnings.len()) {
|
match (errors.len(), warnings.len()) {
|
||||||
(0, 0) => return,
|
(0, 0) => return,
|
||||||
(0, _) => inner
|
(0, _) => {
|
||||||
.emitter
|
// Use `inner.emitter` directly, otherwise the warning might not be emitted, e.g.
|
||||||
.emit_diagnostic(&Diagnostic::new(Warning, DiagnosticMessage::Str(warnings))),
|
// with a configuration like `--cap-lints allow --force-warn bare_trait_objects`.
|
||||||
|
inner
|
||||||
|
.emitter
|
||||||
|
.emit_diagnostic(Diagnostic::new(Warning, DiagnosticMessage::Str(warnings)));
|
||||||
|
}
|
||||||
(_, 0) => {
|
(_, 0) => {
|
||||||
inner.emit_diagnostic(Diagnostic::new(Fatal, errors));
|
inner.emit_diagnostic(Diagnostic::new(Fatal, errors));
|
||||||
}
|
}
|
||||||
|
@ -1057,7 +1051,7 @@ pub fn must_teach(&self, code: ErrCode) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force_print_diagnostic(&self, db: Diagnostic) {
|
pub fn force_print_diagnostic(&self, db: Diagnostic) {
|
||||||
self.inner.borrow_mut().emitter.emit_diagnostic(&db);
|
self.inner.borrow_mut().emitter.emit_diagnostic(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
|
pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
|
||||||
|
@ -1248,43 +1242,19 @@ fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
|
fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
|
||||||
// The `LintExpectationId` can be stable or unstable depending on when it was created.
|
assert!(diagnostic.level.can_be_top_or_sub().0);
|
||||||
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
|
|
||||||
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
|
|
||||||
// a stable one by the `LintLevelsBuilder`.
|
|
||||||
if let Some(LintExpectationId::Unstable { .. }) = diagnostic.level.get_expectation_id() {
|
|
||||||
self.unstable_expect_diagnostics.push(diagnostic);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(eddyb) this should check for `has_errors` and stop pushing
|
if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
|
||||||
// once *any* errors were emitted (and truncate `span_delayed_bugs`
|
// The `LintExpectationId` can be stable or unstable depending on when it was created.
|
||||||
// when an error is first emitted, also), but maybe there's a case
|
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
|
||||||
// in which that's not sound? otherwise this is really inefficient.
|
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
|
||||||
match diagnostic.level {
|
// a stable one by the `LintLevelsBuilder`.
|
||||||
DelayedBug(_) if self.flags.eagerly_emit_delayed_bugs => {
|
if let LintExpectationId::Unstable { .. } = expectation_id {
|
||||||
diagnostic.level = Error;
|
self.unstable_expect_diagnostics.push(diagnostic);
|
||||||
}
|
|
||||||
DelayedBug(DelayedBugKind::Normal) => {
|
|
||||||
let backtrace = std::backtrace::Backtrace::capture();
|
|
||||||
self.span_delayed_bugs
|
|
||||||
.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
|
||||||
#[allow(deprecated)]
|
|
||||||
return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
|
||||||
}
|
|
||||||
DelayedBug(DelayedBugKind::GoodPath) => {
|
|
||||||
let backtrace = std::backtrace::Backtrace::capture();
|
|
||||||
self.good_path_delayed_bugs
|
|
||||||
.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
_ => {}
|
self.suppressed_expected_diag = true;
|
||||||
}
|
self.fulfilled_expectations.insert(expectation_id.normalize());
|
||||||
|
|
||||||
// This must come after the possible promotion of `DelayedBug` to
|
|
||||||
// `Error` above.
|
|
||||||
if matches!(diagnostic.level, Error | Fatal) && self.treat_next_err_as_bug() {
|
|
||||||
diagnostic.level = Bug;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if diagnostic.has_future_breakage() {
|
if diagnostic.has_future_breakage() {
|
||||||
|
@ -1295,21 +1265,45 @@ fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuarant
|
||||||
self.future_breakage_diagnostics.push(diagnostic.clone());
|
self.future_breakage_diagnostics.push(diagnostic.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
|
if matches!(diagnostic.level, DelayedBug | GoodPathDelayedBug)
|
||||||
self.suppressed_expected_diag = true;
|
&& self.flags.eagerly_emit_delayed_bugs
|
||||||
self.fulfilled_expectations.insert(expectation_id.normalize());
|
{
|
||||||
|
diagnostic.level = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if diagnostic.level == Warning && !self.flags.can_emit_warnings {
|
match diagnostic.level {
|
||||||
if diagnostic.has_future_breakage() {
|
// This must come after the possible promotion of `DelayedBug`/`GoodPathDelayedBug` to
|
||||||
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
// `Error` above.
|
||||||
|
Fatal | Error if self.treat_next_err_as_bug() => {
|
||||||
|
diagnostic.level = Bug;
|
||||||
}
|
}
|
||||||
return None;
|
DelayedBug => {
|
||||||
}
|
// FIXME(eddyb) this should check for `has_errors` and stop pushing
|
||||||
|
// once *any* errors were emitted (and truncate `delayed_bugs`
|
||||||
if matches!(diagnostic.level, Expect(_) | Allow) {
|
// when an error is first emitted, also), but maybe there's a case
|
||||||
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
// in which that's not sound? otherwise this is really inefficient.
|
||||||
return None;
|
let backtrace = std::backtrace::Backtrace::capture();
|
||||||
|
self.delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||||
|
#[allow(deprecated)]
|
||||||
|
return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||||
|
}
|
||||||
|
GoodPathDelayedBug => {
|
||||||
|
let backtrace = std::backtrace::Backtrace::capture();
|
||||||
|
self.good_path_delayed_bugs
|
||||||
|
.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Warning if !self.flags.can_emit_warnings => {
|
||||||
|
if diagnostic.has_future_breakage() {
|
||||||
|
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Allow | Expect(_) => {
|
||||||
|
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut guaranteed = None;
|
let mut guaranteed = None;
|
||||||
|
@ -1325,11 +1319,15 @@ fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuarant
|
||||||
!self.emitted_diagnostics.insert(diagnostic_hash)
|
!self.emitted_diagnostics.insert(diagnostic_hash)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let is_error = diagnostic.is_error();
|
||||||
|
let is_lint = diagnostic.is_lint.is_some();
|
||||||
|
|
||||||
// Only emit the diagnostic if we've been asked to deduplicate or
|
// Only emit the diagnostic if we've been asked to deduplicate or
|
||||||
// haven't already emitted an equivalent diagnostic.
|
// haven't already emitted an equivalent diagnostic.
|
||||||
if !(self.flags.deduplicate_diagnostics && already_emitted) {
|
if !(self.flags.deduplicate_diagnostics && already_emitted) {
|
||||||
debug!(?diagnostic);
|
debug!(?diagnostic);
|
||||||
debug!(?self.emitted_diagnostics);
|
debug!(?self.emitted_diagnostics);
|
||||||
|
|
||||||
let already_emitted_sub = |sub: &mut SubDiagnostic| {
|
let already_emitted_sub = |sub: &mut SubDiagnostic| {
|
||||||
debug!(?sub);
|
debug!(?sub);
|
||||||
if sub.level != OnceNote && sub.level != OnceHelp {
|
if sub.level != OnceNote && sub.level != OnceHelp {
|
||||||
|
@ -1341,7 +1339,6 @@ fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuarant
|
||||||
debug!(?diagnostic_hash);
|
debug!(?diagnostic_hash);
|
||||||
!self.emitted_diagnostics.insert(diagnostic_hash)
|
!self.emitted_diagnostics.insert(diagnostic_hash)
|
||||||
};
|
};
|
||||||
|
|
||||||
diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
|
diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
|
||||||
if already_emitted {
|
if already_emitted {
|
||||||
diagnostic.note(
|
diagnostic.note(
|
||||||
|
@ -1349,16 +1346,17 @@ fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuarant
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emitter.emit_diagnostic(&diagnostic);
|
if is_error {
|
||||||
if diagnostic.is_error() {
|
|
||||||
self.deduplicated_err_count += 1;
|
self.deduplicated_err_count += 1;
|
||||||
} else if matches!(diagnostic.level, ForceWarning(_) | Warning) {
|
} else if matches!(diagnostic.level, ForceWarning(_) | Warning) {
|
||||||
self.deduplicated_warn_count += 1;
|
self.deduplicated_warn_count += 1;
|
||||||
}
|
}
|
||||||
self.has_printed = true;
|
self.has_printed = true;
|
||||||
|
|
||||||
|
self.emitter.emit_diagnostic(diagnostic);
|
||||||
}
|
}
|
||||||
if diagnostic.is_error() {
|
if is_error {
|
||||||
if diagnostic.is_lint.is_some() {
|
if is_lint {
|
||||||
self.lint_err_count += 1;
|
self.lint_err_count += 1;
|
||||||
} else {
|
} else {
|
||||||
self.err_count += 1;
|
self.err_count += 1;
|
||||||
|
@ -1397,12 +1395,12 @@ fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) {
|
||||||
fn flush_delayed(&mut self, kind: DelayedBugKind) {
|
fn flush_delayed(&mut self, kind: DelayedBugKind) {
|
||||||
let (bugs, note1) = match kind {
|
let (bugs, note1) = match kind {
|
||||||
DelayedBugKind::Normal => (
|
DelayedBugKind::Normal => (
|
||||||
std::mem::take(&mut self.span_delayed_bugs),
|
std::mem::take(&mut self.delayed_bugs),
|
||||||
"no errors encountered even though `span_delayed_bug` issued",
|
"no errors encountered even though delayed bugs were created",
|
||||||
),
|
),
|
||||||
DelayedBugKind::GoodPath => (
|
DelayedBugKind::GoodPath => (
|
||||||
std::mem::take(&mut self.good_path_delayed_bugs),
|
std::mem::take(&mut self.good_path_delayed_bugs),
|
||||||
"no warnings or errors encountered even though `good_path_delayed_bugs` issued",
|
"no warnings or errors encountered even though good path delayed bugs were created",
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let note2 = "those delayed bugs will now be shown as internal compiler errors";
|
let note2 = "those delayed bugs will now be shown as internal compiler errors";
|
||||||
|
@ -1441,8 +1439,8 @@ fn flush_delayed(&mut self, kind: DelayedBugKind) {
|
||||||
let mut bug =
|
let mut bug =
|
||||||
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
|
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
|
||||||
|
|
||||||
// "Undelay" the `DelayedBug`s (into plain `Bug`s).
|
// "Undelay" the delayed bugs (into plain `Bug`s).
|
||||||
if !matches!(bug.level, DelayedBug(_)) {
|
if !matches!(bug.level, DelayedBug | GoodPathDelayedBug) {
|
||||||
// NOTE(eddyb) not panicking here because we're already producing
|
// NOTE(eddyb) not panicking here because we're already producing
|
||||||
// an ICE, and the more information the merrier.
|
// an ICE, and the more information the merrier.
|
||||||
bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
|
bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
|
||||||
|
@ -1508,34 +1506,58 @@ fn decorate(mut self) -> Diagnostic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Level is_error EmissionGuarantee Top-level Sub Used in lints?
|
||||||
|
/// ----- -------- ----------------- --------- --- --------------
|
||||||
|
/// Bug yes BugAbort yes - -
|
||||||
|
/// Fatal yes FatalAbort/FatalError(*) yes - -
|
||||||
|
/// Error yes ErrorGuaranteed yes - yes
|
||||||
|
/// DelayedBug yes ErrorGuaranteed yes - -
|
||||||
|
/// GoodPathDelayedBug - () yes - -
|
||||||
|
/// ForceWarning - () yes - lint-only
|
||||||
|
/// Warning - () yes yes yes
|
||||||
|
/// Note - () rare yes -
|
||||||
|
/// OnceNote - () - yes lint-only
|
||||||
|
/// Help - () rare yes -
|
||||||
|
/// OnceHelp - () - yes lint-only
|
||||||
|
/// FailureNote - () rare - -
|
||||||
|
/// Allow - () yes - lint-only
|
||||||
|
/// Expect - () yes - lint-only
|
||||||
|
///
|
||||||
|
/// (*) `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is
|
||||||
|
/// occasionally used.
|
||||||
|
///
|
||||||
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
|
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
|
||||||
pub enum Level {
|
pub enum Level {
|
||||||
/// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic.
|
/// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic.
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `BugAbort`.
|
|
||||||
Bug,
|
Bug,
|
||||||
|
|
||||||
|
/// An error that causes an immediate abort. Used for things like configuration errors,
|
||||||
|
/// internal overflows, some file operation errors.
|
||||||
|
Fatal,
|
||||||
|
|
||||||
|
/// An error in the code being compiled, which prevents compilation from finishing. This is the
|
||||||
|
/// most common case.
|
||||||
|
Error,
|
||||||
|
|
||||||
/// This is a strange one: lets you register an error without emitting it. If compilation ends
|
/// This is a strange one: lets you register an error without emitting it. If compilation ends
|
||||||
/// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be
|
/// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be
|
||||||
/// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
|
/// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
|
||||||
/// that should only be reached when compiling erroneous code.
|
/// that should only be reached when compiling erroneous code.
|
||||||
///
|
DelayedBug,
|
||||||
/// Its `EmissionGuarantee` is `ErrorGuaranteed` for `Normal` delayed bugs, and `()` for
|
|
||||||
/// `GoodPath` delayed bugs.
|
|
||||||
DelayedBug(DelayedBugKind),
|
|
||||||
|
|
||||||
/// An error that causes an immediate abort. Used for things like configuration errors,
|
/// Like `DelayedBug`, but weaker: lets you register an error without emitting it. If
|
||||||
/// internal overflows, some file operation errors.
|
/// compilation ends without any other diagnostics being emitted (and without an expected lint
|
||||||
|
/// being suppressed), this will be emitted as a bug. Otherwise, it will be silently dropped.
|
||||||
|
/// I.e. "expect other diagnostics are emitted (or suppressed)" semantics. Useful on code paths
|
||||||
|
/// that should only be reached when emitting diagnostics, e.g. for expensive one-time
|
||||||
|
/// diagnostic formatting operations.
|
||||||
///
|
///
|
||||||
/// Its `EmissionGuarantee` is `FatalAbort`, except in the non-aborting "almost fatal" case
|
/// FIXME(nnethercote) good path delayed bugs are semantically strange: if printed they produce
|
||||||
/// that is occasionally used, where it is `FatalError`.
|
/// an ICE, but they don't satisfy `is_error` and they don't guarantee an error is emitted.
|
||||||
Fatal,
|
/// Plus there's the extra complication with expected (suppressed) lints. They have limited
|
||||||
|
/// use, and are used in very few places, and "good path" isn't a good name. It would be good
|
||||||
/// An error in the code being compiled, which prevents compilation from finishing. This is the
|
/// to remove them.
|
||||||
/// most common case.
|
GoodPathDelayedBug,
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `ErrorGuaranteed`.
|
|
||||||
Error,
|
|
||||||
|
|
||||||
/// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
|
/// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
|
||||||
/// from finishing.
|
/// from finishing.
|
||||||
|
@ -1545,45 +1567,28 @@ pub enum Level {
|
||||||
ForceWarning(Option<LintExpectationId>),
|
ForceWarning(Option<LintExpectationId>),
|
||||||
|
|
||||||
/// A warning about the code being compiled. Does not prevent compilation from finishing.
|
/// A warning about the code being compiled. Does not prevent compilation from finishing.
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
Warning,
|
Warning,
|
||||||
|
|
||||||
/// A message giving additional context. Rare, because notes are more commonly attached to other
|
/// A message giving additional context.
|
||||||
/// diagnostics such as errors.
|
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
Note,
|
Note,
|
||||||
|
|
||||||
/// A note that is only emitted once. Rare, mostly used in circumstances relating to lints.
|
/// A note that is only emitted once.
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
OnceNote,
|
OnceNote,
|
||||||
|
|
||||||
/// A message suggesting how to fix something. Rare, because help messages are more commonly
|
/// A message suggesting how to fix something.
|
||||||
/// attached to other diagnostics such as errors.
|
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
Help,
|
Help,
|
||||||
|
|
||||||
/// A help that is only emitted once. Rare.
|
/// A help that is only emitted once.
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
OnceHelp,
|
OnceHelp,
|
||||||
|
|
||||||
/// Similar to `Note`, but used in cases where compilation has failed. Rare.
|
/// Similar to `Note`, but used in cases where compilation has failed. When printed for human
|
||||||
///
|
/// consumption, it doesn't have any kind of `note:` label.
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
FailureNote,
|
FailureNote,
|
||||||
|
|
||||||
/// Only used for lints.
|
/// Only used for lints.
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
Allow,
|
Allow,
|
||||||
|
|
||||||
/// Only used for lints.
|
/// Only used for lints.
|
||||||
///
|
|
||||||
/// Its `EmissionGuarantee` is `()`.
|
|
||||||
Expect(LintExpectationId),
|
Expect(LintExpectationId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1597,7 +1602,7 @@ impl Level {
|
||||||
fn color(self) -> ColorSpec {
|
fn color(self) -> ColorSpec {
|
||||||
let mut spec = ColorSpec::new();
|
let mut spec = ColorSpec::new();
|
||||||
match self {
|
match self {
|
||||||
Bug | DelayedBug(_) | Fatal | Error => {
|
Bug | Fatal | Error | DelayedBug | GoodPathDelayedBug => {
|
||||||
spec.set_fg(Some(Color::Red)).set_intense(true);
|
spec.set_fg(Some(Color::Red)).set_intense(true);
|
||||||
}
|
}
|
||||||
ForceWarning(_) | Warning => {
|
ForceWarning(_) | Warning => {
|
||||||
|
@ -1617,7 +1622,7 @@ fn color(self) -> ColorSpec {
|
||||||
|
|
||||||
pub fn to_str(self) -> &'static str {
|
pub fn to_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Bug | DelayedBug(_) => "error: internal compiler error",
|
Bug | DelayedBug | GoodPathDelayedBug => "error: internal compiler error",
|
||||||
Fatal | Error => "error",
|
Fatal | Error => "error",
|
||||||
ForceWarning(_) | Warning => "warning",
|
ForceWarning(_) | Warning => "warning",
|
||||||
Note | OnceNote => "note",
|
Note | OnceNote => "note",
|
||||||
|
@ -1637,6 +1642,19 @@ pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can this level be used in a top-level diagnostic message and/or a
|
||||||
|
// subdiagnostic message?
|
||||||
|
fn can_be_top_or_sub(&self) -> (bool, bool) {
|
||||||
|
match self {
|
||||||
|
Bug | DelayedBug | Fatal | Error | GoodPathDelayedBug | ForceWarning(_)
|
||||||
|
| FailureNote | Allow | Expect(_) => (true, false),
|
||||||
|
|
||||||
|
Warning | Note | Help => (true, true),
|
||||||
|
|
||||||
|
OnceNote | OnceHelp => (false, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
|
// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_infer::traits::FulfillmentError;
|
use rustc_infer::traits::FulfillmentError;
|
||||||
|
use rustc_middle::query::Key;
|
||||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_span::edit_distance::find_best_match_for_name;
|
use rustc_span::edit_distance::find_best_match_for_name;
|
||||||
|
@ -859,6 +860,56 @@ pub(crate) fn complain_about_missing_associated_types(
|
||||||
|
|
||||||
self.set_tainted_by_errors(err.emit());
|
self.set_tainted_by_errors(err.emit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On ambiguous associated type, look for an associated function whose name matches the
|
||||||
|
/// extended path and, if found, emit an E0223 error with a structured suggestion.
|
||||||
|
/// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195)
|
||||||
|
pub(crate) fn maybe_report_similar_assoc_fn(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
qself_ty: Ty<'tcx>,
|
||||||
|
qself: &hir::Ty<'_>,
|
||||||
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
if let Some((_, node)) = tcx.hir().parent_iter(qself.hir_id).skip(1).next()
|
||||||
|
&& let hir::Node::Expr(hir::Expr {
|
||||||
|
kind:
|
||||||
|
hir::ExprKind::Path(hir::QPath::TypeRelative(
|
||||||
|
hir::Ty {
|
||||||
|
kind:
|
||||||
|
hir::TyKind::Path(hir::QPath::TypeRelative(
|
||||||
|
_,
|
||||||
|
hir::PathSegment { ident: ident2, .. },
|
||||||
|
)),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
hir::PathSegment { ident: ident3, .. },
|
||||||
|
)),
|
||||||
|
..
|
||||||
|
}) = node
|
||||||
|
&& let Some(ty_def_id) = qself_ty.ty_def_id()
|
||||||
|
&& let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id)
|
||||||
|
&& let name = format!("{ident2}_{ident3}")
|
||||||
|
&& let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx
|
||||||
|
.associated_items(inherent_impl)
|
||||||
|
.filter_by_name_unhygienic(Symbol::intern(&name))
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
let reported =
|
||||||
|
struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type")
|
||||||
|
.with_span_suggestion_verbose(
|
||||||
|
ident2.span.to(ident3.span),
|
||||||
|
format!("there is an associated function with a similar name: `{name}`"),
|
||||||
|
name,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
self.set_tainted_by_errors(reported);
|
||||||
|
Err(reported)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits an error regarding forbidden type binding associations
|
/// Emits an error regarding forbidden type binding associations
|
||||||
|
|
|
@ -1373,6 +1373,8 @@ pub fn associated_path_to_ty(
|
||||||
)
|
)
|
||||||
.emit() // Already reported in an earlier stage.
|
.emit() // Already reported in an earlier stage.
|
||||||
} else {
|
} else {
|
||||||
|
self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?;
|
||||||
|
|
||||||
let traits: Vec<_> =
|
let traits: Vec<_> =
|
||||||
self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
|
self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
|
||||||
|
|
||||||
|
|
|
@ -481,7 +481,7 @@ fn fold_const(&mut self, mut ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
}
|
}
|
||||||
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
|
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
|
||||||
match self.infcx.unwrap().probe_effect_var(vid) {
|
match self.infcx.unwrap().probe_effect_var(vid) {
|
||||||
Some(value) => return self.fold_const(value.as_const(self.tcx)),
|
Some(value) => return self.fold_const(value),
|
||||||
None => {
|
None => {
|
||||||
return self.canonicalize_const_var(
|
return self.canonicalize_const_var(
|
||||||
CanonicalVarInfo { kind: CanonicalVarKind::Effect },
|
CanonicalVarInfo { kind: CanonicalVarKind::Effect },
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
|
use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||||
use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind};
|
use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use rustc_index::IndexVec;
|
use rustc_index::IndexVec;
|
||||||
|
use rustc_middle::infer::unify_key::EffectVarValue;
|
||||||
use rustc_middle::ty::fold::TypeFoldable;
|
use rustc_middle::ty::fold::TypeFoldable;
|
||||||
use rustc_middle::ty::GenericArg;
|
use rustc_middle::ty::GenericArg;
|
||||||
use rustc_middle::ty::{self, List, Ty, TyCtxt};
|
use rustc_middle::ty::{self, List, Ty, TyCtxt};
|
||||||
|
@ -152,7 +153,12 @@ pub fn instantiate_canonical_var(
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
CanonicalVarKind::Effect => {
|
CanonicalVarKind::Effect => {
|
||||||
let vid = self.inner.borrow_mut().effect_unification_table().new_key(None).vid;
|
let vid = self
|
||||||
|
.inner
|
||||||
|
.borrow_mut()
|
||||||
|
.effect_unification_table()
|
||||||
|
.new_key(EffectVarValue::Unknown)
|
||||||
|
.vid;
|
||||||
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool)
|
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,13 +151,8 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
|
self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
|
||||||
}
|
}
|
||||||
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
|
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
|
||||||
let opt_ct = self
|
let opt_ct =
|
||||||
.infcx
|
self.infcx.inner.borrow_mut().effect_unification_table().probe_value(v).known();
|
||||||
.inner
|
|
||||||
.borrow_mut()
|
|
||||||
.effect_unification_table()
|
|
||||||
.probe_value(v)
|
|
||||||
.map(|effect| effect.as_const(self.infcx.tcx));
|
|
||||||
self.freshen_const(
|
self.freshen_const(
|
||||||
opt_ct,
|
opt_ct,
|
||||||
ty::InferConst::EffectVar(v),
|
ty::InferConst::EffectVar(v),
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
pub use relate::combine::ObligationEmittingRelation;
|
pub use relate::combine::ObligationEmittingRelation;
|
||||||
use rustc_data_structures::captures::Captures;
|
use rustc_data_structures::captures::Captures;
|
||||||
use rustc_data_structures::undo_log::UndoLogs;
|
use rustc_data_structures::undo_log::UndoLogs;
|
||||||
|
use rustc_middle::infer::unify_key::EffectVarValue;
|
||||||
use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey};
|
use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey};
|
||||||
|
|
||||||
use self::opaque_types::OpaqueTypeStorage;
|
use self::opaque_types::OpaqueTypeStorage;
|
||||||
|
@ -25,8 +26,8 @@
|
||||||
use rustc_errors::{DiagCtxt, DiagnosticBuilder, ErrorGuaranteed};
|
use rustc_errors::{DiagCtxt, DiagnosticBuilder, ErrorGuaranteed};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
|
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
|
||||||
|
use rustc_middle::infer::unify_key::ConstVariableValue;
|
||||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
|
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
|
||||||
use rustc_middle::infer::unify_key::{ConstVariableValue, EffectVarValue};
|
|
||||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
|
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
|
||||||
use rustc_middle::mir::ConstraintCategory;
|
use rustc_middle::mir::ConstraintCategory;
|
||||||
use rustc_middle::traits::{select, DefiningAnchor};
|
use rustc_middle::traits::{select, DefiningAnchor};
|
||||||
|
@ -818,7 +819,7 @@ pub fn unsolved_effects(&self) -> Vec<ty::Const<'tcx>> {
|
||||||
|
|
||||||
(0..table.len())
|
(0..table.len())
|
||||||
.map(|i| ty::EffectVid::from_usize(i))
|
.map(|i| ty::EffectVid::from_usize(i))
|
||||||
.filter(|&vid| table.probe_value(vid).is_none())
|
.filter(|&vid| table.probe_value(vid).is_unknown())
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v), self.tcx.types.bool)
|
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v), self.tcx.types.bool)
|
||||||
})
|
})
|
||||||
|
@ -1236,7 +1237,8 @@ pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> GenericArg
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
|
pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
|
||||||
let effect_vid = self.inner.borrow_mut().effect_unification_table().new_key(None).vid;
|
let effect_vid =
|
||||||
|
self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid;
|
||||||
let ty = self
|
let ty = self
|
||||||
.tcx
|
.tcx
|
||||||
.type_of(param.def_id)
|
.type_of(param.def_id)
|
||||||
|
@ -1416,8 +1418,8 @@ pub fn probe_const_var(&self, vid: ty::ConstVid) -> Result<ty::Const<'tcx>, ty::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn probe_effect_var(&self, vid: EffectVid) -> Option<EffectVarValue<'tcx>> {
|
pub fn probe_effect_var(&self, vid: EffectVid) -> Option<ty::Const<'tcx>> {
|
||||||
self.inner.borrow_mut().effect_unification_table().probe_value(vid)
|
self.inner.borrow_mut().effect_unification_table().probe_value(vid).known()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to resolve all type/region/const variables in
|
/// Attempts to resolve all type/region/const variables in
|
||||||
|
@ -1897,7 +1899,8 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.effect_unification_table()
|
.effect_unification_table()
|
||||||
.probe_value(vid)
|
.probe_value(vid)
|
||||||
.map_or(ct, |val| val.as_const(self.infcx.tcx)),
|
.known()
|
||||||
|
.unwrap_or(ct),
|
||||||
_ => ct,
|
_ => ct,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,11 +202,7 @@ pub fn super_combine_consts<R>(
|
||||||
ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
|
ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
|
||||||
ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
|
ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
|
||||||
) => {
|
) => {
|
||||||
self.inner
|
self.inner.borrow_mut().effect_unification_table().union(a_vid, b_vid);
|
||||||
.borrow_mut()
|
|
||||||
.effect_unification_table()
|
|
||||||
.unify_var_var(a_vid, b_vid)
|
|
||||||
.map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?;
|
|
||||||
return Ok(a);
|
return Ok(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,19 +229,11 @@ pub fn super_combine_consts<R>(
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
|
(ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
|
||||||
return self.unify_effect_variable(
|
return Ok(self.unify_effect_variable(vid, b));
|
||||||
relation.a_is_expected(),
|
|
||||||
vid,
|
|
||||||
EffectVarValue::Const(b),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
|
(_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
|
||||||
return self.unify_effect_variable(
|
return Ok(self.unify_effect_variable(vid, a));
|
||||||
!relation.a_is_expected(),
|
|
||||||
vid,
|
|
||||||
EffectVarValue::Const(a),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
|
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
|
||||||
|
@ -366,18 +354,12 @@ fn unify_float_variable(
|
||||||
Ok(Ty::new_float(self.tcx, val))
|
Ok(Ty::new_float(self.tcx, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_effect_variable(
|
fn unify_effect_variable(&self, vid: ty::EffectVid, val: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
&self,
|
|
||||||
vid_is_expected: bool,
|
|
||||||
vid: ty::EffectVid,
|
|
||||||
val: EffectVarValue<'tcx>,
|
|
||||||
) -> RelateResult<'tcx, ty::Const<'tcx>> {
|
|
||||||
self.inner
|
self.inner
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.effect_unification_table()
|
.effect_unification_table()
|
||||||
.unify_var_value(vid, Some(val))
|
.union_value(vid, EffectVarValue::Known(val));
|
||||||
.map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?;
|
val
|
||||||
Ok(val.as_const(self.tcx))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -579,11 +561,3 @@ fn float_unification_error<'tcx>(
|
||||||
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
|
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
|
||||||
TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
|
TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn effect_unification_error<'tcx>(
|
|
||||||
_tcx: TyCtxt<'tcx>,
|
|
||||||
_a_is_expected: bool,
|
|
||||||
(_a, _b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>),
|
|
||||||
) -> TypeError<'tcx> {
|
|
||||||
bug!("unexpected effect unification error")
|
|
||||||
}
|
|
||||||
|
|
|
@ -237,14 +237,13 @@ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
}
|
}
|
||||||
ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => {
|
ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => {
|
||||||
debug_assert_eq!(c.ty(), self.infcx.tcx.types.bool);
|
debug_assert_eq!(c.ty(), self.infcx.tcx.types.bool);
|
||||||
match self.infcx.probe_effect_var(vid) {
|
self.infcx.probe_effect_var(vid).unwrap_or_else(|| {
|
||||||
Some(c) => c.as_const(self.infcx.tcx),
|
ty::Const::new_infer(
|
||||||
None => ty::Const::new_infer(
|
|
||||||
self.infcx.tcx,
|
self.infcx.tcx,
|
||||||
ty::InferConst::EffectVar(self.infcx.root_effect_var(vid)),
|
ty::InferConst::EffectVar(self.infcx.root_effect_var(vid)),
|
||||||
self.infcx.tcx.types.bool,
|
self.infcx.tcx.types.bool,
|
||||||
),
|
)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if c.has_infer() {
|
if c.has_infer() {
|
||||||
|
|
|
@ -386,7 +386,7 @@ fn encode_alloc_id(&mut self, alloc_id: &rustc_middle::mir::interpret::AllocId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
|
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy($value))`, which would
|
||||||
// normally need extra variables to avoid errors about multiple mutable borrows.
|
// normally need extra variables to avoid errors about multiple mutable borrows.
|
||||||
macro_rules! record {
|
macro_rules! record {
|
||||||
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
||||||
|
@ -398,7 +398,7 @@ macro_rules! record {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
|
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_array($value))`, which would
|
||||||
// normally need extra variables to avoid errors about multiple mutable borrows.
|
// normally need extra variables to avoid errors about multiple mutable borrows.
|
||||||
macro_rules! record_array {
|
macro_rules! record_array {
|
||||||
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
||||||
|
|
|
@ -194,33 +194,37 @@ fn unify_values(&value1: &Self, &value2: &Self) -> Result<Self, Self::Error> {
|
||||||
/// values for the effect inference variable
|
/// values for the effect inference variable
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum EffectVarValue<'tcx> {
|
pub enum EffectVarValue<'tcx> {
|
||||||
/// The host effect is on, enabling access to syscalls, filesystem access, etc.
|
Unknown,
|
||||||
Host,
|
Known(ty::Const<'tcx>),
|
||||||
/// The host effect is off. Execution is restricted to const operations only.
|
|
||||||
NoHost,
|
|
||||||
Const(ty::Const<'tcx>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> EffectVarValue<'tcx> {
|
impl<'tcx> EffectVarValue<'tcx> {
|
||||||
pub fn as_const(self, tcx: TyCtxt<'tcx>) -> ty::Const<'tcx> {
|
pub fn known(self) -> Option<ty::Const<'tcx>> {
|
||||||
match self {
|
match self {
|
||||||
EffectVarValue::Host => tcx.consts.true_,
|
EffectVarValue::Unknown => None,
|
||||||
EffectVarValue::NoHost => tcx.consts.false_,
|
EffectVarValue::Known(value) => Some(value),
|
||||||
EffectVarValue::Const(c) => c,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_unknown(self) -> bool {
|
||||||
|
match self {
|
||||||
|
EffectVarValue::Unknown => true,
|
||||||
|
EffectVarValue::Known(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
|
impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
|
||||||
type Error = (EffectVarValue<'tcx>, EffectVarValue<'tcx>);
|
type Error = NoError;
|
||||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
|
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
|
||||||
match (value1, value2) {
|
match (*value1, *value2) {
|
||||||
(EffectVarValue::Host, EffectVarValue::Host) => Ok(EffectVarValue::Host),
|
(EffectVarValue::Unknown, EffectVarValue::Unknown) => Ok(EffectVarValue::Unknown),
|
||||||
(EffectVarValue::NoHost, EffectVarValue::NoHost) => Ok(EffectVarValue::NoHost),
|
(EffectVarValue::Unknown, EffectVarValue::Known(val))
|
||||||
(EffectVarValue::NoHost | EffectVarValue::Host, _)
|
| (EffectVarValue::Known(val), EffectVarValue::Unknown) => {
|
||||||
| (_, EffectVarValue::NoHost | EffectVarValue::Host) => Err((*value1, *value2)),
|
Ok(EffectVarValue::Known(val))
|
||||||
(EffectVarValue::Const(_), EffectVarValue::Const(_)) => {
|
}
|
||||||
bug!("equating two const variables, both of which have known values")
|
(EffectVarValue::Known(_), EffectVarValue::Known(_)) => {
|
||||||
|
bug!("equating known inference variables: {value1:?} {value2:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,7 +233,7 @@ fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
|
||||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
pub struct EffectVidKey<'tcx> {
|
pub struct EffectVidKey<'tcx> {
|
||||||
pub vid: ty::EffectVid,
|
pub vid: ty::EffectVid,
|
||||||
pub phantom: PhantomData<EffectVarValue<'tcx>>,
|
pub phantom: PhantomData<ty::Const<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> From<ty::EffectVid> for EffectVidKey<'tcx> {
|
impl<'tcx> From<ty::EffectVid> for EffectVidKey<'tcx> {
|
||||||
|
@ -239,7 +243,7 @@ fn from(vid: ty::EffectVid) -> Self {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> UnifyKey for EffectVidKey<'tcx> {
|
impl<'tcx> UnifyKey for EffectVidKey<'tcx> {
|
||||||
type Value = Option<EffectVarValue<'tcx>>;
|
type Value = EffectVarValue<'tcx>;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self) -> u32 {
|
fn index(&self) -> u32 {
|
||||||
self.vid.as_u32()
|
self.vid.as_u32()
|
||||||
|
|
|
@ -156,7 +156,7 @@ fn partition<'tcx, I>(
|
||||||
placed
|
placed
|
||||||
};
|
};
|
||||||
|
|
||||||
// Merge until we have at most `max_cgu_count` codegen units.
|
// Merge until we don't exceed the max CGU count.
|
||||||
// `merge_codegen_units` is responsible for updating the CGU size
|
// `merge_codegen_units` is responsible for updating the CGU size
|
||||||
// estimates.
|
// estimates.
|
||||||
{
|
{
|
||||||
|
|
|
@ -292,6 +292,9 @@ resolve_underscore_lifetime_name_cannot_be_used_here =
|
||||||
resolve_unexpected_res_change_ty_to_const_param_sugg =
|
resolve_unexpected_res_change_ty_to_const_param_sugg =
|
||||||
you might have meant to write a const parameter here
|
you might have meant to write a const parameter here
|
||||||
|
|
||||||
|
resolve_unexpected_res_use_at_op_in_slice_pat_with_range_sugg =
|
||||||
|
if you meant to collect the rest of the slice in `{$ident}`, use the at operator
|
||||||
|
|
||||||
resolve_unreachable_label =
|
resolve_unreachable_label =
|
||||||
use of unreachable label `{$name}`
|
use of unreachable label `{$name}`
|
||||||
.label = unreachable label `{$name}`
|
.label = unreachable label `{$name}`
|
||||||
|
|
|
@ -800,3 +800,17 @@ pub(crate) struct UnexpectedResChangeTyToConstParamSugg {
|
||||||
#[applicability]
|
#[applicability]
|
||||||
pub applicability: Applicability,
|
pub applicability: Applicability,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[suggestion(
|
||||||
|
resolve_unexpected_res_use_at_op_in_slice_pat_with_range_sugg,
|
||||||
|
code = "{snippet}",
|
||||||
|
applicability = "maybe-incorrect",
|
||||||
|
style = "verbose"
|
||||||
|
)]
|
||||||
|
pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
pub ident: Ident,
|
||||||
|
pub snippet: String,
|
||||||
|
}
|
||||||
|
|
|
@ -1077,24 +1077,34 @@ fn suggest_at_operator_in_slice_pat_with_range(
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
path: &[Segment],
|
path: &[Segment],
|
||||||
) {
|
) {
|
||||||
if let Some(pat) = self.diagnostic_metadata.current_pat
|
let Some(pat) = self.diagnostic_metadata.current_pat else { return };
|
||||||
&& let ast::PatKind::Range(Some(start), None, range) = &pat.kind
|
let (bound, side, range) = match &pat.kind {
|
||||||
&& let ExprKind::Path(None, range_path) = &start.kind
|
ast::PatKind::Range(Some(bound), None, range) => (bound, Side::Start, range),
|
||||||
|
ast::PatKind::Range(None, Some(bound), range) => (bound, Side::End, range),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if let ExprKind::Path(None, range_path) = &bound.kind
|
||||||
&& let [segment] = &range_path.segments[..]
|
&& let [segment] = &range_path.segments[..]
|
||||||
&& let [s] = path
|
&& let [s] = path
|
||||||
&& segment.ident == s.ident
|
&& segment.ident == s.ident
|
||||||
|
&& segment.ident.span.eq_ctxt(range.span)
|
||||||
{
|
{
|
||||||
// We've encountered `[first, rest..]` where the user might have meant
|
// We've encountered `[first, rest..]` (#88404) or `[first, ..rest]` (#120591)
|
||||||
// `[first, rest @ ..]` (#88404).
|
// where the user might have meant `[first, rest @ ..]`.
|
||||||
err.span_suggestion_verbose(
|
let (span, snippet) = match side {
|
||||||
segment.ident.span.between(range.span),
|
Side::Start => (segment.ident.span.between(range.span), " @ ".into()),
|
||||||
format!(
|
Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)),
|
||||||
"if you meant to collect the rest of the slice in `{}`, use the at operator",
|
};
|
||||||
segment.ident,
|
err.subdiagnostic(errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg {
|
||||||
),
|
span,
|
||||||
" @ ",
|
ident: segment.ident,
|
||||||
Applicability::MaybeIncorrect,
|
snippet,
|
||||||
);
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Side {
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emitter for BufferEmitter {
|
impl Emitter for BufferEmitter {
|
||||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
fn emit_diagnostic(&mut self, diag: Diagnostic) {
|
||||||
let mut buffer = self.buffer.borrow_mut();
|
let mut buffer = self.buffer.borrow_mut();
|
||||||
|
|
||||||
let fluent_args = to_fluent_args(diag.args());
|
let fluent_args = to_fluent_args(diag.args());
|
||||||
|
|
|
@ -383,7 +383,7 @@ fn on_stack_pop(
|
||||||
// will never cause UB on the pointer itself.
|
// will never cause UB on the pointer itself.
|
||||||
let (_, _, kind) = this.get_alloc_info(*alloc_id);
|
let (_, _, kind) = this.get_alloc_info(*alloc_id);
|
||||||
if matches!(kind, AllocKind::LiveData) {
|
if matches!(kind, AllocKind::LiveData) {
|
||||||
let alloc_extra = this.get_alloc_extra(*alloc_id).unwrap();
|
let alloc_extra = this.get_alloc_extra(*alloc_id)?; // can still fail for `extern static`
|
||||||
let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
|
let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
|
||||||
alloc_borrow_tracker.release_protector(&this.machine, borrow_tracker, *tag)?;
|
alloc_borrow_tracker.release_protector(&this.machine, borrow_tracker, *tag)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//! `Machine` trait.
|
//! `Machine` trait.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::RefCell;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -336,20 +336,11 @@ pub struct AllocExtra<'tcx> {
|
||||||
/// if this allocation is leakable. The backtrace is not
|
/// if this allocation is leakable. The backtrace is not
|
||||||
/// pruned yet; that should be done before printing it.
|
/// pruned yet; that should be done before printing it.
|
||||||
pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
|
pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
|
||||||
/// An offset inside this allocation that was deemed aligned even for symbolic alignment checks.
|
|
||||||
/// Invariant: the promised alignment will never be less than the native alignment of this allocation.
|
|
||||||
pub symbolic_alignment: Cell<Option<(Size, Align)>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitProvenance for AllocExtra<'_> {
|
impl VisitProvenance for AllocExtra<'_> {
|
||||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let AllocExtra {
|
let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self;
|
||||||
borrow_tracker,
|
|
||||||
data_race,
|
|
||||||
weak_memory,
|
|
||||||
backtrace: _,
|
|
||||||
symbolic_alignment: _,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
borrow_tracker.visit_provenance(visit);
|
borrow_tracker.visit_provenance(visit);
|
||||||
data_race.visit_provenance(visit);
|
data_race.visit_provenance(visit);
|
||||||
|
@ -572,6 +563,14 @@ pub struct MiriMachine<'mir, 'tcx> {
|
||||||
/// that is fixed per stack frame; this lets us have sometimes different results for the
|
/// that is fixed per stack frame; this lets us have sometimes different results for the
|
||||||
/// same const while ensuring consistent results within a single call.
|
/// same const while ensuring consistent results within a single call.
|
||||||
const_cache: RefCell<FxHashMap<(mir::Const<'tcx>, usize), OpTy<'tcx, Provenance>>>,
|
const_cache: RefCell<FxHashMap<(mir::Const<'tcx>, usize), OpTy<'tcx, Provenance>>>,
|
||||||
|
|
||||||
|
/// For each allocation, an offset inside that allocation that was deemed aligned even for
|
||||||
|
/// symbolic alignment checks. This cannot be stored in `AllocExtra` since it needs to be
|
||||||
|
/// tracked for vtables and function allocations as well as regular allocations.
|
||||||
|
///
|
||||||
|
/// Invariant: the promised alignment will never be less than the native alignment of the
|
||||||
|
/// allocation.
|
||||||
|
pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
||||||
|
@ -698,6 +697,7 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>)
|
||||||
collect_leak_backtraces: config.collect_leak_backtraces,
|
collect_leak_backtraces: config.collect_leak_backtraces,
|
||||||
allocation_spans: RefCell::new(FxHashMap::default()),
|
allocation_spans: RefCell::new(FxHashMap::default()),
|
||||||
const_cache: RefCell::new(FxHashMap::default()),
|
const_cache: RefCell::new(FxHashMap::default()),
|
||||||
|
symbolic_alignment: RefCell::new(FxHashMap::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,6 +810,7 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
collect_leak_backtraces: _,
|
collect_leak_backtraces: _,
|
||||||
allocation_spans: _,
|
allocation_spans: _,
|
||||||
const_cache: _,
|
const_cache: _,
|
||||||
|
symbolic_alignment: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
threads.visit_provenance(visit);
|
threads.visit_provenance(visit);
|
||||||
|
@ -893,9 +894,13 @@ fn alignment_check(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// Let's see which alignment we have been promised for this allocation.
|
// Let's see which alignment we have been promised for this allocation.
|
||||||
let alloc_info = ecx.get_alloc_extra(alloc_id).unwrap(); // cannot fail since the allocation is live
|
let (promised_offset, promised_align) = ecx
|
||||||
let (promised_offset, promised_align) =
|
.machine
|
||||||
alloc_info.symbolic_alignment.get().unwrap_or((Size::ZERO, alloc_align));
|
.symbolic_alignment
|
||||||
|
.borrow()
|
||||||
|
.get(&alloc_id)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or((Size::ZERO, alloc_align));
|
||||||
if promised_align < align {
|
if promised_align < align {
|
||||||
// Definitely not enough.
|
// Definitely not enough.
|
||||||
Some(Misalignment { has: promised_align, required: align })
|
Some(Misalignment { has: promised_align, required: align })
|
||||||
|
@ -1132,7 +1137,6 @@ fn adjust_allocation<'b>(
|
||||||
data_race: race_alloc,
|
data_race: race_alloc,
|
||||||
weak_memory: buffer_alloc,
|
weak_memory: buffer_alloc,
|
||||||
backtrace,
|
backtrace,
|
||||||
symbolic_alignment: Cell::new(None),
|
|
||||||
},
|
},
|
||||||
|ptr| ecx.global_base_pointer(ptr),
|
|ptr| ecx.global_base_pointer(ptr),
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -196,6 +196,7 @@ fn remove_unreachable_allocs(&mut self, allocs: FxHashSet<AllocId>) {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let allocs = LiveAllocs { ecx: this, collected: allocs };
|
let allocs = LiveAllocs { ecx: this, collected: allocs };
|
||||||
this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
|
this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
|
||||||
|
this.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
|
||||||
this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs);
|
this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs);
|
||||||
if let Some(borrow_tracker) = &this.machine.borrow_tracker {
|
if let Some(borrow_tracker) = &this.machine.borrow_tracker {
|
||||||
borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
|
borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
|
||||||
|
|
|
@ -585,17 +585,17 @@ fn emulate_foreign_item_inner(
|
||||||
}
|
}
|
||||||
if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr) {
|
if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr) {
|
||||||
let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id);
|
let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id);
|
||||||
// Not `get_alloc_extra_mut`, need to handle read-only allocations!
|
|
||||||
let alloc_extra = this.get_alloc_extra(alloc_id)?;
|
|
||||||
// If the newly promised alignment is bigger than the native alignment of this
|
// If the newly promised alignment is bigger than the native alignment of this
|
||||||
// allocation, and bigger than the previously promised alignment, then set it.
|
// allocation, and bigger than the previously promised alignment, then set it.
|
||||||
if align > alloc_align
|
if align > alloc_align
|
||||||
&& !alloc_extra
|
&& !this
|
||||||
|
.machine
|
||||||
.symbolic_alignment
|
.symbolic_alignment
|
||||||
.get()
|
.get_mut()
|
||||||
.is_some_and(|(_, old_align)| align <= old_align)
|
.get(&alloc_id)
|
||||||
|
.is_some_and(|&(_, old_align)| align <= old_align)
|
||||||
{
|
{
|
||||||
alloc_extra.symbolic_alignment.set(Some((offset, align)));
|
this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
//@compile-flags: -Zmiri-symbolic-alignment-check
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
static _dispatch_queue_attr_concurrent: [u8; 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static DISPATCH_QUEUE_CONCURRENT: &'static [u8; 0] =
|
||||||
|
unsafe { &_dispatch_queue_attr_concurrent };
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _val = *DISPATCH_QUEUE_CONCURRENT; //~ERROR: is not supported
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
error: unsupported operation: `extern` static `_dispatch_queue_attr_concurrent` from crate `issue_miri_3288_ice_symbolic_alignment_extern_static` is not supported by Miri
|
||||||
|
--> $DIR/issue-miri-3288-ice-symbolic-alignment-extern-static.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let _val = *DISPATCH_QUEUE_CONCURRENT;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `extern` static `_dispatch_queue_attr_concurrent` from crate `issue_miri_3288_ice_symbolic_alignment_extern_static` is not supported by Miri
|
||||||
|
|
|
||||||
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
|
||||||
|
= note: BACKTRACE:
|
||||||
|
= note: inside `main` at $DIR/issue-miri-3288-ice-symbolic-alignment-extern-static.rs:LL:CC
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
//@compile-flags: -Zmiri-symbolic-alignment-check
|
//@compile-flags: -Zmiri-symbolic-alignment-check
|
||||||
#![feature(strict_provenance)]
|
#![feature(strict_provenance)]
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
fn test_align_to() {
|
fn test_align_to() {
|
||||||
const N: usize = 4;
|
const N: usize = 4;
|
||||||
let d = Box::new([0u32; N]);
|
let d = Box::new([0u32; N]);
|
||||||
|
@ -68,7 +70,7 @@ fn test_u64_array() {
|
||||||
#[repr(align(8))]
|
#[repr(align(8))]
|
||||||
struct AlignToU64<T>(T);
|
struct AlignToU64<T>(T);
|
||||||
|
|
||||||
const BYTE_LEN: usize = std::mem::size_of::<[u64; 4]>();
|
const BYTE_LEN: usize = mem::size_of::<[u64; 4]>();
|
||||||
type Data = AlignToU64<[u8; BYTE_LEN]>;
|
type Data = AlignToU64<[u8; BYTE_LEN]>;
|
||||||
|
|
||||||
fn example(data: &Data) {
|
fn example(data: &Data) {
|
||||||
|
@ -101,10 +103,29 @@ fn huge_align() {
|
||||||
let _ = std::ptr::invalid::<HugeSize>(SIZE).align_offset(SIZE);
|
let _ = std::ptr::invalid::<HugeSize>(SIZE).align_offset(SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This shows that we cannot store the promised alignment info in `AllocExtra`,
|
||||||
|
// since vtables do not have an `AllocExtra`.
|
||||||
|
fn vtable() {
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
type TWOPTR = u128;
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
type TWOPTR = u64;
|
||||||
|
|
||||||
|
let ptr: &dyn Send = &0;
|
||||||
|
let parts: (*const (), *const u8) = unsafe { mem::transmute(ptr) };
|
||||||
|
let vtable = parts.1 ;
|
||||||
|
let offset = vtable.align_offset(mem::align_of::<TWOPTR>());
|
||||||
|
let _vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0];
|
||||||
|
// FIXME: we can't actually do the access since vtable pointers act like zero-sized allocations.
|
||||||
|
// Enable the next line once https://github.com/rust-lang/rust/issues/117945 is implemented.
|
||||||
|
//let _place = unsafe { &*vtable_aligned };
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
test_align_to();
|
test_align_to();
|
||||||
test_from_utf8();
|
test_from_utf8();
|
||||||
test_u64_array();
|
test_u64_array();
|
||||||
test_cstr();
|
test_cstr();
|
||||||
huge_align();
|
huge_align();
|
||||||
|
vtable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
|
fn emit_diagnostic(&mut self, _db: Diagnostic) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn silent_emitter() -> Box<DynEmitter> {
|
fn silent_emitter() -> Box<DynEmitter> {
|
||||||
|
@ -64,7 +64,7 @@ struct SilentOnIgnoredFilesEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SilentOnIgnoredFilesEmitter {
|
impl SilentOnIgnoredFilesEmitter {
|
||||||
fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
|
fn handle_non_ignoreable_error(&mut self, db: Diagnostic) {
|
||||||
self.has_non_ignorable_parser_errors = true;
|
self.has_non_ignorable_parser_errors = true;
|
||||||
self.can_reset.store(false, Ordering::Release);
|
self.can_reset.store(false, Ordering::Release);
|
||||||
self.emitter.emit_diagnostic(db);
|
self.emitter.emit_diagnostic(db);
|
||||||
|
@ -86,7 +86,7 @@ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_diagnostic(&mut self, db: &Diagnostic) {
|
fn emit_diagnostic(&mut self, db: Diagnostic) {
|
||||||
if db.level() == DiagnosticLevel::Fatal {
|
if db.level() == DiagnosticLevel::Fatal {
|
||||||
return self.handle_non_ignoreable_error(db);
|
return self.handle_non_ignoreable_error(db);
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_diagnostic(&mut self, _db: &Diagnostic) {
|
fn emit_diagnostic(&mut self, _db: Diagnostic) {
|
||||||
self.num_emitted_errors.fetch_add(1, Ordering::Release);
|
self.num_emitted_errors.fetch_add(1, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,7 +424,7 @@ fn handles_fatal_parse_error_in_ignored_file() {
|
||||||
);
|
);
|
||||||
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
||||||
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
|
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
|
||||||
emitter.emit_diagnostic(&fatal_diagnostic);
|
emitter.emit_diagnostic(fatal_diagnostic);
|
||||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
|
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
|
||||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
||||||
}
|
}
|
||||||
|
@ -449,7 +449,7 @@ fn handles_recoverable_parse_error_in_ignored_file() {
|
||||||
);
|
);
|
||||||
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
||||||
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
|
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
|
||||||
emitter.emit_diagnostic(&non_fatal_diagnostic);
|
emitter.emit_diagnostic(non_fatal_diagnostic);
|
||||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
|
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
|
||||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
|
assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
|
||||||
}
|
}
|
||||||
|
@ -473,7 +473,7 @@ fn handles_recoverable_parse_error_in_non_ignored_file() {
|
||||||
);
|
);
|
||||||
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
|
||||||
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
|
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
|
||||||
emitter.emit_diagnostic(&non_fatal_diagnostic);
|
emitter.emit_diagnostic(non_fatal_diagnostic);
|
||||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
|
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
|
||||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
||||||
}
|
}
|
||||||
|
@ -512,9 +512,9 @@ fn handles_mix_of_recoverable_parse_error() {
|
||||||
let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
|
let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
|
||||||
let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
|
let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
|
||||||
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
|
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
|
||||||
emitter.emit_diagnostic(&bar_diagnostic);
|
emitter.emit_diagnostic(bar_diagnostic);
|
||||||
emitter.emit_diagnostic(&foo_diagnostic);
|
emitter.emit_diagnostic(foo_diagnostic);
|
||||||
emitter.emit_diagnostic(&fatal_diagnostic);
|
emitter.emit_diagnostic(fatal_diagnostic);
|
||||||
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
|
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
|
||||||
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,15 @@
|
||||||
// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
|
// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
|
||||||
// ignore-pass (test emits codegen-time warnings)
|
// ignore-pass (test emits codegen-time warnings)
|
||||||
|
|
||||||
#![feature(async_closure, async_fn_traits)]
|
#![feature(async_closure)]
|
||||||
|
|
||||||
extern crate block_on;
|
extern crate block_on;
|
||||||
|
|
||||||
use std::ops::AsyncFnMut;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
block_on::block_on(async {
|
block_on::block_on(async {
|
||||||
let x = async || {};
|
let x = async || {};
|
||||||
|
|
||||||
async fn needs_async_fn_mut(mut x: impl AsyncFnMut()) {
|
async fn needs_async_fn_mut(mut x: impl async FnMut()) {
|
||||||
x().await;
|
x().await;
|
||||||
}
|
}
|
||||||
needs_async_fn_mut(x).await;
|
needs_async_fn_mut(x).await;
|
||||||
|
|
|
@ -5,17 +5,15 @@
|
||||||
// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
|
// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
|
||||||
// ignore-pass (test emits codegen-time warnings)
|
// ignore-pass (test emits codegen-time warnings)
|
||||||
|
|
||||||
#![feature(async_closure, async_fn_traits)]
|
#![feature(async_closure)]
|
||||||
|
|
||||||
extern crate block_on;
|
extern crate block_on;
|
||||||
|
|
||||||
use std::ops::AsyncFnOnce;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
block_on::block_on(async {
|
block_on::block_on(async {
|
||||||
let x = async || {};
|
let x = async || {};
|
||||||
|
|
||||||
async fn needs_async_fn_once(x: impl AsyncFnOnce()) {
|
async fn needs_async_fn_once(x: impl async FnOnce()) {
|
||||||
x().await;
|
x().await;
|
||||||
}
|
}
|
||||||
needs_async_fn_once(x).await;
|
needs_async_fn_once(x).await;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// edition: 2021
|
// edition: 2021
|
||||||
|
|
||||||
#![feature(async_closure, noop_waker, async_fn_traits)]
|
#![feature(async_closure, noop_waker)]
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::pin;
|
use std::pin::pin;
|
||||||
|
|
|
@ -2,19 +2,18 @@
|
||||||
// edition:2021
|
// edition:2021
|
||||||
// build-pass
|
// build-pass
|
||||||
|
|
||||||
#![feature(async_closure, async_fn_traits)]
|
#![feature(async_closure)]
|
||||||
|
|
||||||
extern crate block_on;
|
extern crate block_on;
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::AsyncFn;
|
|
||||||
|
|
||||||
struct S;
|
struct S;
|
||||||
struct B<'b>(PhantomData<&'b mut &'b mut ()>);
|
struct B<'b>(PhantomData<&'b mut &'b mut ()>);
|
||||||
|
|
||||||
impl S {
|
impl S {
|
||||||
async fn q<F: AsyncFn(B<'_>)>(self, f: F) {
|
async fn q<F: async Fn(B<'_>)>(self, f: F) {
|
||||||
f(B(PhantomData)).await;
|
f(B(PhantomData)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,11 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
// check-run-results
|
// check-run-results
|
||||||
|
|
||||||
#![feature(async_closure, async_fn_traits)]
|
#![feature(async_closure)]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
extern crate block_on;
|
extern crate block_on;
|
||||||
|
|
||||||
use std::ops::AsyncFnOnce;
|
|
||||||
|
|
||||||
struct DropMe(i32);
|
struct DropMe(i32);
|
||||||
|
|
||||||
impl Drop for DropMe {
|
impl Drop for DropMe {
|
||||||
|
@ -18,7 +16,7 @@ fn drop(&mut self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn call_once(f: impl AsyncFnOnce()) {
|
async fn call_once(f: impl async FnOnce()) {
|
||||||
println!("before call");
|
println!("before call");
|
||||||
let fut = Box::pin(f());
|
let fut = Box::pin(f());
|
||||||
println!("after call");
|
println!("after call");
|
||||||
|
|
|
@ -8,20 +8,19 @@
|
||||||
// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
|
// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
|
||||||
// ignore-pass (test emits codegen-time warnings)
|
// ignore-pass (test emits codegen-time warnings)
|
||||||
|
|
||||||
#![feature(async_closure, noop_waker, async_fn_traits)]
|
#![feature(async_closure, noop_waker)]
|
||||||
|
|
||||||
extern crate block_on;
|
extern crate block_on;
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::ops::{AsyncFnMut, AsyncFnOnce};
|
|
||||||
use std::pin::pin;
|
use std::pin::pin;
|
||||||
use std::task::*;
|
use std::task::*;
|
||||||
|
|
||||||
async fn call_mut(f: &mut impl AsyncFnMut()) {
|
async fn call_mut(f: &mut impl async FnMut()) {
|
||||||
f().await;
|
f().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn call_once(f: impl AsyncFnOnce()) {
|
async fn call_once(f: impl async FnOnce()) {
|
||||||
f().await;
|
f().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,15 @@
|
||||||
|
|
||||||
// FIXME(async_closures): This needs a better error message!
|
// FIXME(async_closures): This needs a better error message!
|
||||||
|
|
||||||
#![feature(async_closure, async_fn_traits)]
|
#![feature(async_closure)]
|
||||||
|
|
||||||
use std::ops::AsyncFn;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
fn needs_async_fn(_: impl AsyncFn()) {}
|
fn needs_async_fn(_: impl async Fn()) {}
|
||||||
|
|
||||||
let mut x = 1;
|
let mut x = 1;
|
||||||
needs_async_fn(async || {
|
needs_async_fn(async || {
|
||||||
//~^ ERROR i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>
|
//~^ ERROR i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>
|
||||||
// FIXME: Should say "closure is AsyncFnMut but it needs AsyncFn" or sth.
|
// FIXME: Should say "closure is `async FnMut` but it needs `async Fn`" or sth.
|
||||||
x += 1;
|
x += 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
error[E0277]: the trait bound `i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not satisfied
|
error[E0277]: the trait bound `i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not satisfied
|
||||||
--> $DIR/wrong-fn-kind.rs:13:20
|
--> $DIR/wrong-fn-kind.rs:11:20
|
||||||
|
|
|
|
||||||
LL | needs_async_fn(async || {
|
LL | needs_async_fn(async || {
|
||||||
| _____--------------_^
|
| _____--------------_^
|
||||||
| | |
|
| | |
|
||||||
| | required by a bound introduced by this call
|
| | required by a bound introduced by this call
|
||||||
LL | |
|
LL | |
|
||||||
LL | | // FIXME: Should say "closure is AsyncFnMut but it needs AsyncFn" or sth.
|
LL | | // FIXME: Should say "closure is `async FnMut` but it needs `async Fn`" or sth.
|
||||||
LL | | x += 1;
|
LL | | x += 1;
|
||||||
LL | | });
|
LL | | });
|
||||||
| |_____^ the trait `ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not implemented for `i16`
|
| |_____^ the trait `ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not implemented for `i16`
|
||||||
|
|
|
|
||||||
note: required by a bound in `needs_async_fn`
|
note: required by a bound in `needs_async_fn`
|
||||||
--> $DIR/wrong-fn-kind.rs:10:31
|
--> $DIR/wrong-fn-kind.rs:8:31
|
||||||
|
|
|
|
||||||
LL | fn needs_async_fn(_: impl AsyncFn()) {}
|
LL | fn needs_async_fn(_: impl async Fn()) {}
|
||||||
| ^^^^^^^^^ required by this bound in `needs_async_fn`
|
| ^^^^^^^^^^ required by this bound in `needs_async_fn`
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
note: no errors encountered even though `span_delayed_bug` issued
|
note: no errors encountered even though delayed bugs were created
|
||||||
|
|
||||||
note: those delayed bugs will now be shown as internal compiler errors
|
note: those delayed bugs will now be shown as internal compiler errors
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
match &[1, 2, 3][..] {
|
match &[1, 2, 3][..] {
|
||||||
[1, rest..] => println!("{rest:?}"),
|
[1, rest..] => println!("{rest}"),
|
||||||
//~^ ERROR cannot find value `rest` in this scope
|
//~^ ERROR cannot find value `rest` in this scope
|
||||||
//~| ERROR cannot find value `rest` in this scope
|
//~| ERROR cannot find value `rest` in this scope
|
||||||
//~| ERROR `X..` patterns in slices are experimental
|
//~| ERROR `X..` patterns in slices are experimental
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
match &[4, 5, 6][..] {
|
||||||
|
[] => {}
|
||||||
|
[_, ..tail] => println!("{tail}"),
|
||||||
|
//~^ ERROR cannot find value `tail` in this scope
|
||||||
|
//~| ERROR cannot find value `tail` in this scope
|
||||||
|
//~| ERROR exclusive range pattern syntax is experimental
|
||||||
|
}
|
||||||
|
match &[7, 8, 9][..] {
|
||||||
|
[] => {}
|
||||||
|
[_, ...tail] => println!("{tail}"),
|
||||||
|
//~^ ERROR cannot find value `tail` in this scope
|
||||||
|
//~| ERROR cannot find value `tail` in this scope
|
||||||
|
//~| ERROR range-to patterns with `...` are not allowed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,82 @@
|
||||||
|
error: range-to patterns with `...` are not allowed
|
||||||
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:18:13
|
||||||
|
|
|
||||||
|
LL | [_, ...tail] => println!("{tail}"),
|
||||||
|
| ^^^ help: use `..=` instead
|
||||||
|
|
||||||
error[E0425]: cannot find value `rest` in this scope
|
error[E0425]: cannot find value `rest` in this scope
|
||||||
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13
|
||||||
|
|
|
|
||||||
LL | [1, rest..] => println!("{rest:?}"),
|
LL | [1, rest..] => println!("{rest}"),
|
||||||
| ^^^^ not found in this scope
|
| ^^^^ not found in this scope
|
||||||
|
|
|
|
||||||
help: if you meant to collect the rest of the slice in `rest`, use the at operator
|
help: if you meant to collect the rest of the slice in `rest`, use the at operator
|
||||||
|
|
|
|
||||||
LL | [1, rest @ ..] => println!("{rest:?}"),
|
LL | [1, rest @ ..] => println!("{rest}"),
|
||||||
| +
|
| +
|
||||||
|
|
||||||
error[E0425]: cannot find value `rest` in this scope
|
error[E0425]: cannot find value `rest` in this scope
|
||||||
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35
|
||||||
|
|
|
|
||||||
LL | [1, rest..] => println!("{rest:?}"),
|
LL | [1, rest..] => println!("{rest}"),
|
||||||
| ^^^^ not found in this scope
|
| ^^^^ not found in this scope
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `tail` in this scope
|
||||||
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:11:15
|
||||||
|
|
|
||||||
|
LL | [_, ..tail] => println!("{tail}"),
|
||||||
|
| ^^^^ not found in this scope
|
||||||
|
|
|
||||||
|
help: if you meant to collect the rest of the slice in `tail`, use the at operator
|
||||||
|
|
|
||||||
|
LL | [_, tail @ ..] => println!("{tail}"),
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `tail` in this scope
|
||||||
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:11:35
|
||||||
|
|
|
||||||
|
LL | [_, ..tail] => println!("{tail}"),
|
||||||
|
| ^^^^ not found in this scope
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `tail` in this scope
|
||||||
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:18:16
|
||||||
|
|
|
||||||
|
LL | [_, ...tail] => println!("{tail}"),
|
||||||
|
| ^^^^ not found in this scope
|
||||||
|
|
|
||||||
|
help: if you meant to collect the rest of the slice in `tail`, use the at operator
|
||||||
|
|
|
||||||
|
LL | [_, tail @ ..] => println!("{tail}"),
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `tail` in this scope
|
||||||
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:18:36
|
||||||
|
|
|
||||||
|
LL | [_, ...tail] => println!("{tail}"),
|
||||||
|
| ^^^^ not found in this scope
|
||||||
|
|
||||||
error[E0658]: `X..` patterns in slices are experimental
|
error[E0658]: `X..` patterns in slices are experimental
|
||||||
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13
|
||||||
|
|
|
|
||||||
LL | [1, rest..] => println!("{rest:?}"),
|
LL | [1, rest..] => println!("{rest}"),
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
|
= note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information
|
||||||
= help: add `#![feature(half_open_range_patterns_in_slices)]` to the crate attributes to enable
|
= help: add `#![feature(half_open_range_patterns_in_slices)]` to the crate attributes to enable
|
||||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error[E0658]: exclusive range pattern syntax is experimental
|
||||||
|
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:11:13
|
||||||
|
|
|
||||||
|
LL | [_, ..tail] => println!("{tail}"),
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #37854 <https://github.com/rust-lang/rust/issues/37854> for more information
|
||||||
|
= help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
= help: use an inclusive range pattern, like N..=M
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0425, E0658.
|
Some errors have detailed explanations: E0425, E0658.
|
||||||
For more information about an error, try `rustc --explain E0425`.
|
For more information about an error, try `rustc --explain E0425`.
|
||||||
|
|
20
tests/ui/suggestions/issue-109195.rs
Normal file
20
tests/ui/suggestions/issue-109195.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
fn main() {
|
||||||
|
String::from::utf8;
|
||||||
|
//~^ ERROR ambiguous associated type [E0223]
|
||||||
|
//~| HELP there is an associated function with a similar name: `from_utf8`
|
||||||
|
String::from::utf8();
|
||||||
|
//~^ ERROR ambiguous associated type [E0223]
|
||||||
|
//~| HELP there is an associated function with a similar name: `from_utf8`
|
||||||
|
String::from::utf16();
|
||||||
|
//~^ ERROR ambiguous associated type [E0223]
|
||||||
|
//~| HELP there is an associated function with a similar name: `from_utf16`
|
||||||
|
String::from::method_that_doesnt_exist();
|
||||||
|
//~^ ERROR ambiguous associated type [E0223]
|
||||||
|
//~| HELP if there were a trait named `Example` with associated type `from`
|
||||||
|
str::from::utf8();
|
||||||
|
//~^ ERROR ambiguous associated type [E0223]
|
||||||
|
//~| HELP if there were a trait named `Example` with associated type `from`
|
||||||
|
str::from::utf8_mut();
|
||||||
|
//~^ ERROR ambiguous associated type [E0223]
|
||||||
|
//~| HELP if there were a trait named `Example` with associated type `from`
|
||||||
|
}
|
69
tests/ui/suggestions/issue-109195.stderr
Normal file
69
tests/ui/suggestions/issue-109195.stderr
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
error[E0223]: ambiguous associated type
|
||||||
|
--> $DIR/issue-109195.rs:2:5
|
||||||
|
|
|
||||||
|
LL | String::from::utf8;
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: there is an associated function with a similar name: `from_utf8`
|
||||||
|
|
|
||||||
|
LL | String::from_utf8;
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0223]: ambiguous associated type
|
||||||
|
--> $DIR/issue-109195.rs:5:5
|
||||||
|
|
|
||||||
|
LL | String::from::utf8();
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: there is an associated function with a similar name: `from_utf8`
|
||||||
|
|
|
||||||
|
LL | String::from_utf8();
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0223]: ambiguous associated type
|
||||||
|
--> $DIR/issue-109195.rs:8:5
|
||||||
|
|
|
||||||
|
LL | String::from::utf16();
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: there is an associated function with a similar name: `from_utf16`
|
||||||
|
|
|
||||||
|
LL | String::from_utf16();
|
||||||
|
| ~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0223]: ambiguous associated type
|
||||||
|
--> $DIR/issue-109195.rs:11:5
|
||||||
|
|
|
||||||
|
LL | String::from::method_that_doesnt_exist();
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: if there were a trait named `Example` with associated type `from` implemented for `String`, you could use the fully-qualified path
|
||||||
|
|
|
||||||
|
LL | <String as Example>::from::method_that_doesnt_exist();
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0223]: ambiguous associated type
|
||||||
|
--> $DIR/issue-109195.rs:14:5
|
||||||
|
|
|
||||||
|
LL | str::from::utf8();
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path
|
||||||
|
|
|
||||||
|
LL | <str as Example>::from::utf8();
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0223]: ambiguous associated type
|
||||||
|
--> $DIR/issue-109195.rs:17:5
|
||||||
|
|
|
||||||
|
LL | str::from::utf8_mut();
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path
|
||||||
|
|
|
||||||
|
LL | <str as Example>::from::utf8_mut();
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0223`.
|
|
@ -1,4 +1,4 @@
|
||||||
note: no errors encountered even though `span_delayed_bug` issued
|
note: no errors encountered even though delayed bugs were created
|
||||||
|
|
||||||
note: those delayed bugs will now be shown as internal compiler errors
|
note: those delayed bugs will now be shown as internal compiler errors
|
||||||
|
|
||||||
|
|
|
@ -658,7 +658,6 @@ compiler-team = [
|
||||||
]
|
]
|
||||||
compiler-team-contributors = [
|
compiler-team-contributors = [
|
||||||
"@TaKO8Ki",
|
"@TaKO8Ki",
|
||||||
"@b-naber",
|
|
||||||
"@nnethercote",
|
"@nnethercote",
|
||||||
"@fmease",
|
"@fmease",
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue