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:
bors 2024-02-29 19:19:46 +00:00
commit 14e162861b
119 changed files with 2331 additions and 1262 deletions

View File

@ -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",

View File

@ -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 {

View File

@ -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.

View File

@ -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.

View File

@ -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,

View File

@ -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| {

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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)]

View File

@ -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>,

View File

@ -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();
}
}

View File

@ -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 {

View File

@ -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();
});
}
}

View File

@ -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()`

View File

@ -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()

View File

@ -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,

View File

@ -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
}

View File

@ -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);

View File

@ -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);

View File

@ -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),

View File

@ -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 {

View File

@ -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

View File

@ -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.

View File

@ -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 } => {

View File

@ -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 },

View File

@ -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]

View File

@ -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))
}
}
}

View File

@ -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)
}

View File

@ -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")]

View File

@ -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();
}
}
}
}

View File

@ -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);

View File

@ -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`.
///

View File

@ -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)
}

View File

@ -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(

View File

@ -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],

View File

@ -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

View File

@ -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()
};

View File

@ -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!(

View File

@ -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]

View File

@ -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()
}
}

View File

@ -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:

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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(),

View File

@ -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(),

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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,

View File

@ -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(

View File

@ -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"),

View File

@ -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)]

View File

@ -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())
}
}

View File

@ -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 }

View File

@ -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

View File

@ -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
///
/// ```

View File

@ -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
///
/// ```

View File

@ -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> {

View File

@ -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")]

View File

@ -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;

View File

@ -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

View 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 Rusts 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))
}

View File

@ -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;

View File

@ -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 Rusts 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))
}

View File

@ -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> {

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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)]

View File

@ -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)

View File

@ -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

View 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
```

View File

@ -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 \

View File

@ -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,
}

View File

@ -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) =&gt; {};
}
</code></pre>

View File

@ -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() {}

View File

@ -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

View File

@ -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] {
[..] => { }
}
}

View File

@ -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`.

View File

@ -1 +1 @@
d3d145ea1cae47ad392173f890577788117da3d9
1a1876c9790f168fb51afa335a7ba3e6fc267d75

View File

@ -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");
}

View 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);
}

View 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`.

View File

@ -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");
});

View File

@ -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());
}

View File

@ -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"}

View File

@ -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"}}

View File

@ -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)]

View 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,
}

View File

@ -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;
});
}

View 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
});
}

View File

@ -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));
});
}

View File

@ -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| {

View File

@ -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;
});
}

View 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 {});
});
}

View 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 });
}

View File

@ -1,6 +1,5 @@
const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e11; // 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() {}

View File

@ -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.674e11; // 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