mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-30 02:33:55 +00:00
Auto merge of #3336 - RalfJung:rustup, r=RalfJung
Rustup Also add regression test for https://github.com/rust-lang/rust/issues/121508.
This commit is contained in:
commit
14e162861b
@ -1640,9 +1640,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.6"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
|
@ -136,27 +136,6 @@ pub(crate) struct BenchSig {
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_test_arg_non_lifetime)]
|
||||
pub(crate) struct TestArgNonLifetime {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_should_panic)]
|
||||
pub(crate) struct ShouldPanic {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_test_args)]
|
||||
pub(crate) struct TestArgs {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_alloc_must_statics)]
|
||||
pub(crate) struct AllocMustStatics {
|
||||
|
@ -108,6 +108,25 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
|
||||
|
||||
/// Trait implemented by error types. This is rarely implemented manually. Instead, use
|
||||
/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
|
||||
///
|
||||
/// When implemented manually, it should be generic over the emission
|
||||
/// guarantee, i.e.:
|
||||
/// ```ignore (fragment)
|
||||
/// impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for Foo { ... }
|
||||
/// ```
|
||||
/// rather than being specific:
|
||||
/// ```ignore (fragment)
|
||||
/// impl<'a> IntoDiagnostic<'a> for Bar { ... } // the default type param is `ErrorGuaranteed`
|
||||
/// impl<'a> IntoDiagnostic<'a, ()> for Baz { ... }
|
||||
/// ```
|
||||
/// There are two reasons for this.
|
||||
/// - A diagnostic like `Foo` *could* be emitted at any level -- `level` is
|
||||
/// passed in to `into_diagnostic` from outside. Even if in practice it is
|
||||
/// always emitted at a single level, we let the diagnostic creation/emission
|
||||
/// site determine the level (by using `create_err`, `emit_warn`, etc.)
|
||||
/// rather than the `IntoDiagnostic` impl.
|
||||
/// - Derived impls are always generic, and it's good for the hand-written
|
||||
/// impls to be consistent with them.
|
||||
#[rustc_diagnostic_item = "IntoDiagnostic"]
|
||||
pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
|
||||
/// Write out as a diagnostic out of `DiagCtxt`.
|
||||
@ -1289,11 +1308,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
|
||||
drop(self);
|
||||
}
|
||||
|
||||
/// Stashes diagnostic for possible later improvement in a different,
|
||||
/// later stage of the compiler. The diagnostic can be accessed with
|
||||
/// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`].
|
||||
pub fn stash(mut self, span: Span, key: StashKey) {
|
||||
self.dcx.stash_diagnostic(span, key, self.take_diag());
|
||||
/// See `DiagCtxt::stash_diagnostic` for details.
|
||||
pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
|
||||
self.dcx.stash_diagnostic(span, key, self.take_diag())
|
||||
}
|
||||
|
||||
/// Delay emission of this diagnostic as a bug.
|
||||
|
@ -434,10 +434,6 @@ struct DiagCtxtInner {
|
||||
/// The delayed bugs and their error guarantees.
|
||||
delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>,
|
||||
|
||||
/// The number of stashed errors. Unlike the other counts, this can go up
|
||||
/// and down, so it doesn't guarantee anything.
|
||||
stashed_err_count: usize,
|
||||
|
||||
/// The error count shown to the user at the end.
|
||||
deduplicated_err_count: usize,
|
||||
/// The warning count shown to the user at the end.
|
||||
@ -475,7 +471,7 @@ struct DiagCtxtInner {
|
||||
/// add more information). All stashed diagnostics must be emitted with
|
||||
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
|
||||
/// otherwise an assertion failure will occur.
|
||||
stashed_diagnostics: FxIndexMap<(Span, StashKey), DiagInner>,
|
||||
stashed_diagnostics: FxIndexMap<(Span, StashKey), (DiagInner, Option<ErrorGuaranteed>)>,
|
||||
|
||||
future_breakage_diagnostics: Vec<DiagInner>,
|
||||
|
||||
@ -559,10 +555,18 @@ pub struct DiagCtxtFlags {
|
||||
|
||||
impl Drop for DiagCtxtInner {
|
||||
fn drop(&mut self) {
|
||||
// Any stashed diagnostics should have been handled by
|
||||
// `emit_stashed_diagnostics` by now.
|
||||
assert!(self.stashed_diagnostics.is_empty());
|
||||
// For tools using `interface::run_compiler` (e.g. rustc, rustdoc)
|
||||
// stashed diagnostics will have already been emitted. But for others
|
||||
// that don't use `interface::run_compiler` (e.g. rustfmt, some clippy
|
||||
// lints) this fallback is necessary.
|
||||
//
|
||||
// Important: it is sound to produce an `ErrorGuaranteed` when stashing
|
||||
// errors because they are guaranteed to be emitted here or earlier.
|
||||
self.emit_stashed_diagnostics();
|
||||
|
||||
// Important: it is sound to produce an `ErrorGuaranteed` when emitting
|
||||
// delayed bugs because they are guaranteed to be emitted here if
|
||||
// necessary.
|
||||
if self.err_guars.is_empty() {
|
||||
self.flush_delayed()
|
||||
}
|
||||
@ -608,7 +612,6 @@ impl DiagCtxt {
|
||||
err_guars: Vec::new(),
|
||||
lint_err_guars: Vec::new(),
|
||||
delayed_bugs: Vec::new(),
|
||||
stashed_err_count: 0,
|
||||
deduplicated_err_count: 0,
|
||||
deduplicated_warn_count: 0,
|
||||
emitter,
|
||||
@ -669,7 +672,6 @@ impl DiagCtxt {
|
||||
err_guars,
|
||||
lint_err_guars,
|
||||
delayed_bugs,
|
||||
stashed_err_count,
|
||||
deduplicated_err_count,
|
||||
deduplicated_warn_count,
|
||||
emitter: _,
|
||||
@ -692,7 +694,6 @@ impl DiagCtxt {
|
||||
*err_guars = Default::default();
|
||||
*lint_err_guars = Default::default();
|
||||
*delayed_bugs = Default::default();
|
||||
*stashed_err_count = 0;
|
||||
*deduplicated_err_count = 0;
|
||||
*deduplicated_warn_count = 0;
|
||||
*must_produce_diag = false;
|
||||
@ -708,39 +709,111 @@ impl DiagCtxt {
|
||||
*fulfilled_expectations = Default::default();
|
||||
}
|
||||
|
||||
/// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
|
||||
/// Retrieve a stashed diagnostic with `steal_diagnostic`.
|
||||
pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: DiagInner) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
let key = (span.with_parent(None), key);
|
||||
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
inner.stashed_err_count += 1;
|
||||
}
|
||||
}
|
||||
/// Stashes a diagnostic for possible later improvement in a different,
|
||||
/// later stage of the compiler. Possible actions depend on the diagnostic
|
||||
/// level:
|
||||
/// - Level::Error: immediately counted as an error that has occurred, because it
|
||||
/// is guaranteed to be emitted eventually. Can be later accessed with the
|
||||
/// provided `span` and `key` through
|
||||
/// [`DiagCtxt::try_steal_modify_and_emit_err`] or
|
||||
/// [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow
|
||||
/// cancellation or downgrading of the error. Returns
|
||||
/// `Some(ErrorGuaranteed)`.
|
||||
/// - Level::Warning and lower (i.e. !is_error()): can be accessed with the
|
||||
/// provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This
|
||||
/// allows cancelling and downgrading of the diagnostic. Returns `None`.
|
||||
/// - Others: not allowed, will trigger a panic.
|
||||
pub fn stash_diagnostic(
|
||||
&self,
|
||||
span: Span,
|
||||
key: StashKey,
|
||||
diag: DiagInner,
|
||||
) -> Option<ErrorGuaranteed> {
|
||||
let guar = if diag.level() == Level::Error {
|
||||
// This `unchecked_error_guaranteed` is valid. It is where the
|
||||
// `ErrorGuaranteed` for stashed errors originates. See
|
||||
// `DiagCtxtInner::drop`.
|
||||
#[allow(deprecated)]
|
||||
Some(ErrorGuaranteed::unchecked_error_guaranteed())
|
||||
} else if !diag.is_error() {
|
||||
None
|
||||
} else {
|
||||
self.span_bug(span, format!("invalid level in `stash_diagnostic`: {}", diag.level));
|
||||
};
|
||||
|
||||
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
|
||||
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
|
||||
// See the PR for a discussion.
|
||||
inner.stashed_diagnostics.insert(key, diag);
|
||||
let key = (span.with_parent(None), key);
|
||||
self.inner.borrow_mut().stashed_diagnostics.insert(key, (diag, guar));
|
||||
|
||||
guar
|
||||
}
|
||||
|
||||
/// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key.
|
||||
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<Diag<'_, ()>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
/// Steal a previously stashed non-error diagnostic with the given `Span`
|
||||
/// and [`StashKey`] as the key. Panics if the found diagnostic is an
|
||||
/// error.
|
||||
pub fn steal_non_err(&self, span: Span, key: StashKey) -> Option<Diag<'_, ()>> {
|
||||
let key = (span.with_parent(None), key);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let diag = inner.stashed_diagnostics.swap_remove(&key)?;
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
inner.stashed_err_count -= 1;
|
||||
}
|
||||
}
|
||||
let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?;
|
||||
assert!(!diag.is_error());
|
||||
assert!(guar.is_none());
|
||||
Some(Diag::new_diagnostic(self, diag))
|
||||
}
|
||||
|
||||
/// Steals a previously stashed error with the given `Span` and
|
||||
/// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if
|
||||
/// no matching diagnostic is found. Panics if the found diagnostic's level
|
||||
/// isn't `Level::Error`.
|
||||
pub fn try_steal_modify_and_emit_err<F>(
|
||||
&self,
|
||||
span: Span,
|
||||
key: StashKey,
|
||||
mut modify_err: F,
|
||||
) -> Option<ErrorGuaranteed>
|
||||
where
|
||||
F: FnMut(&mut Diag<'_>),
|
||||
{
|
||||
let key = (span.with_parent(None), key);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
|
||||
err.map(|(err, guar)| {
|
||||
// The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
|
||||
assert_eq!(err.level, Level::Error);
|
||||
assert!(guar.is_some());
|
||||
let mut err = Diag::<ErrorGuaranteed>::new_diagnostic(self, err);
|
||||
modify_err(&mut err);
|
||||
assert_eq!(err.level, Level::Error);
|
||||
err.emit()
|
||||
})
|
||||
}
|
||||
|
||||
/// Steals a previously stashed error with the given `Span` and
|
||||
/// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
|
||||
/// Panics if the found diagnostic's level isn't `Level::Error`.
|
||||
pub fn try_steal_replace_and_emit_err(
|
||||
&self,
|
||||
span: Span,
|
||||
key: StashKey,
|
||||
new_err: Diag<'_>,
|
||||
) -> ErrorGuaranteed {
|
||||
let key = (span.with_parent(None), key);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
|
||||
match old_err {
|
||||
Some((old_err, guar)) => {
|
||||
assert_eq!(old_err.level, Level::Error);
|
||||
assert!(guar.is_some());
|
||||
// Because `old_err` has already been counted, it can only be
|
||||
// safely cancelled because the `new_err` supplants it.
|
||||
Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel();
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
new_err.emit()
|
||||
}
|
||||
|
||||
pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
|
||||
self.inner.borrow().stashed_diagnostics.get(&(span.with_parent(None), key)).is_some()
|
||||
}
|
||||
@ -750,41 +823,40 @@ impl DiagCtxt {
|
||||
self.inner.borrow_mut().emit_stashed_diagnostics()
|
||||
}
|
||||
|
||||
/// This excludes lint errors, delayed bugs and stashed errors.
|
||||
/// This excludes lint errors, and delayed bugs.
|
||||
#[inline]
|
||||
pub fn err_count_excluding_lint_errs(&self) -> usize {
|
||||
self.inner.borrow().err_guars.len()
|
||||
let inner = self.inner.borrow();
|
||||
inner.err_guars.len()
|
||||
+ inner
|
||||
.stashed_diagnostics
|
||||
.values()
|
||||
.filter(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
|
||||
.count()
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors.
|
||||
/// This excludes delayed bugs.
|
||||
#[inline]
|
||||
pub fn err_count(&self) -> usize {
|
||||
let inner = self.inner.borrow();
|
||||
inner.err_guars.len() + inner.lint_err_guars.len()
|
||||
inner.err_guars.len()
|
||||
+ inner.lint_err_guars.len()
|
||||
+ inner.stashed_diagnostics.values().filter(|(_diag, guar)| guar.is_some()).count()
|
||||
}
|
||||
|
||||
/// This excludes normal errors, lint errors, and delayed bugs. Unless
|
||||
/// absolutely necessary, avoid using this. It's dubious because stashed
|
||||
/// errors can later be cancelled, so the presence of a stashed error at
|
||||
/// some point of time doesn't guarantee anything -- there are no
|
||||
/// `ErrorGuaranteed`s here.
|
||||
pub fn stashed_err_count(&self) -> usize {
|
||||
self.inner.borrow().stashed_err_count
|
||||
}
|
||||
|
||||
/// This excludes lint errors, delayed bugs, and stashed errors. Unless
|
||||
/// absolutely necessary, prefer `has_errors` to this method.
|
||||
/// This excludes lint errors and delayed bugs. Unless absolutely
|
||||
/// necessary, prefer `has_errors` to this method.
|
||||
pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_excluding_lint_errors()
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors.
|
||||
/// This excludes delayed bugs.
|
||||
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors()
|
||||
}
|
||||
|
||||
/// This excludes stashed errors. Unless absolutely necessary, prefer
|
||||
/// `has_errors` to this method.
|
||||
/// This excludes nothing. Unless absolutely necessary, prefer `has_errors`
|
||||
/// to this method.
|
||||
pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_or_delayed_bugs()
|
||||
}
|
||||
@ -869,10 +941,10 @@ impl DiagCtxt {
|
||||
}
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors. Used for early aborts
|
||||
/// after errors occurred -- e.g. because continuing in the face of errors is
|
||||
/// likely to lead to bad results, such as spurious/uninteresting
|
||||
/// additional errors -- when returning an error `Result` is difficult.
|
||||
/// This excludes delayed bugs. Used for early aborts after errors occurred
|
||||
/// -- e.g. because continuing in the face of errors is likely to lead to
|
||||
/// bad results, such as spurious/uninteresting additional errors -- when
|
||||
/// returning an error `Result` is difficult.
|
||||
pub fn abort_if_errors(&self) {
|
||||
if self.has_errors().is_some() {
|
||||
FatalError.raise();
|
||||
@ -956,7 +1028,7 @@ impl DiagCtxt {
|
||||
inner
|
||||
.stashed_diagnostics
|
||||
.values_mut()
|
||||
.for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable));
|
||||
.for_each(|(diag, _guar)| diag.update_unstable_expectation_id(unstable_to_stable));
|
||||
inner
|
||||
.future_breakage_diagnostics
|
||||
.iter_mut()
|
||||
@ -1263,12 +1335,8 @@ impl DiagCtxtInner {
|
||||
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||
let mut guar = None;
|
||||
let has_errors = !self.err_guars.is_empty();
|
||||
for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
self.stashed_err_count -= 1;
|
||||
}
|
||||
} else {
|
||||
for (_, (diag, _guar)) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
|
||||
if !diag.is_error() {
|
||||
// Unless they're forced, don't flush stashed warnings when
|
||||
// there are errors, to avoid causing warning overload. The
|
||||
// stash would've been stolen already if it were important.
|
||||
@ -1327,7 +1395,8 @@ impl DiagCtxtInner {
|
||||
} else {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
// This `unchecked_error_guaranteed` is valid. It is where the
|
||||
// `ErrorGuaranteed` for delayed bugs originates.
|
||||
// `ErrorGuaranteed` for delayed bugs originates. See
|
||||
// `DiagCtxtInner::drop`.
|
||||
#[allow(deprecated)]
|
||||
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
|
||||
self.delayed_bugs
|
||||
@ -1439,11 +1508,31 @@ impl DiagCtxtInner {
|
||||
}
|
||||
|
||||
fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.err_guars.get(0).copied()
|
||||
self.err_guars.get(0).copied().or_else(|| {
|
||||
if let Some((_diag, guar)) = self
|
||||
.stashed_diagnostics
|
||||
.values()
|
||||
.find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
|
||||
{
|
||||
*guar
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied())
|
||||
self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else(
|
||||
|| {
|
||||
if let Some((_diag, guar)) =
|
||||
self.stashed_diagnostics.values().find(|(_diag, guar)| guar.is_some())
|
||||
{
|
||||
*guar
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
@ -1491,14 +1580,26 @@ impl DiagCtxtInner {
|
||||
let bugs: Vec<_> =
|
||||
std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect();
|
||||
|
||||
// If backtraces are enabled, also print the query stack
|
||||
let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0");
|
||||
for (i, bug) in bugs.into_iter().enumerate() {
|
||||
if let Some(file) = self.ice_file.as_ref()
|
||||
&& let Ok(mut out) = std::fs::File::options().create(true).append(true).open(file)
|
||||
{
|
||||
let _ = write!(
|
||||
&mut out,
|
||||
let decorate = backtrace || self.ice_file.is_none();
|
||||
let mut out = self
|
||||
.ice_file
|
||||
.as_ref()
|
||||
.and_then(|file| std::fs::File::options().create(true).append(true).open(file).ok());
|
||||
|
||||
// Put the overall explanation before the `DelayedBug`s, to frame them
|
||||
// better (e.g. separate warnings from them). Also, use notes, which
|
||||
// don't count as errors, to avoid possibly triggering
|
||||
// `-Ztreat-err-as-bug`, which we don't want.
|
||||
let note1 = "no errors encountered even though delayed bugs were created";
|
||||
let note2 = "those delayed bugs will now be shown as internal compiler errors";
|
||||
self.emit_diagnostic(DiagInner::new(Note, note1));
|
||||
self.emit_diagnostic(DiagInner::new(Note, note2));
|
||||
|
||||
for bug in bugs {
|
||||
if let Some(out) = &mut out {
|
||||
_ = write!(
|
||||
out,
|
||||
"delayed bug: {}\n{}\n",
|
||||
bug.inner
|
||||
.messages
|
||||
@ -1509,21 +1610,9 @@ impl DiagCtxtInner {
|
||||
);
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
// Put the overall explanation before the `DelayedBug`s, to
|
||||
// frame them better (e.g. separate warnings from them). Also,
|
||||
// make it a note so it doesn't count as an error, because that
|
||||
// could trigger `-Ztreat-err-as-bug`, which we don't want.
|
||||
let note1 = "no errors encountered even though delayed bugs were created";
|
||||
let note2 = "those delayed bugs will now be shown as internal compiler errors";
|
||||
self.emit_diagnostic(DiagInner::new(Note, note1));
|
||||
self.emit_diagnostic(DiagInner::new(Note, note2));
|
||||
}
|
||||
let mut bug = if decorate { bug.decorate(self) } else { bug.inner };
|
||||
|
||||
let mut bug =
|
||||
if backtrace || self.ice_file.is_none() { bug.decorate(self) } else { bug.inner };
|
||||
|
||||
// "Undelay" the delayed bugs (into plain `Bug`s).
|
||||
// "Undelay" the delayed bugs into plain bugs.
|
||||
if bug.level != DelayedBug {
|
||||
// NOTE(eddyb) not panicking here because we're already producing
|
||||
// an ICE, and the more information the merrier.
|
||||
|
@ -555,16 +555,19 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
),
|
||||
gated!(
|
||||
rustc_allow_const_fn_unstable, Normal,
|
||||
template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
|
||||
template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, @only_local: true,
|
||||
"rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
|
||||
),
|
||||
gated!(
|
||||
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
|
||||
"allow_internal_unsafe side-steps the unsafe_code lint",
|
||||
@only_local: true, "allow_internal_unsafe side-steps the unsafe_code lint",
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_allowed_through_unstable_modules, Normal, template!(Word),
|
||||
WarnFollowing, @only_local: true,
|
||||
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
|
||||
through unstable paths"
|
||||
),
|
||||
rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
|
||||
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
|
||||
through unstable paths"),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes: Type system related:
|
||||
@ -572,7 +575,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
|
||||
gated!(fundamental, Normal, template!(Word), WarnFollowing, experimental!(fundamental)),
|
||||
gated!(
|
||||
may_dangle, Normal, template!(Word), WarnFollowing, dropck_eyepatch,
|
||||
may_dangle, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, dropck_eyepatch,
|
||||
"`may_dangle` has unstable semantics and may be removed in the future",
|
||||
),
|
||||
|
||||
@ -580,31 +584,51 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
// Internal attributes: Runtime related:
|
||||
// ==========================================================================
|
||||
|
||||
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
|
||||
gated!(
|
||||
default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
|
||||
experimental!(default_lib_allocator),
|
||||
rustc_attr!(
|
||||
rustc_allocator, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, IMPL_DETAIL
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_nounwind, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, IMPL_DETAIL
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_reallocator, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, IMPL_DETAIL
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_deallocator, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, IMPL_DETAIL
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, IMPL_DETAIL
|
||||
),
|
||||
gated!(
|
||||
needs_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
|
||||
experimental!(needs_allocator),
|
||||
default_lib_allocator, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, allocator_internals, experimental!(default_lib_allocator),
|
||||
),
|
||||
gated!(
|
||||
needs_allocator, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, allocator_internals, experimental!(needs_allocator),
|
||||
),
|
||||
gated!(
|
||||
panic_runtime, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, experimental!(panic_runtime)
|
||||
),
|
||||
gated!(panic_runtime, Normal, template!(Word), WarnFollowing, experimental!(panic_runtime)),
|
||||
gated!(
|
||||
needs_panic_runtime, Normal, template!(Word), WarnFollowing,
|
||||
experimental!(needs_panic_runtime)
|
||||
@only_local: true, experimental!(needs_panic_runtime)
|
||||
),
|
||||
gated!(
|
||||
compiler_builtins, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true,
|
||||
"the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
|
||||
which contains compiler-rt intrinsics and will never be stable",
|
||||
),
|
||||
gated!(
|
||||
profiler_runtime, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true,
|
||||
"the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
|
||||
which contains the profiler runtime and will never be stable",
|
||||
),
|
||||
@ -630,7 +654,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing,
|
||||
IMPL_DETAIL,
|
||||
),
|
||||
rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
|
||||
rustc_attr!(
|
||||
rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing,
|
||||
@only_local: true, INTERNAL_UNSTABLE
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_macro_transparency, Normal,
|
||||
template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rustc_ast::TraitObjectSyntax;
|
||||
use rustc_errors::{codes::*, Diag, EmissionGuarantee, StashKey};
|
||||
use rustc_errors::{codes::*, Diag, EmissionGuarantee, Level, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
|
||||
@ -237,7 +237,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
}
|
||||
// check if the impl trait that we are considering is a impl of a local trait
|
||||
self.maybe_lint_blanket_trait_impl(self_ty, &mut diag);
|
||||
diag.stash(self_ty.span, StashKey::TraitMissingMethod);
|
||||
match diag.level() {
|
||||
Level::Error => {
|
||||
diag.stash(self_ty.span, StashKey::TraitMissingMethod);
|
||||
}
|
||||
Level::DelayedBug => {
|
||||
diag.emit();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
let msg = "trait objects without an explicit `dyn` are deprecated";
|
||||
tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| {
|
||||
|
@ -1350,8 +1350,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD
|
||||
let ty = tcx.type_of(def_id).instantiate_identity();
|
||||
if ty.references_error() {
|
||||
// If there is already another error, do not emit an error for not using a type parameter.
|
||||
// Without the `stashed_err_count` part this can fail (#120856).
|
||||
assert!(tcx.dcx().has_errors().is_some() || tcx.dcx().stashed_err_count() > 0);
|
||||
assert!(tcx.dcx().has_errors().is_some());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -136,11 +136,15 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
||||
// 1. Project the RPITIT projections from the trait to the opaques on the impl,
|
||||
// which means that they don't need to be mapped manually.
|
||||
//
|
||||
// 2. Project any other projections that show up in the bound. That makes sure that
|
||||
// we don't consider `tests/ui/async-await/in-trait/async-associated-types.rs`
|
||||
// to be refining.
|
||||
let (trait_bounds, impl_bounds) =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, (trait_bounds, impl_bounds));
|
||||
// 2. Deeply normalize any other projections that show up in the bound. That makes sure
|
||||
// that we don't consider `tests/ui/async-await/in-trait/async-associated-types.rs`
|
||||
// or `tests/ui/impl-trait/in-trait/refine-normalize.rs` to be refining.
|
||||
let Ok((trait_bounds, impl_bounds)) =
|
||||
ocx.deeply_normalize(&ObligationCause::dummy(), param_env, (trait_bounds, impl_bounds))
|
||||
else {
|
||||
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)");
|
||||
return;
|
||||
};
|
||||
|
||||
// Since we've normalized things, we need to resolve regions, since we'll
|
||||
// possibly have introduced region vars during projection. We don't expect
|
||||
|
@ -596,10 +596,11 @@ fn infer_placeholder_type<'a>(
|
||||
// then the user may have written e.g. `const A = 42;`.
|
||||
// In this case, the parser has stashed a diagnostic for
|
||||
// us to improve in typeck so we do that now.
|
||||
match tcx.dcx().steal_diagnostic(span, StashKey::ItemNoType) {
|
||||
Some(mut err) => {
|
||||
let guar = tcx
|
||||
.dcx()
|
||||
.try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| {
|
||||
if !ty.references_error() {
|
||||
// Only suggest adding `:` if it was missing (and suggested by parsing diagnostic)
|
||||
// Only suggest adding `:` if it was missing (and suggested by parsing diagnostic).
|
||||
let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" };
|
||||
|
||||
// The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
|
||||
@ -622,12 +623,8 @@ fn infer_placeholder_type<'a>(
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
// diagnostic stashing loses the information of whether something is a hard error.
|
||||
Ty::new_error_with_message(tcx, span, "ItemNoType is a hard error")
|
||||
}
|
||||
None => {
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let mut diag = bad_placeholder(tcx, vec![span], kind);
|
||||
|
||||
if !ty.references_error() {
|
||||
@ -645,10 +642,9 @@ fn infer_placeholder_type<'a>(
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ty::new_error(tcx, diag.emit())
|
||||
}
|
||||
}
|
||||
diag.emit()
|
||||
});
|
||||
Ty::new_error(tcx, guar)
|
||||
}
|
||||
|
||||
fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
|
||||
|
@ -1004,13 +1004,6 @@ pub(crate) struct StaticSpecialize {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_missing_tilde_const)]
|
||||
pub(crate) struct MissingTildeConst {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub(crate) enum DropImplPolarity {
|
||||
#[diag(hir_analysis_drop_impl_negative)]
|
||||
|
@ -484,12 +484,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
|
||||
&& let [segment] = path.segments
|
||||
&& let Some(mut diag) =
|
||||
self.dcx().steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod)
|
||||
{
|
||||
// Try suggesting `foo(a)` -> `a.foo()` if possible.
|
||||
self.suggest_call_as_method(&mut diag, segment, arg_exprs, call_expr, expected);
|
||||
diag.emit();
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
segment.ident.span,
|
||||
StashKey::CallIntoMethod,
|
||||
|err| {
|
||||
// Try suggesting `foo(a)` -> `a.foo()` if possible.
|
||||
self.suggest_call_as_method(
|
||||
err, segment, arg_exprs, call_expr, expected,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
|
||||
@ -601,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// and suggesting the fix if the method probe is successful.
|
||||
fn suggest_call_as_method(
|
||||
&self,
|
||||
diag: &mut Diag<'_, ()>,
|
||||
diag: &mut Diag<'_>,
|
||||
segment: &'tcx hir::PathSegment<'tcx>,
|
||||
arg_exprs: &'tcx [hir::Expr<'tcx>],
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
|
@ -1460,18 +1460,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
|
||||
{
|
||||
let span = self.tcx.hir().span(hir_id);
|
||||
match self.dcx().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) {
|
||||
Some(mut err) => {
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
span,
|
||||
StashKey::UnderscoreForArrayLengths,
|
||||
|err| {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider specifying the array length",
|
||||
array_len,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1751,11 +1751,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let (_, diag) =
|
||||
self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No);
|
||||
|
||||
if let Some(mut diag) = diag {
|
||||
if let Some(diag) = diag {
|
||||
if idx == ast_fields.len() - 1 {
|
||||
if remaining_fields.is_empty() {
|
||||
self.suggest_fru_from_range(field, variant, args, &mut diag);
|
||||
diag.emit();
|
||||
self.suggest_fru_from_range_and_emit(field, variant, args, diag);
|
||||
} else {
|
||||
diag.stash(field.span, StashKey::MaybeFruTypo);
|
||||
}
|
||||
@ -1986,20 +1985,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}"));
|
||||
|
||||
if let Some(last) = ast_fields.last() {
|
||||
self.suggest_fru_from_range(last, variant, args, &mut err);
|
||||
self.suggest_fru_from_range_and_emit(last, variant, args, err);
|
||||
} else {
|
||||
err.emit();
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
|
||||
/// If the last field is a range literal, but it isn't supposed to be, then they probably
|
||||
/// meant to use functional update syntax.
|
||||
fn suggest_fru_from_range(
|
||||
fn suggest_fru_from_range_and_emit(
|
||||
&self,
|
||||
last_expr_field: &hir::ExprField<'tcx>,
|
||||
variant: &ty::VariantDef,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
mut err: Diag<'_>,
|
||||
) {
|
||||
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
|
||||
if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) =
|
||||
@ -2012,16 +2011,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.map(|adt| adt.did())
|
||||
!= range_def_id
|
||||
{
|
||||
// Suppress any range expr type mismatches
|
||||
if let Some(diag) =
|
||||
self.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo)
|
||||
{
|
||||
diag.delay_as_bug();
|
||||
}
|
||||
|
||||
// Use a (somewhat arbitrary) filtering heuristic to avoid printing
|
||||
// expressions that are either too long, or have control character
|
||||
//such as newlines in them.
|
||||
// such as newlines in them.
|
||||
let expr = self
|
||||
.tcx
|
||||
.sess
|
||||
@ -2043,6 +2035,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.dcx(),
|
||||
TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr },
|
||||
);
|
||||
|
||||
// Suppress any range expr type mismatches
|
||||
self.dcx().try_steal_replace_and_emit_err(
|
||||
last_expr_field.span,
|
||||
StashKey::MaybeFruTypo,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -848,11 +848,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
|
||||
.map(|r| {
|
||||
// lint bare trait if the method is found in the trait
|
||||
if span.edition().at_least_rust_2021()
|
||||
&& let Some(diag) =
|
||||
self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
|
||||
{
|
||||
diag.emit();
|
||||
if span.edition().at_least_rust_2021() {
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
qself.span,
|
||||
StashKey::TraitMissingMethod,
|
||||
|_err| {},
|
||||
);
|
||||
}
|
||||
r
|
||||
})
|
||||
@ -879,17 +880,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
// emit or cancel the diagnostic for bare traits
|
||||
if span.edition().at_least_rust_2021()
|
||||
&& let Some(diag) =
|
||||
self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
|
||||
{
|
||||
if trait_missing_method {
|
||||
// cancel the diag for bare traits when meeting `MyTrait::missing_method`
|
||||
diag.cancel();
|
||||
} else {
|
||||
diag.emit();
|
||||
}
|
||||
// Emit the diagnostic for bare traits. (We used to cancel for slightly better
|
||||
// error messages, but cancelling stashed diagnostics is no longer allowed because
|
||||
// it causes problems when tracking whether errors have actually occurred.)
|
||||
if span.edition().at_least_rust_2021() {
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
qself.span,
|
||||
StashKey::TraitMissingMethod,
|
||||
|_err| {},
|
||||
);
|
||||
}
|
||||
|
||||
if item_name.name != kw::Empty {
|
||||
|
@ -1941,53 +1941,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
errors_causecode: Vec<(Span, ObligationCauseCode<'tcx>)>,
|
||||
) {
|
||||
for (span, code) in errors_causecode {
|
||||
let Some(mut diag) = self.dcx().steal_diagnostic(span, StashKey::MaybeForgetReturn)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(fn_sig) = self.body_fn_sig()
|
||||
&& let ExprBindingObligation(_, _, hir_id, ..) = code
|
||||
&& !fn_sig.output().is_unit()
|
||||
{
|
||||
let mut block_num = 0;
|
||||
let mut found_semi = false;
|
||||
for (_, node) in self.tcx.hir().parent_iter(hir_id) {
|
||||
match node {
|
||||
hir::Node::Stmt(stmt) => {
|
||||
if let hir::StmtKind::Semi(expr) = stmt.kind {
|
||||
let expr_ty = self.typeck_results.borrow().expr_ty(expr);
|
||||
let return_ty = fn_sig.output();
|
||||
if !matches!(expr.kind, hir::ExprKind::Ret(..))
|
||||
&& self.can_coerce(expr_ty, return_ty)
|
||||
{
|
||||
found_semi = true;
|
||||
self.dcx().try_steal_modify_and_emit_err(span, StashKey::MaybeForgetReturn, |err| {
|
||||
if let Some(fn_sig) = self.body_fn_sig()
|
||||
&& let ExprBindingObligation(_, _, hir_id, ..) = code
|
||||
&& !fn_sig.output().is_unit()
|
||||
{
|
||||
let mut block_num = 0;
|
||||
let mut found_semi = false;
|
||||
for (_, node) in self.tcx.hir().parent_iter(hir_id) {
|
||||
match node {
|
||||
hir::Node::Stmt(stmt) => {
|
||||
if let hir::StmtKind::Semi(expr) = stmt.kind {
|
||||
let expr_ty = self.typeck_results.borrow().expr_ty(expr);
|
||||
let return_ty = fn_sig.output();
|
||||
if !matches!(expr.kind, hir::ExprKind::Ret(..))
|
||||
&& self.can_coerce(expr_ty, return_ty)
|
||||
{
|
||||
found_semi = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::Node::Block(_block) => {
|
||||
if found_semi {
|
||||
block_num += 1;
|
||||
hir::Node::Block(_block) => {
|
||||
if found_semi {
|
||||
block_num += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::Node::Item(item) => {
|
||||
if let hir::ItemKind::Fn(..) = item.kind {
|
||||
break;
|
||||
hir::Node::Item(item) => {
|
||||
if let hir::ItemKind::Fn(..) = item.kind {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if block_num > 1 && found_semi {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"you might have meant to return this to infer its type parameters",
|
||||
"return ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
if block_num > 1 && found_semi {
|
||||
diag.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"you might have meant to return this to infer its type parameters",
|
||||
"return ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2194,59 +2194,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let [seg1, seg2] = segs else {
|
||||
return;
|
||||
};
|
||||
let Some(mut diag) =
|
||||
self.dcx().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let map = self.infcx.tcx.hir();
|
||||
let body_id = self.tcx.hir().body_owned_by(self.body_id);
|
||||
let body = map.body(body_id);
|
||||
struct LetVisitor<'a> {
|
||||
result: Option<&'a hir::Expr<'a>>,
|
||||
ident_name: Symbol,
|
||||
}
|
||||
|
||||
// FIXME: This really should be taking scoping, etc into account.
|
||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
|
||||
&& let Binding(_, _, ident, ..) = pat.kind
|
||||
&& ident.name == self.ident_name
|
||||
{
|
||||
self.result = *init;
|
||||
} else {
|
||||
hir::intravisit::walk_stmt(self, ex);
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
seg1.ident.span,
|
||||
StashKey::CallAssocMethod,
|
||||
|err| {
|
||||
let map = self.infcx.tcx.hir();
|
||||
let body_id = self.tcx.hir().body_owned_by(self.body_id);
|
||||
let body = map.body(body_id);
|
||||
struct LetVisitor<'a> {
|
||||
result: Option<&'a hir::Expr<'a>>,
|
||||
ident_name: Symbol,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
|
||||
visitor.visit_body(body);
|
||||
// FIXME: This really should be taking scoping, etc into account.
|
||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
|
||||
&& let Binding(_, _, ident, ..) = pat.kind
|
||||
&& ident.name == self.ident_name
|
||||
{
|
||||
self.result = *init;
|
||||
} else {
|
||||
hir::intravisit::walk_stmt(self, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id)
|
||||
&& let Some(expr) = visitor.result
|
||||
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
|
||||
{
|
||||
let probe = self.lookup_probe_for_diagnostic(
|
||||
seg2.ident,
|
||||
self_ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
);
|
||||
if probe.is_ok() {
|
||||
let sm = self.infcx.tcx.sess.source_map();
|
||||
diag.span_suggestion_verbose(
|
||||
sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
|
||||
"you may have meant to call an instance method",
|
||||
".",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.emit();
|
||||
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
|
||||
visitor.visit_body(body);
|
||||
|
||||
if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id)
|
||||
&& let Some(expr) = visitor.result
|
||||
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
|
||||
{
|
||||
let probe = self.lookup_probe_for_diagnostic(
|
||||
seg2.ident,
|
||||
self_ty,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
);
|
||||
if probe.is_ok() {
|
||||
let sm = self.infcx.tcx.sess.source_map();
|
||||
err.span_suggestion_verbose(
|
||||
sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':')
|
||||
.unwrap(),
|
||||
"you may have meant to call an instance method",
|
||||
".",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
|
||||
|
@ -772,10 +772,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
|
||||
fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed {
|
||||
if let Some(guar) = self.fcx.dcx().has_errors() {
|
||||
guar
|
||||
} else if self.fcx.dcx().stashed_err_count() > 0 {
|
||||
// Without this case we sometimes get uninteresting and extraneous
|
||||
// "type annotations needed" errors.
|
||||
self.fcx.dcx().delayed_bug("error in Resolver")
|
||||
} else {
|
||||
self.fcx
|
||||
.err_ctxt()
|
||||
|
@ -87,7 +87,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
reported_signature_mismatch: self.reported_signature_mismatch.clone(),
|
||||
tainted_by_errors: self.tainted_by_errors.clone(),
|
||||
err_count_on_creation: self.err_count_on_creation,
|
||||
stashed_err_count_on_creation: self.stashed_err_count_on_creation,
|
||||
universe: self.universe.clone(),
|
||||
intercrate,
|
||||
next_trait_solver: self.next_trait_solver,
|
||||
|
@ -306,12 +306,6 @@ pub struct InferCtxt<'tcx> {
|
||||
// FIXME(matthewjasper) Merge into `tainted_by_errors`
|
||||
err_count_on_creation: usize,
|
||||
|
||||
/// Track how many errors were stashed when this infcx is created.
|
||||
/// Used for the same purpose as `err_count_on_creation`, even
|
||||
/// though it's weaker because the count can go up and down.
|
||||
// FIXME(matthewjasper) Merge into `tainted_by_errors`
|
||||
stashed_err_count_on_creation: usize,
|
||||
|
||||
/// What is the innermost universe we have created? Starts out as
|
||||
/// `UniverseIndex::root()` but grows from there as we enter
|
||||
/// universal quantifiers.
|
||||
@ -717,7 +711,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||
reported_signature_mismatch: Default::default(),
|
||||
tainted_by_errors: Cell::new(None),
|
||||
err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(),
|
||||
stashed_err_count_on_creation: tcx.dcx().stashed_err_count(),
|
||||
universe: Cell::new(ty::UniverseIndex::ROOT),
|
||||
intercrate,
|
||||
next_trait_solver,
|
||||
@ -1274,14 +1267,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
self.set_tainted_by_errors(guar);
|
||||
Some(guar)
|
||||
} else if self.dcx().stashed_err_count() > self.stashed_err_count_on_creation {
|
||||
// Errors stashed since this infcx was made. Not entirely reliable
|
||||
// because the count of stashed errors can go down. But without
|
||||
// this case we get a moderate number of uninteresting and
|
||||
// extraneous "type annotations needed" errors.
|
||||
let guar = self.dcx().delayed_bug("tainted_by_errors: stashed bug awaiting emission");
|
||||
self.set_tainted_by_errors(guar);
|
||||
Some(guar)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -62,6 +62,7 @@
|
||||
use crate::infer::outlives::components::{push_outlives_components, Component};
|
||||
use crate::infer::outlives::env::RegionBoundPairs;
|
||||
use crate::infer::outlives::verify::VerifyBoundCx;
|
||||
use crate::infer::resolve::OpportunisticRegionResolver;
|
||||
use crate::infer::{
|
||||
self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound,
|
||||
};
|
||||
@ -69,7 +70,9 @@ use crate::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArgsRef, Region, Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt,
|
||||
};
|
||||
use rustc_middle::ty::{GenericArgKind, PolyTypeOutlivesPredicate};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use smallvec::smallvec;
|
||||
@ -176,6 +179,11 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
.map_err(|NoSolution| (outlives, origin.clone()))?
|
||||
.no_bound_vars()
|
||||
.expect("started with no bound vars, should end with no bound vars");
|
||||
// `TypeOutlives` is structural, so we should try to opportunistically resolve all
|
||||
// region vids before processing regions, so we have a better chance to match clauses
|
||||
// in our param-env.
|
||||
let (sup_type, sub_region) =
|
||||
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));
|
||||
|
||||
debug!(?sup_type, ?sub_region, ?origin);
|
||||
|
||||
|
@ -70,15 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
|
||||
|
||||
// Check if the method call actually calls the libcore
|
||||
// `IntoIterator::into_iter`.
|
||||
let trait_id = cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|did| cx.tcx.trait_of_item(did));
|
||||
if trait_id.is_none()
|
||||
|| !cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id.unwrap())
|
||||
{
|
||||
return;
|
||||
}
|
||||
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
|
||||
match cx.tcx.trait_of_item(def_id) {
|
||||
Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// As this is a method call expression, we have at least one argument.
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
|
||||
|
@ -815,7 +815,9 @@ pub enum PatKind<'tcx> {
|
||||
/// The boundaries must be of the same type and that type must be numeric.
|
||||
#[derive(Clone, Debug, PartialEq, HashStable, TypeVisitable)]
|
||||
pub struct PatRange<'tcx> {
|
||||
/// Must not be `PosInfinity`.
|
||||
pub lo: PatRangeBoundary<'tcx>,
|
||||
/// Must not be `NegInfinity`.
|
||||
pub hi: PatRangeBoundary<'tcx>,
|
||||
#[type_visitable(ignore)]
|
||||
pub end: RangeEnd,
|
||||
@ -958,22 +960,6 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
||||
Self::NegInfinity | Self::PosInfinity => None,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
|
||||
match self {
|
||||
Self::Finite(value) => value,
|
||||
Self::NegInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let c = ty.numeric_min_val(tcx).unwrap();
|
||||
mir::Const::from_ty_const(c, tcx)
|
||||
}
|
||||
Self::PosInfinity => {
|
||||
// Unwrap is ok because the type is known to be numeric.
|
||||
let c = ty.numeric_max_val(tcx).unwrap();
|
||||
mir::Const::from_ty_const(c, tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
|
||||
match self {
|
||||
Self::Finite(value) => value.eval_bits(tcx, param_env),
|
||||
|
@ -847,13 +847,15 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
|
||||
opaque_def_id: LocalDefId,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Result<Diag<'tcx>, ErrorGuaranteed> {
|
||||
if let Some(diag) = tcx
|
||||
.sess
|
||||
.dcx()
|
||||
.steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch)
|
||||
{
|
||||
diag.cancel();
|
||||
}
|
||||
// We used to cancel here for slightly better error messages, but
|
||||
// cancelling stashed diagnostics is no longer allowed because it
|
||||
// causes problems when tracking whether errors have actually
|
||||
// occurred.
|
||||
tcx.sess.dcx().try_steal_modify_and_emit_err(
|
||||
tcx.def_span(opaque_def_id),
|
||||
StashKey::OpaqueHiddenTypeMismatch,
|
||||
|_err| {},
|
||||
);
|
||||
(self.ty, other.ty).error_reported()?;
|
||||
// Found different concrete types for the opaque type.
|
||||
let sub_diag = if self.span == other.span {
|
||||
|
@ -91,8 +91,8 @@ impl<'tcx> Region<'tcx> {
|
||||
|
||||
/// Constructs a `RegionKind::ReError` region.
|
||||
#[track_caller]
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Region<'tcx> {
|
||||
tcx.intern_region(ty::ReError(reported))
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Region<'tcx> {
|
||||
tcx.intern_region(ty::ReError(guar))
|
||||
}
|
||||
|
||||
/// Constructs a `RegionKind::ReError` region and registers a delayed bug to ensure it gets
|
||||
|
@ -1528,8 +1528,8 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
|
||||
/// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Ty<'tcx> {
|
||||
Ty::new(tcx, Error(reported))
|
||||
pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Ty<'tcx> {
|
||||
Ty::new(tcx, Error(guar))
|
||||
}
|
||||
|
||||
/// Constructs a `TyKind::Error` type and registers a `span_delayed_bug` to ensure it gets used.
|
||||
|
@ -291,33 +291,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
TestKind::Range(ref range) => {
|
||||
let lower_bound_success = self.cfg.start_new_block();
|
||||
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
// FIXME: skip useless comparison when the range is half-open.
|
||||
let lo = range.lo.to_const(range.ty, self.tcx);
|
||||
let hi = range.hi.to_const(range.ty, self.tcx);
|
||||
let lo = self.literal_operand(test.span, lo);
|
||||
let hi = self.literal_operand(test.span, hi);
|
||||
let val = Operand::Copy(place);
|
||||
|
||||
let [success, fail] = *target_blocks else {
|
||||
bug!("`TestKind::Range` should have two target blocks");
|
||||
};
|
||||
self.compare(
|
||||
block,
|
||||
lower_bound_success,
|
||||
fail,
|
||||
source_info,
|
||||
BinOp::Le,
|
||||
lo,
|
||||
val.clone(),
|
||||
);
|
||||
let op = match range.end {
|
||||
RangeEnd::Included => BinOp::Le,
|
||||
RangeEnd::Excluded => BinOp::Lt,
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
let val = Operand::Copy(place);
|
||||
|
||||
let intermediate_block = if !range.lo.is_finite() {
|
||||
block
|
||||
} else if !range.hi.is_finite() {
|
||||
success
|
||||
} else {
|
||||
self.cfg.start_new_block()
|
||||
};
|
||||
self.compare(lower_bound_success, success, fail, source_info, op, val, hi);
|
||||
|
||||
if let Some(lo) = range.lo.as_finite() {
|
||||
let lo = self.literal_operand(test.span, lo);
|
||||
self.compare(
|
||||
block,
|
||||
intermediate_block,
|
||||
fail,
|
||||
source_info,
|
||||
BinOp::Le,
|
||||
lo,
|
||||
val.clone(),
|
||||
);
|
||||
};
|
||||
|
||||
if let Some(hi) = range.hi.as_finite() {
|
||||
let hi = self.literal_operand(test.span, hi);
|
||||
let op = match range.end {
|
||||
RangeEnd::Included => BinOp::Le,
|
||||
RangeEnd::Excluded => BinOp::Lt,
|
||||
};
|
||||
self.compare(intermediate_block, success, fail, source_info, op, val, hi);
|
||||
}
|
||||
}
|
||||
|
||||
TestKind::Len { len, op } => {
|
||||
|
@ -37,7 +37,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||
}
|
||||
ty::InstanceDef::FnPtrShim(def_id, ty) => {
|
||||
let trait_ = tcx.trait_of_item(def_id).unwrap();
|
||||
let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) {
|
||||
// Supports `Fn` or `async Fn` traits.
|
||||
let adjustment = match tcx
|
||||
.fn_trait_kind_from_def_id(trait_)
|
||||
.or_else(|| tcx.async_fn_trait_kind_from_def_id(trait_))
|
||||
{
|
||||
Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
|
||||
Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef },
|
||||
Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef },
|
||||
|
@ -557,14 +557,6 @@ pub(crate) struct CatchAfterTry {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_gen_fn)]
|
||||
#[help]
|
||||
pub(crate) struct GenFn {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_comma_after_base_struct)]
|
||||
#[note]
|
||||
|
@ -501,9 +501,11 @@ impl<'sess, 'src> StringReader<'sess, 'src> {
|
||||
(kind, self.symbol_from_to(start, end))
|
||||
}
|
||||
rustc_lexer::LiteralKind::Float { base, empty_exponent } => {
|
||||
let mut kind = token::Float;
|
||||
if empty_exponent {
|
||||
let span = self.mk_sp(start, self.pos);
|
||||
self.dcx().emit_err(errors::EmptyExponentFloat { span });
|
||||
let guar = self.dcx().emit_err(errors::EmptyExponentFloat { span });
|
||||
kind = token::Err(guar);
|
||||
}
|
||||
let base = match base {
|
||||
Base::Hexadecimal => Some("hexadecimal"),
|
||||
@ -513,9 +515,11 @@ impl<'sess, 'src> StringReader<'sess, 'src> {
|
||||
};
|
||||
if let Some(base) = base {
|
||||
let span = self.mk_sp(start, end);
|
||||
self.dcx().emit_err(errors::FloatLiteralUnsupportedBase { span, base });
|
||||
let guar =
|
||||
self.dcx().emit_err(errors::FloatLiteralUnsupportedBase { span, base });
|
||||
kind = token::Err(guar)
|
||||
}
|
||||
(token::Float, self.symbol_from_to(start, end))
|
||||
(kind, self.symbol_from_to(start, end))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1762,24 +1762,25 @@ impl<'a> Parser<'a> {
|
||||
err: impl FnOnce(&Self) -> Diag<'a>,
|
||||
) -> L {
|
||||
assert!(could_be_unclosed_char_literal(ident));
|
||||
if let Some(diag) = self.dcx().steal_diagnostic(ident.span, StashKey::LifetimeIsChar) {
|
||||
diag.with_span_suggestion_verbose(
|
||||
ident.span.shrink_to_hi(),
|
||||
"add `'` to close the char literal",
|
||||
"'",
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
err(self)
|
||||
.with_span_suggestion_verbose(
|
||||
self.dcx()
|
||||
.try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| {
|
||||
err.span_suggestion_verbose(
|
||||
ident.span.shrink_to_hi(),
|
||||
"add `'` to close the char literal",
|
||||
"'",
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
err(self)
|
||||
.with_span_suggestion_verbose(
|
||||
ident.span.shrink_to_hi(),
|
||||
"add `'` to close the char literal",
|
||||
"'",
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit()
|
||||
});
|
||||
let name = ident.without_first_quote().name;
|
||||
mk_lit_char(name, ident.span)
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ passes_doc_attr_not_crate_level =
|
||||
`#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
|
||||
|
||||
passes_doc_cfg_hide_takes_list =
|
||||
`#[doc(cfg_hide(...)]` takes a list of attributes
|
||||
`#[doc(cfg_hide(...))]` takes a list of attributes
|
||||
|
||||
passes_doc_expect_str =
|
||||
doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
|
||||
|
@ -2520,14 +2520,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
||||
if attr.style == AttrStyle::Inner {
|
||||
for attr_to_check in ATTRS_TO_CHECK {
|
||||
if attr.has_name(*attr_to_check) {
|
||||
if let AttrKind::Normal(ref p) = attr.kind
|
||||
&& let Some(diag) = tcx.dcx().steal_diagnostic(
|
||||
p.item.path.span,
|
||||
StashKey::UndeterminedMacroResolution,
|
||||
)
|
||||
{
|
||||
diag.cancel();
|
||||
}
|
||||
let item = tcx
|
||||
.hir()
|
||||
.items()
|
||||
@ -2537,7 +2529,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
||||
span: item.ident.span,
|
||||
kind: item.kind.descr(),
|
||||
});
|
||||
tcx.dcx().emit_err(errors::InvalidAttrAtCrateLevel {
|
||||
let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel {
|
||||
span: attr.span,
|
||||
sugg_span: tcx
|
||||
.sess
|
||||
@ -2553,6 +2545,16 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
||||
name: *attr_to_check,
|
||||
item,
|
||||
});
|
||||
|
||||
if let AttrKind::Normal(ref p) = attr.kind {
|
||||
tcx.dcx().try_steal_replace_and_emit_err(
|
||||
p.item.path.span,
|
||||
StashKey::UndeterminedMacroResolution,
|
||||
err,
|
||||
);
|
||||
} else {
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -688,6 +688,9 @@ pub enum Constructor<Cx: TypeCx> {
|
||||
/// Fake extra constructor for constructors that are not seen in the matrix, as explained at the
|
||||
/// top of the file.
|
||||
Missing,
|
||||
/// Fake extra constructor that indicates and empty field that is private. When we encounter one
|
||||
/// we skip the column entirely so we don't observe its emptiness. Only used for specialization.
|
||||
PrivateUninhabited,
|
||||
}
|
||||
|
||||
impl<Cx: TypeCx> Clone for Constructor<Cx> {
|
||||
@ -709,6 +712,7 @@ impl<Cx: TypeCx> Clone for Constructor<Cx> {
|
||||
Constructor::NonExhaustive => Constructor::NonExhaustive,
|
||||
Constructor::Hidden => Constructor::Hidden,
|
||||
Constructor::Missing => Constructor::Missing,
|
||||
Constructor::PrivateUninhabited => Constructor::PrivateUninhabited,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -763,6 +767,8 @@ impl<Cx: TypeCx> Constructor<Cx> {
|
||||
}
|
||||
// Wildcards cover anything
|
||||
(_, Wildcard) => true,
|
||||
// `PrivateUninhabited` skips everything.
|
||||
(PrivateUninhabited, _) => true,
|
||||
// Only a wildcard pattern can match these special constructors.
|
||||
(Missing { .. } | NonExhaustive | Hidden, _) => false,
|
||||
|
||||
@ -940,7 +946,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
|
||||
}
|
||||
ConstructorSet::Variants { variants, non_exhaustive } => {
|
||||
let mut seen_set = index::IdxSet::new_empty(variants.len());
|
||||
for idx in seen.iter().map(|c| c.as_variant().unwrap()) {
|
||||
for idx in seen.iter().filter_map(|c| c.as_variant()) {
|
||||
seen_set.insert(idx);
|
||||
}
|
||||
let mut skipped_a_hidden_variant = false;
|
||||
@ -969,7 +975,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
|
||||
ConstructorSet::Bool => {
|
||||
let mut seen_false = false;
|
||||
let mut seen_true = false;
|
||||
for b in seen.iter().map(|ctor| ctor.as_bool().unwrap()) {
|
||||
for b in seen.iter().filter_map(|ctor| ctor.as_bool()) {
|
||||
if b {
|
||||
seen_true = true;
|
||||
} else {
|
||||
@ -989,7 +995,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
|
||||
}
|
||||
ConstructorSet::Integers { range_1, range_2 } => {
|
||||
let seen_ranges: Vec<_> =
|
||||
seen.iter().map(|ctor| *ctor.as_int_range().unwrap()).collect();
|
||||
seen.iter().filter_map(|ctor| ctor.as_int_range()).copied().collect();
|
||||
for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) {
|
||||
match seen {
|
||||
Presence::Unseen => missing.push(IntRange(splitted_range)),
|
||||
@ -1006,7 +1012,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
|
||||
}
|
||||
}
|
||||
ConstructorSet::Slice { array_len, subtype_is_empty } => {
|
||||
let seen_slices = seen.iter().map(|c| c.as_slice().unwrap());
|
||||
let seen_slices = seen.iter().filter_map(|c| c.as_slice());
|
||||
let base_slice = Slice::new(*array_len, VarLen(0, 0));
|
||||
for (seen, splitted_slice) in base_slice.split(seen_slices) {
|
||||
let ctor = Slice(splitted_slice);
|
||||
|
@ -82,6 +82,11 @@ use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
|
||||
pub trait Captures<'a> {}
|
||||
impl<'a, T: ?Sized> Captures<'a> for T {}
|
||||
|
||||
/// `bool` newtype that indicates whether this is a privately uninhabited field that we should skip
|
||||
/// during analysis.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PrivateUninhabitedField(pub bool);
|
||||
|
||||
/// Context that provides type information about constructors.
|
||||
///
|
||||
/// Most of the crate is parameterized on a type that implements this trait.
|
||||
@ -105,13 +110,12 @@ pub trait TypeCx: Sized + fmt::Debug {
|
||||
/// The number of fields for this constructor.
|
||||
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;
|
||||
|
||||
/// The types of the fields for this constructor. The result must have a length of
|
||||
/// `ctor_arity()`.
|
||||
/// The types of the fields for this constructor. The result must contain `ctor_arity()` fields.
|
||||
fn ctor_sub_tys<'a>(
|
||||
&'a self,
|
||||
ctor: &'a Constructor<Self>,
|
||||
ty: &'a Self::Ty,
|
||||
) -> impl Iterator<Item = Self::Ty> + ExactSizeIterator + Captures<'a>;
|
||||
) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator + Captures<'a>;
|
||||
|
||||
/// The set of all the constructors for `ty`.
|
||||
///
|
||||
|
@ -5,7 +5,7 @@ use std::fmt;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::constructor::{Constructor, Slice, SliceKind};
|
||||
use crate::TypeCx;
|
||||
use crate::{PrivateUninhabitedField, TypeCx};
|
||||
|
||||
use self::Constructor::*;
|
||||
|
||||
@ -23,11 +23,6 @@ impl PatId {
|
||||
/// Values and patterns can be represented as a constructor applied to some fields. This represents
|
||||
/// a pattern in this form. A `DeconstructedPat` will almost always come from user input; the only
|
||||
/// exception are some `Wildcard`s introduced during pattern lowering.
|
||||
///
|
||||
/// Note that the number of fields may not match the fields declared in the original struct/variant.
|
||||
/// This happens if a private or `non_exhaustive` field is uninhabited, because the code mustn't
|
||||
/// observe that it is uninhabited. In that case that field is not included in `fields`. Care must
|
||||
/// be taken when converting to/from `thir::Pat`.
|
||||
pub struct DeconstructedPat<Cx: TypeCx> {
|
||||
ctor: Constructor<Cx>,
|
||||
fields: Vec<DeconstructedPat<Cx>>,
|
||||
@ -84,6 +79,8 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||
match (&self.ctor, other_ctor) {
|
||||
// Return a wildcard for each field of `other_ctor`.
|
||||
(Wildcard, _) => wildcard_sub_tys(),
|
||||
// Skip this column.
|
||||
(_, PrivateUninhabited) => smallvec![],
|
||||
// The only non-trivial case: two slices of different arity. `other_slice` is
|
||||
// guaranteed to have a larger arity, so we fill the middle part with enough
|
||||
// wildcards to reach the length of the new, larger slice.
|
||||
@ -192,7 +189,9 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Wildcard | Missing { .. } | NonExhaustive | Hidden => write!(f, "_ : {:?}", pat.ty()),
|
||||
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => {
|
||||
write!(f, "_ : {:?}", pat.ty())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -300,7 +299,11 @@ impl<Cx: TypeCx> WitnessPat<Cx> {
|
||||
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
|
||||
/// `Some(_)`.
|
||||
pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor<Cx>, ty: Cx::Ty) -> Self {
|
||||
let fields = cx.ctor_sub_tys(&ctor, &ty).map(|ty| Self::wildcard(ty)).collect();
|
||||
let fields = cx
|
||||
.ctor_sub_tys(&ctor, &ty)
|
||||
.filter(|(_, PrivateUninhabitedField(skip))| !skip)
|
||||
.map(|(ty, _)| Self::wildcard(ty))
|
||||
.collect();
|
||||
Self::new(ctor, fields, ty)
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
|
||||
use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
|
||||
@ -18,7 +18,7 @@ use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
|
||||
use crate::constructor::{
|
||||
IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
|
||||
};
|
||||
use crate::{errors, Captures, TypeCx};
|
||||
use crate::{errors, Captures, PrivateUninhabitedField, TypeCx};
|
||||
|
||||
use crate::constructor::Constructor::*;
|
||||
|
||||
@ -158,34 +158,19 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
|
||||
// uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
|
||||
// This lists the fields we keep along with their types.
|
||||
pub(crate) fn list_variant_nonhidden_fields(
|
||||
pub(crate) fn variant_sub_tys(
|
||||
&self,
|
||||
ty: RevealedTy<'tcx>,
|
||||
variant: &'tcx VariantDef,
|
||||
) -> impl Iterator<Item = (FieldIdx, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_> {
|
||||
let cx = self;
|
||||
let ty::Adt(adt, args) = ty.kind() else { bug!() };
|
||||
// Whether we must not match the fields of this variant exhaustively.
|
||||
let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local();
|
||||
|
||||
variant.fields.iter().enumerate().filter_map(move |(i, field)| {
|
||||
let ty = field.ty(cx.tcx, args);
|
||||
) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_>
|
||||
{
|
||||
let ty::Adt(_, args) = ty.kind() else { bug!() };
|
||||
variant.fields.iter().map(move |field| {
|
||||
let ty = field.ty(self.tcx, args);
|
||||
// `field.ty()` doesn't normalize after instantiating.
|
||||
let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||
let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
|
||||
let is_uninhabited = (cx.tcx.features().exhaustive_patterns
|
||||
|| cx.tcx.features().min_exhaustive_patterns)
|
||||
&& cx.is_uninhabited(ty);
|
||||
|
||||
if is_uninhabited && (!is_visible || is_non_exhaustive) {
|
||||
None
|
||||
} else {
|
||||
let ty = cx.reveal_opaque_ty(ty);
|
||||
Some((FieldIdx::new(i), ty))
|
||||
}
|
||||
let ty = self.tcx.normalize_erasing_regions(self.param_env, ty);
|
||||
let ty = self.reveal_opaque_ty(ty);
|
||||
(field, ty)
|
||||
})
|
||||
}
|
||||
|
||||
@ -210,12 +195,17 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
&'a self,
|
||||
ctor: &'a Constructor<'p, 'tcx>,
|
||||
ty: RevealedTy<'tcx>,
|
||||
) -> impl Iterator<Item = RevealedTy<'tcx>> + ExactSizeIterator + Captures<'a> {
|
||||
) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)>
|
||||
+ ExactSizeIterator
|
||||
+ Captures<'a> {
|
||||
fn reveal_and_alloc<'a, 'tcx>(
|
||||
cx: &'a RustcMatchCheckCtxt<'_, 'tcx>,
|
||||
iter: impl Iterator<Item = Ty<'tcx>>,
|
||||
) -> &'a [RevealedTy<'tcx>] {
|
||||
cx.dropless_arena.alloc_from_iter(iter.map(|ty| cx.reveal_opaque_ty(ty)))
|
||||
) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] {
|
||||
cx.dropless_arena.alloc_from_iter(
|
||||
iter.map(|ty| cx.reveal_opaque_ty(ty))
|
||||
.map(|ty| (ty, PrivateUninhabitedField(false))),
|
||||
)
|
||||
}
|
||||
let cx = self;
|
||||
let slice = match ctor {
|
||||
@ -229,7 +219,21 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
} else {
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty);
|
||||
|
||||
// In the cases of either a `#[non_exhaustive]` field list or a non-public
|
||||
// field, we skip uninhabited fields in order not to reveal the
|
||||
// uninhabitedness of the whole variant.
|
||||
let is_non_exhaustive =
|
||||
variant.is_field_list_non_exhaustive() && !adt.did().is_local();
|
||||
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
|
||||
let is_visible =
|
||||
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
|
||||
let is_uninhabited = (cx.tcx.features().exhaustive_patterns
|
||||
|| cx.tcx.features().min_exhaustive_patterns)
|
||||
&& cx.is_uninhabited(*ty);
|
||||
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
|
||||
(ty, PrivateUninhabitedField(skip))
|
||||
});
|
||||
cx.dropless_arena.alloc_from_iter(tys)
|
||||
}
|
||||
}
|
||||
@ -246,16 +250,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
|
||||
},
|
||||
Bool(..)
|
||||
| IntRange(..)
|
||||
| F32Range(..)
|
||||
| F64Range(..)
|
||||
| Str(..)
|
||||
| Opaque(..)
|
||||
| NonExhaustive
|
||||
| Hidden
|
||||
| Missing { .. }
|
||||
| Wildcard => &[],
|
||||
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
|
||||
| NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[],
|
||||
Or => {
|
||||
bug!("called `Fields::wildcards` on an `Or` ctor")
|
||||
}
|
||||
@ -274,25 +270,16 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
// patterns. If we're here we can assume this is a box pattern.
|
||||
1
|
||||
} else {
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
self.list_variant_nonhidden_fields(ty, variant).count()
|
||||
let variant_idx = RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt);
|
||||
adt.variant(variant_idx).fields.len()
|
||||
}
|
||||
}
|
||||
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
|
||||
},
|
||||
Ref => 1,
|
||||
Slice(slice) => slice.arity(),
|
||||
Bool(..)
|
||||
| IntRange(..)
|
||||
| F32Range(..)
|
||||
| F64Range(..)
|
||||
| Str(..)
|
||||
| Opaque(..)
|
||||
| NonExhaustive
|
||||
| Hidden
|
||||
| Missing { .. }
|
||||
| Wildcard => 0,
|
||||
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
|
||||
| NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0,
|
||||
Or => bug!("The `Or` constructor doesn't have a fixed arity"),
|
||||
}
|
||||
}
|
||||
@ -520,20 +507,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
};
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
// For each field in the variant, we store the relevant index into `self.fields` if any.
|
||||
let mut field_id_to_id: Vec<Option<usize>> =
|
||||
(0..variant.fields.len()).map(|_| None).collect();
|
||||
let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map(
|
||||
|(i, (field, ty))| {
|
||||
field_id_to_id[field.index()] = Some(i);
|
||||
ty
|
||||
},
|
||||
);
|
||||
fields = tys.map(|ty| DeconstructedPat::wildcard(ty)).collect();
|
||||
fields = cx
|
||||
.variant_sub_tys(ty, variant)
|
||||
.map(|(_, ty)| DeconstructedPat::wildcard(ty))
|
||||
.collect();
|
||||
for pat in subpatterns {
|
||||
if let Some(i) = field_id_to_id[pat.field.index()] {
|
||||
fields[i] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
fields[pat.field.index()] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
}
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
|
||||
@ -775,11 +754,9 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
ty::Adt(adt_def, args) => {
|
||||
let variant_index =
|
||||
RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
|
||||
let variant = &adt_def.variant(variant_index);
|
||||
let subpatterns = cx
|
||||
.list_variant_nonhidden_fields(*pat.ty(), variant)
|
||||
.zip(subpatterns)
|
||||
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
|
||||
let subpatterns = subpatterns
|
||||
.enumerate()
|
||||
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
|
||||
.collect();
|
||||
|
||||
if adt_def.is_enum() {
|
||||
@ -830,7 +807,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
&Str(value) => PatKind::Constant { value },
|
||||
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
|
||||
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
|
||||
Missing { .. } => bug!(
|
||||
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
|
||||
`Missing` should have been processed in `apply_constructors`"
|
||||
@ -866,7 +843,8 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
&'a self,
|
||||
ctor: &'a crate::constructor::Constructor<Self>,
|
||||
ty: &'a Self::Ty,
|
||||
) -> impl Iterator<Item = Self::Ty> + ExactSizeIterator + Captures<'a> {
|
||||
) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator + Captures<'a>
|
||||
{
|
||||
self.ctor_sub_tys(ctor, *ty)
|
||||
}
|
||||
fn ctors_for_ty(
|
||||
|
@ -716,7 +716,7 @@ use std::fmt;
|
||||
|
||||
use crate::constructor::{Constructor, ConstructorSet, IntRange};
|
||||
use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat};
|
||||
use crate::{Captures, MatchArm, TypeCx};
|
||||
use crate::{Captures, MatchArm, PrivateUninhabitedField, TypeCx};
|
||||
|
||||
use self::ValidityConstraint::*;
|
||||
|
||||
@ -817,6 +817,9 @@ impl fmt::Display for ValidityConstraint {
|
||||
struct PlaceInfo<Cx: TypeCx> {
|
||||
/// The type of the place.
|
||||
ty: Cx::Ty,
|
||||
/// Whether the place is a private uninhabited field. If so we skip this field during analysis
|
||||
/// so that we don't observe its emptiness.
|
||||
private_uninhabited: bool,
|
||||
/// Whether the place is known to contain valid data.
|
||||
validity: ValidityConstraint,
|
||||
/// Whether the place is the scrutinee itself or a subplace of it.
|
||||
@ -833,8 +836,9 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
|
||||
) -> impl Iterator<Item = Self> + ExactSizeIterator + Captures<'a> {
|
||||
let ctor_sub_tys = cx.ctor_sub_tys(ctor, &self.ty);
|
||||
let ctor_sub_validity = self.validity.specialize(ctor);
|
||||
ctor_sub_tys.map(move |ty| PlaceInfo {
|
||||
ctor_sub_tys.map(move |(ty, PrivateUninhabitedField(private_uninhabited))| PlaceInfo {
|
||||
ty,
|
||||
private_uninhabited,
|
||||
validity: ctor_sub_validity,
|
||||
is_scrutinee: false,
|
||||
})
|
||||
@ -856,6 +860,11 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
|
||||
where
|
||||
Cx: 'a,
|
||||
{
|
||||
if self.private_uninhabited {
|
||||
// Skip the whole column
|
||||
return Ok((smallvec![Constructor::PrivateUninhabited], vec![]));
|
||||
}
|
||||
|
||||
let ctors_for_ty = cx.ctors_for_ty(&self.ty)?;
|
||||
|
||||
// We treat match scrutinees of type `!` or `EmptyEnum` differently.
|
||||
@ -914,7 +923,12 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
|
||||
|
||||
impl<Cx: TypeCx> Clone for PlaceInfo<Cx> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { ty: self.ty.clone(), validity: self.validity, is_scrutinee: self.is_scrutinee }
|
||||
Self {
|
||||
ty: self.ty.clone(),
|
||||
private_uninhabited: self.private_uninhabited,
|
||||
validity: self.validity,
|
||||
is_scrutinee: self.is_scrutinee,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1121,7 +1135,12 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
|
||||
scrut_ty: Cx::Ty,
|
||||
scrut_validity: ValidityConstraint,
|
||||
) -> Self {
|
||||
let place_info = PlaceInfo { ty: scrut_ty, validity: scrut_validity, is_scrutinee: true };
|
||||
let place_info = PlaceInfo {
|
||||
ty: scrut_ty,
|
||||
private_uninhabited: false,
|
||||
validity: scrut_validity,
|
||||
is_scrutinee: true,
|
||||
};
|
||||
let mut matrix = Matrix {
|
||||
rows: Vec::with_capacity(arms.len()),
|
||||
place_info: smallvec![place_info],
|
||||
|
@ -988,10 +988,7 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if let hir::ExprKind::Struct(qpath, fields, ref base) = expr.kind {
|
||||
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
|
||||
let Some(adt) = self.typeck_results().expr_ty(expr).ty_adt_def() else {
|
||||
self.tcx.dcx().span_delayed_bug(expr.span, "no adt_def for expression");
|
||||
return;
|
||||
};
|
||||
let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
|
||||
let variant = adt.variant_of_res(res);
|
||||
if let Some(base) = *base {
|
||||
// If the expression uses FRU we need to make sure all the unmentioned fields
|
||||
|
@ -149,8 +149,7 @@ where
|
||||
let guar = if let Some(root) = cycle_error.cycle.first()
|
||||
&& let Some(span) = root.query.span
|
||||
{
|
||||
error.stash(span, StashKey::Cycle);
|
||||
qcx.dep_context().sess().dcx().span_delayed_bug(span, "delayed cycle error")
|
||||
error.stash(span, StashKey::Cycle).unwrap()
|
||||
} else {
|
||||
error.emit()
|
||||
};
|
||||
|
@ -1585,9 +1585,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
{
|
||||
// When the suggested binding change would be from `x` to `_x`, suggest changing the
|
||||
// original binding definition instead. (#60164)
|
||||
(span, snippet, ", consider changing it")
|
||||
let post = format!(", consider renaming `{}` into `{snippet}`", suggestion.candidate);
|
||||
(span, snippet, post)
|
||||
} else {
|
||||
(span, suggestion.candidate.to_string(), "")
|
||||
(span, suggestion.candidate.to_string(), String::new())
|
||||
};
|
||||
let msg = match suggestion.target {
|
||||
SuggestionTarget::SimilarlyNamed => format!(
|
||||
|
@ -7,32 +7,6 @@ use rustc_span::{
|
||||
|
||||
use crate::{late::PatternSource, Res};
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_parent_module_reset_for_binding, code = E0637)]
|
||||
pub(crate) struct ParentModuleResetForBinding;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_ampersand_used_without_explicit_lifetime_name, code = E0637)]
|
||||
#[note]
|
||||
pub(crate) struct AmpersandUsedWithoutExplicitLifetimeName(#[primary_span] pub(crate) Span);
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_underscore_lifetime_name_cannot_be_used_here, code = E0637)]
|
||||
#[note]
|
||||
pub(crate) struct UnderscoreLifetimeNameCannotBeUsedHere(#[primary_span] pub(crate) Span);
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_crate_may_not_be_imported)]
|
||||
pub(crate) struct CrateMayNotBeImported(#[primary_span] pub(crate) Span);
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_crate_root_imports_must_be_named_explicitly)]
|
||||
pub(crate) struct CrateRootNamesMustBeNamedExplicitly(#[primary_span] pub(crate) Span);
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_crate_root_imports_must_be_named_explicitly)]
|
||||
pub(crate) struct ResolutionError(#[primary_span] pub(crate) Span);
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_generic_params_from_outer_item, code = E0401)]
|
||||
pub(crate) struct GenericParamsFromOuterItem {
|
||||
@ -467,19 +441,6 @@ pub(crate) struct UnreachableLabelSubLabelUnreachable {
|
||||
pub(crate) ident_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_trait_impl_mismatch)]
|
||||
pub(crate) struct TraitImplMismatch {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) name: Symbol,
|
||||
pub(crate) kind: String,
|
||||
#[label(resolve_label_trait_item)]
|
||||
pub(crate) trait_item_span: Span,
|
||||
pub(crate) trait_path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_invalid_asm_sym)]
|
||||
#[help]
|
||||
|
@ -106,8 +106,7 @@ pub fn feature_err_issue(
|
||||
|
||||
// Cancel an earlier warning for this same error, if it exists.
|
||||
if let Some(span) = span.primary_span() {
|
||||
if let Some(err) = sess.parse_sess.dcx.steal_diagnostic(span, StashKey::EarlySyntaxWarning)
|
||||
{
|
||||
if let Some(err) = sess.parse_sess.dcx.steal_non_err(span, StashKey::EarlySyntaxWarning) {
|
||||
err.cancel()
|
||||
}
|
||||
}
|
||||
|
@ -546,8 +546,20 @@ fn encode_ty<'tcx>(
|
||||
if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) {
|
||||
// Use user-defined CFI encoding for type
|
||||
if let Some(value_str) = cfi_encoding.value_str() {
|
||||
if !value_str.to_string().trim().is_empty() {
|
||||
s.push_str(value_str.to_string().trim());
|
||||
let value_str = value_str.to_string();
|
||||
let str = value_str.trim();
|
||||
if !str.is_empty() {
|
||||
s.push_str(str);
|
||||
// Don't compress user-defined builtin types (see
|
||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and
|
||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
|
||||
let builtin_types = [
|
||||
"v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y",
|
||||
"n", "o", "f", "d", "e", "g", "z",
|
||||
];
|
||||
if !builtin_types.contains(&str) {
|
||||
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
|
||||
}
|
||||
} else {
|
||||
#[allow(
|
||||
rustc::diagnostic_outside_of_impl,
|
||||
@ -563,7 +575,6 @@ fn encode_ty<'tcx>(
|
||||
} else {
|
||||
bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
|
||||
}
|
||||
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
|
||||
} else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
|
||||
// For cross-language LLVM CFI support, the encoding must be compatible at the FFI
|
||||
// boundary. For instance:
|
||||
|
@ -3,6 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_gnu::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
// Use high-entropy 64 bit address space for ASLR
|
||||
base.add_pre_link_args(
|
||||
@ -10,7 +11,7 @@ pub fn target() -> Target {
|
||||
&["-m", "i386pep", "--high-entropy-va"],
|
||||
);
|
||||
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]);
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
base.linker = Some("x86_64-w64-mingw32-gcc".into());
|
||||
|
||||
Target {
|
||||
|
@ -3,9 +3,10 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_gnullvm::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
base.linker = Some("x86_64-w64-mingw32-clang".into());
|
||||
|
||||
Target {
|
||||
|
@ -3,8 +3,9 @@ use crate::spec::{base, SanitizerSet, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_msvc::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
base.supported_sanitizers = SanitizerSet::ADDRESS;
|
||||
|
||||
Target {
|
||||
|
@ -3,6 +3,7 @@ use crate::spec::{base, Cc, LinkerFlavor, Lld, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_uwp_gnu::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
// Use high-entropy 64 bit address space for ASLR
|
||||
base.add_pre_link_args(
|
||||
@ -10,7 +11,7 @@ pub fn target() -> Target {
|
||||
&["-m", "i386pep", "--high-entropy-va"],
|
||||
);
|
||||
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]);
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
|
||||
Target {
|
||||
llvm_target: "x86_64-pc-windows-gnu".into(),
|
||||
|
@ -3,8 +3,9 @@ use crate::spec::{base, Target};
|
||||
pub fn target() -> Target {
|
||||
let mut base = base::windows_uwp_msvc::opts();
|
||||
base.cpu = "x86-64".into();
|
||||
base.features = "+cx16,+sse3,+sahf".into();
|
||||
base.plt_by_default = false;
|
||||
base.max_atomic_width = Some(64);
|
||||
base.max_atomic_width = Some(128);
|
||||
|
||||
Target {
|
||||
llvm_target: "x86_64-pc-windows-msvc".into(),
|
||||
|
@ -281,7 +281,58 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||
}
|
||||
|
||||
// Coroutine-closures don't implement `Fn` traits the normal way.
|
||||
ty::CoroutineClosure(..) => Err(NoSolution),
|
||||
// Instead, they always implement `FnOnce`, but only implement
|
||||
// `FnMut`/`Fn` if they capture no upvars, since those may borrow
|
||||
// from the closure.
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
let args = args.as_coroutine_closure();
|
||||
let kind_ty = args.kind_ty();
|
||||
let sig = args.coroutine_closure_sig().skip_binder();
|
||||
|
||||
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
|
||||
if !closure_kind.extends(goal_kind) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// If `Fn`/`FnMut`, we only implement this goal if we
|
||||
// have no captures.
|
||||
let no_borrows = match args.tupled_upvars_ty().kind() {
|
||||
ty::Tuple(tys) => tys.is_empty(),
|
||||
ty::Error(_) => false,
|
||||
_ => bug!("tuple_fields called on non-tuple"),
|
||||
};
|
||||
if closure_kind != ty::ClosureKind::FnOnce && !no_borrows {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
coroutine_closure_to_certain_coroutine(
|
||||
tcx,
|
||||
goal_kind,
|
||||
// No captures by ref, so this doesn't matter.
|
||||
tcx.lifetimes.re_static,
|
||||
def_id,
|
||||
args,
|
||||
sig,
|
||||
)
|
||||
} else {
|
||||
// Closure kind is not yet determined, so we return ambiguity unless
|
||||
// the expected kind is `FnOnce` as that is always implemented.
|
||||
if goal_kind != ty::ClosureKind::FnOnce {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
coroutine_closure_to_ambiguous_coroutine(
|
||||
tcx,
|
||||
goal_kind, // No captures by ref, so this doesn't matter.
|
||||
tcx.lifetimes.re_static,
|
||||
def_id,
|
||||
args,
|
||||
sig,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty))))
|
||||
}
|
||||
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
@ -313,6 +364,19 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Relevant types for an async callable, including its inputs, output,
|
||||
/// and the return type you get from awaiting the output.
|
||||
#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)]
|
||||
pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> {
|
||||
pub tupled_inputs_ty: Ty<'tcx>,
|
||||
/// Type returned by calling the closure
|
||||
/// i.e. `f()`.
|
||||
pub output_coroutine_ty: Ty<'tcx>,
|
||||
/// Type returned by `await`ing the output
|
||||
/// i.e. `f().await`.
|
||||
pub coroutine_return_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
// Returns a binder of the tupled inputs types, output type, and coroutine type
|
||||
// from a builtin coroutine-closure type. If we don't yet know the closure kind of
|
||||
// the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper`
|
||||
@ -323,8 +387,10 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
self_ty: Ty<'tcx>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
env_region: ty::Region<'tcx>,
|
||||
) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
|
||||
{
|
||||
) -> Result<
|
||||
(ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec<ty::Predicate<'tcx>>),
|
||||
NoSolution,
|
||||
> {
|
||||
match *self_ty.kind() {
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
let args = args.as_coroutine_closure();
|
||||
@ -335,24 +401,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
if !closure_kind.extends(goal_kind) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
sig.to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
args.parent_args(),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
goal_kind,
|
||||
env_region,
|
||||
args.tupled_upvars_ty(),
|
||||
args.coroutine_captures_by_ref_ty(),
|
||||
|
||||
coroutine_closure_to_certain_coroutine(
|
||||
tcx, goal_kind, env_region, def_id, args, sig,
|
||||
)
|
||||
} else {
|
||||
let async_fn_kind_trait_def_id =
|
||||
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
|
||||
let upvars_projection_def_id = tcx
|
||||
.associated_items(async_fn_kind_trait_def_id)
|
||||
.filter_by_name_unhygienic(sym::Upvars)
|
||||
.next()
|
||||
.unwrap()
|
||||
.def_id;
|
||||
// When we don't know the closure kind (and therefore also the closure's upvars,
|
||||
// which are computed at the same time), we must delay the computation of the
|
||||
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
|
||||
@ -363,38 +416,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
nested.push(
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
async_fn_kind_trait_def_id,
|
||||
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None),
|
||||
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
|
||||
)
|
||||
.to_predicate(tcx),
|
||||
);
|
||||
let tupled_upvars_ty = Ty::new_projection(
|
||||
tcx,
|
||||
upvars_projection_def_id,
|
||||
[
|
||||
ty::GenericArg::from(kind_ty),
|
||||
Ty::from_closure_kind(tcx, goal_kind).into(),
|
||||
env_region.into(),
|
||||
sig.tupled_inputs_ty.into(),
|
||||
args.tupled_upvars_ty().into(),
|
||||
args.coroutine_captures_by_ref_ty().into(),
|
||||
],
|
||||
);
|
||||
sig.to_coroutine(
|
||||
tcx,
|
||||
args.parent_args(),
|
||||
Ty::from_closure_kind(tcx, goal_kind),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
tupled_upvars_ty,
|
||||
|
||||
coroutine_closure_to_ambiguous_coroutine(
|
||||
tcx, goal_kind, env_region, def_id, args, sig,
|
||||
)
|
||||
};
|
||||
|
||||
Ok((
|
||||
args.coroutine_closure_sig().rebind((
|
||||
sig.tupled_inputs_ty,
|
||||
sig.return_ty,
|
||||
coroutine_ty,
|
||||
)),
|
||||
args.coroutine_closure_sig().rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: sig.tupled_inputs_ty,
|
||||
output_coroutine_ty: coroutine_ty,
|
||||
coroutine_return_ty: sig.return_ty,
|
||||
}),
|
||||
nested,
|
||||
))
|
||||
}
|
||||
@ -418,7 +456,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
.def_id;
|
||||
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
|
||||
Ok((
|
||||
bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
|
||||
bound_sig.rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()),
|
||||
output_coroutine_ty: sig.output(),
|
||||
coroutine_return_ty: future_output_ty,
|
||||
}),
|
||||
nested,
|
||||
))
|
||||
}
|
||||
@ -469,7 +511,14 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
.unwrap()
|
||||
.def_id;
|
||||
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
|
||||
Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
|
||||
Ok((
|
||||
bound_sig.rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: sig.inputs()[0],
|
||||
output_coroutine_ty: sig.output(),
|
||||
coroutine_return_ty: future_output_ty,
|
||||
}),
|
||||
nested,
|
||||
))
|
||||
}
|
||||
|
||||
ty::Bool
|
||||
@ -502,6 +551,68 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a coroutine-closure, project to its returned coroutine when we are *certain*
|
||||
/// that the closure's kind is compatible with the goal.
|
||||
fn coroutine_closure_to_certain_coroutine<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
goal_region: ty::Region<'tcx>,
|
||||
def_id: DefId,
|
||||
args: ty::CoroutineClosureArgs<'tcx>,
|
||||
sig: ty::CoroutineClosureSignature<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
sig.to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
args.parent_args(),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
goal_kind,
|
||||
goal_region,
|
||||
args.tupled_upvars_ty(),
|
||||
args.coroutine_captures_by_ref_ty(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Given a coroutine-closure, project to its returned coroutine when we are *not certain*
|
||||
/// that the closure's kind is compatible with the goal, and therefore also don't know
|
||||
/// yet what the closure's upvars are.
|
||||
///
|
||||
/// Note that we do not also push a `AsyncFnKindHelper` goal here.
|
||||
fn coroutine_closure_to_ambiguous_coroutine<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
goal_region: ty::Region<'tcx>,
|
||||
def_id: DefId,
|
||||
args: ty::CoroutineClosureArgs<'tcx>,
|
||||
sig: ty::CoroutineClosureSignature<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let async_fn_kind_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
|
||||
let upvars_projection_def_id = tcx
|
||||
.associated_items(async_fn_kind_trait_def_id)
|
||||
.filter_by_name_unhygienic(sym::Upvars)
|
||||
.next()
|
||||
.unwrap()
|
||||
.def_id;
|
||||
let tupled_upvars_ty = Ty::new_projection(
|
||||
tcx,
|
||||
upvars_projection_def_id,
|
||||
[
|
||||
ty::GenericArg::from(args.kind_ty()),
|
||||
Ty::from_closure_kind(tcx, goal_kind).into(),
|
||||
goal_region.into(),
|
||||
sig.tupled_inputs_ty.into(),
|
||||
args.tupled_upvars_ty().into(),
|
||||
args.coroutine_captures_by_ref_ty().into(),
|
||||
],
|
||||
);
|
||||
sig.to_coroutine(
|
||||
tcx,
|
||||
args.parent_args(),
|
||||
Ty::from_closure_kind(tcx, goal_kind),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
tupled_upvars_ty,
|
||||
)
|
||||
}
|
||||
|
||||
/// Assemble a list of predicates that would be present on a theoretical
|
||||
/// user impl for an object type. These predicates must be checked any time
|
||||
/// we assemble a built-in object candidate for an object type, since they
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::traits::{check_args_compatible, specialization_graph};
|
||||
|
||||
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
|
||||
use super::assembly::{self, structural_traits, Candidate};
|
||||
use super::{EvalCtxt, GoalSource};
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -392,46 +393,56 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
|
||||
goal_kind,
|
||||
env_region,
|
||||
)?;
|
||||
let output_is_sized_pred =
|
||||
tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
|
||||
});
|
||||
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|
||||
|AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_ty])
|
||||
},
|
||||
);
|
||||
|
||||
let pred = tupled_inputs_and_output_and_coroutine
|
||||
.map_bound(|(inputs, output, coroutine)| {
|
||||
let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) {
|
||||
sym::CallOnceFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), inputs],
|
||||
.map_bound(
|
||||
|AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty,
|
||||
output_coroutine_ty,
|
||||
coroutine_return_ty,
|
||||
}| {
|
||||
let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) {
|
||||
sym::CallOnceFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), tupled_inputs_ty],
|
||||
),
|
||||
output_coroutine_ty.into(),
|
||||
),
|
||||
coroutine.into(),
|
||||
),
|
||||
sym::CallMutFuture | sym::CallFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
ty::GenericArg::from(goal.predicate.self_ty()),
|
||||
inputs.into(),
|
||||
env_region.into(),
|
||||
],
|
||||
sym::CallMutFuture | sym::CallFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
ty::GenericArg::from(goal.predicate.self_ty()),
|
||||
tupled_inputs_ty.into(),
|
||||
env_region.into(),
|
||||
],
|
||||
),
|
||||
output_coroutine_ty.into(),
|
||||
),
|
||||
coroutine.into(),
|
||||
),
|
||||
sym::Output => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[ty::GenericArg::from(goal.predicate.self_ty()), inputs.into()],
|
||||
sym::Output => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
ty::GenericArg::from(goal.predicate.self_ty()),
|
||||
tupled_inputs_ty.into(),
|
||||
],
|
||||
),
|
||||
coroutine_return_ty.into(),
|
||||
),
|
||||
output.into(),
|
||||
),
|
||||
name => bug!("no such associated type: {name}"),
|
||||
};
|
||||
ty::ProjectionPredicate { projection_ty, term }
|
||||
})
|
||||
name => bug!("no such associated type: {name}"),
|
||||
};
|
||||
ty::ProjectionPredicate { projection_ty, term }
|
||||
},
|
||||
)
|
||||
.to_predicate(tcx);
|
||||
|
||||
// A built-in `AsyncFn` impl only holds if the output is sized.
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::traits::supertrait_def_ids;
|
||||
|
||||
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
|
||||
use super::assembly::{self, structural_traits, Candidate};
|
||||
use super::{EvalCtxt, GoalSource, SolverMode};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
@ -327,14 +328,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
// This region doesn't matter because we're throwing away the coroutine type
|
||||
tcx.lifetimes.re_static,
|
||||
)?;
|
||||
let output_is_sized_pred =
|
||||
tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
|
||||
});
|
||||
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|
||||
|AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_coroutine_ty])
|
||||
},
|
||||
);
|
||||
|
||||
let pred = tupled_inputs_and_output_and_coroutine
|
||||
.map_bound(|(inputs, _, _)| {
|
||||
ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
||||
.map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| {
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), tupled_inputs_ty],
|
||||
)
|
||||
})
|
||||
.to_predicate(tcx);
|
||||
// A built-in `AsyncFn` impl only holds if the output is sized.
|
||||
|
@ -107,6 +107,15 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
|
||||
self.register_infer_ok_obligations(infer_ok)
|
||||
}
|
||||
|
||||
pub fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
value: T,
|
||||
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
|
||||
self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut())
|
||||
}
|
||||
|
||||
/// Makes `expected <: actual`.
|
||||
pub fn eq_exp<T>(
|
||||
&self,
|
||||
|
@ -889,7 +889,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage(
|
||||
SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => return self.report_opaque_type_auto_trait_leakage(
|
||||
&obligation,
|
||||
def_id,
|
||||
),
|
||||
@ -2252,8 +2252,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
ErrorCode::E0282,
|
||||
false,
|
||||
);
|
||||
err.stash(span, StashKey::MaybeForgetReturn);
|
||||
return self.dcx().delayed_bug("stashed error never reported");
|
||||
return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
|
||||
}
|
||||
Some(e) => return e,
|
||||
}
|
||||
@ -2766,7 +2765,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
self.suggest_unsized_bound_if_applicable(err, obligation);
|
||||
if let Some(span) = err.span.primary_span()
|
||||
&& let Some(mut diag) =
|
||||
self.tcx.dcx().steal_diagnostic(span, StashKey::AssociatedTypeSuggestion)
|
||||
self.tcx.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion)
|
||||
&& let Ok(ref mut s1) = err.suggestions
|
||||
&& let Ok(ref mut s2) = diag.suggestions
|
||||
{
|
||||
@ -3291,7 +3290,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
def_id: DefId,
|
||||
) -> Diag<'tcx> {
|
||||
) -> ErrorGuaranteed {
|
||||
let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
|
||||
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
|
||||
"opaque type".to_string()
|
||||
@ -3318,12 +3317,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(diag) = self.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle)
|
||||
{
|
||||
diag.cancel();
|
||||
}
|
||||
|
||||
err
|
||||
self.note_obligation_cause(&mut err, &obligation);
|
||||
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||
self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err)
|
||||
}
|
||||
|
||||
fn report_signature_mismatch_error(
|
||||
|
@ -923,14 +923,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
[self_ty, Ty::new_tup(tcx, sig.inputs())],
|
||||
)
|
||||
});
|
||||
|
||||
// We must additionally check that the return type impls `Future`.
|
||||
|
||||
// FIXME(async_closures): Investigate this before stabilization.
|
||||
// We instantiate this binder eagerly because the `confirm_future_candidate`
|
||||
// method doesn't support higher-ranked futures, which the `AsyncFn`
|
||||
// traits expressly allow the user to write. To fix this correctly,
|
||||
// we'd need to instantiate trait bounds before we get to selection,
|
||||
// like the new trait solver does.
|
||||
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
|
||||
let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
|
||||
nested.push(obligation.with(
|
||||
tcx,
|
||||
sig.map_bound(|sig| {
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
|
||||
}),
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]),
|
||||
));
|
||||
|
||||
(trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
|
||||
}
|
||||
ty::Closure(_, args) => {
|
||||
@ -943,14 +951,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
[self_ty, sig.inputs()[0]],
|
||||
)
|
||||
});
|
||||
|
||||
// We must additionally check that the return type impls `Future`.
|
||||
// See FIXME in last branch for why we instantiate the binder eagerly.
|
||||
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
|
||||
let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
|
||||
nested.push(obligation.with(
|
||||
tcx,
|
||||
sig.map_bound(|sig| {
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
|
||||
}),
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]),
|
||||
));
|
||||
|
||||
(trait_ref, args.kind_ty())
|
||||
}
|
||||
_ => bug!("expected callable type for AsyncFn candidate"),
|
||||
|
@ -152,6 +152,7 @@
|
||||
#![feature(set_ptr_value)]
|
||||
#![feature(sized_type_properties)]
|
||||
#![feature(slice_from_ptr_range)]
|
||||
#![feature(slice_index_methods)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(slice_ptr_len)]
|
||||
#![feature(slice_range)]
|
||||
|
@ -54,7 +54,7 @@ use core::ops::Add;
|
||||
use core::ops::AddAssign;
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::ops::Bound::{Excluded, Included, Unbounded};
|
||||
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
|
||||
use core::ops::{self, Range, RangeBounds};
|
||||
use core::ptr;
|
||||
use core::slice;
|
||||
use core::str::pattern::Pattern;
|
||||
@ -2433,100 +2433,26 @@ impl AddAssign<&str> for String {
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ops::Index<ops::Range<usize>> for String {
|
||||
type Output = str;
|
||||
impl<I> ops::Index<I> for String
|
||||
where
|
||||
I: slice::SliceIndex<str>,
|
||||
{
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: ops::Range<usize>) -> &str {
|
||||
&self[..][index]
|
||||
fn index(&self, index: I) -> &I::Output {
|
||||
index.index(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ops::Index<ops::RangeTo<usize>> for String {
|
||||
type Output = str;
|
||||
|
||||
impl<I> ops::IndexMut<I> for String
|
||||
where
|
||||
I: slice::SliceIndex<str>,
|
||||
{
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeTo<usize>) -> &str {
|
||||
&self[..][index]
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ops::Index<ops::RangeFrom<usize>> for String {
|
||||
type Output = str;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeFrom<usize>) -> &str {
|
||||
&self[..][index]
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ops::Index<ops::RangeFull> for String {
|
||||
type Output = str;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, _index: ops::RangeFull) -> &str {
|
||||
unsafe { str::from_utf8_unchecked(&self.vec) }
|
||||
}
|
||||
}
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::Index<ops::RangeInclusive<usize>> for String {
|
||||
type Output = str;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeInclusive<usize>) -> &str {
|
||||
Index::index(&**self, index)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::Index<ops::RangeToInclusive<usize>> for String {
|
||||
type Output = str;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: ops::RangeToInclusive<usize>) -> &str {
|
||||
Index::index(&**self, index)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "derefmut_for_string", since = "1.3.0")]
|
||||
impl ops::IndexMut<ops::Range<usize>> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
|
||||
&mut self[..][index]
|
||||
}
|
||||
}
|
||||
#[stable(feature = "derefmut_for_string", since = "1.3.0")]
|
||||
impl ops::IndexMut<ops::RangeTo<usize>> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
|
||||
&mut self[..][index]
|
||||
}
|
||||
}
|
||||
#[stable(feature = "derefmut_for_string", since = "1.3.0")]
|
||||
impl ops::IndexMut<ops::RangeFrom<usize>> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
|
||||
&mut self[..][index]
|
||||
}
|
||||
}
|
||||
#[stable(feature = "derefmut_for_string", since = "1.3.0")]
|
||||
impl ops::IndexMut<ops::RangeFull> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
|
||||
unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) }
|
||||
}
|
||||
}
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::IndexMut<ops::RangeInclusive<usize>> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut str {
|
||||
IndexMut::index_mut(&mut **self, index)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
impl ops::IndexMut<ops::RangeToInclusive<usize>> for String {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut str {
|
||||
IndexMut::index_mut(&mut **self, index)
|
||||
fn index_mut(&mut self, index: I) -> &mut I::Output {
|
||||
index.index_mut(self.as_mut_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] }
|
||||
fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true }
|
||||
|
||||
[target.'cfg(target_os = "hermit")'.dependencies]
|
||||
hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true }
|
||||
hermit-abi = { version = "0.3.9", features = ['rustc-dep-of-std'], public = true }
|
||||
|
||||
[target.'cfg(target_os = "wasi")'.dependencies]
|
||||
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
|
||||
|
@ -258,6 +258,9 @@ fn _var(key: &OsStr) -> Result<String, VarError> {
|
||||
/// None => println!("{key} is not defined in the environment.")
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If expecting a delimited variable (such as `PATH`), [`split_paths`]
|
||||
/// can be used to separate items.
|
||||
#[must_use]
|
||||
#[stable(feature = "env", since = "1.0.0")]
|
||||
pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> {
|
||||
@ -441,6 +444,16 @@ pub struct SplitPaths<'a> {
|
||||
/// Returns an iterator over the paths contained in `unparsed`. The iterator
|
||||
/// element type is [`PathBuf`].
|
||||
///
|
||||
/// On most Unix platforms, the separator is `:` and on Windows it is `;`. This
|
||||
/// also performs unquoting on Windows.
|
||||
///
|
||||
/// [`join_paths`] can be used to recombine elements.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This will panic on systems where there is no delimited `PATH` variable,
|
||||
/// such as UEFI.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -456,6 +469,7 @@ pub struct SplitPaths<'a> {
|
||||
/// None => println!("{key} is not defined in the environment.")
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias = "PATH")]
|
||||
#[stable(feature = "env", since = "1.0.0")]
|
||||
pub fn split_paths<T: AsRef<OsStr> + ?Sized>(unparsed: &T) -> SplitPaths<'_> {
|
||||
SplitPaths { inner: os_imp::split_paths(unparsed.as_ref()) }
|
||||
@ -496,7 +510,8 @@ pub struct JoinPathsError {
|
||||
///
|
||||
/// Returns an [`Err`] (containing an error message) if one of the input
|
||||
/// [`Path`]s contains an invalid character for constructing the `PATH`
|
||||
/// variable (a double quote on Windows or a colon on Unix).
|
||||
/// variable (a double quote on Windows or a colon on Unix), or if the system
|
||||
/// does not have a `PATH`-like variable (e.g. UEFI or WASI).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -550,6 +565,7 @@ pub struct JoinPathsError {
|
||||
/// ```
|
||||
///
|
||||
/// [`env::split_paths()`]: split_paths
|
||||
#[doc(alias = "PATH")]
|
||||
#[stable(feature = "env", since = "1.0.0")]
|
||||
pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
|
||||
where
|
||||
|
@ -31,6 +31,8 @@ pub use core::f32::{
|
||||
impl f32 {
|
||||
/// Returns the largest integer less than or equal to `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -52,6 +54,8 @@ impl f32 {
|
||||
|
||||
/// Returns the smallest integer greater than or equal to `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -73,6 +77,8 @@ impl f32 {
|
||||
/// Returns the nearest integer to `self`. If a value is half-way between two
|
||||
/// integers, round away from `0.0`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -99,6 +105,8 @@ impl f32 {
|
||||
/// Returns the nearest integer to a number. Rounds half-way cases to the number
|
||||
/// with an even least significant digit.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -123,6 +131,8 @@ impl f32 {
|
||||
/// Returns the integer part of `self`.
|
||||
/// This means that non-integer numbers are always truncated towards zero.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -145,6 +155,8 @@ impl f32 {
|
||||
|
||||
/// Returns the fractional part of `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -166,6 +178,8 @@ impl f32 {
|
||||
|
||||
/// Computes the absolute value of `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -249,6 +263,12 @@ impl f32 {
|
||||
/// this is not always true, and will be heavily dependant on designing
|
||||
/// algorithms with specific target hardware in mind.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result. It is specified by IEEE 754 as
|
||||
/// `fusedMultiplyAdd` and guaranteed not to change.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -276,6 +296,11 @@ impl f32 {
|
||||
/// In other words, the result is `self / rhs` rounded to the integer `n`
|
||||
/// such that `self >= n * rhs`.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -309,6 +334,11 @@ impl f32 {
|
||||
/// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)`
|
||||
/// approximately.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -337,6 +367,10 @@ impl f32 {
|
||||
/// It might have a different sequence of rounding operations than `powf`,
|
||||
/// so the results are not guaranteed to agree.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -355,6 +389,10 @@ impl f32 {
|
||||
|
||||
/// Raises a number to a floating point power.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -375,6 +413,12 @@ impl f32 {
|
||||
///
|
||||
/// Returns NaN if `self` is a negative number other than `-0.0`.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result. It is specified by IEEE 754 as `squareRoot`
|
||||
/// and guaranteed not to change.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -398,6 +442,10 @@ impl f32 {
|
||||
|
||||
/// Returns `e^(self)`, (the exponential function).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -420,6 +468,10 @@ impl f32 {
|
||||
|
||||
/// Returns `2^(self)`.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -440,6 +492,10 @@ impl f32 {
|
||||
|
||||
/// Returns the natural logarithm of the number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -466,6 +522,10 @@ impl f32 {
|
||||
/// `self.log2()` can produce more accurate results for base 2, and
|
||||
/// `self.log10()` can produce more accurate results for base 10.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -486,6 +546,10 @@ impl f32 {
|
||||
|
||||
/// Returns the base 2 logarithm of the number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -506,6 +570,10 @@ impl f32 {
|
||||
|
||||
/// Returns the base 10 logarithm of the number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -529,6 +597,12 @@ impl f32 {
|
||||
/// * If `self <= other`: `0.0`
|
||||
/// * Else: `self - other`
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `fdimf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -561,6 +635,12 @@ impl f32 {
|
||||
|
||||
/// Returns the cube root of a number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `cbrtf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -584,6 +664,12 @@ impl f32 {
|
||||
/// right-angle triangle with other sides having length `x.abs()` and
|
||||
/// `y.abs()`.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `hypotf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -605,6 +691,10 @@ impl f32 {
|
||||
|
||||
/// Computes the sine of a number (in radians).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -624,6 +714,10 @@ impl f32 {
|
||||
|
||||
/// Computes the cosine of a number (in radians).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -643,6 +737,12 @@ impl f32 {
|
||||
|
||||
/// Computes the tangent of a number (in radians).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `tanf` from libc on Unix and
|
||||
/// Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -663,6 +763,12 @@ impl f32 {
|
||||
/// the range [-pi/2, pi/2] or NaN if the number is outside the range
|
||||
/// [-1, 1].
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `asinf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -686,6 +792,12 @@ impl f32 {
|
||||
/// the range [0, pi] or NaN if the number is outside the range
|
||||
/// [-1, 1].
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `acosf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -708,6 +820,12 @@ impl f32 {
|
||||
/// Computes the arctangent of a number. Return value is in radians in the
|
||||
/// range [-pi/2, pi/2];
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `atanf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -734,6 +852,12 @@ impl f32 {
|
||||
/// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
|
||||
/// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `atan2f` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -764,6 +888,12 @@ impl f32 {
|
||||
/// Simultaneously computes the sine and cosine of the number, `x`. Returns
|
||||
/// `(sin(x), cos(x))`.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `(f32::sin(x),
|
||||
/// f32::cos(x))`. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -787,6 +917,12 @@ impl f32 {
|
||||
/// Returns `e^(self) - 1` in a way that is accurate even if the
|
||||
/// number is close to zero.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `expm1f` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -809,6 +945,12 @@ impl f32 {
|
||||
/// Returns `ln(1+n)` (natural logarithm) more accurately than if
|
||||
/// the operations were performed separately.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `log1pf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -831,6 +973,12 @@ impl f32 {
|
||||
|
||||
/// Hyperbolic sine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `sinhf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -854,6 +1002,12 @@ impl f32 {
|
||||
|
||||
/// Hyperbolic cosine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `coshf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -877,6 +1031,12 @@ impl f32 {
|
||||
|
||||
/// Hyperbolic tangent function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `tanhf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -900,6 +1060,10 @@ impl f32 {
|
||||
|
||||
/// Inverse hyperbolic sine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -923,6 +1087,10 @@ impl f32 {
|
||||
|
||||
/// Inverse hyperbolic cosine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -948,6 +1116,10 @@ impl f32 {
|
||||
|
||||
/// Inverse hyperbolic tangent function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -969,6 +1141,12 @@ impl f32 {
|
||||
|
||||
/// Gamma function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `tgammaf` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -991,6 +1169,12 @@ impl f32 {
|
||||
///
|
||||
/// The integer part of the tuple indicates the sign of the gamma function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `lgamma_r` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
@ -31,6 +31,8 @@ pub use core::f64::{
|
||||
impl f64 {
|
||||
/// Returns the largest integer less than or equal to `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -52,6 +54,8 @@ impl f64 {
|
||||
|
||||
/// Returns the smallest integer greater than or equal to `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -73,6 +77,8 @@ impl f64 {
|
||||
/// Returns the nearest integer to `self`. If a value is half-way between two
|
||||
/// integers, round away from `0.0`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -99,6 +105,8 @@ impl f64 {
|
||||
/// Returns the nearest integer to a number. Rounds half-way cases to the number
|
||||
/// with an even least significant digit.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -123,6 +131,8 @@ impl f64 {
|
||||
/// Returns the integer part of `self`.
|
||||
/// This means that non-integer numbers are always truncated towards zero.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -145,6 +155,8 @@ impl f64 {
|
||||
|
||||
/// Returns the fractional part of `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -166,6 +178,8 @@ impl f64 {
|
||||
|
||||
/// Computes the absolute value of `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -249,6 +263,12 @@ impl f64 {
|
||||
/// this is not always true, and will be heavily dependant on designing
|
||||
/// algorithms with specific target hardware in mind.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result. It is specified by IEEE 754 as
|
||||
/// `fusedMultiplyAdd` and guaranteed not to change.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -276,6 +296,11 @@ impl f64 {
|
||||
/// In other words, the result is `self / rhs` rounded to the integer `n`
|
||||
/// such that `self >= n * rhs`.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -309,6 +334,11 @@ impl f64 {
|
||||
/// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)`
|
||||
/// approximately.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -337,6 +367,10 @@ impl f64 {
|
||||
/// It might have a different sequence of rounding operations than `powf`,
|
||||
/// so the results are not guaranteed to agree.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -355,6 +389,10 @@ impl f64 {
|
||||
|
||||
/// Raises a number to a floating point power.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -375,6 +413,12 @@ impl f64 {
|
||||
///
|
||||
/// Returns NaN if `self` is a negative number other than `-0.0`.
|
||||
///
|
||||
/// # Precision
|
||||
///
|
||||
/// The result of this operation is guaranteed to be the rounded
|
||||
/// infinite-precision result. It is specified by IEEE 754 as `squareRoot`
|
||||
/// and guaranteed not to change.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -398,6 +442,10 @@ impl f64 {
|
||||
|
||||
/// Returns `e^(self)`, (the exponential function).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -420,6 +468,10 @@ impl f64 {
|
||||
|
||||
/// Returns `2^(self)`.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -440,6 +492,10 @@ impl f64 {
|
||||
|
||||
/// Returns the natural logarithm of the number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -466,6 +522,10 @@ impl f64 {
|
||||
/// `self.log2()` can produce more accurate results for base 2, and
|
||||
/// `self.log10()` can produce more accurate results for base 10.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -486,6 +546,10 @@ impl f64 {
|
||||
|
||||
/// Returns the base 2 logarithm of the number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -506,6 +570,10 @@ impl f64 {
|
||||
|
||||
/// Returns the base 10 logarithm of the number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -529,6 +597,12 @@ impl f64 {
|
||||
/// * If `self <= other`: `0.0`
|
||||
/// * Else: `self - other`
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `fdim` from libc on Unix and
|
||||
/// Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -561,6 +635,12 @@ impl f64 {
|
||||
|
||||
/// Returns the cube root of a number.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `cbrt` from libc on Unix and
|
||||
/// Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -584,6 +664,12 @@ impl f64 {
|
||||
/// right-angle triangle with other sides having length `x.abs()` and
|
||||
/// `y.abs()`.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `hypot` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -605,6 +691,10 @@ impl f64 {
|
||||
|
||||
/// Computes the sine of a number (in radians).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -624,6 +714,10 @@ impl f64 {
|
||||
|
||||
/// Computes the cosine of a number (in radians).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -643,6 +737,12 @@ impl f64 {
|
||||
|
||||
/// Computes the tangent of a number (in radians).
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `tan` from libc on Unix and
|
||||
/// Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -663,6 +763,12 @@ impl f64 {
|
||||
/// the range [-pi/2, pi/2] or NaN if the number is outside the range
|
||||
/// [-1, 1].
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `asin` from libc on Unix and
|
||||
/// Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -686,6 +792,12 @@ impl f64 {
|
||||
/// the range [0, pi] or NaN if the number is outside the range
|
||||
/// [-1, 1].
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `acos` from libc on Unix and
|
||||
/// Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -708,6 +820,12 @@ impl f64 {
|
||||
/// Computes the arctangent of a number. Return value is in radians in the
|
||||
/// range [-pi/2, pi/2];
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `atan` from libc on Unix and
|
||||
/// Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -734,6 +852,12 @@ impl f64 {
|
||||
/// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]`
|
||||
/// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)`
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `atan2` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -764,6 +888,12 @@ impl f64 {
|
||||
/// Simultaneously computes the sine and cosine of the number, `x`. Returns
|
||||
/// `(sin(x), cos(x))`.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `(f64::sin(x),
|
||||
/// f64::cos(x))`. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -787,6 +917,12 @@ impl f64 {
|
||||
/// Returns `e^(self) - 1` in a way that is accurate even if the
|
||||
/// number is close to zero.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `expm1` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -809,6 +945,12 @@ impl f64 {
|
||||
/// Returns `ln(1+n)` (natural logarithm) more accurately than if
|
||||
/// the operations were performed separately.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `log1p` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -831,6 +973,12 @@ impl f64 {
|
||||
|
||||
/// Hyperbolic sine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `sinh` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -854,6 +1002,12 @@ impl f64 {
|
||||
|
||||
/// Hyperbolic cosine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `cosh` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -877,6 +1031,12 @@ impl f64 {
|
||||
|
||||
/// Hyperbolic tangent function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `tanh` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -900,6 +1060,10 @@ impl f64 {
|
||||
|
||||
/// Inverse hyperbolic sine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -923,6 +1087,10 @@ impl f64 {
|
||||
|
||||
/// Inverse hyperbolic cosine function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -948,6 +1116,10 @@ impl f64 {
|
||||
|
||||
/// Inverse hyperbolic tangent function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -969,6 +1141,12 @@ impl f64 {
|
||||
|
||||
/// Gamma function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `tgamma` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -991,6 +1169,12 @@ impl f64 {
|
||||
///
|
||||
/// The integer part of the tuple indicates the sign of the gamma function.
|
||||
///
|
||||
/// # Platform-specific precision
|
||||
///
|
||||
/// The precision of this function varies by platform and Rust version.
|
||||
/// This function currently corresponds to the `lgamma_r` from libc on Unix
|
||||
/// and Windows. Note that this might change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
@ -11,8 +11,9 @@ use crate::fs::File;
|
||||
use crate::io::{
|
||||
self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, SpecReadByte,
|
||||
};
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantMutex, ReentrantMutexGuard};
|
||||
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantLock, ReentrantLockGuard};
|
||||
use crate::sys::stdio;
|
||||
|
||||
type LocalStream = Arc<Mutex<Vec<u8>>>;
|
||||
@ -545,7 +546,7 @@ pub struct Stdout {
|
||||
// FIXME: this should be LineWriter or BufWriter depending on the state of
|
||||
// stdout (tty or not). Note that if this is not line buffered it
|
||||
// should also flush-on-panic or some form of flush-on-abort.
|
||||
inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>,
|
||||
inner: &'static ReentrantLock<RefCell<LineWriter<StdoutRaw>>>,
|
||||
}
|
||||
|
||||
/// A locked reference to the [`Stdout`] handle.
|
||||
@ -567,10 +568,10 @@ pub struct Stdout {
|
||||
#[must_use = "if unused stdout will immediately unlock"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StdoutLock<'a> {
|
||||
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
|
||||
inner: ReentrantLockGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
|
||||
}
|
||||
|
||||
static STDOUT: OnceLock<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = OnceLock::new();
|
||||
static STDOUT: OnceLock<ReentrantLock<RefCell<LineWriter<StdoutRaw>>>> = OnceLock::new();
|
||||
|
||||
/// Constructs a new handle to the standard output of the current process.
|
||||
///
|
||||
@ -624,7 +625,7 @@ static STDOUT: OnceLock<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = OnceLo
|
||||
pub fn stdout() -> Stdout {
|
||||
Stdout {
|
||||
inner: STDOUT
|
||||
.get_or_init(|| ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))),
|
||||
.get_or_init(|| ReentrantLock::new(RefCell::new(LineWriter::new(stdout_raw())))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -635,7 +636,7 @@ pub fn cleanup() {
|
||||
let mut initialized = false;
|
||||
let stdout = STDOUT.get_or_init(|| {
|
||||
initialized = true;
|
||||
ReentrantMutex::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw())))
|
||||
ReentrantLock::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw())))
|
||||
});
|
||||
|
||||
if !initialized {
|
||||
@ -678,6 +679,12 @@ impl Stdout {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl UnwindSafe for Stdout {}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl RefUnwindSafe for Stdout {}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Stdout {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -737,6 +744,12 @@ impl Write for &Stdout {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl UnwindSafe for StdoutLock<'_> {}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl RefUnwindSafe for StdoutLock<'_> {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for StdoutLock<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
@ -786,7 +799,7 @@ impl fmt::Debug for StdoutLock<'_> {
|
||||
/// standard library or via raw Windows API calls, will fail.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Stderr {
|
||||
inner: &'static ReentrantMutex<RefCell<StderrRaw>>,
|
||||
inner: &'static ReentrantLock<RefCell<StderrRaw>>,
|
||||
}
|
||||
|
||||
/// A locked reference to the [`Stderr`] handle.
|
||||
@ -808,7 +821,7 @@ pub struct Stderr {
|
||||
#[must_use = "if unused stderr will immediately unlock"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StderrLock<'a> {
|
||||
inner: ReentrantMutexGuard<'a, RefCell<StderrRaw>>,
|
||||
inner: ReentrantLockGuard<'a, RefCell<StderrRaw>>,
|
||||
}
|
||||
|
||||
/// Constructs a new handle to the standard error of the current process.
|
||||
@ -862,8 +875,8 @@ pub fn stderr() -> Stderr {
|
||||
// Note that unlike `stdout()` we don't use `at_exit` here to register a
|
||||
// destructor. Stderr is not buffered, so there's no need to run a
|
||||
// destructor for flushing the buffer
|
||||
static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> =
|
||||
ReentrantMutex::new(RefCell::new(stderr_raw()));
|
||||
static INSTANCE: ReentrantLock<RefCell<StderrRaw>> =
|
||||
ReentrantLock::new(RefCell::new(stderr_raw()));
|
||||
|
||||
Stderr { inner: &INSTANCE }
|
||||
}
|
||||
@ -898,6 +911,12 @@ impl Stderr {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl UnwindSafe for Stderr {}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl RefUnwindSafe for Stderr {}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Stderr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -957,6 +976,12 @@ impl Write for &Stderr {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl UnwindSafe for StderrLock<'_> {}
|
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl RefUnwindSafe for StderrLock<'_> {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for StderrLock<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
|
@ -6,7 +6,7 @@ use crate::any::Any;
|
||||
use crate::collections;
|
||||
use crate::panicking;
|
||||
use crate::sync::atomic::{AtomicU8, Ordering};
|
||||
use crate::sync::{Mutex, RwLock};
|
||||
use crate::sync::{Condvar, Mutex, RwLock};
|
||||
use crate::thread::Result;
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -67,11 +67,15 @@ pub fn panic_any<M: 'static + Any + Send>(msg: M) -> ! {
|
||||
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
||||
impl UnwindSafe for Condvar {}
|
||||
|
||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
||||
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
|
||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
||||
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
|
||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
||||
impl RefUnwindSafe for Condvar {}
|
||||
|
||||
// https://github.com/rust-lang/rust/issues/62301
|
||||
#[stable(feature = "hashbrown", since = "1.36.0")]
|
||||
|
@ -184,7 +184,8 @@ pub use self::lazy_lock::LazyLock;
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub use self::once_lock::OnceLock;
|
||||
|
||||
pub(crate) use self::remutex::{ReentrantMutex, ReentrantMutexGuard};
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard};
|
||||
|
||||
pub mod mpsc;
|
||||
|
||||
@ -196,5 +197,5 @@ mod mutex;
|
||||
pub(crate) mod once;
|
||||
mod once_lock;
|
||||
mod poison;
|
||||
mod remutex;
|
||||
mod reentrant_lock;
|
||||
mod rwlock;
|
||||
|
@ -547,6 +547,9 @@ impl<T> Channel<T> {
|
||||
}
|
||||
|
||||
let mut head = self.head.index.load(Ordering::Acquire);
|
||||
// The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts
|
||||
// to initalize the first block before noticing that the receivers disconnected. Late allocations
|
||||
// will be deallocated by the sender in Drop.
|
||||
let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||
|
||||
// If we're going to be dropping messages we need to synchronize with initialization
|
||||
|
320
library/std/src/sync/reentrant_lock.rs
Normal file
320
library/std/src/sync/reentrant_lock.rs
Normal file
@ -0,0 +1,320 @@
|
||||
#[cfg(all(test, not(target_os = "emscripten")))]
|
||||
mod tests;
|
||||
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::ops::Deref;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
||||
use crate::sys::locks as sys;
|
||||
|
||||
/// A re-entrant mutual exclusion lock
|
||||
///
|
||||
/// This lock will block *other* threads waiting for the lock to become
|
||||
/// available. The thread which has already locked the mutex can lock it
|
||||
/// multiple times without blocking, preventing a common source of deadlocks.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Allow recursively calling a function needing synchronization from within
|
||||
/// a callback (this is how [`StdoutLock`](crate::io::StdoutLock) is currently
|
||||
/// implemented):
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
///
|
||||
/// use std::cell::RefCell;
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// pub struct Log {
|
||||
/// data: RefCell<String>,
|
||||
/// }
|
||||
///
|
||||
/// impl Log {
|
||||
/// pub fn append(&self, msg: &str) {
|
||||
/// self.data.borrow_mut().push_str(msg);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// static LOG: ReentrantLock<Log> = ReentrantLock::new(Log { data: RefCell::new(String::new()) });
|
||||
///
|
||||
/// pub fn with_log<R>(f: impl FnOnce(&Log) -> R) -> R {
|
||||
/// let log = LOG.lock();
|
||||
/// f(&*log)
|
||||
/// }
|
||||
///
|
||||
/// with_log(|log| {
|
||||
/// log.append("Hello");
|
||||
/// with_log(|log| log.append(" there!"));
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
// # Implementation details
|
||||
//
|
||||
// The 'owner' field tracks which thread has locked the mutex.
|
||||
//
|
||||
// We use current_thread_unique_ptr() as the thread identifier,
|
||||
// which is just the address of a thread local variable.
|
||||
//
|
||||
// If `owner` is set to the identifier of the current thread,
|
||||
// we assume the mutex is already locked and instead of locking it again,
|
||||
// we increment `lock_count`.
|
||||
//
|
||||
// When unlocking, we decrement `lock_count`, and only unlock the mutex when
|
||||
// it reaches zero.
|
||||
//
|
||||
// `lock_count` is protected by the mutex and only accessed by the thread that has
|
||||
// locked the mutex, so needs no synchronization.
|
||||
//
|
||||
// `owner` can be checked by other threads that want to see if they already
|
||||
// hold the lock, so needs to be atomic. If it compares equal, we're on the
|
||||
// same thread that holds the mutex and memory access can use relaxed ordering
|
||||
// since we're not dealing with multiple threads. If it's not equal,
|
||||
// synchronization is left to the mutex, making relaxed memory ordering for
|
||||
// the `owner` field fine in all cases.
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
pub struct ReentrantLock<T: ?Sized> {
|
||||
mutex: sys::Mutex,
|
||||
owner: AtomicUsize,
|
||||
lock_count: UnsafeCell<u32>,
|
||||
data: T,
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
unsafe impl<T: Send + ?Sized> Send for ReentrantLock<T> {}
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
unsafe impl<T: Send + ?Sized> Sync for ReentrantLock<T> {}
|
||||
|
||||
// Because of the `UnsafeCell`, these traits are not implemented automatically
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: UnwindSafe + ?Sized> UnwindSafe for ReentrantLock<T> {}
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for ReentrantLock<T> {}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a re-entrant lock. When this
|
||||
/// structure is dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] implementation.
|
||||
///
|
||||
/// This structure is created by the [`lock`](ReentrantLock::lock) method on
|
||||
/// [`ReentrantLock`].
|
||||
///
|
||||
/// # Mutability
|
||||
///
|
||||
/// Unlike [`MutexGuard`](super::MutexGuard), `ReentrantLockGuard` does not
|
||||
/// implement [`DerefMut`](crate::ops::DerefMut), because implementation of
|
||||
/// the trait would violate Rust’s reference aliasing rules. Use interior
|
||||
/// mutability (usually [`RefCell`](crate::cell::RefCell)) in order to mutate
|
||||
/// the guarded data.
|
||||
#[must_use = "if unused the ReentrantLock will immediately unlock"]
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
pub struct ReentrantLockGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a ReentrantLock<T>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> !Send for ReentrantLockGuard<'_, T> {}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T> ReentrantLock<T> {
|
||||
/// Creates a new re-entrant lock in an unlocked state ready for use.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// let lock = ReentrantLock::new(0);
|
||||
/// ```
|
||||
pub const fn new(t: T) -> ReentrantLock<T> {
|
||||
ReentrantLock {
|
||||
mutex: sys::Mutex::new(),
|
||||
owner: AtomicUsize::new(0),
|
||||
lock_count: UnsafeCell::new(0),
|
||||
data: t,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes this lock, returning the underlying data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
///
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// let lock = ReentrantLock::new(0);
|
||||
/// assert_eq!(lock.into_inner(), 0);
|
||||
/// ```
|
||||
pub fn into_inner(self) -> T {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> ReentrantLock<T> {
|
||||
/// Acquires the lock, blocking the current thread until it is able to do
|
||||
/// so.
|
||||
///
|
||||
/// This function will block the caller until it is available to acquire
|
||||
/// the lock. Upon returning, the thread is the only thread with the lock
|
||||
/// held. When the thread calling this method already holds the lock, the
|
||||
/// call succeeds without blocking.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
/// use std::cell::Cell;
|
||||
/// use std::sync::{Arc, ReentrantLock};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let lock = Arc::new(ReentrantLock::new(Cell::new(0)));
|
||||
/// let c_lock = Arc::clone(&lock);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// c_lock.lock().set(10);
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(lock.lock().get(), 10);
|
||||
/// ```
|
||||
pub fn lock(&self) -> ReentrantLockGuard<'_, T> {
|
||||
let this_thread = current_thread_unique_ptr();
|
||||
// Safety: We only touch lock_count when we own the lock.
|
||||
unsafe {
|
||||
if self.owner.load(Relaxed) == this_thread {
|
||||
self.increment_lock_count().expect("lock count overflow in reentrant mutex");
|
||||
} else {
|
||||
self.mutex.lock();
|
||||
self.owner.store(this_thread, Relaxed);
|
||||
debug_assert_eq!(*self.lock_count.get(), 0);
|
||||
*self.lock_count.get() = 1;
|
||||
}
|
||||
}
|
||||
ReentrantLockGuard { lock: self }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
///
|
||||
/// Since this call borrows the `ReentrantLock` mutably, no actual locking
|
||||
/// needs to take place -- the mutable borrow statically guarantees no locks
|
||||
/// exist.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(reentrant_lock)]
|
||||
/// use std::sync::ReentrantLock;
|
||||
///
|
||||
/// let mut lock = ReentrantLock::new(0);
|
||||
/// *lock.get_mut() = 10;
|
||||
/// assert_eq!(*lock.lock(), 10);
|
||||
/// ```
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock.
|
||||
///
|
||||
/// If the lock could not be acquired at this time, then `None` is returned.
|
||||
/// Otherwise, an RAII guard is returned.
|
||||
///
|
||||
/// This function does not block.
|
||||
pub(crate) fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> {
|
||||
let this_thread = current_thread_unique_ptr();
|
||||
// Safety: We only touch lock_count when we own the lock.
|
||||
unsafe {
|
||||
if self.owner.load(Relaxed) == this_thread {
|
||||
self.increment_lock_count()?;
|
||||
Some(ReentrantLockGuard { lock: self })
|
||||
} else if self.mutex.try_lock() {
|
||||
self.owner.store(this_thread, Relaxed);
|
||||
debug_assert_eq!(*self.lock_count.get(), 0);
|
||||
*self.lock_count.get() = 1;
|
||||
Some(ReentrantLockGuard { lock: self })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn increment_lock_count(&self) -> Option<()> {
|
||||
*self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?;
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: fmt::Debug + ?Sized> fmt::Debug for ReentrantLock<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("ReentrantLock");
|
||||
match self.try_lock() {
|
||||
Some(v) => d.field("data", &&*v),
|
||||
None => d.field("data", &format_args!("<locked>")),
|
||||
};
|
||||
d.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: Default> Default for ReentrantLock<T> {
|
||||
fn default() -> Self {
|
||||
Self::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T> From<T> for ReentrantLock<T> {
|
||||
fn from(t: T) -> Self {
|
||||
Self::new(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> Deref for ReentrantLockGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.lock.data
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: fmt::Debug + ?Sized> fmt::Debug for ReentrantLockGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: fmt::Display + ?Sized> fmt::Display for ReentrantLockGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
impl<T: ?Sized> Drop for ReentrantLockGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// Safety: We own the lock.
|
||||
unsafe {
|
||||
*self.lock.lock_count.get() -= 1;
|
||||
if *self.lock.lock_count.get() == 0 {
|
||||
self.lock.owner.store(0, Relaxed);
|
||||
self.lock.mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an address that is unique per running thread.
|
||||
///
|
||||
/// This can be used as a non-null usize-sized ID.
|
||||
pub(crate) fn current_thread_unique_ptr() -> usize {
|
||||
// Use a non-drop type to make sure it's still available during thread destruction.
|
||||
thread_local! { static X: u8 = const { 0 } }
|
||||
X.with(|x| <*const _>::addr(x))
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
use super::{ReentrantMutex, ReentrantMutexGuard};
|
||||
use super::{ReentrantLock, ReentrantLockGuard};
|
||||
use crate::cell::RefCell;
|
||||
use crate::sync::Arc;
|
||||
use crate::thread;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let m = ReentrantMutex::new(());
|
||||
let l = ReentrantLock::new(());
|
||||
{
|
||||
let a = m.lock();
|
||||
let a = l.lock();
|
||||
{
|
||||
let b = m.lock();
|
||||
let b = l.lock();
|
||||
{
|
||||
let c = m.lock();
|
||||
let c = l.lock();
|
||||
assert_eq!(*c, ());
|
||||
}
|
||||
assert_eq!(*b, ());
|
||||
@ -22,15 +22,15 @@ fn smoke() {
|
||||
|
||||
#[test]
|
||||
fn is_mutex() {
|
||||
let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
|
||||
let m2 = m.clone();
|
||||
let lock = m.lock();
|
||||
let l = Arc::new(ReentrantLock::new(RefCell::new(0)));
|
||||
let l2 = l.clone();
|
||||
let lock = l.lock();
|
||||
let child = thread::spawn(move || {
|
||||
let lock = m2.lock();
|
||||
let lock = l2.lock();
|
||||
assert_eq!(*lock.borrow(), 4950);
|
||||
});
|
||||
for i in 0..100 {
|
||||
let lock = m.lock();
|
||||
let lock = l.lock();
|
||||
*lock.borrow_mut() += i;
|
||||
}
|
||||
drop(lock);
|
||||
@ -39,20 +39,20 @@ fn is_mutex() {
|
||||
|
||||
#[test]
|
||||
fn trylock_works() {
|
||||
let m = Arc::new(ReentrantMutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let _lock = m.try_lock();
|
||||
let _lock2 = m.try_lock();
|
||||
let l = Arc::new(ReentrantLock::new(()));
|
||||
let l2 = l.clone();
|
||||
let _lock = l.try_lock();
|
||||
let _lock2 = l.try_lock();
|
||||
thread::spawn(move || {
|
||||
let lock = m2.try_lock();
|
||||
let lock = l2.try_lock();
|
||||
assert!(lock.is_none());
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
let _lock3 = m.try_lock();
|
||||
let _lock3 = l.try_lock();
|
||||
}
|
||||
|
||||
pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
|
||||
pub struct Answer<'a>(pub ReentrantLockGuard<'a, RefCell<u32>>);
|
||||
impl Drop for Answer<'_> {
|
||||
fn drop(&mut self) {
|
||||
*self.0.borrow_mut() = 42;
|
@ -1,178 +0,0 @@
|
||||
#[cfg(all(test, not(target_os = "emscripten")))]
|
||||
mod tests;
|
||||
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::ops::Deref;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
||||
use crate::sys::locks as sys;
|
||||
|
||||
/// A reentrant mutual exclusion
|
||||
///
|
||||
/// This mutex will block *other* threads waiting for the lock to become
|
||||
/// available. The thread which has already locked the mutex can lock it
|
||||
/// multiple times without blocking, preventing a common source of deadlocks.
|
||||
///
|
||||
/// This is used by stdout().lock() and friends.
|
||||
///
|
||||
/// ## Implementation details
|
||||
///
|
||||
/// The 'owner' field tracks which thread has locked the mutex.
|
||||
///
|
||||
/// We use current_thread_unique_ptr() as the thread identifier,
|
||||
/// which is just the address of a thread local variable.
|
||||
///
|
||||
/// If `owner` is set to the identifier of the current thread,
|
||||
/// we assume the mutex is already locked and instead of locking it again,
|
||||
/// we increment `lock_count`.
|
||||
///
|
||||
/// When unlocking, we decrement `lock_count`, and only unlock the mutex when
|
||||
/// it reaches zero.
|
||||
///
|
||||
/// `lock_count` is protected by the mutex and only accessed by the thread that has
|
||||
/// locked the mutex, so needs no synchronization.
|
||||
///
|
||||
/// `owner` can be checked by other threads that want to see if they already
|
||||
/// hold the lock, so needs to be atomic. If it compares equal, we're on the
|
||||
/// same thread that holds the mutex and memory access can use relaxed ordering
|
||||
/// since we're not dealing with multiple threads. If it's not equal,
|
||||
/// synchronization is left to the mutex, making relaxed memory ordering for
|
||||
/// the `owner` field fine in all cases.
|
||||
pub struct ReentrantMutex<T> {
|
||||
mutex: sys::Mutex,
|
||||
owner: AtomicUsize,
|
||||
lock_count: UnsafeCell<u32>,
|
||||
data: T,
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for ReentrantMutex<T> {}
|
||||
unsafe impl<T: Send> Sync for ReentrantMutex<T> {}
|
||||
|
||||
impl<T> UnwindSafe for ReentrantMutex<T> {}
|
||||
impl<T> RefUnwindSafe for ReentrantMutex<T> {}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
|
||||
/// dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// Deref implementation.
|
||||
///
|
||||
/// # Mutability
|
||||
///
|
||||
/// Unlike `MutexGuard`, `ReentrantMutexGuard` does not implement `DerefMut`,
|
||||
/// because implementation of the trait would violate Rust’s reference aliasing
|
||||
/// rules. Use interior mutability (usually `RefCell`) in order to mutate the
|
||||
/// guarded data.
|
||||
#[must_use = "if unused the ReentrantMutex will immediately unlock"]
|
||||
pub struct ReentrantMutexGuard<'a, T: 'a> {
|
||||
lock: &'a ReentrantMutex<T>,
|
||||
}
|
||||
|
||||
impl<T> !Send for ReentrantMutexGuard<'_, T> {}
|
||||
|
||||
impl<T> ReentrantMutex<T> {
|
||||
/// Creates a new reentrant mutex in an unlocked state.
|
||||
pub const fn new(t: T) -> ReentrantMutex<T> {
|
||||
ReentrantMutex {
|
||||
mutex: sys::Mutex::new(),
|
||||
owner: AtomicUsize::new(0),
|
||||
lock_count: UnsafeCell::new(0),
|
||||
data: t,
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires a mutex, blocking the current thread until it is able to do so.
|
||||
///
|
||||
/// This function will block the caller until it is available to acquire the mutex.
|
||||
/// Upon returning, the thread is the only thread with the mutex held. When the thread
|
||||
/// calling this method already holds the lock, the call shall succeed without
|
||||
/// blocking.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return failure if the mutex would otherwise be
|
||||
/// acquired.
|
||||
pub fn lock(&self) -> ReentrantMutexGuard<'_, T> {
|
||||
let this_thread = current_thread_unique_ptr();
|
||||
// Safety: We only touch lock_count when we own the lock.
|
||||
unsafe {
|
||||
if self.owner.load(Relaxed) == this_thread {
|
||||
self.increment_lock_count();
|
||||
} else {
|
||||
self.mutex.lock();
|
||||
self.owner.store(this_thread, Relaxed);
|
||||
debug_assert_eq!(*self.lock_count.get(), 0);
|
||||
*self.lock_count.get() = 1;
|
||||
}
|
||||
}
|
||||
ReentrantMutexGuard { lock: self }
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock.
|
||||
///
|
||||
/// If the lock could not be acquired at this time, then `Err` is returned.
|
||||
/// Otherwise, an RAII guard is returned.
|
||||
///
|
||||
/// This function does not block.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return failure if the mutex would otherwise be
|
||||
/// acquired.
|
||||
pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> {
|
||||
let this_thread = current_thread_unique_ptr();
|
||||
// Safety: We only touch lock_count when we own the lock.
|
||||
unsafe {
|
||||
if self.owner.load(Relaxed) == this_thread {
|
||||
self.increment_lock_count();
|
||||
Some(ReentrantMutexGuard { lock: self })
|
||||
} else if self.mutex.try_lock() {
|
||||
self.owner.store(this_thread, Relaxed);
|
||||
debug_assert_eq!(*self.lock_count.get(), 0);
|
||||
*self.lock_count.get() = 1;
|
||||
Some(ReentrantMutexGuard { lock: self })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn increment_lock_count(&self) {
|
||||
*self.lock_count.get() = (*self.lock_count.get())
|
||||
.checked_add(1)
|
||||
.expect("lock count overflow in reentrant mutex");
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for ReentrantMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.lock.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for ReentrantMutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// Safety: We own the lock.
|
||||
unsafe {
|
||||
*self.lock.lock_count.get() -= 1;
|
||||
if *self.lock.lock_count.get() == 0 {
|
||||
self.lock.owner.store(0, Relaxed);
|
||||
self.lock.mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an address that is unique per running thread.
|
||||
///
|
||||
/// This can be used as a non-null usize-sized ID.
|
||||
pub fn current_thread_unique_ptr() -> usize {
|
||||
// Use a non-drop type to make sure it's still available during thread destruction.
|
||||
thread_local! { static X: u8 = const { 0 } }
|
||||
X.with(|x| <*const _>::addr(x))
|
||||
}
|
@ -14,15 +14,15 @@ use crate::sys::unsupported;
|
||||
use crate::vec;
|
||||
|
||||
pub fn errno() -> i32 {
|
||||
0
|
||||
unsafe { abi::get_errno() }
|
||||
}
|
||||
|
||||
pub fn error_string(_errno: i32) -> String {
|
||||
"operation successful".to_string()
|
||||
pub fn error_string(errno: i32) -> String {
|
||||
abi::error_string(errno).to_string()
|
||||
}
|
||||
|
||||
pub fn getcwd() -> io::Result<PathBuf> {
|
||||
unsupported()
|
||||
Ok(PathBuf::from("/"))
|
||||
}
|
||||
|
||||
pub fn chdir(_: &path::Path) -> io::Result<()> {
|
||||
@ -188,7 +188,7 @@ pub fn unsetenv(k: &OsStr) -> io::Result<()> {
|
||||
}
|
||||
|
||||
pub fn temp_dir() -> PathBuf {
|
||||
panic!("no filesystem on hermit")
|
||||
PathBuf::from("/tmp")
|
||||
}
|
||||
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{
|
||||
AtomicBool, AtomicPtr, AtomicU32,
|
||||
AtomicPtr, AtomicU32,
|
||||
Ordering::{AcqRel, Acquire, Relaxed, Release},
|
||||
};
|
||||
use crate::sys::c;
|
||||
@ -9,10 +9,6 @@ use crate::sys::c;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// An optimization hint. The compiler is often smart enough to know if an atomic
|
||||
/// is never set and can remove dead code based on that fact.
|
||||
static HAS_DTORS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
// Using a per-thread list avoids the problems in synchronizing global state.
|
||||
#[thread_local]
|
||||
#[cfg(target_thread_local)]
|
||||
@ -24,12 +20,11 @@ static DESTRUCTORS: crate::cell::RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut
|
||||
#[inline(never)]
|
||||
#[cfg(target_thread_local)]
|
||||
pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
|
||||
dtors_used();
|
||||
match DESTRUCTORS.try_borrow_mut() {
|
||||
Ok(mut dtors) => dtors.push((t, dtor)),
|
||||
Err(_) => rtabort!("global allocator may not use TLS"),
|
||||
}
|
||||
|
||||
HAS_DTORS.store(true, Relaxed);
|
||||
}
|
||||
|
||||
#[inline(never)] // See comment above
|
||||
@ -130,6 +125,7 @@ impl StaticKey {
|
||||
#[cold]
|
||||
unsafe fn init(&'static self) -> Key {
|
||||
if self.dtor.is_some() {
|
||||
dtors_used();
|
||||
let mut pending = c::FALSE;
|
||||
let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut());
|
||||
assert_eq!(r, c::TRUE);
|
||||
@ -215,7 +211,6 @@ unsafe fn register_dtor(key: &'static StaticKey) {
|
||||
Err(new) => head = new,
|
||||
}
|
||||
}
|
||||
HAS_DTORS.store(true, Release);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -281,17 +276,16 @@ unsafe fn register_dtor(key: &'static StaticKey) {
|
||||
// the address of the symbol to ensure it sticks around.
|
||||
|
||||
#[link_section = ".CRT$XLB"]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
#[used] // we don't want LLVM eliminating this symbol for any reason, and
|
||||
// when the symbol makes it to the linker the linker will take over
|
||||
pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) =
|
||||
on_tls_callback;
|
||||
|
||||
#[allow(dead_code, unused_variables)]
|
||||
unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) {
|
||||
if !HAS_DTORS.load(Acquire) {
|
||||
return;
|
||||
}
|
||||
fn dtors_used() {
|
||||
// we don't want LLVM eliminating p_thread_callback when destructors are used.
|
||||
// when the symbol makes it to the linker the linker will take over
|
||||
unsafe { crate::intrinsics::volatile_load(&p_thread_callback) };
|
||||
}
|
||||
|
||||
unsafe extern "system" fn on_tls_callback(_h: c::LPVOID, dwReason: c::DWORD, _pv: c::LPVOID) {
|
||||
if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH {
|
||||
#[cfg(not(target_thread_local))]
|
||||
run_dtors();
|
||||
@ -301,19 +295,16 @@ unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv:
|
||||
|
||||
// See comments above for what this is doing. Note that we don't need this
|
||||
// trickery on GNU windows, just on MSVC.
|
||||
reference_tls_used();
|
||||
#[cfg(target_env = "msvc")]
|
||||
unsafe fn reference_tls_used() {
|
||||
#[cfg(all(target_env = "msvc", not(target_thread_local)))]
|
||||
{
|
||||
extern "C" {
|
||||
static _tls_used: u8;
|
||||
}
|
||||
crate::intrinsics::volatile_load(&_tls_used);
|
||||
}
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
unsafe fn reference_tls_used() {}
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // actually called below
|
||||
#[cfg(not(target_thread_local))]
|
||||
unsafe fn run_dtors() {
|
||||
for _ in 0..5 {
|
||||
let mut any_run = false;
|
||||
|
@ -279,8 +279,8 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
|
||||
let thread = std::thread::spawn(move || {
|
||||
let mut children = VecDeque::new();
|
||||
while let Ok(path) = rx.recv() {
|
||||
// try getting a few more paths from the channel to amortize the overhead of spawning processes
|
||||
let paths: Vec<_> = rx.try_iter().take(7).chain(std::iter::once(path)).collect();
|
||||
// try getting more paths from the channel to amortize the overhead of spawning processes
|
||||
let paths: Vec<_> = rx.try_iter().take(63).chain(std::iter::once(path)).collect();
|
||||
|
||||
let child = rustfmt(&src, &rustfmt_path, paths.as_slice(), check);
|
||||
children.push_back(child);
|
||||
|
@ -642,22 +642,22 @@ impl<'a> ShouldRun<'a> {
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||
pub enum Kind {
|
||||
#[clap(alias = "b")]
|
||||
#[value(alias = "b")]
|
||||
Build,
|
||||
#[clap(alias = "c")]
|
||||
#[value(alias = "c")]
|
||||
Check,
|
||||
Clippy,
|
||||
Fix,
|
||||
Format,
|
||||
#[clap(alias = "t")]
|
||||
#[value(alias = "t")]
|
||||
Test,
|
||||
Bench,
|
||||
#[clap(alias = "d")]
|
||||
#[value(alias = "d")]
|
||||
Doc,
|
||||
Clean,
|
||||
Dist,
|
||||
Install,
|
||||
#[clap(alias = "r")]
|
||||
#[value(alias = "r")]
|
||||
Run,
|
||||
Setup,
|
||||
Suggest,
|
||||
|
@ -31,7 +31,7 @@ pub enum Warnings {
|
||||
|
||||
/// Deserialized version of all flags for this compile.
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
#[command(
|
||||
override_usage = "x.py <subcommand> [options] [<paths>...]",
|
||||
disable_help_subcommand(true),
|
||||
about = "",
|
||||
@ -118,7 +118,7 @@ pub struct Flags {
|
||||
// This overrides the deny-warnings configuration option,
|
||||
// which passes -Dwarnings to the compiler invocations.
|
||||
#[arg(global(true), long)]
|
||||
#[clap(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")]
|
||||
#[arg(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")]
|
||||
/// if value is deny, will deny warnings
|
||||
/// if value is warn, will emit warnings
|
||||
/// otherwise, use the default configured behaviour
|
||||
@ -132,7 +132,7 @@ pub struct Flags {
|
||||
pub json_output: bool,
|
||||
|
||||
#[arg(global(true), long, value_name = "STYLE")]
|
||||
#[clap(value_enum, default_value_t = Color::Auto)]
|
||||
#[arg(value_enum, default_value_t = Color::Auto)]
|
||||
/// whether to use color in cargo and rustc output
|
||||
pub color: Color,
|
||||
|
||||
@ -188,7 +188,7 @@ impl Flags {
|
||||
let it = std::iter::once(&first).chain(args.iter());
|
||||
// We need to check for `<cmd> -h -v`, in which case we list the paths
|
||||
#[derive(Parser)]
|
||||
#[clap(disable_help_flag(true))]
|
||||
#[command(disable_help_flag(true))]
|
||||
struct HelpVerboseOnly {
|
||||
#[arg(short, long)]
|
||||
help: bool,
|
||||
@ -218,7 +218,7 @@ impl Flags {
|
||||
|
||||
#[derive(Debug, Clone, Default, clap::Subcommand)]
|
||||
pub enum Subcommand {
|
||||
#[clap(aliases = ["b"], long_about = "\n
|
||||
#[command(aliases = ["b"], long_about = "\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories to the crates
|
||||
and/or artifacts to compile. For example, for a quick build of a usable
|
||||
@ -233,7 +233,7 @@ pub enum Subcommand {
|
||||
/// Compile either the compiler or libraries
|
||||
#[default]
|
||||
Build,
|
||||
#[clap(aliases = ["c"], long_about = "\n
|
||||
#[command(aliases = ["c"], long_about = "\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories to the crates
|
||||
and/or artifacts to compile. For example:
|
||||
@ -246,7 +246,7 @@ pub enum Subcommand {
|
||||
all_targets: bool,
|
||||
},
|
||||
/// Run Clippy (uses rustup/cargo-installed clippy binary)
|
||||
#[clap(long_about = "\n
|
||||
#[command(long_about = "\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories to the crates
|
||||
and/or artifacts to run clippy against. For example:
|
||||
@ -273,14 +273,14 @@ pub enum Subcommand {
|
||||
forbid: Vec<String>,
|
||||
},
|
||||
/// Run cargo fix
|
||||
#[clap(long_about = "\n
|
||||
#[command(long_about = "\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories to the crates
|
||||
and/or artifacts to run `cargo fix` against. For example:
|
||||
./x.py fix library/core
|
||||
./x.py fix library/core library/proc_macro")]
|
||||
Fix,
|
||||
#[clap(
|
||||
#[command(
|
||||
name = "fmt",
|
||||
long_about = "\n
|
||||
Arguments:
|
||||
@ -295,7 +295,7 @@ pub enum Subcommand {
|
||||
#[arg(long)]
|
||||
check: bool,
|
||||
},
|
||||
#[clap(aliases = ["d"], long_about = "\n
|
||||
#[command(aliases = ["d"], long_about = "\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories of documentation
|
||||
to build. For example:
|
||||
@ -316,7 +316,7 @@ pub enum Subcommand {
|
||||
/// render the documentation in JSON format in addition to the usual HTML format
|
||||
json: bool,
|
||||
},
|
||||
#[clap(aliases = ["t"], long_about = "\n
|
||||
#[command(aliases = ["t"], long_about = "\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to test directories that
|
||||
should be compiled and run. For example:
|
||||
@ -400,7 +400,7 @@ pub enum Subcommand {
|
||||
Dist,
|
||||
/// Install distribution artifacts
|
||||
Install,
|
||||
#[clap(aliases = ["r"], long_about = "\n
|
||||
#[command(aliases = ["r"], long_about = "\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to tools to build and run. For
|
||||
example:
|
||||
@ -413,7 +413,7 @@ pub enum Subcommand {
|
||||
args: Vec<String>,
|
||||
},
|
||||
/// Set up the environment for development
|
||||
#[clap(long_about = format!(
|
||||
#[command(long_about = format!(
|
||||
"\n
|
||||
x.py setup creates a `config.toml` which changes the defaults for x.py itself,
|
||||
as well as setting up a git pre-push hook, VS Code config and toolchain link.
|
||||
@ -434,7 +434,7 @@ Arguments:
|
||||
profile: Option<PathBuf>,
|
||||
},
|
||||
/// Suggest a subset of tests to run, based on modified files
|
||||
#[clap(long_about = "\n")]
|
||||
#[command(long_about = "\n")]
|
||||
Suggest {
|
||||
/// run suggested tests
|
||||
#[arg(long)]
|
||||
|
@ -39,6 +39,7 @@
|
||||
- [\*-unknown-fuchsia](platform-support/fuchsia.md)
|
||||
- [\*-kmc-solid_\*](platform-support/kmc-solid.md)
|
||||
- [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md)
|
||||
- [hexagon-unknown-linux-musl](platform-support/hexagon-unknown-linux-musl.md)
|
||||
- [hexagon-unknown-none-elf](platform-support/hexagon-unknown-none-elf.md)
|
||||
- [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md)
|
||||
- [loongarch\*-unknown-none\*](platform-support/loongarch-none.md)
|
||||
|
@ -286,8 +286,8 @@ target | std | host | notes
|
||||
`bpfel-unknown-none` | * | | BPF (little endian)
|
||||
`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian)
|
||||
`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian)
|
||||
[`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | Bare Hexagon (v60+, HVX)
|
||||
`hexagon-unknown-linux-musl` | ? | |
|
||||
[`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX)
|
||||
[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux
|
||||
`i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI]
|
||||
[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI]
|
||||
[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium
|
||||
|
102
src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
Normal file
102
src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
Normal file
@ -0,0 +1,102 @@
|
||||
# `hexagon-unknown-linux-musl`
|
||||
|
||||
**Tier: 3**
|
||||
|
||||
Target for cross-compiling Linux user-mode applications targeting the Hexagon
|
||||
DSP architecture.
|
||||
|
||||
| Target | Descriptions |
|
||||
| ------------------------ | ----------------------------------------- |
|
||||
| hexagon-unknown-linux-musl | Hexagon 32-bit Linux |
|
||||
|
||||
## Target maintainers
|
||||
|
||||
- [Brian Cain](https://github.com/androm3da), `bcain@quicinc.com`
|
||||
|
||||
## Requirements
|
||||
The target is cross-compiled. This target supports `std`. By default, code
|
||||
generated with this target should run on Hexagon DSP hardware.
|
||||
|
||||
- `-Ctarget-cpu=hexagonv73` adds support for instructions defined up to Hexagon V73.
|
||||
|
||||
Binaries can be run using QEMU user emulation. On Debian-based systems, it should be
|
||||
sufficient to install the package `qemu-user-static` to be able to run simple static
|
||||
binaries:
|
||||
|
||||
```text
|
||||
# apt install qemu-user-static
|
||||
# qemu-hexagon-static ./hello
|
||||
```
|
||||
|
||||
In order to build linux programs with Rust, you will require a linker capable
|
||||
of targeting hexagon. You can use `clang`/`lld` from the [hexagon toolchain
|
||||
using exclusively public open source repos](https://github.com/quic/toolchain_for_hexagon/releases).
|
||||
|
||||
Also included in that toolchain is the C library that can be used when creating
|
||||
dynamically linked executables.
|
||||
|
||||
```text
|
||||
# /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello
|
||||
```
|
||||
|
||||
## Building the target
|
||||
Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this
|
||||
target.
|
||||
|
||||
Therefore, you can build Rust with support for the target by adding it to the
|
||||
target list in `config.toml`, a sample configuration is shown below.
|
||||
|
||||
```toml
|
||||
[build]
|
||||
target = [ "hexagon-unknown-linux-musl"]
|
||||
|
||||
[target.hexagon-unknown-linux-musl]
|
||||
|
||||
cc = "hexagon-unknown-linux-musl-clang"
|
||||
cxx = "hexagon-unknown-linux-musl-clang++"
|
||||
linker = "hexagon-unknown-linux-musl-clang"
|
||||
ar = "hexagon-unknown-linux-musl-ar"
|
||||
ranlib = "hexagon-unknown-linux-musl-ranlib"
|
||||
musl-root = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
|
||||
llvm-libunwind = 'in-tree'
|
||||
qemu-rootfs = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
Currently there is no support to run the rustc test suite for this target.
|
||||
|
||||
|
||||
## Building Rust programs
|
||||
|
||||
Download and install the hexagon open source toolchain from https://github.com/quic/toolchain_for_hexagon/releases
|
||||
|
||||
The following `.cargo/config` is needed inside any project directory to build
|
||||
for the Hexagon Linux target:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
target = "hexagon-unknown-linux-musl"
|
||||
|
||||
[target.hexagon-unknown-linux-musl]
|
||||
linker = "hexagon-unknown-linux-musl-clang"
|
||||
ar = "hexagon-unknown-linux-musl-ar"
|
||||
runner = "qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
|
||||
```
|
||||
|
||||
Edit the "runner" in `.cargo/config` to point to the path to your toolchain's
|
||||
C library.
|
||||
|
||||
```text
|
||||
...
|
||||
runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
|
||||
...
|
||||
```
|
||||
|
||||
Build/run your rust program with `qemu-hexagon` in your `PATH`:
|
||||
|
||||
```text
|
||||
export PATH=/path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH
|
||||
cargo run -Zbuild-std -Zbuild-std-features=llvm-libunwind
|
||||
```
|
@ -41,6 +41,8 @@ target = ["<target for your host>", "hexagon-unknown-none-elf"]
|
||||
cc = "hexagon-unknown-none-elf-clang"
|
||||
cxx = "hexagon-unknown-none-elf-clang++"
|
||||
linker = "hexagon-unknown-none-elf-clang"
|
||||
ranlib = "hexagon-unknown-none-elf-ranlib"
|
||||
ar = "hexagon-unknown-none-elf-ar"
|
||||
llvm-libunwind = 'in-tree'
|
||||
```
|
||||
|
||||
@ -142,7 +144,7 @@ ${cc} --target=hexagon-unknown-none-elf -o testit \
|
||||
${g0_lib_path}/init.o \
|
||||
-L${sdk_libs}/${q6_arch}/ \
|
||||
-L${sdk_libs}/ \
|
||||
testit.c \
|
||||
wrap.c \
|
||||
target/hexagon-unknown-none-elf/${build_cfg}/libdemo1_hexagon.rlib \
|
||||
target/hexagon-unknown-none-elf/${build_cfg}/deps/libcore-*.rlib \
|
||||
target/hexagon-unknown-none-elf/${build_cfg}/deps/libcompiler_builtins-*.rlib \
|
||||
@ -217,7 +219,18 @@ fn rust_eh_personality() {}
|
||||
|
||||
```
|
||||
|
||||
Next, save the script below as `build.sh` and edit it to suit your
|
||||
Next, create a C program as an entry point, save the content below as
|
||||
`wrap.c`:
|
||||
|
||||
```C
|
||||
int hello();
|
||||
|
||||
int main() {
|
||||
hello();
|
||||
}
|
||||
```
|
||||
|
||||
Then, save the script below as `build.sh` and edit it to suit your
|
||||
environment. The script below will build a shared object against the QuRT
|
||||
RTOS which is suitable for emulation or on-device testing when loaded via
|
||||
the fastrpc-shell.
|
||||
@ -248,7 +261,7 @@ ${cc} --target=hexagon-unknown-none-elf -o testit.so \
|
||||
-Wl,--wrap=realloc \
|
||||
-Wl,--wrap=memalign \
|
||||
-m${q6_arch} \
|
||||
testit.c \
|
||||
wrap.c \
|
||||
target/hexagon-unknown-none-elf/${build_cfg}/libdemo2_hexagon.rlib \
|
||||
target/hexagon-unknown-none-elf/${build_cfg}/deps/libcore-*.rlib \
|
||||
target/hexagon-unknown-none-elf/${build_cfg}/deps/libcompiler_builtins-*.rlib \
|
||||
|
@ -136,6 +136,7 @@ fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool {
|
||||
match (class1, class2) {
|
||||
(Some(c1), Some(c2)) => c1.is_equal_to(c2),
|
||||
(Some(Class::Ident(_)), None) | (None, Some(Class::Ident(_))) => true,
|
||||
(Some(Class::Macro(_)), _) => false,
|
||||
(Some(_), None) | (None, Some(_)) => text.trim().is_empty(),
|
||||
(None, None) => true,
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
<span class="macro">macro_rules! </span>bar {
|
||||
<span class="macro">macro_rules!</span> bar {
|
||||
(<span class="macro-nonterminal">$foo</span>:tt) => {};
|
||||
}
|
||||
</code></pre>
|
||||
|
@ -2,7 +2,5 @@
|
||||
//@no-rustfix
|
||||
fn f2() -> impl Sized { && 3.14159265358979323846E }
|
||||
//~^ ERROR: expected at least one digit in exponent
|
||||
//~| ERROR: long literal lacking separators
|
||||
//~| NOTE: `-D clippy::unreadable-literal` implied by `-D warnings`
|
||||
|
||||
fn main() {}
|
||||
|
@ -4,14 +4,5 @@ error: expected at least one digit in exponent
|
||||
LL | fn f2() -> impl Sized { && 3.14159265358979323846E }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: long literal lacking separators
|
||||
--> tests/ui/crashes/ice-10912.rs:3:28
|
||||
|
|
||||
LL | fn f2() -> impl Sized { && 3.14159265358979323846E }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider: `3.141_592_653_589_793_238_46`
|
||||
|
|
||||
= note: `-D clippy::unreadable-literal` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::unreadable_literal)]`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
struct Foo(isize, isize, isize, isize);
|
||||
|
||||
pub fn main() {
|
||||
let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5);
|
||||
match [5, 5, 5, 5] {
|
||||
[..] => { }
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
|
||||
--> tests/ui/crashes/unreachable-array-or-slice.rs:4:9
|
||||
|
|
||||
LL | let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5);
|
||||
| ^^^^ `Self` is only available in impls, traits, and type definitions
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
@ -1 +1 @@
|
||||
d3d145ea1cae47ad392173f890577788117da3d9
|
||||
1a1876c9790f168fb51afa335a7ba3e6fc267d75
|
||||
|
@ -68,7 +68,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
||||
queries: &'tcx rustc_interface::Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
queries.global_ctxt().unwrap().enter(|tcx| {
|
||||
if tcx.sess.dcx().has_errors().is_some() {
|
||||
if tcx.sess.dcx().has_errors_or_delayed_bugs().is_some() {
|
||||
tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
|
||||
}
|
||||
|
||||
|
16
src/tools/miri/tests/fail/rustc-error2.rs
Normal file
16
src/tools/miri/tests/fail/rustc-error2.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Regression test for https://github.com/rust-lang/rust/issues/121508.
|
||||
struct Struct<T>(T);
|
||||
|
||||
impl<T> std::ops::Deref for Struct<T> {
|
||||
type Target = dyn Fn(T);
|
||||
fn deref(&self) -> &assert_mem_uninitialized_valid::Target {
|
||||
//~^ERROR: undeclared crate or module
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let f = Struct(Default::default());
|
||||
f(0);
|
||||
f(0);
|
||||
}
|
9
src/tools/miri/tests/fail/rustc-error2.stderr
Normal file
9
src/tools/miri/tests/fail/rustc-error2.stderr
Normal file
@ -0,0 +1,9 @@
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `assert_mem_uninitialized_valid`
|
||||
--> $DIR/rustc-error2.rs:LL:CC
|
||||
|
|
||||
LL | fn deref(&self) -> &assert_mem_uninitialized_valid::Target {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared crate or module `assert_mem_uninitialized_valid`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
@ -109,7 +109,7 @@ fn format_project<T: FormatHandler>(
|
||||
let main_file = input.file_name();
|
||||
let input_is_stdin = main_file == FileName::Stdin;
|
||||
|
||||
let mut parse_session = ParseSess::new(config)?;
|
||||
let parse_session = ParseSess::new(config)?;
|
||||
if config.skip_children() && parse_session.ignore_file(&main_file) {
|
||||
return Ok(FormatReport::new());
|
||||
}
|
||||
@ -118,20 +118,11 @@ fn format_project<T: FormatHandler>(
|
||||
let mut report = FormatReport::new();
|
||||
let directory_ownership = input.to_directory_ownership();
|
||||
|
||||
// rustfmt doesn't use `run_compiler` like other tools, so it must emit any
|
||||
// stashed diagnostics itself, otherwise the `DiagCtxt` will assert when
|
||||
// dropped. The final result here combines the parsing result and the
|
||||
// `emit_stashed_diagnostics` result.
|
||||
let parse_res = Parser::parse_crate(input, &parse_session);
|
||||
let stashed_res = parse_session.emit_stashed_diagnostics();
|
||||
let krate = match (parse_res, stashed_res) {
|
||||
(Ok(krate), None) => krate,
|
||||
(parse_res, _) => {
|
||||
// Surface parse error via Session (errors are merged there from report).
|
||||
let forbid_verbose = match parse_res {
|
||||
Err(e) if e != ParserError::ParsePanicError => true,
|
||||
_ => input_is_stdin,
|
||||
};
|
||||
let krate = match Parser::parse_crate(input, &parse_session) {
|
||||
Ok(krate) => krate,
|
||||
// Surface parse error via Session (errors are merged there from report)
|
||||
Err(e) => {
|
||||
let forbid_verbose = input_is_stdin || e != ParserError::ParsePanicError;
|
||||
should_emit_verbose(forbid_verbose, config, || {
|
||||
eprintln!("The Rust parser panicked");
|
||||
});
|
||||
|
@ -5,9 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
|
||||
use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter};
|
||||
use rustc_errors::translation::Translate;
|
||||
use rustc_errors::{
|
||||
ColorConfig, Diag, DiagCtxt, DiagInner, ErrorGuaranteed, Level as DiagnosticLevel,
|
||||
};
|
||||
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
|
||||
use rustc_session::parse::ParseSess as RawParseSess;
|
||||
use rustc_span::{
|
||||
source_map::{FilePathMapping, SourceMap},
|
||||
@ -233,10 +231,6 @@ impl ParseSess {
|
||||
self.ignore_path_set.as_ref().is_match(path)
|
||||
}
|
||||
|
||||
pub(crate) fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||
self.parse_sess.dcx.emit_stashed_diagnostics()
|
||||
}
|
||||
|
||||
pub(crate) fn set_silent_emitter(&mut self) {
|
||||
self.parse_sess.dcx = DiagCtxt::new(silent_emitter());
|
||||
}
|
||||
|
@ -18,6 +18,13 @@ extern {
|
||||
#[repr(transparent)]
|
||||
pub struct Type3(i32);
|
||||
|
||||
#[cfi_encoding = "i"]
|
||||
pub struct Type4(i32);
|
||||
|
||||
#[cfi_encoding = "j"]
|
||||
#[repr(transparent)]
|
||||
pub struct Type5(u32);
|
||||
|
||||
pub fn foo0(_: Type1) { }
|
||||
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo1(_: Type1, _: Type1) { }
|
||||
@ -36,6 +43,18 @@ pub fn foo7(_: *mut Type3, _: *mut Type3) { }
|
||||
// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo8(_: *mut Type3, _: *mut Type3, _: *mut Type3) { }
|
||||
// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo9(_: Type4) { }
|
||||
// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo10(_: Type4, _: Type4) { }
|
||||
// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo11(_: Type4, _: Type4, _: Type4) { }
|
||||
// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo12(_: Type5) { }
|
||||
// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo13(_: Type5, _: Type5) { }
|
||||
// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
pub fn foo14(_: Type5, _: Type5, _: Type5) { }
|
||||
// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
|
||||
|
||||
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFv3FooE"}
|
||||
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFv3FooS_E"}
|
||||
@ -46,3 +65,9 @@ pub fn foo8(_: *mut Type3, _: *mut Type3, _: *mut Type3) { }
|
||||
// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvP3BazE"}
|
||||
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvP3BazS0_E"}
|
||||
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvP3BazS0_S0_E"}
|
||||
// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFviE"}
|
||||
// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFviiE"}
|
||||
// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFviiiE"}
|
||||
// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvjE"}
|
||||
// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvjjE"}
|
||||
// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvjjjE"}
|
||||
|
@ -12,4 +12,4 @@ pub unsafe fn crc32sse(v: u8) -> u32 {
|
||||
_mm_crc32_u8(out, v)
|
||||
}
|
||||
|
||||
// CHECK: attributes #0 {{.*"target-features"="\+sse4.2,\+crc32"}}
|
||||
// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32"}}
|
||||
|
@ -18,7 +18,7 @@ help: to apply to the crate, use an inner attribute
|
||||
LL | #![doc(cfg_hide(doc))]
|
||||
| +
|
||||
|
||||
error: `#[doc(cfg_hide(...)]` takes a list of attributes
|
||||
error: `#[doc(cfg_hide(...))]` takes a list of attributes
|
||||
--> $DIR/doc_cfg_hide.rs:4:8
|
||||
|
|
||||
LL | #![doc(cfg_hide = "test")]
|
||||
@ -27,7 +27,7 @@ LL | #![doc(cfg_hide = "test")]
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730>
|
||||
|
||||
error: `#[doc(cfg_hide(...)]` takes a list of attributes
|
||||
error: `#[doc(cfg_hide(...))]` takes a list of attributes
|
||||
--> $DIR/doc_cfg_hide.rs:6:8
|
||||
|
|
||||
LL | #![doc(cfg_hide)]
|
||||
|
29
tests/rustdoc/source-code-highlight.rs
Normal file
29
tests/rustdoc/source-code-highlight.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// We need this option to be enabled for the `foo` macro declaration to ensure
|
||||
// that the link on the ident is not including whitespace characters.
|
||||
|
||||
//@ compile-flags: -Zunstable-options --generate-link-to-definition
|
||||
#![crate_name = "foo"]
|
||||
|
||||
// @has 'src/foo/source-code-highlight.rs.html'
|
||||
|
||||
// @hasraw - '<a href="../../foo/macro.foo.html">foo</a>'
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
() => {}
|
||||
}
|
||||
|
||||
// @hasraw - '<span class="macro">foo!</span>'
|
||||
foo! {}
|
||||
|
||||
// @hasraw - '<a href="../../foo/fn.f.html">f</a>'
|
||||
#[rustfmt::skip]
|
||||
pub fn f () {}
|
||||
// @hasraw - '<a href="../../foo/struct.Bar.html">Bar</a>'
|
||||
// @hasraw - '<a href="../../foo/struct.Bar.html">Bar</a>'
|
||||
// @hasraw - '<a href="{{channel}}/std/primitive.u32.html">u32</a>'
|
||||
#[rustfmt::skip]
|
||||
pub struct Bar ( u32 );
|
||||
// @hasraw - '<a href="../../foo/enum.Foo.html">Foo</a>'
|
||||
pub enum Foo {
|
||||
A,
|
||||
}
|
@ -8,11 +8,15 @@ extern crate block_on;
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
let x = async || {};
|
||||
|
||||
async fn needs_async_fn_once(x: impl async FnOnce()) {
|
||||
x().await;
|
||||
}
|
||||
needs_async_fn_once(x).await;
|
||||
|
||||
needs_async_fn_once(async || {}).await;
|
||||
|
||||
needs_async_fn_once(|| async {}).await;
|
||||
|
||||
async fn foo() {}
|
||||
needs_async_fn_once(foo).await;
|
||||
});
|
||||
}
|
||||
|
24
tests/ui/async-await/async-closures/is-fn.rs
Normal file
24
tests/ui/async-await/async-closures/is-fn.rs
Normal file
@ -0,0 +1,24 @@
|
||||
//@ aux-build:block-on.rs
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
// Check that closures that don't capture any state may implement `Fn`.
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
async fn call_once<F: Future>(x: impl FnOnce(&'static str) -> F) -> F::Output {
|
||||
x("hello, world").await
|
||||
}
|
||||
call_once(async |x: &'static str| {
|
||||
println!("hello, {x}");
|
||||
}).await
|
||||
});
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
|
||||
#![feature(async_closure)]
|
||||
#![feature(async_closure, async_fn_traits)]
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
@ -15,7 +15,11 @@ fn main() {
|
||||
c().await;
|
||||
c().await;
|
||||
|
||||
fn is_static<T: 'static>(_: T) {}
|
||||
is_static(c);
|
||||
fn is_static<T: 'static>(_: &T) {}
|
||||
is_static(&c);
|
||||
|
||||
// Check that `<{async fn} as AsyncFnOnce>::CallOnceFuture` owns its captures.
|
||||
fn call_once<F: async FnOnce()>(f: F) -> F::CallOnceFuture { f() }
|
||||
is_static(&call_once(c));
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
//@ aux-build:block-on.rs
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
@ -8,11 +10,11 @@ use std::future::Future;
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
struct NoCopy;
|
||||
// Check that async closures always implement `FnOnce`
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
async fn call_once<F: Future>(x: impl Fn(&'static str) -> F) -> F::Output {
|
||||
async fn call_once<F: Future>(x: impl FnOnce(&'static str) -> F) -> F::Output {
|
||||
x("hello, world").await
|
||||
}
|
||||
call_once(async |x: &'static str| {
|
||||
|
@ -2,8 +2,6 @@
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
|
||||
// check that `&{async-closure}` implements `AsyncFn`.
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
extern crate block_on;
|
||||
@ -13,6 +11,15 @@ struct NoCopy;
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
async fn call_once(x: impl async Fn()) { x().await }
|
||||
call_once(&async || {}).await
|
||||
|
||||
// check that `&{async-closure}` implements `async Fn`.
|
||||
call_once(&async || {}).await;
|
||||
|
||||
// check that `&{closure}` implements `async Fn`.
|
||||
call_once(&|| async {}).await;
|
||||
|
||||
// check that `&fndef` implements `async Fn`.
|
||||
async fn foo() {}
|
||||
call_once(&foo).await;
|
||||
});
|
||||
}
|
||||
|
31
tests/ui/async-await/async-fn/higher-ranked-async-fn.rs
Normal file
31
tests/ui/async-await/async-fn/higher-ranked-async-fn.rs
Normal file
@ -0,0 +1,31 @@
|
||||
//@ aux-build:block-on.rs
|
||||
//@ edition:2018
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ build-pass (since it ICEs during mono)
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
async fn f(arg: &i32) {}
|
||||
|
||||
async fn func<F>(f: F)
|
||||
where
|
||||
F: async for<'a> Fn(&'a i32),
|
||||
{
|
||||
let x: i32 = 0;
|
||||
f(&x).await;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
// Function
|
||||
func(f).await;
|
||||
|
||||
// Regular closure (doesn't capture)
|
||||
func(|x: &i32| async {});
|
||||
});
|
||||
}
|
12
tests/ui/async-await/async-fn/project.rs
Normal file
12
tests/ui/async-await/async-fn/project.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//@ edition:2018
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ check-pass
|
||||
|
||||
#![feature(async_closure, unboxed_closures, async_fn_traits)]
|
||||
|
||||
fn project<F: async Fn<()>>(_: F) -> Option<F::Output> { None }
|
||||
|
||||
fn main() {
|
||||
let x: Option<i32> = project(|| async { 1i32 });
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻²
|
||||
//~^ ERROR expected at least one digit in exponent
|
||||
//~| ERROR unknown start of token: \u{2212}
|
||||
//~| ERROR cannot subtract `{integer}` from `{float}`
|
||||
|
||||
fn main() {}
|
||||
|
@ -15,24 +15,5 @@ help: Unicode character '−' (Minus Sign) looks like '-' (Minus/Hyphen), but it
|
||||
LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e-11; // m³⋅kg⁻¹⋅s⁻²
|
||||
| ~
|
||||
|
||||
error[E0277]: cannot subtract `{integer}` from `{float}`
|
||||
--> $DIR/issue-49746-unicode-confusable-in-float-literal-expt.rs:1:53
|
||||
|
|
||||
LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻²
|
||||
| ^ no implementation for `{float} - {integer}`
|
||||
|
|
||||
= help: the trait `Sub<{integer}>` is not implemented for `{float}`
|
||||
= help: the following other types implement trait `Sub<Rhs>`:
|
||||
<isize as Sub>
|
||||
<isize as Sub<&isize>>
|
||||
<i8 as Sub>
|
||||
<i8 as Sub<&i8>>
|
||||
<i16 as Sub>
|
||||
<i16 as Sub<&i16>>
|
||||
<i32 as Sub>
|
||||
<i32 as Sub<&i32>>
|
||||
and 48 others
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user