Auto merge of #106730 - Nilstrieb:rollup-f7p8dsa, r=Nilstrieb
Rollup of 9 pull requests Successful merges: - #106321 (Collect and emit proper backtraces for `delay_span_bug`s) - #106397 (Check `impl`'s `where` clauses in `consider_impl_candidate` in experimental solver) - #106427 (Improve fluent error messages) - #106570 (add tests for div_duration_* functions) - #106648 (Polymorphization cleanup) - #106664 (Remove unnecessary lseek syscall when using std::fs::read) - #106709 (Disable "split dwarf inlining" by default.) - #106715 (Autolabel and ping wg for changes to new solver) - #106717 (fix typo LocalItemId -> ItemLocalId) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
ef4046e4f3
@ -40,12 +40,11 @@ where
|
||||
let index = index
|
||||
.try_into()
|
||||
.expect("more generic parameters than can fit into a `u32`");
|
||||
let is_used = unused_params.contains(index).map_or(true, |unused| !unused);
|
||||
// Only recurse when generic parameters in fns, closures and generators
|
||||
// are used and require substitution.
|
||||
// Just in case there are closures or generators within this subst,
|
||||
// recurse.
|
||||
if is_used && subst.needs_subst() {
|
||||
if unused_params.is_used(index) && subst.needs_subst() {
|
||||
return subst.visit_with(self);
|
||||
}
|
||||
}
|
||||
|
@ -1196,8 +1196,8 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
|
||||
};
|
||||
|
||||
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
|
||||
// Don't do this for `GoodPathBug`, which already emits its own more useful backtrace.
|
||||
if !info.payload().is::<rustc_errors::GoodPathBug>() {
|
||||
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
|
||||
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
|
||||
(*DEFAULT_HOOK)(info);
|
||||
|
||||
// Separate the output with an empty line
|
||||
@ -1235,7 +1235,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
|
||||
// a .span_bug or .bug call has already printed what
|
||||
// it wants to print.
|
||||
if !info.payload().is::<rustc_errors::ExplicitBug>()
|
||||
&& !info.payload().is::<rustc_errors::GoodPathBug>()
|
||||
&& !info.payload().is::<rustc_errors::DelayedBugPanic>()
|
||||
{
|
||||
let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
|
||||
handler.emit_diagnostic(&mut d);
|
||||
|
@ -28,6 +28,7 @@ use rustc_error_messages::{FluentArgs, SpanLabel};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::{max, min, Reverse};
|
||||
use std::error::Report;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, IsTerminal};
|
||||
use std::iter;
|
||||
@ -250,7 +251,7 @@ pub trait Emitter: Translate {
|
||||
let mut primary_span = diag.span.clone();
|
||||
let suggestions = diag.suggestions.as_deref().unwrap_or(&[]);
|
||||
if let Some((sugg, rest)) = suggestions.split_first() {
|
||||
let msg = self.translate_message(&sugg.msg, fluent_args);
|
||||
let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
|
||||
if rest.is_empty() &&
|
||||
// ^ if there is only one suggestion
|
||||
// don't display multi-suggestions as labels
|
||||
@ -1325,7 +1326,7 @@ impl EmitterWriter {
|
||||
// very *weird* formats
|
||||
// see?
|
||||
for (text, style) in msg.iter() {
|
||||
let text = self.translate_message(text, args);
|
||||
let text = self.translate_message(text, args).map_err(Report::new).unwrap();
|
||||
let lines = text.split('\n').collect::<Vec<_>>();
|
||||
if lines.len() > 1 {
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
@ -1387,7 +1388,7 @@ impl EmitterWriter {
|
||||
label_width += 2;
|
||||
}
|
||||
for (text, _) in msg.iter() {
|
||||
let text = self.translate_message(text, args);
|
||||
let text = self.translate_message(text, args).map_err(Report::new).unwrap();
|
||||
// Account for newlines to align output to its label.
|
||||
for (line, text) in normalize_whitespace(&text).lines().enumerate() {
|
||||
buffer.append(
|
||||
@ -2301,7 +2302,9 @@ impl FileWithAnnotatedLines {
|
||||
hi.col_display += 1;
|
||||
}
|
||||
|
||||
let label = label.as_ref().map(|m| emitter.translate_message(m, args).to_string());
|
||||
let label = label.as_ref().map(|m| {
|
||||
emitter.translate_message(m, args).map_err(Report::new).unwrap().to_string()
|
||||
});
|
||||
|
||||
if lo.line != hi.line {
|
||||
let ml = MultilineAnnotation {
|
||||
|
137
compiler/rustc_errors/src/error.rs
Normal file
137
compiler/rustc_errors/src/error.rs
Normal file
@ -0,0 +1,137 @@
|
||||
use rustc_error_messages::{
|
||||
fluent_bundle::resolver::errors::{ReferenceKind, ResolverError},
|
||||
FluentArgs, FluentError,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TranslateError<'args> {
|
||||
One {
|
||||
id: &'args Cow<'args, str>,
|
||||
args: &'args FluentArgs<'args>,
|
||||
kind: TranslateErrorKind<'args>,
|
||||
},
|
||||
Two {
|
||||
primary: Box<TranslateError<'args>>,
|
||||
fallback: Box<TranslateError<'args>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'args> TranslateError<'args> {
|
||||
pub fn message(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::MessageMissing }
|
||||
}
|
||||
pub fn primary(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::PrimaryBundleMissing }
|
||||
}
|
||||
pub fn attribute(
|
||||
id: &'args Cow<'args, str>,
|
||||
args: &'args FluentArgs<'args>,
|
||||
attr: &'args str,
|
||||
) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::AttributeMissing { attr } }
|
||||
}
|
||||
pub fn value(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::ValueMissing }
|
||||
}
|
||||
|
||||
pub fn fluent(
|
||||
id: &'args Cow<'args, str>,
|
||||
args: &'args FluentArgs<'args>,
|
||||
errs: Vec<FluentError>,
|
||||
) -> Self {
|
||||
Self::One { id, args, kind: TranslateErrorKind::Fluent { errs } }
|
||||
}
|
||||
|
||||
pub fn and(self, fallback: TranslateError<'args>) -> TranslateError<'args> {
|
||||
Self::Two { primary: Box::new(self), fallback: Box::new(fallback) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TranslateErrorKind<'args> {
|
||||
MessageMissing,
|
||||
PrimaryBundleMissing,
|
||||
AttributeMissing { attr: &'args str },
|
||||
ValueMissing,
|
||||
Fluent { errs: Vec<FluentError> },
|
||||
}
|
||||
|
||||
impl fmt::Display for TranslateError<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use TranslateErrorKind::*;
|
||||
|
||||
match self {
|
||||
Self::One { id, args, kind } => {
|
||||
writeln!(f, "failed while formatting fluent string `{id}`: ")?;
|
||||
match kind {
|
||||
MessageMissing => writeln!(f, "message was missing")?,
|
||||
PrimaryBundleMissing => writeln!(f, "the primary bundle was missing")?,
|
||||
AttributeMissing { attr } => {
|
||||
writeln!(f, "the attribute `{attr}` was missing")?;
|
||||
writeln!(f, "help: add `.{attr} = <message>`")?;
|
||||
}
|
||||
ValueMissing => writeln!(f, "the value was missing")?,
|
||||
Fluent { errs } => {
|
||||
for err in errs {
|
||||
match err {
|
||||
FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. }
|
||||
| ReferenceKind::Variable { id, .. },
|
||||
)) => {
|
||||
if args.iter().any(|(arg_id, _)| arg_id == id) {
|
||||
writeln!(
|
||||
f,
|
||||
"argument `{id}` exists but was not referenced correctly"
|
||||
)?;
|
||||
writeln!(f, "help: try using `{{${id}}}` instead")?;
|
||||
} else {
|
||||
writeln!(
|
||||
f,
|
||||
"the fluent string has an argument `{id}` that was not found."
|
||||
)?;
|
||||
let vars: Vec<&str> =
|
||||
args.iter().map(|(a, _v)| a).collect();
|
||||
match &*vars {
|
||||
[] => writeln!(f, "help: no arguments are available")?,
|
||||
[one] => writeln!(
|
||||
f,
|
||||
"help: the argument `{one}` is available"
|
||||
)?,
|
||||
[first, middle @ .., last] => {
|
||||
write!(f, "help: the arguments `{first}`")?;
|
||||
for a in middle {
|
||||
write!(f, ", `{a}`")?;
|
||||
}
|
||||
writeln!(f, " and `{last}` are available")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => writeln!(f, "{err}")?,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If someone cares about primary bundles, they'll probably notice it's missing
|
||||
// regardless or will be using `debug_assertions`
|
||||
// so we skip the arm below this one to avoid confusing the regular user.
|
||||
Self::Two { primary: box Self::One { kind: PrimaryBundleMissing, .. }, fallback } => {
|
||||
fmt::Display::fmt(fallback, f)?;
|
||||
}
|
||||
Self::Two { primary, fallback } => {
|
||||
writeln!(
|
||||
f,
|
||||
"first, fluent formatting using the primary bundle failed:\n {primary}\n \
|
||||
while attempting to recover by using the fallback bundle instead, another error occurred:\n{fallback}"
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for TranslateError<'_> {}
|
@ -24,6 +24,7 @@ use rustc_data_structures::sync::Lrc;
|
||||
use rustc_error_messages::FluentArgs;
|
||||
use rustc_span::hygiene::ExpnData;
|
||||
use rustc_span::Span;
|
||||
use std::error::Report;
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@ -321,7 +322,8 @@ impl Diagnostic {
|
||||
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
||||
let args = to_fluent_args(diag.args());
|
||||
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
||||
let translated_message = je.translate_message(&sugg.msg, &args);
|
||||
let translated_message =
|
||||
je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
|
||||
Diagnostic {
|
||||
message: translated_message.to_string(),
|
||||
code: None,
|
||||
@ -411,7 +413,10 @@ impl DiagnosticSpan {
|
||||
Self::from_span_etc(
|
||||
span.span,
|
||||
span.is_primary,
|
||||
span.label.as_ref().map(|m| je.translate_message(m, args)).map(|m| m.to_string()),
|
||||
span.label
|
||||
.as_ref()
|
||||
.map(|m| je.translate_message(m, args).unwrap())
|
||||
.map(|m| m.to_string()),
|
||||
suggestion,
|
||||
je,
|
||||
)
|
||||
|
@ -11,6 +11,10 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(result_option_inspect)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(yeet_expr)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(error_reporter)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
#[macro_use]
|
||||
@ -40,8 +44,8 @@ use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::HashStableContext;
|
||||
use rustc_span::{Loc, Span};
|
||||
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::error::Report;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroUsize;
|
||||
@ -55,11 +59,14 @@ mod diagnostic;
|
||||
mod diagnostic_builder;
|
||||
mod diagnostic_impls;
|
||||
pub mod emitter;
|
||||
pub mod error;
|
||||
pub mod json;
|
||||
mod lock;
|
||||
pub mod registry;
|
||||
mod snippet;
|
||||
mod styled_buffer;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod translation;
|
||||
|
||||
pub use diagnostic_builder::IntoDiagnostic;
|
||||
@ -364,9 +371,9 @@ pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
|
||||
/// or `.span_bug` rather than a failed assertion, etc.
|
||||
pub struct ExplicitBug;
|
||||
|
||||
/// Signifies that the compiler died with an explicit call to `.delay_good_path_bug`
|
||||
/// Signifies that the compiler died with an explicit call to `.delay_*_bug`
|
||||
/// rather than a failed assertion, etc.
|
||||
pub struct GoodPathBug;
|
||||
pub struct DelayedBugPanic;
|
||||
|
||||
pub use diagnostic::{
|
||||
AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
|
||||
@ -399,7 +406,7 @@ struct HandlerInner {
|
||||
warn_count: usize,
|
||||
deduplicated_err_count: usize,
|
||||
emitter: Box<dyn Emitter + sync::Send>,
|
||||
delayed_span_bugs: Vec<Diagnostic>,
|
||||
delayed_span_bugs: Vec<DelayedDiagnostic>,
|
||||
delayed_good_path_bugs: Vec<DelayedDiagnostic>,
|
||||
/// This flag indicates that an expected diagnostic was emitted and suppressed.
|
||||
/// This is used for the `delayed_good_path_bugs` check.
|
||||
@ -505,11 +512,7 @@ impl Drop for HandlerInner {
|
||||
|
||||
if !self.has_errors() {
|
||||
let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new());
|
||||
self.flush_delayed(
|
||||
bugs,
|
||||
"no errors encountered even though `delay_span_bug` issued",
|
||||
ExplicitBug,
|
||||
);
|
||||
self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) this explains what `delayed_good_path_bugs` are!
|
||||
@ -520,9 +523,8 @@ impl Drop for HandlerInner {
|
||||
if !self.has_any_message() && !self.suppressed_expected_diag {
|
||||
let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new());
|
||||
self.flush_delayed(
|
||||
bugs.into_iter().map(DelayedDiagnostic::decorate),
|
||||
bugs,
|
||||
"no warnings or errors encountered even though `delayed_good_path_bugs` issued",
|
||||
GoodPathBug,
|
||||
);
|
||||
}
|
||||
|
||||
@ -622,7 +624,14 @@ impl Handler {
|
||||
) -> SubdiagnosticMessage {
|
||||
let inner = self.inner.borrow();
|
||||
let args = crate::translation::to_fluent_args(args);
|
||||
SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string())
|
||||
SubdiagnosticMessage::Eager(
|
||||
inner
|
||||
.emitter
|
||||
.translate_message(&message, &args)
|
||||
.map_err(Report::new)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
// This is here to not allow mutation of flags;
|
||||
@ -1223,11 +1232,7 @@ impl Handler {
|
||||
pub fn flush_delayed(&self) {
|
||||
let mut inner = self.inner.lock();
|
||||
let bugs = std::mem::replace(&mut inner.delayed_span_bugs, Vec::new());
|
||||
inner.flush_delayed(
|
||||
bugs,
|
||||
"no errors encountered even though `delay_span_bug` issued",
|
||||
ExplicitBug,
|
||||
);
|
||||
inner.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1287,7 +1292,9 @@ impl HandlerInner {
|
||||
// once *any* errors were emitted (and truncate `delayed_span_bugs`
|
||||
// when an error is first emitted, also), but maybe there's a case
|
||||
// in which that's not sound? otherwise this is really inefficient.
|
||||
self.delayed_span_bugs.push(diagnostic.clone());
|
||||
let backtrace = std::backtrace::Backtrace::force_capture();
|
||||
self.delayed_span_bugs
|
||||
.push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
|
||||
|
||||
if !self.flags.report_delayed_bugs {
|
||||
return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||
@ -1562,7 +1569,6 @@ impl HandlerInner {
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
|
||||
diagnostic.set_span(sp.into());
|
||||
diagnostic.note(&format!("delayed at {}", std::panic::Location::caller()));
|
||||
self.emit_diagnostic(&mut diagnostic).unwrap()
|
||||
}
|
||||
|
||||
@ -1605,12 +1611,13 @@ impl HandlerInner {
|
||||
|
||||
fn flush_delayed(
|
||||
&mut self,
|
||||
bugs: impl IntoIterator<Item = Diagnostic>,
|
||||
bugs: impl IntoIterator<Item = DelayedDiagnostic>,
|
||||
explanation: impl Into<DiagnosticMessage> + Copy,
|
||||
panic_with: impl Any + Send + 'static,
|
||||
) {
|
||||
let mut no_bugs = true;
|
||||
for mut bug in bugs {
|
||||
for bug in bugs {
|
||||
let mut bug = bug.decorate();
|
||||
|
||||
if no_bugs {
|
||||
// Put the overall explanation before the `DelayedBug`s, to
|
||||
// frame them better (e.g. separate warnings from them).
|
||||
@ -1633,9 +1640,9 @@ impl HandlerInner {
|
||||
self.emit_diagnostic(&mut bug);
|
||||
}
|
||||
|
||||
// Panic with `ExplicitBug` to avoid "unexpected panic" messages.
|
||||
// Panic with `DelayedBugPanic` to avoid "unexpected panic" messages.
|
||||
if !no_bugs {
|
||||
panic::panic_any(panic_with);
|
||||
panic::panic_any(DelayedBugPanic);
|
||||
}
|
||||
}
|
||||
|
||||
|
188
compiler/rustc_errors/src/tests.rs
Normal file
188
compiler/rustc_errors/src/tests.rs
Normal file
@ -0,0 +1,188 @@
|
||||
use crate::error::{TranslateError, TranslateErrorKind};
|
||||
use crate::fluent_bundle::*;
|
||||
use crate::translation::Translate;
|
||||
use crate::FluentBundle;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError};
|
||||
use rustc_error_messages::langid;
|
||||
use rustc_error_messages::DiagnosticMessage;
|
||||
|
||||
struct Dummy {
|
||||
bundle: FluentBundle,
|
||||
}
|
||||
|
||||
impl Translate for Dummy {
|
||||
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn fallback_fluent_bundle(&self) -> &FluentBundle {
|
||||
&self.bundle
|
||||
}
|
||||
}
|
||||
|
||||
fn make_dummy(ftl: &'static str) -> Dummy {
|
||||
let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string.");
|
||||
|
||||
let langid_en = langid!("en-US");
|
||||
|
||||
#[cfg(parallel_compiler)]
|
||||
let mut bundle = FluentBundle::new_concurrent(vec![langid_en]);
|
||||
|
||||
#[cfg(not(parallel_compiler))]
|
||||
let mut bundle = FluentBundle::new(vec![langid_en]);
|
||||
|
||||
bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle.");
|
||||
|
||||
Dummy { bundle }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wellformed_fluent() {
|
||||
let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
|
||||
.label = value moved into `{$name}` here
|
||||
.occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
|
||||
.value_borrowed_label = value borrowed here after move
|
||||
.suggestion = borrow this binding in the pattern to avoid moving the value");
|
||||
|
||||
let mut args = FluentArgs::new();
|
||||
args.set("name", "Foo");
|
||||
args.set("ty", "std::string::String");
|
||||
{
|
||||
let message = DiagnosticMessage::FluentIdentifier(
|
||||
"mir_build_borrow_of_moved_value".into(),
|
||||
Some("suggestion".into()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
dummy.translate_message(&message, &args).unwrap(),
|
||||
"borrow this binding in the pattern to avoid moving the value"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let message = DiagnosticMessage::FluentIdentifier(
|
||||
"mir_build_borrow_of_moved_value".into(),
|
||||
Some("value_borrowed_label".into()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
dummy.translate_message(&message, &args).unwrap(),
|
||||
"value borrowed here after move"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let message = DiagnosticMessage::FluentIdentifier(
|
||||
"mir_build_borrow_of_moved_value".into(),
|
||||
Some("occurs_because_label".into()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
dummy.translate_message(&message, &args).unwrap(),
|
||||
"move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait"
|
||||
);
|
||||
|
||||
{
|
||||
let message = DiagnosticMessage::FluentIdentifier(
|
||||
"mir_build_borrow_of_moved_value".into(),
|
||||
Some("label".into()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
dummy.translate_message(&message, &args).unwrap(),
|
||||
"value moved into `\u{2068}Foo\u{2069}` here"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn misformed_fluent() {
|
||||
let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
|
||||
.label = value moved into `{name}` here
|
||||
.occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait
|
||||
.suggestion = borrow this binding in the pattern to avoid moving the value");
|
||||
|
||||
let mut args = FluentArgs::new();
|
||||
args.set("name", "Foo");
|
||||
args.set("ty", "std::string::String");
|
||||
{
|
||||
let message = DiagnosticMessage::FluentIdentifier(
|
||||
"mir_build_borrow_of_moved_value".into(),
|
||||
Some("value_borrowed_label".into()),
|
||||
);
|
||||
|
||||
let err = dummy.translate_message(&message, &args).unwrap_err();
|
||||
assert!(
|
||||
matches!(
|
||||
&err,
|
||||
TranslateError::Two {
|
||||
primary: box TranslateError::One {
|
||||
kind: TranslateErrorKind::PrimaryBundleMissing,
|
||||
..
|
||||
},
|
||||
fallback: box TranslateError::One {
|
||||
kind: TranslateErrorKind::AttributeMissing { attr: "value_borrowed_label" },
|
||||
..
|
||||
}
|
||||
}
|
||||
),
|
||||
"{err:#?}"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{err}"),
|
||||
"failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe attribute `value_borrowed_label` was missing\nhelp: add `.value_borrowed_label = <message>`\n"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let message = DiagnosticMessage::FluentIdentifier(
|
||||
"mir_build_borrow_of_moved_value".into(),
|
||||
Some("label".into()),
|
||||
);
|
||||
|
||||
let err = dummy.translate_message(&message, &args).unwrap_err();
|
||||
if let TranslateError::Two {
|
||||
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
|
||||
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
|
||||
} = &err
|
||||
&& let [FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. }
|
||||
| ReferenceKind::Variable { id, .. },
|
||||
))] = &**errs
|
||||
&& id == "name"
|
||||
{} else {
|
||||
panic!("{err:#?}")
|
||||
};
|
||||
assert_eq!(
|
||||
format!("{err}"),
|
||||
"failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nargument `name` exists but was not referenced correctly\nhelp: try using `{$name}` instead\n"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let message = DiagnosticMessage::FluentIdentifier(
|
||||
"mir_build_borrow_of_moved_value".into(),
|
||||
Some("occurs_because_label".into()),
|
||||
);
|
||||
|
||||
let err = dummy.translate_message(&message, &args).unwrap_err();
|
||||
if let TranslateError::Two {
|
||||
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
|
||||
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
|
||||
} = &err
|
||||
&& let [FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. }
|
||||
| ReferenceKind::Variable { id, .. },
|
||||
))] = &**errs
|
||||
&& id == "oops"
|
||||
{} else {
|
||||
panic!("{err:#?}")
|
||||
};
|
||||
assert_eq!(
|
||||
format!("{err}"),
|
||||
"failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe fluent string has an argument `oops` that was not found.\nhelp: the arguments `name` and `ty` are available\n"
|
||||
);
|
||||
}
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
use crate::error::TranslateError;
|
||||
use crate::snippet::Style;
|
||||
use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_error_messages::{
|
||||
fluent_bundle::resolver::errors::{ReferenceKind, ResolverError},
|
||||
FluentArgs, FluentError,
|
||||
};
|
||||
use rustc_error_messages::FluentArgs;
|
||||
use std::borrow::Cow;
|
||||
use std::error::Report;
|
||||
|
||||
/// Convert diagnostic arguments (a rustc internal type that exists to implement
|
||||
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
|
||||
@ -46,7 +45,10 @@ pub trait Translate {
|
||||
args: &FluentArgs<'_>,
|
||||
) -> Cow<'_, str> {
|
||||
Cow::Owned(
|
||||
messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
|
||||
messages
|
||||
.iter()
|
||||
.map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap())
|
||||
.collect::<String>(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -55,83 +57,56 @@ pub trait Translate {
|
||||
&'a self,
|
||||
message: &'a DiagnosticMessage,
|
||||
args: &'a FluentArgs<'_>,
|
||||
) -> Cow<'_, str> {
|
||||
) -> Result<Cow<'_, str>, TranslateError<'_>> {
|
||||
trace!(?message, ?args);
|
||||
let (identifier, attr) = match message {
|
||||
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
|
||||
return Cow::Borrowed(msg);
|
||||
return Ok(Cow::Borrowed(msg));
|
||||
}
|
||||
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
|
||||
};
|
||||
let translate_with_bundle =
|
||||
|bundle: &'a FluentBundle| -> Result<Cow<'_, str>, TranslateError<'_>> {
|
||||
let message = bundle
|
||||
.get_message(identifier)
|
||||
.ok_or(TranslateError::message(identifier, args))?;
|
||||
let value = match attr {
|
||||
Some(attr) => message
|
||||
.get_attribute(attr)
|
||||
.ok_or(TranslateError::attribute(identifier, args, attr))?
|
||||
.value(),
|
||||
None => message.value().ok_or(TranslateError::value(identifier, args))?,
|
||||
};
|
||||
debug!(?message, ?value);
|
||||
|
||||
let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
|
||||
let message = bundle.get_message(identifier)?;
|
||||
let value = match attr {
|
||||
Some(attr) => message.get_attribute(attr)?.value(),
|
||||
None => message.value()?,
|
||||
};
|
||||
debug!(?message, ?value);
|
||||
|
||||
let mut errs = vec![];
|
||||
let translated = bundle.format_pattern(value, Some(args), &mut errs);
|
||||
debug!(?translated, ?errs);
|
||||
Some((translated, errs))
|
||||
};
|
||||
|
||||
self.fluent_bundle()
|
||||
.and_then(|bundle| translate_with_bundle(bundle))
|
||||
// If `translate_with_bundle` returns `None` with the primary bundle, this is likely
|
||||
// just that the primary bundle doesn't contain the message being translated, so
|
||||
// proceed to the fallback bundle.
|
||||
//
|
||||
// However, when errors are produced from translation, then that means the translation
|
||||
// is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided).
|
||||
//
|
||||
// In debug builds, assert so that compiler devs can spot the broken translation and
|
||||
// fix it..
|
||||
.inspect(|(_, errs)| {
|
||||
debug_assert!(
|
||||
errs.is_empty(),
|
||||
"identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
|
||||
identifier,
|
||||
attr,
|
||||
args,
|
||||
errs
|
||||
);
|
||||
})
|
||||
// ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
|
||||
// just hide it and try with the fallback bundle.
|
||||
.filter(|(_, errs)| errs.is_empty())
|
||||
.or_else(|| translate_with_bundle(self.fallback_fluent_bundle()))
|
||||
.map(|(translated, errs)| {
|
||||
// Always bail out for errors with the fallback bundle.
|
||||
|
||||
let mut help_messages = vec![];
|
||||
|
||||
if !errs.is_empty() {
|
||||
for error in &errs {
|
||||
match error {
|
||||
FluentError::ResolverError(ResolverError::Reference(
|
||||
ReferenceKind::Message { id, .. },
|
||||
)) if args.iter().any(|(arg_id, _)| arg_id == id) => {
|
||||
help_messages.push(format!("Argument `{id}` exists but was not referenced correctly. Try using `{{${id}}}` instead"));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
panic!(
|
||||
"Encountered errors while formatting message for `{identifier}`\n\
|
||||
help: {}\n\
|
||||
attr: `{attr:?}`\n\
|
||||
args: `{args:?}`\n\
|
||||
errors: `{errs:?}`",
|
||||
help_messages.join("\nhelp: ")
|
||||
);
|
||||
let mut errs = vec![];
|
||||
let translated = bundle.format_pattern(value, Some(args), &mut errs);
|
||||
debug!(?translated, ?errs);
|
||||
if errs.is_empty() {
|
||||
Ok(translated)
|
||||
} else {
|
||||
Err(TranslateError::fluent(identifier, args, errs))
|
||||
}
|
||||
};
|
||||
|
||||
translated
|
||||
})
|
||||
.expect("failed to find message in primary or fallback fluent bundles")
|
||||
try {
|
||||
match self.fluent_bundle().map(|b| translate_with_bundle(b)) {
|
||||
// The primary bundle was present and translation succeeded
|
||||
Some(Ok(t)) => t,
|
||||
|
||||
// Always yeet out for errors on debug
|
||||
Some(Err(primary)) if cfg!(debug_assertions) => do yeet primary,
|
||||
|
||||
// If `translate_with_bundle` returns `Err` with the primary bundle, this is likely
|
||||
// just that the primary bundle doesn't contain the message being translated or
|
||||
// something else went wrong) so proceed to the fallback bundle.
|
||||
Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle())
|
||||
.map_err(|fallback| primary.and(fallback))?,
|
||||
|
||||
// The primary bundle is missing, proceed to the fallback bundle
|
||||
None => translate_with_bundle(self.fallback_fluent_bundle())
|
||||
.map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ rustc_index::newtype_index! {
|
||||
/// that is, within a `hir::Item`, `hir::TraitItem`, or `hir::ImplItem`. There is no
|
||||
/// guarantee that the numerical value of a given `ItemLocalId` corresponds to
|
||||
/// the node's position within the owning item in any way, but there is a
|
||||
/// guarantee that the `LocalItemId`s within an owner occupy a dense range of
|
||||
/// guarantee that the `ItemLocalId`s within an owner occupy a dense range of
|
||||
/// integers starting at zero, so a mapping that maps all or most nodes within
|
||||
/// an "item-like" to something else can be implemented by a `Vec` instead of a
|
||||
/// tree or hash map.
|
||||
@ -161,7 +161,7 @@ impl ItemLocalId {
|
||||
pub const INVALID: ItemLocalId = ItemLocalId::MAX;
|
||||
}
|
||||
|
||||
// Safety: Ord is implement as just comparing the LocalItemId's numerical
|
||||
// Safety: Ord is implement as just comparing the ItemLocalId's numerical
|
||||
// values and these are not changed by (de-)serialization.
|
||||
unsafe impl StableOrd for ItemLocalId {}
|
||||
|
||||
|
@ -1429,7 +1429,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
let instance =
|
||||
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
|
||||
let unused = tcx.unused_generic_params(instance);
|
||||
if !unused.is_empty() {
|
||||
if !unused.all_used() {
|
||||
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use rustc_hir::def::{CtorKind, DefKind};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId};
|
||||
use rustc_hir::definitions::DefKey;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::bit_set::{BitSet, FiniteBitSet};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::metadata::ModChild;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
@ -22,7 +22,7 @@ use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, ReprOptions, Ty};
|
||||
use rustc_middle::ty::{self, ReprOptions, Ty, UnusedGenericParams};
|
||||
use rustc_middle::ty::{DeducedParamAttrs, GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt};
|
||||
use rustc_serialize::opaque::FileEncoder;
|
||||
use rustc_session::config::SymbolManglingVersion;
|
||||
@ -384,7 +384,7 @@ define_tables! {
|
||||
trait_item_def_id: Table<DefIndex, RawDefId>,
|
||||
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
|
||||
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
|
||||
unused_generic_params: Table<DefIndex, LazyValue<FiniteBitSet<u32>>>,
|
||||
unused_generic_params: Table<DefIndex, LazyValue<UnusedGenericParams>>,
|
||||
params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
|
||||
repr_options: Table<DefIndex, LazyValue<ReprOptions>>,
|
||||
// `def_keys` and `def_path_hashes` represent a lazy version of a
|
||||
|
@ -1839,7 +1839,7 @@ rustc_queries! {
|
||||
desc { "getting codegen unit `{sym}`" }
|
||||
}
|
||||
|
||||
query unused_generic_params(key: ty::InstanceDef<'tcx>) -> FiniteBitSet<u32> {
|
||||
query unused_generic_params(key: ty::InstanceDef<'tcx>) -> UnusedGenericParams {
|
||||
cache_on_disk_if { key.def_id().is_local() }
|
||||
desc {
|
||||
|tcx| "determining which generic parameters are unused by `{}`",
|
||||
|
@ -6,6 +6,7 @@ use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::{CrateNum, DefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::bit_set::FiniteBitSet;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
|
||||
use rustc_span::Symbol;
|
||||
@ -711,7 +712,7 @@ fn polymorphize<'tcx>(
|
||||
}
|
||||
|
||||
InternalSubsts::for_item(tcx, def_id, |param, _| {
|
||||
let is_unused = unused.contains(param.index).unwrap_or(false);
|
||||
let is_unused = unused.is_unused(param.index);
|
||||
debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused);
|
||||
match param.kind {
|
||||
// Upvar case: If parameter is a type parameter..
|
||||
@ -733,7 +734,7 @@ fn polymorphize<'tcx>(
|
||||
// Simple case: If parameter is a const or type parameter..
|
||||
ty::GenericParamDefKind::Const { .. } | ty::GenericParamDefKind::Type { .. } if
|
||||
// ..and is within range and unused..
|
||||
unused.contains(param.index).unwrap_or(false) =>
|
||||
unused.is_unused(param.index) =>
|
||||
// ..then use the identity for this parameter.
|
||||
tcx.mk_param_from_def(param),
|
||||
|
||||
@ -774,3 +775,36 @@ fn needs_fn_once_adapter_shim(
|
||||
(ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
// Set bits represent unused generic parameters.
|
||||
// An empty set indicates that all parameters are used.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Decodable, Encodable, HashStable)]
|
||||
pub struct UnusedGenericParams(FiniteBitSet<u32>);
|
||||
|
||||
impl UnusedGenericParams {
|
||||
pub fn new_all_unused(amount: u32) -> Self {
|
||||
let mut bitset = FiniteBitSet::new_empty();
|
||||
bitset.set_range(0..amount);
|
||||
Self(bitset)
|
||||
}
|
||||
|
||||
pub fn new_all_used() -> Self {
|
||||
Self(FiniteBitSet::new_empty())
|
||||
}
|
||||
|
||||
pub fn mark_used(&mut self, idx: u32) {
|
||||
self.0.clear(idx);
|
||||
}
|
||||
|
||||
pub fn is_unused(&self, idx: u32) -> bool {
|
||||
self.0.contains(idx).unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn is_used(&self, idx: u32) -> bool {
|
||||
!self.is_unused(idx)
|
||||
}
|
||||
|
||||
pub fn all_used(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ pub use self::context::{
|
||||
tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, OnDiskCache, TyCtxt,
|
||||
TyCtxtFeed,
|
||||
};
|
||||
pub use self::instance::{Instance, InstanceDef, ShortInstance};
|
||||
pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams};
|
||||
pub use self::list::List;
|
||||
pub use self::parameterized::ParameterizedOverTcx;
|
||||
pub use self::rvalue_scopes::RvalueScopes;
|
||||
|
@ -60,6 +60,7 @@ trivially_parameterized_over_tcx! {
|
||||
ty::ImplPolarity,
|
||||
ty::ReprOptions,
|
||||
ty::TraitDef,
|
||||
ty::UnusedGenericParams,
|
||||
ty::Visibility<DefIndex>,
|
||||
ty::adjustment::CoerceUnsizedInfo,
|
||||
ty::fast_reject::SimplifiedType,
|
||||
|
@ -34,7 +34,7 @@ use crate::ty::layout::TyAndLayout;
|
||||
use crate::ty::subst::{GenericArg, SubstsRef};
|
||||
use crate::ty::util::AlwaysRequiresDrop;
|
||||
use crate::ty::GeneratorDiagnosticData;
|
||||
use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
|
||||
use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt, UnusedGenericParams};
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_attr as attr;
|
||||
@ -50,7 +50,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId};
|
||||
use rustc_hir::hir_id::OwnerId;
|
||||
use rustc_hir::lang_items::{LangItem, LanguageItems};
|
||||
use rustc_hir::{Crate, ItemLocalId, TraitCandidate};
|
||||
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
|
||||
use rustc_session::cstore::{CrateDepKind, CrateSource};
|
||||
use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib};
|
||||
|
@ -32,13 +32,13 @@ pub struct TypeLengthLimit {
|
||||
pub type_length: usize,
|
||||
}
|
||||
|
||||
pub struct UnusedGenericParams {
|
||||
pub struct UnusedGenericParamsHint {
|
||||
pub span: Span,
|
||||
pub param_spans: Vec<Span>,
|
||||
pub param_names: Vec<String>,
|
||||
}
|
||||
|
||||
impl IntoDiagnostic<'_> for UnusedGenericParams {
|
||||
impl IntoDiagnostic<'_> for UnusedGenericParamsHint {
|
||||
#[track_caller]
|
||||
fn into_diagnostic(
|
||||
self,
|
||||
|
@ -6,7 +6,6 @@
|
||||
//! for their size, offset of a field, etc.).
|
||||
|
||||
use rustc_hir::{def::DefKind, def_id::DefId, ConstContext};
|
||||
use rustc_index::bit_set::FiniteBitSet;
|
||||
use rustc_middle::mir::{
|
||||
self,
|
||||
visit::{TyContext, Visitor},
|
||||
@ -17,12 +16,12 @@ use rustc_middle::ty::{
|
||||
query::Providers,
|
||||
subst::SubstsRef,
|
||||
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
|
||||
Const, Ty, TyCtxt,
|
||||
Const, Ty, TyCtxt, UnusedGenericParams,
|
||||
};
|
||||
use rustc_span::symbol::sym;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::errors::UnusedGenericParams;
|
||||
use crate::errors::UnusedGenericParamsHint;
|
||||
|
||||
/// Provide implementations of queries relating to polymorphization analysis.
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
@ -36,16 +35,16 @@ pub fn provide(providers: &mut Providers) {
|
||||
fn unused_generic_params<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
instance: ty::InstanceDef<'tcx>,
|
||||
) -> FiniteBitSet<u32> {
|
||||
) -> UnusedGenericParams {
|
||||
if !tcx.sess.opts.unstable_opts.polymorphize {
|
||||
// If polymorphization disabled, then all parameters are used.
|
||||
return FiniteBitSet::new_empty();
|
||||
return UnusedGenericParams::new_all_used();
|
||||
}
|
||||
|
||||
let def_id = instance.def_id();
|
||||
// Exit early if this instance should not be polymorphized.
|
||||
if !should_polymorphize(tcx, def_id, instance) {
|
||||
return FiniteBitSet::new_empty();
|
||||
return UnusedGenericParams::new_all_used();
|
||||
}
|
||||
|
||||
let generics = tcx.generics_of(def_id);
|
||||
@ -53,14 +52,13 @@ fn unused_generic_params<'tcx>(
|
||||
|
||||
// Exit early when there are no parameters to be unused.
|
||||
if generics.count() == 0 {
|
||||
return FiniteBitSet::new_empty();
|
||||
return UnusedGenericParams::new_all_used();
|
||||
}
|
||||
|
||||
// Create a bitset with N rightmost ones for each parameter.
|
||||
let generics_count: u32 =
|
||||
generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
|
||||
let mut unused_parameters = FiniteBitSet::<u32>::new_empty();
|
||||
unused_parameters.set_range(0..generics_count);
|
||||
let mut unused_parameters = UnusedGenericParams::new_all_unused(generics_count);
|
||||
debug!(?unused_parameters, "(start)");
|
||||
|
||||
mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
|
||||
@ -78,7 +76,7 @@ fn unused_generic_params<'tcx>(
|
||||
debug!(?unused_parameters, "(end)");
|
||||
|
||||
// Emit errors for debugging and testing if enabled.
|
||||
if !unused_parameters.is_empty() {
|
||||
if !unused_parameters.all_used() {
|
||||
emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters);
|
||||
}
|
||||
|
||||
@ -136,13 +134,13 @@ fn mark_used_by_default_parameters<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
generics: &'tcx ty::Generics,
|
||||
unused_parameters: &mut FiniteBitSet<u32>,
|
||||
unused_parameters: &mut UnusedGenericParams,
|
||||
) {
|
||||
match tcx.def_kind(def_id) {
|
||||
DefKind::Closure | DefKind::Generator => {
|
||||
for param in &generics.params {
|
||||
debug!(?param, "(closure/gen)");
|
||||
unused_parameters.clear(param.index);
|
||||
unused_parameters.mark_used(param.index);
|
||||
}
|
||||
}
|
||||
DefKind::Mod
|
||||
@ -178,7 +176,7 @@ fn mark_used_by_default_parameters<'tcx>(
|
||||
for param in &generics.params {
|
||||
debug!(?param, "(other)");
|
||||
if let ty::GenericParamDefKind::Lifetime = param.kind {
|
||||
unused_parameters.clear(param.index);
|
||||
unused_parameters.mark_used(param.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +194,7 @@ fn emit_unused_generic_params_error<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
generics: &'tcx ty::Generics,
|
||||
unused_parameters: &FiniteBitSet<u32>,
|
||||
unused_parameters: &UnusedGenericParams,
|
||||
) {
|
||||
let base_def_id = tcx.typeck_root_def_id(def_id);
|
||||
if !tcx.has_attr(base_def_id, sym::rustc_polymorphize_error) {
|
||||
@ -213,7 +211,7 @@ fn emit_unused_generic_params_error<'tcx>(
|
||||
let mut next_generics = Some(generics);
|
||||
while let Some(generics) = next_generics {
|
||||
for param in &generics.params {
|
||||
if unused_parameters.contains(param.index).unwrap_or(false) {
|
||||
if unused_parameters.is_unused(param.index) {
|
||||
debug!(?param);
|
||||
let def_span = tcx.def_span(param.def_id);
|
||||
param_spans.push(def_span);
|
||||
@ -224,14 +222,14 @@ fn emit_unused_generic_params_error<'tcx>(
|
||||
next_generics = generics.parent.map(|did| tcx.generics_of(did));
|
||||
}
|
||||
|
||||
tcx.sess.emit_err(UnusedGenericParams { span: fn_span, param_spans, param_names });
|
||||
tcx.sess.emit_err(UnusedGenericParamsHint { span: fn_span, param_spans, param_names });
|
||||
}
|
||||
|
||||
/// Visitor used to aggregate generic parameter uses.
|
||||
struct MarkUsedGenericParams<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
unused_parameters: &'a mut FiniteBitSet<u32>,
|
||||
unused_parameters: &'a mut UnusedGenericParams,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
|
||||
@ -244,7 +242,7 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
|
||||
debug!(?self.unused_parameters, ?unused);
|
||||
for (i, arg) in substs.iter().enumerate() {
|
||||
let i = i.try_into().unwrap();
|
||||
if !unused.contains(i).unwrap_or(false) {
|
||||
if unused.is_used(i) {
|
||||
arg.visit_with(self);
|
||||
}
|
||||
}
|
||||
@ -308,7 +306,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
|
||||
match c.kind() {
|
||||
ty::ConstKind::Param(param) => {
|
||||
debug!(?param);
|
||||
self.unused_parameters.clear(param.index);
|
||||
self.unused_parameters.mark_used(param.index);
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs })
|
||||
@ -342,55 +340,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
|
||||
}
|
||||
ty::Param(param) => {
|
||||
debug!(?param);
|
||||
self.unused_parameters.clear(param.index);
|
||||
self.unused_parameters.mark_used(param.index);
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
_ => ty.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor used to check if a generic parameter is used.
|
||||
struct HasUsedGenericParams<'a> {
|
||||
unused_parameters: &'a FiniteBitSet<u32>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> {
|
||||
type BreakTy = ();
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if !c.has_non_region_param() {
|
||||
return ControlFlow::CONTINUE;
|
||||
}
|
||||
|
||||
match c.kind() {
|
||||
ty::ConstKind::Param(param) => {
|
||||
if self.unused_parameters.contains(param.index).unwrap_or(false) {
|
||||
ControlFlow::CONTINUE
|
||||
} else {
|
||||
ControlFlow::BREAK
|
||||
}
|
||||
}
|
||||
_ => c.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if !ty.has_non_region_param() {
|
||||
return ControlFlow::CONTINUE;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
ty::Param(param) => {
|
||||
if self.unused_parameters.contains(param.index).unwrap_or(false) {
|
||||
ControlFlow::CONTINUE
|
||||
} else {
|
||||
ControlFlow::BREAK
|
||||
}
|
||||
}
|
||||
_ => ty.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1570,7 +1570,7 @@ options! {
|
||||
/// o/w tests have closure@path
|
||||
span_free_formats: bool = (false, parse_bool, [UNTRACKED],
|
||||
"exclude spans when debug-printing compiler state (default: no)"),
|
||||
split_dwarf_inlining: bool = (true, parse_bool, [TRACKED],
|
||||
split_dwarf_inlining: bool = (false, parse_bool, [TRACKED],
|
||||
"provide minimal debug info in the object/executable to facilitate online \
|
||||
symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),
|
||||
split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED],
|
||||
|
@ -131,8 +131,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
else {
|
||||
return
|
||||
};
|
||||
let where_clause_bounds = tcx
|
||||
.predicates_of(impl_def_id)
|
||||
.instantiate(tcx, impl_substs)
|
||||
.predicates
|
||||
.into_iter()
|
||||
.map(|pred| goal.with(tcx, pred));
|
||||
|
||||
let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
|
||||
let nested_goals = obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect();
|
||||
let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
|
||||
|
||||
let Some(assoc_def) = fetch_eligible_assoc_item_def(
|
||||
|
@ -71,7 +71,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
||||
impl_def_id: DefId,
|
||||
) {
|
||||
let impl_trait_ref = acx.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
|
||||
let tcx = acx.cx.tcx;
|
||||
|
||||
let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap();
|
||||
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
|
||||
if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
|
||||
.any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
|
||||
@ -81,7 +83,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
|
||||
acx.infcx.probe(|_| {
|
||||
let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
|
||||
let impl_trait_ref = impl_trait_ref.subst(acx.cx.tcx, impl_substs);
|
||||
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
||||
|
||||
let Ok(InferOk { obligations, .. }) = acx
|
||||
.infcx
|
||||
@ -92,8 +94,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
else {
|
||||
return
|
||||
};
|
||||
let where_clause_bounds = tcx
|
||||
.predicates_of(impl_def_id)
|
||||
.instantiate(tcx, impl_substs)
|
||||
.predicates
|
||||
.into_iter()
|
||||
.map(|pred| goal.with(tcx, pred));
|
||||
|
||||
let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
|
||||
let nested_goals =
|
||||
obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect();
|
||||
|
||||
let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
|
||||
acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
|
||||
|
@ -173,6 +173,32 @@ fn div() {
|
||||
assert_eq!(Duration::new(99, 999_999_000) / 100, Duration::new(0, 999_999_990));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_duration_f32() {
|
||||
assert_eq!(Duration::ZERO.div_duration_f32(Duration::MAX), 0.0);
|
||||
assert_eq!(Duration::MAX.div_duration_f32(Duration::ZERO), f32::INFINITY);
|
||||
assert_eq!((Duration::SECOND * 2).div_duration_f32(Duration::SECOND), 2.0);
|
||||
assert!(Duration::ZERO.div_duration_f32(Duration::ZERO).is_nan());
|
||||
// These tests demonstrate it doesn't panic with extreme values.
|
||||
// Accuracy of the computed value is not a huge concern, we know floats don't work well
|
||||
// at these extremes.
|
||||
assert!((Duration::MAX).div_duration_f32(Duration::NANOSECOND) > 10.0f32.powf(28.0));
|
||||
assert!((Duration::NANOSECOND).div_duration_f32(Duration::MAX) < 0.1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_duration_f64() {
|
||||
assert_eq!(Duration::ZERO.div_duration_f64(Duration::MAX), 0.0);
|
||||
assert_eq!(Duration::MAX.div_duration_f64(Duration::ZERO), f64::INFINITY);
|
||||
assert_eq!((Duration::SECOND * 2).div_duration_f64(Duration::SECOND), 2.0);
|
||||
assert!(Duration::ZERO.div_duration_f64(Duration::ZERO).is_nan());
|
||||
// These tests demonstrate it doesn't panic with extreme values.
|
||||
// Accuracy of the computed value is not a huge concern, we know floats don't work well
|
||||
// at these extremes.
|
||||
assert!((Duration::MAX).div_duration_f64(Duration::NANOSECOND) > 10.0f64.powf(28.0));
|
||||
assert!((Duration::NANOSECOND).div_duration_f64(Duration::MAX) < 0.1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div() {
|
||||
assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));
|
||||
|
@ -249,8 +249,9 @@ pub struct DirBuilder {
|
||||
pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
||||
fn inner(path: &Path) -> io::Result<Vec<u8>> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)?;
|
||||
let size = file.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
let mut bytes = Vec::with_capacity(size as usize);
|
||||
io::default_read_to_end(&mut file, &mut bytes)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
inner(path.as_ref())
|
||||
@ -288,8 +289,9 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
||||
pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
|
||||
fn inner(path: &Path) -> io::Result<String> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut string = String::new();
|
||||
file.read_to_string(&mut string)?;
|
||||
let size = file.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
let mut string = String::with_capacity(size as usize);
|
||||
io::default_read_to_string(&mut file, &mut string)?;
|
||||
Ok(string)
|
||||
}
|
||||
inner(path.as_ref())
|
||||
|
@ -156,7 +156,9 @@ impl Emitter for BufferEmitter {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
|
||||
let fluent_args = to_fluent_args(diag.args());
|
||||
let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
|
||||
let translated_main_message = self
|
||||
.translate_message(&diag.message[0].0, &fluent_args)
|
||||
.unwrap_or_else(|e| panic!("{e}"));
|
||||
|
||||
buffer.messages.push(format!("error from rustc: {}", translated_main_message));
|
||||
if diag.is_error() {
|
||||
|
@ -249,6 +249,11 @@ trigger_files = [
|
||||
[autolabel."S-waiting-on-review"]
|
||||
new_pr = true
|
||||
|
||||
[autolabel."WG-trait-system-refactor"]
|
||||
trigger_files = [
|
||||
"compiler/rustc_trait_selection/solve"
|
||||
]
|
||||
|
||||
[notify-zulip."I-prioritize"]
|
||||
zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
|
||||
topic = "#{number} {title}"
|
||||
@ -344,7 +349,7 @@ cc = ["@BoxyUwU"]
|
||||
|
||||
[mentions."compiler/rustc_trait_selection/src/solve/"]
|
||||
message = "Some changes occurred to the core trait solver"
|
||||
cc = ["@lcnr", "@compiler-errors"]
|
||||
cc = ["@rust-lang/initiative-trait-system-refactor"]
|
||||
|
||||
[mentions."compiler/rustc_trait_selection/src/traits/engine.rs"]
|
||||
message = """
|
||||
|
Loading…
x
Reference in New Issue
Block a user