From 84bb0f07e6c3e920db567ff95a5f8365cf042c75 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 3 May 2025 19:39:13 +0200 Subject: [PATCH 01/21] std: stop using TLS in signal handler TLS is not async-signal-safe, making its use in the signal handler used to detect stack overflows unsound (c.f. #133698). POSIX however lists two thread-specific identifiers that can be obtained in a signal handler: the current `pthread_t` and the address of `errno`. Since `pthread_equal` is not AS-safe, `pthread_t` should be considered opaque, so for our purposes, `&errno` is the only option. This however works nicely: we can use the address as a key into a map that stores information for each thread. This PR uses a `BTreeMap` protected by a spin lock to hold the guard page address and thread name and thus fixes #133698. --- .../std/src/sys/pal/unix/stack_overflow.rs | 92 +++++++------ .../pal/unix/stack_overflow/thread_info.rs | 129 ++++++++++++++++++ 2 files changed, 183 insertions(+), 38 deletions(-) create mode 100644 library/std/src/sys/pal/unix/stack_overflow/thread_info.rs diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 8bf6d8335159b..804353178aaca 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -25,6 +25,18 @@ impl Drop for Handler { } } +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", +))] +mod thread_info; + #[cfg(any( target_os = "linux", target_os = "freebsd", @@ -46,22 +58,13 @@ mod imp { use libc::{mmap64, mprotect, munmap}; use super::Handler; - use crate::cell::Cell; + use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::{io, mem, ptr, thread}; - - // We use a TLS variable to store the address of the guard page. While TLS - // variables are not guaranteed to be signal-safe, this works out in practice - // since we make sure to write to the variable before the signal stack is - // installed, thereby ensuring that the variable is always allocated when - // the signal handler is called. - thread_local! { - // FIXME: use `Range` once that implements `Copy`. - static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; - } + use crate::thread::with_current_name; + use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -93,29 +96,35 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let (start, end) = GUARD.get(); // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. - let addr = unsafe { (*info).si_addr().addr() }; + let fault_addr = unsafe { (*info).si_addr().addr() }; + + // `with_current_info` expects that the process aborts after it is + // called. If the signal was not caused by a memory access, this might + // not be true. We detect this by noticing that the `si_addr` field is + // zero if the signal is synthetic. + if fault_addr != 0 { + with_current_info(|thread_info| { + // If the faulting address is within the guard page, then we print a + // message saying so and abort. + if let Some(thread_info) = thread_info + && thread_info.guard_page_range.contains(&fault_addr) + { + let name = thread_info.thread_name.as_deref().unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + rtabort!("stack overflow"); + } + }) + } - // If the faulting address is within the guard page, then we print a - // message saying so and abort. - if start <= addr && addr < end { - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); - }); + // Unregister ourselves by reverting back to the default behavior. + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; + action.sa_sigaction = SIG_DFL; + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; - rtabort!("stack overflow"); - } else { - // Unregister ourselves by reverting back to the default behavior. - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" - let mut action: sigaction = unsafe { mem::zeroed() }; - action.sa_sigaction = SIG_DFL; - // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction - unsafe { sigaction(signum, &action, ptr::null_mut()) }; - - // See comment above for why this function returns. - } + // See comment above for why this function returns. } static PAGE_SIZE: Atomic = AtomicUsize::new(0); @@ -128,9 +137,7 @@ mod imp { pub unsafe fn init() { PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { install_main_guard().unwrap_or(0..0) }; - GUARD.set((guard.start, guard.end)); + let mut guard_page_range = unsafe { install_main_guard() }; // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" let mut action: sigaction = unsafe { mem::zeroed() }; @@ -145,7 +152,13 @@ mod imp { let handler = unsafe { make_handler(true) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); + + if let Some(guard_page_range) = guard_page_range.take() { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } + action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; // SAFETY: only overriding signals if the default is set @@ -214,9 +227,10 @@ mod imp { } if !main_thread { - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { current_guard() }.unwrap_or(0..0); - GUARD.set((guard.start, guard.end)); + if let Some(guard_page_range) = unsafe { current_guard() } { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } // SAFETY: assuming stack_t is zero-initializable @@ -261,6 +275,8 @@ mod imp { // a mapping that started one page earlier, so walk back a page and unmap from there. unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } + + delete_current_info(); } /// Modern kernels on modern hardware can have dynamic signal stack sizes. diff --git a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs new file mode 100644 index 0000000000000..e81429b98a6c7 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs @@ -0,0 +1,129 @@ +//! TLS, but async-signal-safe. +//! +//! Unfortunately, because thread local storage isn't async-signal-safe, we +//! cannot soundly use it in our stack overflow handler. While this works +//! without problems on most platforms, it can lead to undefined behaviour +//! on others (such as GNU/Linux). Luckily, the POSIX specification documents +//! two thread-specific values that can be accessed in asynchronous signal +//! handlers: the value of `pthread_self()` and the address of `errno`. As +//! `pthread_t` is an opaque platform-specific type, we use the address of +//! `errno` here. As it is thread-specific and does not change over the +//! lifetime of a thread, we can use `&errno` as a key for a `BTreeMap` +//! that stores thread-specific data. +//! +//! Concurrent access to this map is synchronized by two locks – an outer +//! [`Mutex`] and an inner spin lock that also remembers the identity of +//! the lock owner: +//! * The spin lock is the primary means of synchronization: since it only +//! uses native atomics, it can be soundly used inside the signal handle +//! as opposed to [`Mutex`], which might not be async-signal-safe. +//! * The [`Mutex`] prevents busy-waiting in the setup logic, as all accesses +//! there are performed with the [`Mutex`] held, which makes the spin-lock +//! redundant in the common case. +//! * Finally, by using the `errno` address as the locked value of the spin +//! lock, we can detect cases where a SIGSEGV occurred while the thread +//! info is being modified. + +use crate::collections::BTreeMap; +use crate::hint::spin_loop; +use crate::ops::Range; +use crate::sync::Mutex; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sys::os::errno_location; + +pub struct ThreadInfo { + pub guard_page_range: Range, + pub thread_name: Option>, +} + +static LOCK: Mutex<()> = Mutex::new(()); +static SPIN_LOCK: AtomicUsize = AtomicUsize::new(0); +// This uses a `BTreeMap` instead of a hashmap since it supports constant +// initialization and automatically reduces the amount of memory used when +// items are removed. +static mut THREAD_INFO: BTreeMap = BTreeMap::new(); + +struct UnlockOnDrop; + +impl Drop for UnlockOnDrop { + fn drop(&mut self) { + SPIN_LOCK.store(0, Ordering::Release); + } +} + +/// Get the current thread's information, if available. +/// +/// Calling this function might freeze other threads if they attempt to modify +/// their thread information. Thus, the caller should ensure that the process +/// is aborted shortly after this function is called. +/// +/// This function is guaranteed to be async-signal-safe if `f` is too. +pub fn with_current_info(f: impl FnOnce(Option<&ThreadInfo>) -> R) -> R { + let this = errno_location().addr(); + let mut attempt = 0; + let _guard = loop { + // If we are just spinning endlessly, it's very likely that the thread + // modifying the thread info map has a lower priority than us and will + // not continue until we stop running. Just give up in that case. + if attempt == 10_000_000 { + rtprintpanic!("deadlock in SIGSEGV handler"); + return f(None); + } + + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => break UnlockOnDrop, + Err(owner) if owner == this => { + rtabort!("a thread received SIGSEGV while modifying its stack overflow information") + } + // Spin until the lock can be acquired – there is nothing better to + // do. This is unfortunately a priority hole, but a stack overflow + // is a fatal error anyway. + Err(_) => { + spin_loop(); + attempt += 1; + } + } + }; + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &*(&raw const THREAD_INFO) }; + f(thread_info.get(&this)) +} + +fn spin_lock_in_setup(this: usize) -> UnlockOnDrop { + loop { + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return UnlockOnDrop, + Err(owner) if owner == this => { + unreachable!("the thread info setup logic isn't recursive") + } + // This function is always called with the outer lock held, + // meaning the only time locking can fail is if another thread has + // encountered a stack overflow. Since that will abort the process, + // we just stop the current thread until that time. We use `pause` + // instead of spinning to avoid priority inversion. + // SAFETY: this doesn't have any safety preconditions. + Err(_) => drop(unsafe { libc::pause() }), + } + } +} + +pub fn set_current_info(guard_page_range: Range, thread_name: Option>) { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.insert(this, ThreadInfo { guard_page_range, thread_name }); +} + +pub fn delete_current_info() { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.remove(&this); +} From 3007433e2c65e289aa02155debf500d836539890 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 7 May 2025 00:52:58 -0700 Subject: [PATCH 02/21] add a type alias for the pattern bindings stack I'll be modifying it in future commits, so I think it's cleanest to abstract it out. Possibly a newtype would be ideal, but for now this is least disruptive. --- compiler/rustc_resolve/src/late.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index faee0e7dd5ff9..1da1bfeaf607c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -111,6 +111,17 @@ enum PatBoundCtx { Or, } +/// Tracks bindings resolved within a pattern. This serves two purposes: +/// +/// - This tracks when identifiers are bound multiple times within a pattern. In a product context, +/// this is an error. In an or-pattern, this lets us reuse the same resolution for each instance. +/// See `fresh_binding` and `resolve_pattern_inner` for more information. +/// +/// - The guard expression of a guard pattern may use bindings from within the guard pattern, but +/// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the +/// subpattern to construct the scope for the guard. +type PatternBindings = SmallVec<[(PatBoundCtx, FxHashSet); 1]>; + /// Does this the item (from the item rib scope) allow generic parameters? #[derive(Copy, Clone, Debug)] pub(crate) enum HasGenericParams { @@ -3857,7 +3868,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, pat: &'ast Pat, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) { // We walk the pattern before declaring the pattern's inner bindings, // so that we avoid resolving a literal expression to a binding defined @@ -3892,7 +3903,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, pat: &Pat, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) { // Visit all direct subpatterns of this pattern. pat.walk(&mut |pat| { @@ -3988,7 +3999,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ident: Ident, pat_id: NodeId, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) -> Res { // Add the binding to the local ribs, if it doesn't already exist in the bindings map. // (We must not add it if it's in the bindings map because that breaks the assumptions From 30a0ac66dbb15ee8dd3951499b34df48e1d758a0 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 7 May 2025 02:59:18 -0700 Subject: [PATCH 03/21] delay introducing pattern bindings into scope This splits introduction of bindings into scope (`apply_pattern_bindings`) apart from manipulation of the pattern's binding map (`fresh_binding`). By delaying the latter, we can keep bindings from appearing in-scope in guards. Since `fresh_binding` is now specifically for manipulating a pattern's bindings map, this commit also inlines a use of `fresh_binding` that was only adding to the innermost rib. --- compiler/rustc_resolve/src/late.rs | 82 +++++++++++++++++++----------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1da1bfeaf607c..aa211a8f3c292 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -120,7 +120,9 @@ enum PatBoundCtx { /// - The guard expression of a guard pattern may use bindings from within the guard pattern, but /// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the /// subpattern to construct the scope for the guard. -type PatternBindings = SmallVec<[(PatBoundCtx, FxHashSet); 1]>; +/// +/// Each identifier must map to at most one distinct [`Res`]. +type PatternBindings = SmallVec<[(PatBoundCtx, FxIndexMap); 1]>; /// Does this the item (from the item rib scope) allow generic parameters? #[derive(Copy, Clone, Debug)] @@ -2308,7 +2310,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_fn_params( &mut self, has_self: bool, - inputs: impl Iterator, &'ast Ty)>, + inputs: impl Iterator, &'ast Ty)> + Clone, ) -> Result, Vec)> { enum Elision { /// We have not found any candidate. @@ -2330,15 +2332,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let mut parameter_info = Vec::new(); let mut all_candidates = Vec::new(); + // Resolve and apply bindings first so diagnostics can see if they're used in types. let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - for (index, (pat, ty)) in inputs.enumerate() { - debug!(?pat, ?ty); + for (pat, _) in inputs.clone() { + debug!("resolving bindings in pat = {pat:?}"); self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { if let Some(pat) = pat { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } }); + } + self.apply_pattern_bindings(bindings); + for (index, (pat, ty)) in inputs.enumerate() { + debug!("resolving type for pat = {pat:?}, ty = {ty:?}"); // Record elision candidates only for this parameter. debug_assert_matches!(self.lifetime_elision_candidates, None); self.lifetime_elision_candidates = Some(Default::default()); @@ -3626,16 +3633,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.visit_path(&delegation.path, delegation.id); let Some(body) = &delegation.body else { return }; self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - let span = delegation.path.segments.last().unwrap().ident.span; - this.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); + let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules()); + let res = Res::Local(delegation.id); + this.innermost_rib_bindings(ValueNS).insert(ident, res); this.visit_block(body); }); } @@ -3646,6 +3647,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { for Param { pat, .. } in params { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } + this.apply_pattern_bindings(bindings); }); for Param { ty, .. } in params { self.visit_ty(ty); @@ -3862,8 +3864,27 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; self.resolve_pattern(pat, pat_src, &mut bindings); + self.apply_pattern_bindings(bindings); } + /// Apply the bindings from a pattern to the innermost rib of the current scope. + fn apply_pattern_bindings(&mut self, mut pat_bindings: PatternBindings) { + let rib_bindings = self.innermost_rib_bindings(ValueNS); + let Some((_, pat_bindings)) = pat_bindings.pop() else { + bug!("tried applying nonexistent bindings from pattern"); + }; + + if rib_bindings.is_empty() { + // Often, such as for match arms, the bindings are introduced into a new rib. + // In this case, we can move the bindings over directly. + *rib_bindings = pat_bindings; + } else { + rib_bindings.extend(pat_bindings); + } + } + + /// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce + /// the bindings into scope. fn resolve_pattern( &mut self, pat: &'ast Pat, @@ -4001,18 +4022,15 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { pat_src: PatternSource, bindings: &mut PatternBindings, ) -> Res { - // Add the binding to the local ribs, if it doesn't already exist in the bindings map. + // Add the binding to the bindings map, if it doesn't already exist. // (We must not add it if it's in the bindings map because that breaks the assumptions // later passes make about or-patterns.) let ident = ident.normalize_to_macro_rules(); - let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident)); // Already bound in a product pattern? e.g. `(a, a)` which is not allowed. - let already_bound_and = bound_iter.clone().any(|(ctx, _)| *ctx == PatBoundCtx::Product); - // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`. - // This is *required* for consistency which is checked later. - let already_bound_or = bound_iter.any(|(ctx, _)| *ctx == PatBoundCtx::Or); - + let already_bound_and = bindings + .iter() + .any(|(ctx, map)| *ctx == PatBoundCtx::Product && map.contains_key(&ident)); if already_bound_and { // Overlap in a product pattern somewhere; report an error. use ResolutionError::*; @@ -4025,19 +4043,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.report_error(ident.span, error(ident)); } - // Record as bound. - bindings.last_mut().unwrap().1.insert(ident); - - if already_bound_or { + // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`. + // This is *required* for consistency which is checked later. + let already_bound_or = bindings + .iter() + .find_map(|(ctx, map)| if *ctx == PatBoundCtx::Or { map.get(&ident) } else { None }); + let res = if let Some(&res) = already_bound_or { // `Variant1(a) | Variant2(a)`, ok // Reuse definition from the first `a`. - self.innermost_rib_bindings(ValueNS)[&ident] - } else { - // A completely fresh binding is added to the set. - let res = Res::Local(pat_id); - self.innermost_rib_bindings(ValueNS).insert(ident, res); res - } + } else { + // A completely fresh binding is added to the map. + Res::Local(pat_id) + }; + + // Record as bound. + bindings.last_mut().unwrap().1.insert(ident, res); + res } fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut FxIndexMap { From ba80d820e5b4fb975f12eabac19e2a9c264a3afb Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Sun, 11 May 2025 20:58:52 +0700 Subject: [PATCH 04/21] Return value of coroutine_layout fn changed to Result with LayoutError --- compiler/rustc_middle/src/ty/mod.rs | 31 ++++++++++++++------ compiler/rustc_mir_transform/src/validate.rs | 4 +-- compiler/rustc_ty_utils/src/layout.rs | 4 +-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dda0faa3afedd..4ba5d8f3ce33b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -120,6 +120,7 @@ use crate::ty; use crate::ty::codec::{TyDecoder, TyEncoder}; pub use crate::ty::diagnostics::*; use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::LayoutError; use crate::ty::util::Discr; use crate::ty::walk::TypeWalker; @@ -1877,6 +1878,11 @@ impl<'tcx> TyCtxt<'tcx> { self.def_kind(trait_def_id) == DefKind::TraitAlias } + /// Arena-alloc of LayoutError for coroutine layout + fn layout_error(self, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> { + self.arena.alloc(err) + } + /// Returns layout of a non-async-drop coroutine. Layout might be unavailable if the /// coroutine is tainted by errors. /// @@ -1885,12 +1891,14 @@ impl<'tcx> TyCtxt<'tcx> { fn ordinary_coroutine_layout( self, def_id: DefId, - coroutine_kind_ty: Ty<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + args: GenericArgsRef<'tcx>, + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let coroutine_kind_ty = args.as_coroutine().kind_ty(); let mir = self.optimized_mir(def_id); + let ty = || Ty::new_coroutine(self, def_id, args); // Regular coroutine if coroutine_kind_ty.is_unit() { - mir.coroutine_layout_raw() + mir.coroutine_layout_raw().ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { // If we have a `Coroutine` that comes from an coroutine-closure, // then it may be a by-move or by-ref body. @@ -1904,6 +1912,7 @@ impl<'tcx> TyCtxt<'tcx> { // a by-ref coroutine. if identity_kind_ty == coroutine_kind_ty { mir.coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { assert_matches!(coroutine_kind_ty.to_opt_closure_kind(), Some(ClosureKind::FnOnce)); assert_matches!( @@ -1912,6 +1921,7 @@ impl<'tcx> TyCtxt<'tcx> { ); self.optimized_mir(self.coroutine_by_move_body_def_id(def_id)) .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } } } @@ -1923,12 +1933,15 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let ty = || Ty::new_coroutine(self, def_id, args); if args[0].has_placeholders() || args[0].has_non_region_param() { - return None; + return Err(self.layout_error(LayoutError::TooGeneric(ty()))); } let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args)); - self.mir_shims(instance).coroutine_layout_raw() + self.mir_shims(instance) + .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } /// Returns layout of a coroutine. Layout might be unavailable if the @@ -1937,7 +1950,7 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { if self.is_async_drop_in_place_coroutine(def_id) { // layout of `async_drop_in_place::{closure}` in case, // when T is a coroutine, contains this internal coroutine's ptr in upvars @@ -1959,12 +1972,12 @@ impl<'tcx> TyCtxt<'tcx> { variant_source_info, storage_conflicts: BitMatrix::new(0, 0), }; - return Some(self.arena.alloc(proxy_layout)); + return Ok(self.arena.alloc(proxy_layout)); } else { self.async_drop_coroutine_layout(def_id, args) } } else { - self.ordinary_coroutine_layout(def_id, args.as_coroutine().kind_ty()) + self.ordinary_coroutine_layout(def_id, args) } } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index f541a32cd2645..f8d1629b0e266 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -752,7 +752,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let layout = if def_id == self.caller_body.source.def_id() { self.caller_body .coroutine_layout_raw() - .or_else(|| self.tcx.coroutine_layout(def_id, args)) + .or_else(|| self.tcx.coroutine_layout(def_id, args).ok()) } else if self.tcx.needs_coroutine_by_move_body_def_id(def_id) && let ty::ClosureKind::FnOnce = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() @@ -762,7 +762,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // Same if this is the by-move body of a coroutine-closure. self.caller_body.coroutine_layout_raw() } else { - self.tcx.coroutine_layout(def_id, args) + self.tcx.coroutine_layout(def_id, args).ok() }; let Some(layout) = layout else { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 908fcb14cb2fc..ad57555bd24d3 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -492,9 +492,7 @@ fn layout_of_uncached<'tcx>( ty::Coroutine(def_id, args) => { use rustc_middle::ty::layout::PrimitiveExt as _; - let Some(info) = tcx.coroutine_layout(def_id, args) else { - return Err(error(cx, LayoutError::Unknown(ty))); - }; + let info = tcx.coroutine_layout(def_id, args)?; let local_layouts = info .field_tys From 2cdbd69abfbd5a1f0c5b9ceee2393f1a87181d97 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 5 May 2025 11:05:29 +0200 Subject: [PATCH 05/21] disable the stack overflow handler on miri --- .../std/src/sys/pal/unix/stack_overflow.rs | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 804353178aaca..a3be2cdf738f5 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -25,27 +25,36 @@ impl Drop for Handler { } } -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ), ))] mod thread_info; -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", +// miri doesn't model signals nor stack overflows and this code has some +// synchronization properties that we don't want to expose to user code, +// hence we disable it on miri. +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ) ))] mod imp { use libc::{ @@ -606,17 +615,20 @@ mod imp { // usually have fewer qualms about forwards compatibility, since the runtime // is shipped with the OS): // -#[cfg(not(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - target_os = "cygwin", -)))] +#[cfg(any( + miri, + not(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "cygwin", + )) +))] mod imp { pub unsafe fn init() {} From 9cacafdd1ae7b23692d9894c6110b7f8f404b4bb Mon Sep 17 00:00:00 2001 From: klensy Date: Sat, 17 May 2025 15:25:32 +0300 Subject: [PATCH 06/21] compiler & tools: bump windows crate to dedupe versions --- Cargo.lock | 71 +++++------------------ compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_data_structures/Cargo.toml | 2 +- compiler/rustc_driver_impl/Cargo.toml | 2 +- compiler/rustc_errors/Cargo.toml | 2 +- compiler/rustc_session/Cargo.toml | 2 +- src/tools/compiletest/Cargo.toml | 2 +- src/tools/tidy/src/deps.rs | 3 + 8 files changed, 23 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1fddefcb0144..59f7f3dda8fcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -738,7 +738,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows 0.59.0", + "windows", ] [[package]] @@ -1587,7 +1587,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core", ] [[package]] @@ -3493,7 +3493,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.219.2", - "windows 0.59.0", + "windows", ] [[package]] @@ -3552,7 +3552,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3615,7 +3615,7 @@ dependencies = [ "shlex", "stable_mir", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3670,7 +3670,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -4415,7 +4415,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -5102,7 +5102,7 @@ dependencies = [ "libc", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -6002,16 +6002,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" -dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", -] - [[package]] name = "windows" version = "0.61.1" @@ -6019,7 +6009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -6042,20 +6032,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" -dependencies = [ - "windows-implement 0.59.0", - "windows-interface", - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core", ] [[package]] @@ -6064,11 +6041,11 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", + "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -6077,21 +6054,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -6126,7 +6092,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] @@ -6139,15 +6105,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.0" diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 97eebffd1fe8e..d4c8ab80a3313 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -58,5 +58,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index f48c73b13b961..f6a0201161851 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -38,7 +38,7 @@ features = ["nightly"] # for may_dangle version = "0.12" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 9da4f2dbc2730..1971d06aad64d 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -60,7 +60,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index b11793c190a14..82e7468211db0 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -33,7 +33,7 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 63772a3222210..f0ee19e3c6774 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -33,7 +33,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 93f7b1cb7cf2c..3b544d8b82817 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -37,7 +37,7 @@ libc = "0.2" miow = "0.6" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 4195258af8854..9bb06c31c5c06 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -429,10 +429,13 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "winapi-util", "winapi-x86_64-pc-windows-gnu", "windows", + "windows-collections", "windows-core", + "windows-future", "windows-implement", "windows-interface", "windows-link", + "windows-numerics", "windows-result", "windows-strings", "windows-sys", From 40940e1294338080f88c87f0bde86e75ca603a27 Mon Sep 17 00:00:00 2001 From: klensy Date: Sat, 17 May 2025 15:31:02 +0300 Subject: [PATCH 07/21] bootstrap: bump windows too --- src/bootstrap/Cargo.lock | 71 +++++----------------------------- src/bootstrap/Cargo.toml | 2 +- src/bootstrap/src/bin/rustc.rs | 2 +- src/bootstrap/src/utils/job.rs | 1 - 4 files changed, 11 insertions(+), 65 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index ff63b8c62d3a0..d10d2d9bf8ce8 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -64,7 +64,7 @@ dependencies = [ "tracing-subscriber", "tracing-tree", "walkdir", - "windows 0.57.0", + "windows", "xz2", ] @@ -703,7 +703,7 @@ dependencies = [ "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -916,16 +916,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets", -] - [[package]] name = "windows" version = "0.61.1" @@ -933,7 +923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -945,19 +935,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets", + "windows-core", ] [[package]] @@ -966,10 +944,10 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link", - "windows-result 0.3.2", + "windows-result", "windows-strings", ] @@ -979,21 +957,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -1005,17 +972,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.1" @@ -1039,19 +995,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-result" version = "0.3.2" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e34de924cc18a..9652d18f1a6c5 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -70,7 +70,7 @@ tracing-tree = { version = "0.4.0", optional = true } version = "1.0.0" [target.'cfg(windows)'.dependencies.windows] -version = "0.57" +version = "0.61" features = [ "Win32_Foundation", "Win32_Security", diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 85c682a46c5d1..374884d8a9a07 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -342,7 +342,7 @@ fn format_rusage_data(child: Child) -> Option { use windows::Win32::System::Threading::GetProcessTimes; use windows::Win32::System::Time::FileTimeToSystemTime; - let handle = HANDLE(child.as_raw_handle() as isize); + let handle = HANDLE(child.as_raw_handle()); let mut user_filetime = Default::default(); let mut user_time = Default::default(); diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index 4949518de79b0..887deb41ca8bc 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -66,7 +66,6 @@ mod for_windows { // Enable the Windows Error Reporting dialog which msys disables, // so we can JIT debug rustc let mode = SetErrorMode(THREAD_ERROR_MODE::default()); - let mode = THREAD_ERROR_MODE(mode); SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX); // Create a new job object for us to use From cd22c1b88385b65d657dedd520abaf1a0aaf7e3f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 20:25:59 +0200 Subject: [PATCH 08/21] determine later whether an explicit reg was used --- compiler/rustc_builtin_macros/src/asm.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 3e8ddb8abd43f..58aba3e590323 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -135,9 +135,8 @@ pub fn parse_asm_args<'a>( None }; - let mut explicit_reg = false; let op = if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); @@ -145,15 +144,15 @@ pub fn parse_asm_args<'a>( let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); @@ -167,7 +166,7 @@ pub fn parse_asm_args<'a>( ast::InlineAsmOperand::InOut { reg, expr, late: false } } } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); @@ -223,6 +222,8 @@ pub fn parse_asm_args<'a>( p.unexpected_any()? }; + let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + allow_templates = false; let span = span_start.to(p.prev_token.span); let slot = args.operands.len(); @@ -231,6 +232,7 @@ pub fn parse_asm_args<'a>( // Validate the order of named, positional & explicit register operands and // clobber_abi/options. We do this at the end once we have the full span // of the argument available. + if explicit_reg { if name.is_some() { dcx.emit_err(errors::AsmExplicitRegisterName { span }); @@ -478,15 +480,11 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, Ok(()) } -fn parse_reg<'a>( - p: &mut Parser<'a>, - explicit_reg: &mut bool, -) -> PResult<'a, ast::InlineAsmRegOrRegClass> { +fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { p.expect(exp!(OpenParen))?; let result = match p.token.uninterpolate().kind { token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { - *explicit_reg = true; ast::InlineAsmRegOrRegClass::Reg(symbol) } _ => { From 5af9652e5c733eb14c9a28b92c7a2608cbf7ea59 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 20:35:39 +0200 Subject: [PATCH 09/21] extract operand parser --- compiler/rustc_builtin_macros/src/asm.rs | 176 ++++++++++++----------- 1 file changed, 93 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 58aba3e590323..3afa2d3dd8eec 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -69,6 +69,76 @@ fn parse_args<'a>( parse_asm_args(&mut p, sp, asm_macro) } +fn parse_asm_operand<'a>( + p: &mut Parser<'a>, + asm_macro: AsmMacro, +) -> PResult<'a, Option> { + let dcx = p.dcx(); + + Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + ast::InlineAsmOperand::In { reg, expr } + } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: false } + } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: true } + } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: false } + } + } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: true } + } + } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } + } else if p.eat_keyword(exp!(Const)) { + let anon_const = p.parse_expr_anon_const()?; + ast::InlineAsmOperand::Const { anon_const } + } else if p.eat_keyword(exp!(Sym)) { + let expr = p.parse_expr()?; + let ast::ExprKind::Path(qself, path) = &expr.kind else { + let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); + return Err(err); + }; + let sym = + ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; + ast::InlineAsmOperand::Sym { sym } + } else { + return Ok(None); + })) +} + // Primarily public for rustfmt consumption. // Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` pub fn parse_asm_args<'a>( @@ -135,91 +205,31 @@ pub fn parse_asm_args<'a>( None }; - let op = if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: false } - } - } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: true } - } - } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { - let block = p.parse_block()?; - ast::InlineAsmOperand::Label { block } - } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const()?; - ast::InlineAsmOperand::Const { anon_const } - } else if p.eat_keyword(exp!(Sym)) { - let expr = p.parse_expr()?; - let ast::ExprKind::Path(qself, path) = &expr.kind else { - let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); - return Err(err); - }; - let sym = ast::InlineAsmSym { - id: ast::DUMMY_NODE_ID, - qself: qself.clone(), - path: path.clone(), - }; - ast::InlineAsmOperand::Sym { sym } - } else if allow_templates { - let template = p.parse_expr()?; - // If it can't possibly expand to a string, provide diagnostics here to include other - // things it could have been. - match template.kind { - ast::ExprKind::Lit(token_lit) - if matches!( - token_lit.kind, - token::LitKind::Str | token::LitKind::StrRaw(_) - ) => {} - ast::ExprKind::MacCall(..) => {} - _ => { - let err = dcx.create_err(errors::AsmExpectedOther { - span: template.span, - is_inline_asm: matches!(asm_macro, AsmMacro::Asm), - }); - return Err(err); + let Some(op) = parse_asm_operand(p, asm_macro)? else { + if allow_templates { + let template = p.parse_expr()?; + // If it can't possibly expand to a string, provide diagnostics here to include other + // things it could have been. + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } } + args.templates.push(template); + continue; + } else { + p.unexpected_any()? } - args.templates.push(template); - continue; - } else { - p.unexpected_any()? }; let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); From 4320e6f474ba042f235327c4afe0f27cd454a565 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 18 May 2025 04:54:45 +0000 Subject: [PATCH 10/21] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 79abbfaeaf116..8b98fe3c4fc3e 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a69bc17fb8026bdc0d24bb1896ff95f0eba1da4e +ac17c3486c6fdfbb0c3c18b99f3d8dfbff625d29 From 84506c64fff3e636443022abd84c82ba6542dae0 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 18 May 2025 05:02:46 +0000 Subject: [PATCH 11/21] fmt --- src/tools/miri/src/helpers.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index d2a7351662393..8e7c9edfcc078 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -933,7 +933,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Check that the calling convention is what we expect. - fn check_callconv<'a>(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: Conv) -> InterpResult<'a, ()> { + fn check_callconv<'a>( + &self, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: Conv, + ) -> InterpResult<'a, ()> { if fn_abi.conv != exp_abi { throw_ub_format!( "calling a function with calling convention {exp_abi} using caller calling convention {}", From cf7caded0bc2d0d84f4c84cc6dc108c16d23f134 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 17 Apr 2025 03:28:16 +0530 Subject: [PATCH 12/21] Stabilize `avx512_target_feature` --- compiler/rustc_feature/src/accepted.rs | 2 + compiler/rustc_feature/src/unstable.rs | 1 - compiler/rustc_target/src/target_features.rs | 44 +++++++++---------- library/core/src/lib.rs | 2 +- library/stdarch | 2 +- .../using-target-feature-unstable.rs | 4 +- tests/ui/target-feature/gate.rs | 3 +- tests/ui/target-feature/gate.stderr | 10 ++--- tests/ui/target-feature/unstable-feature.rs | 4 +- .../ui/target-feature/unstable-feature.stderr | 2 +- 10 files changed, 37 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 809d1630ddec1..820af9ac84b2c 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -82,6 +82,8 @@ declare_features! ( (accepted, attr_literals, "1.30.0", Some(34981)), /// Allows overloading augmented assignment operations like `a += b`. (accepted, augmented_assignments, "1.8.0", Some(28235)), + /// Allows using `avx512*` target features. + (accepted, avx512_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), /// Allows mixing bind-by-move in patterns and references to those identifiers in guards. (accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287)), /// Allows bindings in the subpattern of a binding pattern. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8fb10736539a3..6cdcf451f37e3 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -318,7 +318,6 @@ declare_features! ( (unstable, aarch64_ver_target_feature, "1.27.0", Some(44839)), (unstable, apx_target_feature, "1.88.0", Some(139284)), (unstable, arm_target_feature, "1.27.0", Some(44839)), - (unstable, avx512_target_feature, "1.27.0", Some(44839)), (unstable, bpf_target_feature, "1.54.0", Some(44839)), (unstable, csky_target_feature, "1.73.0", Some(44839)), (unstable, ermsb_target_feature, "1.49.0", Some(44839)), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5428aa4cf7085..99b04ac272009 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -416,25 +416,25 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ), ("avx10.2", Unstable(sym::avx10_target_feature), &["avx10.1"]), ("avx2", Stable, &["avx"]), - ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avx512bf16", Stable, &["avx512bw"]), + ("avx512bitalg", Stable, &["avx512bw"]), + ("avx512bw", Stable, &["avx512f"]), + ("avx512cd", Stable, &["avx512f"]), + ("avx512dq", Stable, &["avx512f"]), + ("avx512f", Stable, &["avx2", "fma", "f16c"]), + ("avx512fp16", Stable, &["avx512bw"]), + ("avx512ifma", Stable, &["avx512f"]), + ("avx512vbmi", Stable, &["avx512bw"]), + ("avx512vbmi2", Stable, &["avx512bw"]), + ("avx512vl", Stable, &["avx512f"]), + ("avx512vnni", Stable, &["avx512f"]), + ("avx512vp2intersect", Stable, &["avx512f"]), + ("avx512vpopcntdq", Stable, &["avx512f"]), + ("avxifma", Stable, &["avx2"]), + ("avxneconvert", Stable, &["avx2"]), + ("avxvnni", Stable, &["avx2"]), + ("avxvnniint16", Stable, &["avx2"]), + ("avxvnniint8", Stable, &["avx2"]), ("bmi1", Stable, &[]), ("bmi2", Stable, &[]), ("cmpxchg16b", Stable, &[]), @@ -442,7 +442,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f16c", Stable, &["avx"]), ("fma", Stable, &["avx"]), ("fxsr", Stable, &[]), - ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("gfni", Stable, &["sse2"]), ("kl", Unstable(sym::keylocker_x86), &["sse2"]), ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), ("lzcnt", Stable, &[]), @@ -469,8 +469,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), ("ssse3", Stable, &["sse3"]), ("tbm", Unstable(sym::tbm_target_feature), &[]), - ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("vaes", Stable, &["avx2", "aes"]), + ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]), ("widekl", Unstable(sym::keylocker_x86), &["kl"]), ("x87", Unstable(sym::x87_target_feature), &[]), ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 64a7ec8906b6b..b3c7e2c1d2da2 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -188,9 +188,9 @@ // // Target features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(avx512_target_feature))] #![feature(aarch64_unstable_target_feature)] #![feature(arm_target_feature)] -#![feature(avx512_target_feature)] #![feature(hexagon_target_feature)] #![feature(keylocker_x86)] #![feature(loongarch_target_feature)] diff --git a/library/stdarch b/library/stdarch index f1c1839c0deb9..1dfaa4db24797 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit f1c1839c0deb985a9f98cbd6b38a6d43f2df6157 +Subproject commit 1dfaa4db2479753a46a3e90f2c3c89d89d0b21f1 diff --git a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs index 2682028936c19..15bcfdd9076e0 100644 --- a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs +++ b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs @@ -1,5 +1,5 @@ -#![feature(avx512_target_feature)] +#![feature(x87_target_feature)] #[inline] -#[target_feature(enable = "avx512ifma")] +#[target_feature(enable = "x87")] pub unsafe fn foo() {} diff --git a/tests/ui/target-feature/gate.rs b/tests/ui/target-feature/gate.rs index 14fdad02f5665..9244a98d82fdf 100644 --- a/tests/ui/target-feature/gate.rs +++ b/tests/ui/target-feature/gate.rs @@ -2,7 +2,6 @@ // // gate-test-sse4a_target_feature // gate-test-powerpc_target_feature -// gate-test-avx512_target_feature // gate-test-tbm_target_feature // gate-test-arm_target_feature // gate-test-hexagon_target_feature @@ -27,7 +26,7 @@ // gate-test-x87_target_feature // gate-test-m68k_target_feature -#[target_feature(enable = "avx512bw")] +#[target_feature(enable = "x87")] //~^ ERROR: currently unstable unsafe fn foo() {} diff --git a/tests/ui/target-feature/gate.stderr b/tests/ui/target-feature/gate.stderr index fa876893848f7..32d60ce438227 100644 --- a/tests/ui/target-feature/gate.stderr +++ b/tests/ui/target-feature/gate.stderr @@ -1,11 +1,11 @@ -error[E0658]: the target feature `avx512bw` is currently unstable - --> $DIR/gate.rs:30:18 +error[E0658]: the target feature `x87` is currently unstable + --> $DIR/gate.rs:29:18 | -LL | #[target_feature(enable = "avx512bw")] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "x87")] + | ^^^^^^^^^^^^^^ | = note: see issue #44839 for more information - = help: add `#![feature(avx512_target_feature)]` to the crate attributes to enable + = help: add `#![feature(x87_target_feature)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/unstable-feature.rs b/tests/ui/target-feature/unstable-feature.rs index f62c4dd938a0b..a79ad4696033a 100644 --- a/tests/ui/target-feature/unstable-feature.rs +++ b/tests/ui/target-feature/unstable-feature.rs @@ -1,8 +1,8 @@ -//@ compile-flags: -Ctarget-feature=+vaes --crate-type=rlib --target=x86_64-unknown-linux-gnu +//@ compile-flags: -Ctarget-feature=+x87 --crate-type=rlib --target=x86_64-unknown-linux-gnu //@ build-pass //@ needs-llvm-components: x86 #![feature(no_core)] #![no_core] -//~? WARN unstable feature specified for `-Ctarget-feature`: `vaes` +//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/unstable-feature.stderr b/tests/ui/target-feature/unstable-feature.stderr index d34544c5c2773..309b64afd9224 100644 --- a/tests/ui/target-feature/unstable-feature.stderr +++ b/tests/ui/target-feature/unstable-feature.stderr @@ -1,4 +1,4 @@ -warning: unstable feature specified for `-Ctarget-feature`: `vaes` +warning: unstable feature specified for `-Ctarget-feature`: `x87` | = note: this feature is not stably supported; its behavior can change in the future From 2898680ebdb6608deccf40a93c1fa062b588b365 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 17 Apr 2025 03:31:00 +0530 Subject: [PATCH 13/21] Remove uses of `#[feature(avx512_target_feature)]` --- .../pass/shims/x86/intrinsics-x86-aes-vaes.rs | 2 +- .../pass/shims/x86/intrinsics-x86-avx512.rs | 1 - .../pass/shims/x86/intrinsics-x86-gfni.rs | 1 - .../shims/x86/intrinsics-x86-vpclmulqdq.rs | 1 - .../homogenous-floats-target-feature-mixup.rs | 2 -- tests/ui/abi/simd-abi-checks-avx.rs | 1 - tests/ui/abi/simd-abi-checks-avx.stderr | 24 +++++++++---------- .../ui/asm/x86_64/evex512-implicit-feature.rs | 1 - tests/ui/asm/x86_64/target-feature-attr.rs | 2 -- .../ui/asm/x86_64/target-feature-attr.stderr | 8 +++---- tests/ui/simd/target-feature-mixup.rs | 1 - 11 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 47f086f7340d7..48633c0a7fe66 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -2,7 +2,7 @@ //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f -#![feature(avx512_target_feature, stdarch_x86_avx512)] +#![feature(stdarch_x86_avx512)] use core::mem::transmute; #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index db59306389016..0ec2f679d80ba 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -2,7 +2,6 @@ //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs index 882b5e3f79524..b58d68e2ef9ea 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -6,7 +6,6 @@ // be interpreted as integers; signedness does not make sense for them, but // __mXXXi happens to be defined in terms of signed integers. #![allow(overflowing_literals)] -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs index 68964728e4ea4..c7c9eb5e3951f 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs @@ -8,7 +8,6 @@ // be interpreted as integers; signedness does not make sense for them, but // __mXXXi happens to be defined in terms of signed integers. #![allow(overflowing_literals)] -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_arch = "x86")] diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs index 22b9b029a4049..2c78b794a8d74 100644 --- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs +++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs @@ -7,8 +7,6 @@ //@ run-pass //@ needs-subprocess -#![feature(avx512_target_feature)] - #![allow(overflowing_literals)] #![allow(unused_variables)] diff --git a/tests/ui/abi/simd-abi-checks-avx.rs b/tests/ui/abi/simd-abi-checks-avx.rs index 772512702ece1..7432381d15b72 100644 --- a/tests/ui/abi/simd-abi-checks-avx.rs +++ b/tests/ui/abi/simd-abi-checks-avx.rs @@ -2,7 +2,6 @@ //@ build-fail //@ compile-flags: -C target-feature=-avx -#![feature(avx512_target_feature)] #![feature(portable_simd)] #![feature(simd_ffi)] #![allow(improper_ctypes_definitions)] diff --git a/tests/ui/abi/simd-abi-checks-avx.stderr b/tests/ui/abi/simd-abi-checks-avx.stderr index 48db30bf45371..7489ca0194600 100644 --- a/tests/ui/abi/simd-abi-checks-avx.stderr +++ b/tests/ui/abi/simd-abi-checks-avx.stderr @@ -1,5 +1,5 @@ error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:60:11 + --> $DIR/simd-abi-checks-avx.rs:59:11 | LL | f(g()); | ^^^ function called here @@ -7,7 +7,7 @@ LL | f(g()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:60:9 + --> $DIR/simd-abi-checks-avx.rs:59:9 | LL | f(g()); | ^^^^^^ function called here @@ -15,7 +15,7 @@ LL | f(g()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:66:14 + --> $DIR/simd-abi-checks-avx.rs:65:14 | LL | gavx(favx()); | ^^^^^^ function called here @@ -23,7 +23,7 @@ LL | gavx(favx()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:66:9 + --> $DIR/simd-abi-checks-avx.rs:65:9 | LL | gavx(favx()); | ^^^^^^^^^^^^ function called here @@ -31,7 +31,7 @@ LL | gavx(favx()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:76:19 + --> $DIR/simd-abi-checks-avx.rs:75:19 | LL | w(Wrapper(g())); | ^^^ function called here @@ -39,7 +39,7 @@ LL | w(Wrapper(g())); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:76:9 + --> $DIR/simd-abi-checks-avx.rs:75:9 | LL | w(Wrapper(g())); | ^^^^^^^^^^^^^^^ function called here @@ -47,7 +47,7 @@ LL | w(Wrapper(g())); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:90:9 + --> $DIR/simd-abi-checks-avx.rs:89:9 | LL | some_extern(); | ^^^^^^^^^^^^^ function called here @@ -55,7 +55,7 @@ LL | some_extern(); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:25:1 + --> $DIR/simd-abi-checks-avx.rs:24:1 | LL | unsafe extern "C" fn g() -> __m256 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -63,7 +63,7 @@ LL | unsafe extern "C" fn g() -> __m256 { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:20:1 + --> $DIR/simd-abi-checks-avx.rs:19:1 | LL | unsafe extern "C" fn f(_: __m256) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -71,7 +71,7 @@ LL | unsafe extern "C" fn f(_: __m256) { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:15:1 + --> $DIR/simd-abi-checks-avx.rs:14:1 | LL | unsafe extern "C" fn w(_: Wrapper) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -79,7 +79,7 @@ LL | unsafe extern "C" fn w(_: Wrapper) { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:54:8 + --> $DIR/simd-abi-checks-avx.rs:53:8 | LL | || g() | ^^^ function called here @@ -87,7 +87,7 @@ LL | || g() = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) note: the above error was encountered while instantiating `fn in_closure::{closure#0}` - --> $DIR/simd-abi-checks-avx.rs:82:9 + --> $DIR/simd-abi-checks-avx.rs:81:9 | LL | in_closure()(); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/x86_64/evex512-implicit-feature.rs b/tests/ui/asm/x86_64/evex512-implicit-feature.rs index ea2acd424e2c6..ec5da7c7fa488 100644 --- a/tests/ui/asm/x86_64/evex512-implicit-feature.rs +++ b/tests/ui/asm/x86_64/evex512-implicit-feature.rs @@ -2,7 +2,6 @@ //@ only-x86_64 //@ compile-flags: --crate-type=lib -C target-cpu=skylake -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] use std::arch::x86_64::*; diff --git a/tests/ui/asm/x86_64/target-feature-attr.rs b/tests/ui/asm/x86_64/target-feature-attr.rs index 6bb277ac16598..2193117caeb6d 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.rs +++ b/tests/ui/asm/x86_64/target-feature-attr.rs @@ -2,8 +2,6 @@ // Set the base cpu explicitly, in case the default has been changed. //@ compile-flags: -C target-cpu=x86-64 -#![feature(avx512_target_feature)] - use std::arch::asm; #[target_feature(enable = "avx")] diff --git a/tests/ui/asm/x86_64/target-feature-attr.stderr b/tests/ui/asm/x86_64/target-feature-attr.stderr index 0cd571ac8cce8..c852726ee7ff8 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.stderr +++ b/tests/ui/asm/x86_64/target-feature-attr.stderr @@ -1,23 +1,23 @@ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:40 + --> $DIR/target-feature-attr.rs:18:40 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:55 + --> $DIR/target-feature-attr.rs:18:55 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:70 + --> $DIR/target-feature-attr.rs:18:70 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^^^^^^ error: register class `kreg` requires at least one of the following target features: avx512bw, avx512f - --> $DIR/target-feature-attr.rs:35:23 + --> $DIR/target-feature-attr.rs:33:23 | LL | asm!("/* {0} */", in(kreg) x); | ^^^^^^^^^^ diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs index 2786251c7951b..77f1861524870 100644 --- a/tests/ui/simd/target-feature-mixup.rs +++ b/tests/ui/simd/target-feature-mixup.rs @@ -7,7 +7,6 @@ //@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590) #![feature(repr_simd, target_feature, cfg_target_feature)] -#![feature(avx512_target_feature)] use std::process::{Command, ExitStatus}; use std::env; From 48093fd6959835408dc15d878ad7ae0d9c469a42 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 18 May 2025 08:10:09 +0200 Subject: [PATCH 14/21] attempt to make doctests work properly with old and new cargo --- src/tools/miri/cargo-miri/src/phases.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 171e157789d7f..4857f62cd3a1c 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -176,8 +176,11 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // Set `--target-dir` to `miri` inside the original target directory. let target_dir = get_target_dir(&metadata); cmd.arg("--target-dir").arg(target_dir); - // Enable cross-target doctests (for consistency between different cargo versions). - cmd.arg("-Zdoctest-xcompile"); + // Only when running in x.py (where we are running with beta cargo): set `RUSTC_STAGE`. + // Will have to be removed on next bootstrap bump. tag: cfg(bootstrap). + if env::var_os("RUSTC_STAGE").is_some() { + cmd.arg("-Zdoctest-xcompile"); + } // *After* we set all the flags that need setting, forward everything else. Make sure to skip // `--target-dir` (which would otherwise be set twice). From de8e305ba8feae17e5bc1281647865dc3f1deadf Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 22:43:46 +0200 Subject: [PATCH 15/21] a new parser generating the exact same error messages Co-authored-by: Travis Cross --- compiler/rustc_builtin_macros/src/asm.rs | 376 ++++++++++++++--------- 1 file changed, 226 insertions(+), 150 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 3afa2d3dd8eec..be6f6b601369f 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -4,7 +4,7 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::PResult; +use rustc_errors::{DiagCtxtHandle, PResult}; use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; use rustc_parse::exp; @@ -18,6 +18,21 @@ use {rustc_ast as ast, rustc_parse_format as parse}; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; +/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise +/// not validated at all. +pub struct RawAsmArg { + pub kind: RawAsmArgKind, + pub span: Span, +} + +pub enum RawAsmArgKind { + Template(P), + Operand(Option, ast::InlineAsmOperand), + Options(Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>), + ClobberAbi(Vec<(Symbol, Span)>), +} + +/// Validated assembly arguments, ready for macro expansion. pub struct AsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, @@ -59,16 +74,6 @@ fn eat_operand_keyword<'a>( } } -fn parse_args<'a>( - ecx: &ExtCtxt<'a>, - sp: Span, - tts: TokenStream, - asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let mut p = ecx.new_parser_from_tts(tts); - parse_asm_args(&mut p, sp, asm_macro) -} - fn parse_asm_operand<'a>( p: &mut Parser<'a>, asm_macro: AsmMacro, @@ -139,31 +144,28 @@ fn parse_asm_operand<'a>( })) } -// Primarily public for rustfmt consumption. -// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` -pub fn parse_asm_args<'a>( +// Public for rustfmt. +pub fn parse_raw_asm_args<'a>( p: &mut Parser<'a>, sp: Span, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { +) -> PResult<'a, Vec> { let dcx = p.dcx(); if p.token == token::Eof { return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); } + let mut args = Vec::new(); + let first_template = p.parse_expr()?; - let mut args = AsmArgs { - templates: vec![first_template], - operands: vec![], - named_args: Default::default(), - reg_args: Default::default(), - clobber_abis: Vec::new(), - options: ast::InlineAsmOptions::empty(), - options_spans: vec![], - }; + args.push(RawAsmArg { + span: first_template.span, + kind: RawAsmArgKind::Template(first_template), + }); let mut allow_templates = true; + while p.token != token::Eof { if !p.eat(exp!(Comma)) { if allow_templates { @@ -174,27 +176,39 @@ pub fn parse_asm_args<'a>( return Err(p.expect(exp!(Comma)).err().unwrap()); } } + + // Accept trailing commas. if p.token == token::Eof { break; - } // accept trailing commas + } + + let span_start = p.token.span; - // Parse clobber_abi + // Parse `clobber_abi`. if p.eat_keyword(exp!(ClobberAbi)) { - parse_clobber_abi(p, &mut args)?; allow_templates = false; + + args.push(RawAsmArg { + kind: RawAsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + span: span_start.to(p.prev_token.span), + }); + continue; } - // Parse options + // Parse `options`. if p.eat_keyword(exp!(Options)) { - parse_options(p, &mut args, asm_macro)?; allow_templates = false; + + args.push(RawAsmArg { + kind: RawAsmArgKind::Options(parse_options(p, asm_macro)?), + span: span_start.to(p.prev_token.span), + }); + continue; } - let span_start = p.token.span; - - // Parse operand names + // Parse operand names. let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { let (ident, _) = p.token.ident().unwrap(); p.bump(); @@ -205,60 +219,172 @@ pub fn parse_asm_args<'a>( None }; - let Some(op) = parse_asm_operand(p, asm_macro)? else { - if allow_templates { - let template = p.parse_expr()?; - // If it can't possibly expand to a string, provide diagnostics here to include other - // things it could have been. - match template.kind { - ast::ExprKind::Lit(token_lit) - if matches!( - token_lit.kind, - token::LitKind::Str | token::LitKind::StrRaw(_) - ) => {} - ast::ExprKind::MacCall(..) => {} - _ => { - let err = dcx.create_err(errors::AsmExpectedOther { - span: template.span, - is_inline_asm: matches!(asm_macro, AsmMacro::Asm), - }); - return Err(err); + if let Some(op) = parse_asm_operand(p, asm_macro)? { + allow_templates = false; + + args.push(RawAsmArg { + span: span_start.to(p.prev_token.span), + kind: RawAsmArgKind::Operand(name, op), + }); + } else if allow_templates { + let template = p.parse_expr()?; + // If it can't possibly expand to a string, provide diagnostics here to include other + // things it could have been. + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } + } + + args.push(RawAsmArg { span: template.span, kind: RawAsmArgKind::Template(template) }); + } else { + p.unexpected_any()? + } + } + + Ok(args) +} + +fn parse_args<'a>( + ecx: &ExtCtxt<'a>, + sp: Span, + tts: TokenStream, + asm_macro: AsmMacro, +) -> PResult<'a, AsmArgs> { + let mut p = ecx.new_parser_from_tts(tts); + parse_asm_args(&mut p, sp, asm_macro) +} + +// public for use in rustfmt +// FIXME: use `RawAsmArg` in the formatting code instead. +pub fn parse_asm_args<'a>( + p: &mut Parser<'a>, + sp: Span, + asm_macro: AsmMacro, +) -> PResult<'a, AsmArgs> { + let raw_args = parse_raw_asm_args(p, sp, asm_macro)?; + validate_raw_asm_args(p.dcx(), asm_macro, raw_args) +} + +pub fn validate_raw_asm_args<'a>( + dcx: DiagCtxtHandle<'a>, + asm_macro: AsmMacro, + raw_args: Vec, +) -> PResult<'a, AsmArgs> { + let mut args = AsmArgs { + templates: vec![], + operands: vec![], + named_args: Default::default(), + reg_args: Default::default(), + clobber_abis: Vec::new(), + options: ast::InlineAsmOptions::empty(), + options_spans: vec![], + }; + + let mut allow_templates = true; + + for arg in raw_args { + match arg.kind { + RawAsmArgKind::Template(template) => { + // The error for the first template is delayed. + if !allow_templates { + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } } } + args.templates.push(template); - continue; - } else { - p.unexpected_any()? } - }; + RawAsmArgKind::Operand(name, op) => { + allow_templates = false; - let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + let span = arg.span; + let slot = args.operands.len(); + args.operands.push((op, span)); - allow_templates = false; - let span = span_start.to(p.prev_token.span); - let slot = args.operands.len(); - args.operands.push((op, span)); + // Validate the order of named, positional & explicit register operands and + // clobber_abi/options. We do this at the end once we have the full span + // of the argument available. - // Validate the order of named, positional & explicit register operands and - // clobber_abi/options. We do this at the end once we have the full span - // of the argument available. + if explicit_reg { + if name.is_some() { + dcx.emit_err(errors::AsmExplicitRegisterName { span }); + } + args.reg_args.insert(slot); + } else if let Some(name) = name { + if let Some(&prev) = args.named_args.get(&name) { + dcx.emit_err(errors::AsmDuplicateArg { + span, + name, + prev: args.operands[prev].1, + }); + continue; + } + args.named_args.insert(name, slot); + } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { + let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); + let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); - if explicit_reg { - if name.is_some() { - dcx.emit_err(errors::AsmExplicitRegisterName { span }); - } - args.reg_args.insert(slot); - } else if let Some(name) = name { - if let Some(&prev) = args.named_args.get(&name) { - dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 }); - continue; + dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + } } - args.named_args.insert(name, slot); - } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); - let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); + RawAsmArgKind::Options(new_options) => { + allow_templates = false; + + for (symbol, option, span, full_span) in new_options { + if !asm_macro.is_supported_option(option) { + /* + // Tool-only output + p.dcx().emit_err(errors::AsmUnsupportedOption { + span, + symbol, + full_span, + macro_name: asm_macro.macro_name(), + }); + */ + } else if args.options.contains(option) { + // Tool-only output. + dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); + } else { + args.options |= option; + } + } - dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + args.options_spans.push(arg.span); + } + RawAsmArgKind::ClobberAbi(new_abis) => { + allow_templates = false; + + match &new_abis[..] { + // This should have errored above during parsing. + [] => unreachable!(), + [(abi, _span)] => args.clobber_abis.push((*abi, arg.span)), + _ => args.clobber_abis.extend(new_abis), + } + } } } @@ -348,61 +474,14 @@ pub fn parse_asm_args<'a>( Ok(args) } -/// Report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); -} - -/// Report an invalid option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmUnsupportedOption { - span, - symbol, - full_span, - macro_name: asm_macro.macro_name(), - }); -} - -/// Try to set the provided option in the provided `AsmArgs`. -/// If it is already set, report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the error will not point to the correct spot. -fn try_set_option<'a>( - p: &Parser<'a>, - args: &mut AsmArgs, - asm_macro: AsmMacro, - symbol: Symbol, - option: ast::InlineAsmOptions, -) { - if !asm_macro.is_supported_option(option) { - err_unsupported_option(p, asm_macro, symbol, p.prev_token.span); - } else if args.options.contains(option) { - err_duplicate_option(p, symbol, p.prev_token.span); - } else { - args.options |= option; - } -} - fn parse_options<'a>( p: &mut Parser<'a>, - args: &mut AsmArgs, asm_macro: AsmMacro, -) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +) -> PResult<'a, Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>> { p.expect(exp!(OpenParen))?; + let mut options = Vec::new(); + while !p.eat(exp!(CloseParen)) { const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ (exp!(Pure), ast::InlineAsmOptions::PURE), @@ -418,6 +497,7 @@ fn parse_options<'a>( 'blk: { for (exp, option) in OPTIONS { + // Gives a more accurate list of expected next tokens. let kw_matched = if asm_macro.is_supported_option(option) { p.eat_keyword(exp) } else { @@ -425,30 +505,39 @@ fn parse_options<'a>( }; if kw_matched { - try_set_option(p, args, asm_macro, exp.kw, option); + let span = p.prev_token.span; + let full_span = + if p.token == token::Comma { span.to(p.token.span) } else { span }; + + if !asm_macro.is_supported_option(option) { + // Tool-only output. + p.dcx().emit_err(errors::AsmUnsupportedOption { + span, + symbol: exp.kw, + full_span, + macro_name: asm_macro.macro_name(), + }); + } + + options.push((exp.kw, option, span, full_span)); break 'blk; } } - return p.unexpected(); + return p.unexpected_any(); } - // Allow trailing commas + // Allow trailing commas. if p.eat(exp!(CloseParen)) { break; } p.expect(exp!(Comma))?; } - let new_span = span_start.to(p.prev_token.span); - args.options_spans.push(new_span); - - Ok(()) + Ok(options) } -fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { p.expect(exp!(OpenParen))?; if p.eat(exp!(CloseParen)) { @@ -474,20 +563,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, p.expect(exp!(Comma))?; } - let full_span = span_start.to(p.prev_token.span); - - match &new_abis[..] { - // should have errored above during parsing - [] => unreachable!(), - [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), - abis => { - for (abi, span) in abis { - args.clobber_abis.push((*abi, *span)); - } - } - } - - Ok(()) + Ok(new_abis) } fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { From 7ec06fc3b14933b1937f11ee028e2e8f245e7857 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 30 Apr 2025 01:06:38 +0200 Subject: [PATCH 16/21] attempt to have rustfmt use the new logic apparently it doesn't really use the asm parsing at present, so this may work? --- compiler/rustc_builtin_macros/src/asm.rs | 25 ++++++++--------------- src/tools/rustfmt/src/parse/macros/asm.rs | 9 +++++--- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index be6f6b601369f..8298b77f7b1cf 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -4,7 +4,7 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::{DiagCtxtHandle, PResult}; +use rustc_errors::PResult; use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; use rustc_parse::exp; @@ -33,7 +33,7 @@ pub enum RawAsmArgKind { } /// Validated assembly arguments, ready for macro expansion. -pub struct AsmArgs { +struct AsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxIndexMap, @@ -261,26 +261,17 @@ fn parse_args<'a>( tts: TokenStream, asm_macro: AsmMacro, ) -> PResult<'a, AsmArgs> { - let mut p = ecx.new_parser_from_tts(tts); - parse_asm_args(&mut p, sp, asm_macro) + let raw_args = parse_raw_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; + validate_raw_asm_args(ecx, asm_macro, raw_args) } -// public for use in rustfmt -// FIXME: use `RawAsmArg` in the formatting code instead. -pub fn parse_asm_args<'a>( - p: &mut Parser<'a>, - sp: Span, - asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let raw_args = parse_raw_asm_args(p, sp, asm_macro)?; - validate_raw_asm_args(p.dcx(), asm_macro, raw_args) -} - -pub fn validate_raw_asm_args<'a>( - dcx: DiagCtxtHandle<'a>, +fn validate_raw_asm_args<'a>( + ecx: &ExtCtxt<'a>, asm_macro: AsmMacro, raw_args: Vec, ) -> PResult<'a, AsmArgs> { + let dcx = ecx.dcx(); + let mut args = AsmArgs { templates: vec![], operands: vec![], diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 58c8d21bd7a4c..18e3386f4f10c 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,11 +1,14 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args}; +use rustc_builtin_macros::asm::{RawAsmArg, parse_raw_asm_args}; use crate::rewrite::RewriteContext; #[allow(dead_code)] -pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { +pub(crate) fn parse_asm( + context: &RewriteContext<'_>, + mac: &ast::MacCall, +) -> Option> { let ts = mac.args.tokens.clone(); let mut parser = super::build_parser(context, ts); - parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() + parse_raw_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() } From e12d6757393035aa2de55d0f1712ef7656c60589 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 23:39:10 +0200 Subject: [PATCH 17/21] delay error for unsupported options --- compiler/rustc_builtin_macros/src/asm.rs | 16 +------ tests/ui/asm/aarch64/parse-error.rs | 2 - tests/ui/asm/aarch64/parse-error.stderr | 40 ++++++------------ tests/ui/asm/parse-error.rs | 6 +-- tests/ui/asm/parse-error.stderr | 54 +++++++++--------------- 5 files changed, 39 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 8298b77f7b1cf..d9412a3cb423a 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -347,15 +347,13 @@ fn validate_raw_asm_args<'a>( for (symbol, option, span, full_span) in new_options { if !asm_macro.is_supported_option(option) { - /* - // Tool-only output - p.dcx().emit_err(errors::AsmUnsupportedOption { + // Tool-only output. + dcx.emit_err(errors::AsmUnsupportedOption { span, symbol, full_span, macro_name: asm_macro.macro_name(), }); - */ } else if args.options.contains(option) { // Tool-only output. dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); @@ -500,16 +498,6 @@ fn parse_options<'a>( let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - if !asm_macro.is_supported_option(option) { - // Tool-only output. - p.dcx().emit_err(errors::AsmUnsupportedOption { - span, - symbol: exp.kw, - full_span, - macro_name: asm_macro.macro_name(), - }); - } - options.push((exp.kw, option, span, full_span)); break 'blk; } diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs index aa731c35dda88..35e1d037f3888 100644 --- a/tests/ui/asm/aarch64/parse-error.rs +++ b/tests/ui/asm/aarch64/parse-error.rs @@ -96,10 +96,8 @@ global_asm!("", options(FOO)); //~^ ERROR expected one of global_asm!("", options(nomem FOO)); //~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` global_asm!("", options(nomem, FOO)); //~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` global_asm!("{}", options(), const FOO); global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index b5e1169e5f6b4..45f9e7989c2e8 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -218,68 +218,56 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` LL | global_asm!("", options(FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:97:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)` or `,`, found `FOO` --> $DIR/parse-error.rs:97:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:100:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:100:32 + --> $DIR/parse-error.rs:99:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:104:29 + --> $DIR/parse-error.rs:102:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:106:33 + --> $DIR/parse-error.rs:104:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:108:34 + --> $DIR/parse-error.rs:106:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:110:19 + --> $DIR/parse-error.rs:108:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:112:28 + --> $DIR/parse-error.rs:110:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:114:30 + --> $DIR/parse-error.rs:112:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:116:35 + --> $DIR/parse-error.rs:114:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -287,7 +275,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:116:35 + --> $DIR/parse-error.rs:114:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -295,19 +283,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:119:28 + --> $DIR/parse-error.rs:117:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:121:30 + --> $DIR/parse-error.rs:119:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:123:13 + --> $DIR/parse-error.rs:121:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -315,7 +303,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:125:20 + --> $DIR/parse-error.rs:123:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -418,6 +406,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 59 previous errors +error: aborting due to 57 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs index 4d7b522f5fc5b..d135ccae12804 100644 --- a/tests/ui/asm/parse-error.rs +++ b/tests/ui/asm/parse-error.rs @@ -113,11 +113,9 @@ global_asm!("", options(FOO)); global_asm!("", options(FOO,)); //~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("", options(nomem FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)` or `,`, found `FOO` +//~^ ERROR expected one of `)` or `,`, found `FOO` global_asm!("", options(nomem, FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` +//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("{}", options(), const FOO); global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 74647372a3557..0bba1fd8d9b64 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -270,74 +270,62 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` LL | global_asm!("", options(FOO,)); | ^^^ expected one of `)`, `att_syntax`, or `raw` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:115:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)` or `,`, found `FOO` --> $DIR/parse-error.rs:115:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:118:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:118:32 + --> $DIR/parse-error.rs:117:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:122:29 + --> $DIR/parse-error.rs:120:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:124:33 + --> $DIR/parse-error.rs:122:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:126:34 + --> $DIR/parse-error.rs:124:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:128:19 + --> $DIR/parse-error.rs:126:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:130:28 + --> $DIR/parse-error.rs:128:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:132:30 + --> $DIR/parse-error.rs:130:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:134:17 + --> $DIR/parse-error.rs:132:17 | LL | global_asm!("", clobber_abi("C"), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -345,7 +333,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -353,19 +341,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:139:28 + --> $DIR/parse-error.rs:137:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:141:30 + --> $DIR/parse-error.rs:139:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:143:13 + --> $DIR/parse-error.rs:141:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -373,7 +361,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:145:20 + --> $DIR/parse-error.rs:143:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -381,37 +369,37 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: the `in` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:148:19 + --> $DIR/parse-error.rs:146:19 | LL | global_asm!("{}", in(reg)); | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `out` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:150:19 + --> $DIR/parse-error.rs:148:19 | LL | global_asm!("{}", out(reg)); | ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it error: the `lateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:152:19 + --> $DIR/parse-error.rs:150:19 | LL | global_asm!("{}", lateout(reg)); | ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:154:19 + --> $DIR/parse-error.rs:152:19 | LL | global_asm!("{}", inout(reg)); | ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inlateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:156:19 + --> $DIR/parse-error.rs:154:19 | LL | global_asm!("{}", inlateout(reg)); | ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `label` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:158:19 + --> $DIR/parse-error.rs:156:19 | LL | global_asm!("{}", label(reg)); | ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it @@ -476,6 +464,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 72 previous errors +error: aborting due to 70 previous errors For more information about this error, try `rustc --explain E0435`. From 85053d1cd1e65466e6ceae2a925d7d010d7585ea Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 17 May 2025 23:17:06 +0200 Subject: [PATCH 18/21] rename to get rid of the 'raw' concept --- compiler/rustc_builtin_macros/src/asm.rs | 127 +++++++++++----------- src/tools/rustfmt/src/parse/macros/asm.rs | 9 +- 2 files changed, 67 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index d9412a3cb423a..867a5b80f918f 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -20,12 +20,12 @@ use crate::util::{ExprToSpannedString, expr_to_spanned_string}; /// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise /// not validated at all. -pub struct RawAsmArg { - pub kind: RawAsmArgKind, +pub struct AsmArg { + pub kind: AsmArgKind, pub span: Span, } -pub enum RawAsmArgKind { +pub enum AsmArgKind { Template(P), Operand(Option, ast::InlineAsmOperand), Options(Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>), @@ -33,7 +33,7 @@ pub enum RawAsmArgKind { } /// Validated assembly arguments, ready for macro expansion. -struct AsmArgs { +struct ValidatedAsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxIndexMap, @@ -145,11 +145,11 @@ fn parse_asm_operand<'a>( } // Public for rustfmt. -pub fn parse_raw_asm_args<'a>( +pub fn parse_asm_args<'a>( p: &mut Parser<'a>, sp: Span, asm_macro: AsmMacro, -) -> PResult<'a, Vec> { +) -> PResult<'a, Vec> { let dcx = p.dcx(); if p.token == token::Eof { @@ -159,10 +159,7 @@ pub fn parse_raw_asm_args<'a>( let mut args = Vec::new(); let first_template = p.parse_expr()?; - args.push(RawAsmArg { - span: first_template.span, - kind: RawAsmArgKind::Template(first_template), - }); + args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) }); let mut allow_templates = true; @@ -188,8 +185,8 @@ pub fn parse_raw_asm_args<'a>( if p.eat_keyword(exp!(ClobberAbi)) { allow_templates = false; - args.push(RawAsmArg { - kind: RawAsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + args.push(AsmArg { + kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), span: span_start.to(p.prev_token.span), }); @@ -200,8 +197,8 @@ pub fn parse_raw_asm_args<'a>( if p.eat_keyword(exp!(Options)) { allow_templates = false; - args.push(RawAsmArg { - kind: RawAsmArgKind::Options(parse_options(p, asm_macro)?), + args.push(AsmArg { + kind: AsmArgKind::Options(parse_options(p, asm_macro)?), span: span_start.to(p.prev_token.span), }); @@ -222,9 +219,9 @@ pub fn parse_raw_asm_args<'a>( if let Some(op) = parse_asm_operand(p, asm_macro)? { allow_templates = false; - args.push(RawAsmArg { + args.push(AsmArg { span: span_start.to(p.prev_token.span), - kind: RawAsmArgKind::Operand(name, op), + kind: AsmArgKind::Operand(name, op), }); } else if allow_templates { let template = p.parse_expr()?; @@ -246,7 +243,7 @@ pub fn parse_raw_asm_args<'a>( } } - args.push(RawAsmArg { span: template.span, kind: RawAsmArgKind::Template(template) }); + args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) }); } else { p.unexpected_any()? } @@ -260,19 +257,19 @@ fn parse_args<'a>( sp: Span, tts: TokenStream, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let raw_args = parse_raw_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; - validate_raw_asm_args(ecx, asm_macro, raw_args) +) -> PResult<'a, ValidatedAsmArgs> { + let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; + validate_asm_args(ecx, asm_macro, args) } -fn validate_raw_asm_args<'a>( +fn validate_asm_args<'a>( ecx: &ExtCtxt<'a>, asm_macro: AsmMacro, - raw_args: Vec, -) -> PResult<'a, AsmArgs> { + args: Vec, +) -> PResult<'a, ValidatedAsmArgs> { let dcx = ecx.dcx(); - let mut args = AsmArgs { + let mut validated = ValidatedAsmArgs { templates: vec![], operands: vec![], named_args: Default::default(), @@ -284,9 +281,9 @@ fn validate_raw_asm_args<'a>( let mut allow_templates = true; - for arg in raw_args { + for arg in args { match arg.kind { - RawAsmArgKind::Template(template) => { + AsmArgKind::Template(template) => { // The error for the first template is delayed. if !allow_templates { match template.kind { @@ -306,15 +303,15 @@ fn validate_raw_asm_args<'a>( } } - args.templates.push(template); + validated.templates.push(template); } - RawAsmArgKind::Operand(name, op) => { + AsmArgKind::Operand(name, op) => { allow_templates = false; let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); let span = arg.span; - let slot = args.operands.len(); - args.operands.push((op, span)); + let slot = validated.operands.len(); + validated.operands.push((op, span)); // Validate the order of named, positional & explicit register operands and // clobber_abi/options. We do this at the end once we have the full span @@ -324,25 +321,27 @@ fn validate_raw_asm_args<'a>( if name.is_some() { dcx.emit_err(errors::AsmExplicitRegisterName { span }); } - args.reg_args.insert(slot); + validated.reg_args.insert(slot); } else if let Some(name) = name { - if let Some(&prev) = args.named_args.get(&name) { + if let Some(&prev) = validated.named_args.get(&name) { dcx.emit_err(errors::AsmDuplicateArg { span, name, - prev: args.operands[prev].1, + prev: validated.operands[prev].1, }); continue; } - args.named_args.insert(name, slot); - } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); - let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); + validated.named_args.insert(name, slot); + } else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() { + let named = + validated.named_args.values().map(|p| validated.operands[*p].1).collect(); + let explicit = + validated.reg_args.iter().map(|p| validated.operands[p].1).collect(); dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); } } - RawAsmArgKind::Options(new_options) => { + AsmArgKind::Options(new_options) => { allow_templates = false; for (symbol, option, span, full_span) in new_options { @@ -354,45 +353,47 @@ fn validate_raw_asm_args<'a>( full_span, macro_name: asm_macro.macro_name(), }); - } else if args.options.contains(option) { + } else if validated.options.contains(option) { // Tool-only output. dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); } else { - args.options |= option; + validated.options |= option; } } - args.options_spans.push(arg.span); + validated.options_spans.push(arg.span); } - RawAsmArgKind::ClobberAbi(new_abis) => { + AsmArgKind::ClobberAbi(new_abis) => { allow_templates = false; match &new_abis[..] { // This should have errored above during parsing. [] => unreachable!(), - [(abi, _span)] => args.clobber_abis.push((*abi, arg.span)), - _ => args.clobber_abis.extend(new_abis), + [(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)), + _ => validated.clobber_abis.extend(new_abis), } } } } - if args.options.contains(ast::InlineAsmOptions::NOMEM) - && args.options.contains(ast::InlineAsmOptions::READONLY) + if validated.options.contains(ast::InlineAsmOptions::NOMEM) + && validated.options.contains(ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && validated.options.contains(ast::InlineAsmOptions::NORETURN) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && !validated + .options + .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmPureCombine { spans }); } @@ -400,7 +401,7 @@ fn validate_raw_asm_args<'a>( let mut outputs_sp = vec![]; let mut regclass_outputs = vec![]; let mut labels_sp = vec![]; - for (op, op_sp) in &args.operands { + for (op, op_sp) in &validated.operands { match op { ast::InlineAsmOperand::Out { reg, expr, .. } | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => { @@ -423,10 +424,10 @@ fn validate_raw_asm_args<'a>( _ => {} } } - if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() }); + if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { + dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() }); } - if args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() && labels_sp.is_empty() { @@ -434,15 +435,15 @@ fn validate_raw_asm_args<'a>( // Bail out now since this is likely to confuse MIR return Err(err); } - if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { + if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { dcx.emit_err(errors::AsmMayUnwind { labels_sp }); } - if !args.clobber_abis.is_empty() { + if !validated.clobber_abis.is_empty() { match asm_macro { AsmMacro::GlobalAsm | AsmMacro::NakedAsm => { let err = dcx.create_err(errors::AsmUnsupportedClobberAbi { - spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), macro_name: asm_macro.macro_name(), }); @@ -453,14 +454,14 @@ fn validate_raw_asm_args<'a>( if !regclass_outputs.is_empty() { dcx.emit_err(errors::AsmClobberNoReg { spans: regclass_outputs, - clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), }); } } } } - Ok(args) + Ok(validated) } fn parse_options<'a>( @@ -566,7 +567,7 @@ fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, asm_macro: AsmMacro, - args: AsmArgs, + args: ValidatedAsmArgs, ) -> ExpandResult, ()> { let mut template = vec![]; // Register operands are implicitly used since they are not allowed to be diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 18e3386f4f10c..1a9614bacec89 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,14 +1,11 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{RawAsmArg, parse_raw_asm_args}; +use rustc_builtin_macros::asm::{AsmArg, parse_asm_args}; use crate::rewrite::RewriteContext; #[allow(dead_code)] -pub(crate) fn parse_asm( - context: &RewriteContext<'_>, - mac: &ast::MacCall, -) -> Option> { +pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option> { let ts = mac.args.tokens.clone(); let mut parser = super::build_parser(context, ts); - parse_raw_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() + parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() } From 26e3a5041a3d9bcba04a7b201a47b1b03ccb2b2f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 17 May 2025 23:27:33 +0200 Subject: [PATCH 19/21] add `AsmOptions` with some named fields --- compiler/rustc_builtin_macros/src/asm.rs | 46 +++++++++++++-------- compiler/rustc_builtin_macros/src/errors.rs | 4 +- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 867a5b80f918f..62ee71fecc273 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -28,10 +28,19 @@ pub struct AsmArg { pub enum AsmArgKind { Template(P), Operand(Option, ast::InlineAsmOperand), - Options(Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>), + Options(Vec), ClobberAbi(Vec<(Symbol, Span)>), } +pub struct AsmOption { + pub symbol: Symbol, + pub span: Span, + // A bitset, with only the bit for this option's symbol set. + pub options: ast::InlineAsmOptions, + // Used when suggesting to remove an option. + pub span_with_comma: Span, +} + /// Validated assembly arguments, ready for macro expansion. struct ValidatedAsmArgs { pub templates: Vec>, @@ -344,20 +353,26 @@ fn validate_asm_args<'a>( AsmArgKind::Options(new_options) => { allow_templates = false; - for (symbol, option, span, full_span) in new_options { - if !asm_macro.is_supported_option(option) { + for asm_option in new_options { + let AsmOption { span, symbol, span_with_comma, options } = asm_option; + + if !asm_macro.is_supported_option(options) { // Tool-only output. dcx.emit_err(errors::AsmUnsupportedOption { span, symbol, - full_span, + span_with_comma, macro_name: asm_macro.macro_name(), }); - } else if validated.options.contains(option) { + } else if validated.options.contains(options) { // Tool-only output. - dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); + dcx.emit_err(errors::AsmOptAlreadyprovided { + span, + symbol, + span_with_comma, + }); } else { - validated.options |= option; + validated.options |= asm_option.options; } } @@ -464,13 +479,10 @@ fn validate_asm_args<'a>( Ok(validated) } -fn parse_options<'a>( - p: &mut Parser<'a>, - asm_macro: AsmMacro, -) -> PResult<'a, Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>> { +fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { p.expect(exp!(OpenParen))?; - let mut options = Vec::new(); + let mut asm_options = Vec::new(); while !p.eat(exp!(CloseParen)) { const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ @@ -486,9 +498,9 @@ fn parse_options<'a>( ]; 'blk: { - for (exp, option) in OPTIONS { + for (exp, options) in OPTIONS { // Gives a more accurate list of expected next tokens. - let kw_matched = if asm_macro.is_supported_option(option) { + let kw_matched = if asm_macro.is_supported_option(options) { p.eat_keyword(exp) } else { p.eat_keyword_noexpect(exp.kw) @@ -496,10 +508,10 @@ fn parse_options<'a>( if kw_matched { let span = p.prev_token.span; - let full_span = + let span_with_comma = if p.token == token::Comma { span.to(p.token.span) } else { span }; - options.push((exp.kw, option, span, full_span)); + asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); break 'blk; } } @@ -514,7 +526,7 @@ fn parse_options<'a>( p.expect(exp!(Comma))?; } - Ok(options) + Ok(asm_options) } fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d14ad8f40144c..b28f7d312d937 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -910,7 +910,7 @@ pub(crate) struct AsmOptAlreadyprovided { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, } #[derive(Diagnostic)] @@ -921,7 +921,7 @@ pub(crate) struct AsmUnsupportedOption { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, pub(crate) macro_name: &'static str, } From d2e5a3d131bd5a4ac92c0e6cfd3c49b5b6d44ab6 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 18 May 2025 17:04:49 +0800 Subject: [PATCH 20/21] gvn: avoid creating overlapping assignments --- compiler/rustc_mir_transform/src/gvn.rs | 19 ++++++---- .../gvn_overlapping.overlapping.GVN.diff | 18 ++++++++++ tests/mir-opt/gvn_overlapping.rs | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/mir-opt/gvn_overlapping.overlapping.GVN.diff create mode 100644 tests/mir-opt/gvn_overlapping.rs diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8b8d1efbbd2e0..209e818e9e32d 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -836,6 +836,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn simplify_rvalue( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -855,7 +856,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Repeat(op, amount) } Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty), - Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location), + Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location), Rvalue::Ref(_, borrow_kind, ref mut place) => { self.simplify_place_projection(place, location); return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind))); @@ -943,6 +944,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate_to_copy( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, fields: &[VnIndex], @@ -982,12 +984,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Allow introducing places with non-constant offsets, as those are still better than // reconstructing an aggregate. - if let Some(place) = self.try_as_place(copy_from_local_value, location, true) { - if rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty { + if let Some(place) = self.try_as_place(copy_from_local_value, location, true) + && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty + { + // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments. + // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections. + if lhs.as_local().is_some() { self.reused_locals.insert(place.local); *rvalue = Rvalue::Use(Operand::Copy(place)); - return Some(copy_from_local_value); } + return Some(copy_from_local_value); } None @@ -995,6 +1001,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -1090,7 +1097,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let AggregateTy::Def(_, _) = ty && let Some(value) = - self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index) + self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) { return Some(value); } @@ -1765,7 +1772,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind { self.simplify_place_projection(lhs, location); - let value = self.simplify_rvalue(rvalue, location); + let value = self.simplify_rvalue(lhs, rvalue, location); let value = if let Some(local) = lhs.as_local() && self.ssa.is_ssa(local) // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark diff --git a/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff new file mode 100644 index 0000000000000..fcabcdbcfef2c --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff @@ -0,0 +1,18 @@ +- // MIR for `overlapping` before GVN ++ // MIR for `overlapping` after GVN + + fn overlapping(_1: Adt) -> () { + let mut _0: (); + let mut _2: *mut Adt; + let mut _3: u32; + let mut _4: &Adt; + + bb0: { + _2 = &raw mut _1; + _4 = &(*_2); + _3 = copy (((*_4) as variant#1).0: u32); + (*_2) = Adt::Some(copy _3); + return; + } + } + diff --git a/tests/mir-opt/gvn_overlapping.rs b/tests/mir-opt/gvn_overlapping.rs new file mode 100644 index 0000000000000..99113445e683d --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.rs @@ -0,0 +1,36 @@ +//@ test-mir-pass: GVN + +#![feature(custom_mir, core_intrinsics)] + +// Check that we do not create overlapping assignments. + +use std::intrinsics::mir::*; + +// EMIT_MIR gvn_overlapping.overlapping.GVN.diff +#[custom_mir(dialect = "runtime")] +fn overlapping(_17: Adt) { + // CHECK-LABEL: fn overlapping( + // CHECK: let mut [[PTR:.*]]: *mut Adt; + // CHECK: (*[[PTR]]) = Adt::Some(copy {{.*}}); + mir! { + let _33: *mut Adt; + let _48: u32; + let _73: &Adt; + { + _33 = core::ptr::addr_of_mut!(_17); + _73 = &(*_33); + _48 = Field(Variant((*_73), 1), 0); + (*_33) = Adt::Some(_48); + Return() + } + } +} + +fn main() { + overlapping(Adt::Some(0)); +} + +enum Adt { + None, + Some(u32), +} From f0b8ec1d71f055cbdb741565eaddabc93bf1ae75 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 7 May 2025 06:59:30 -0700 Subject: [PATCH 21/21] name resolution for guard patterns --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_resolve/src/late.rs | 36 ++++- .../feature-gate-guard-patterns.rs | 2 - .../feature-gate-guard-patterns.stderr | 31 +--- .../name-resolution.rs | 81 +++++++++++ .../name-resolution.stderr | 133 ++++++++++++++++++ 6 files changed, 255 insertions(+), 30 deletions(-) create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 114b9835b98cf..ab48a2899a758 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -611,7 +611,7 @@ impl Pat { /// Walk top-down and call `it` in each place where a pattern occurs /// starting with the root pattern `walk` is called on. If `it` returns /// false then we will descend no further but siblings will be processed. - pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) { + pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) { if !it(self) { return; } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index aa211a8f3c292..1b682d0cf8ae9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -799,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r fn visit_pat(&mut self, p: &'ast Pat) { let prev = self.diag_metadata.current_pat; self.diag_metadata.current_pat = Some(p); - visit::walk_pat(self, p); + + if let PatKind::Guard(subpat, _) = &p.kind { + // We walk the guard expression in `resolve_pattern_inner`. Don't resolve it twice. + self.visit_pat(subpat); + } else { + visit::walk_pat(self, p); + } + self.diag_metadata.current_pat = prev; } fn visit_local(&mut self, local: &'ast Local) { @@ -3922,7 +3929,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { #[tracing::instrument(skip(self, bindings), level = "debug")] fn resolve_pattern_inner( &mut self, - pat: &Pat, + pat: &'ast Pat, pat_src: PatternSource, bindings: &mut PatternBindings, ) { @@ -3982,6 +3989,31 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // Prevent visiting `ps` as we've already done so above. return false; } + PatKind::Guard(ref subpat, ref guard) => { + // Add a new set of bindings to the stack to collect bindings in `subpat`. + bindings.push((PatBoundCtx::Product, Default::default())); + // Resolving `subpat` adds bindings onto the newly-pushed context. After, the + // total number of contexts on the stack should be the same as before. + let binding_ctx_stack_len = bindings.len(); + self.resolve_pattern_inner(subpat, pat_src, bindings); + assert_eq!(bindings.len(), binding_ctx_stack_len); + // These bindings, but none from the surrounding pattern, are visible in the + // guard; put them in scope and resolve `guard`. + let subpat_bindings = bindings.pop().unwrap().1; + self.with_rib(ValueNS, RibKind::Normal, |this| { + *this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone(); + this.resolve_expr(guard, None); + }); + // Propagate the subpattern's bindings upwards. + // FIXME(guard_patterns): For `if let` guards, we'll also need to get the + // bindings introduced by the guard from its rib and propagate them upwards. + // This will require checking the identifiers for overlaps with `bindings`, like + // what `fresh_binding` does (ideally sharing its logic). To keep them separate + // from `subpat_bindings`, we can introduce a fresh rib for the guard. + bindings.last_mut().unwrap().1.extend(subpat_bindings); + // Prevent visiting `subpat` as we've already done so above. + return false; + } _ => {} } true diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs index 74fb5817081c7..095f66eeb9068 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -22,7 +22,6 @@ fn other_guards_dont() { let ((x if guard(x)) | x) = 0; //~^ ERROR: guard patterns are experimental - //~| ERROR: cannot find value `x` if let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental @@ -37,7 +36,6 @@ fn other_guards_dont() { fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} //~^ ERROR: guard patterns are experimental -//~| ERROR: cannot find value `x` fn guard(x: T) -> bool { unimplemented!() diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr index 8b85b663889f7..b0bf302f3cb6c 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -10,24 +10,6 @@ LL - (0 if guard(0)) => {}, LL + 0 if guard(0) => {}, | -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:23:22 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ not found in this scope - -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:38:45 - | -LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} - | ^ - | -help: the binding `x` is available in a different scope in the same function - --> $DIR/feature-gate-guard-patterns.rs:23:11 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ - error[E0658]: guard patterns are experimental --> $DIR/feature-gate-guard-patterns.rs:18:15 | @@ -51,7 +33,7 @@ LL | let ((x if guard(x)) | x) = 0; = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:27:18 + --> $DIR/feature-gate-guard-patterns.rs:26:18 | LL | if let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -62,7 +44,7 @@ LL | if let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:30:21 + --> $DIR/feature-gate-guard-patterns.rs:29:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -73,7 +55,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:34:21 + --> $DIR/feature-gate-guard-patterns.rs:33:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -84,7 +66,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:38:39 + --> $DIR/feature-gate-guard-patterns.rs:37:39 | LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} | ^^^^^^^^ @@ -94,7 +76,6 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider using match arm guards -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0425, E0658. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs new file mode 100644 index 0000000000000..83ad8c76bb1cf --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs @@ -0,0 +1,81 @@ +//! Test that guard patterns can see bindings already in scope and bindings introduced in their +//! subpattern, but no other bindings from the containing pattern. Also make sure bindings +//! introduced in guard patterns are visible in fn/arm/loop/etc bodies. + +#![feature(guard_patterns)] +#![expect(incomplete_features)] + +fn good_fn_item(((x if x) | x): bool) -> bool { x } + +fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} +//~^ ERROR cannot find value `x` in this scope +fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} +//~^ ERROR cannot find value `y` in this scope + +fn main() { + let ((local if local) if local) = false; + + match (true, true) { + (x if local, y if good_fn_item(y)) => x && y, + (x, y if x) => x && y, + //~^ ERROR cannot find value `x` in this scope + (x if y, y) => x && y, + //~^ ERROR cannot find value `y` in this scope + }; + + match (true,) { + (x @ y if x && y,) => x && y, + (x @ (y if y),) => x && y, + (x @ (y if x),) => x && y, + //~^ ERROR cannot find value `x` in this scope + }; + + match (Ok(true),) { + ((Ok(x) | Err(x)) if good_fn_item(x),) => x, + ((Ok(x) if local) | (Err(x) if good_fn_item(x)),) => x, + ((Ok(x if x) if x) | (Err(x if x) if x) if x,) if x => x, + ((Ok(x) if y) | (Err(y) if x),) => x && y, + //~^ ERROR variable `x` is not bound in all patterns + //~| ERROR variable `y` is not bound in all patterns + //~| ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + }; + + let (_ if nonexistent) = true; + //~^ ERROR cannot find value `nonexistent` in this scope + if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + (|(x if x), (y if y)| x && y)(true, true); + (|(x if y), (y if x)| x && y)(true, true); + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + // FIXME(guard_patterns): mismatched bindings are not yet allowed + match Some(0) { + Some(x if x > 0) | None => {} + //~^ ERROR variable `x` is not bound in all patterns + } +} + +/// Make sure shadowing is handled properly. In particular, if a pattern shadows an identifier, +/// a guard pattern's guard should still see the original binding if the shadowing binding isn't in +/// its subpattern. +fn test_shadowing(local: bool) -> u8 { + match (0, 0) { + // The `local` binding here shadows the `bool` definition, so we get a type error. + //~v ERROR mismatched types + local if local => 0, + // The guards here should see the `bool` definition of `local`, not the new `u8` binding. + // The body should see the new binding. + (local, _ if local) => local, + (_ if local, local) => local, + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr new file mode 100644 index 0000000000000..d76e60478a146 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -0,0 +1,133 @@ +error[E0408]: variable `y` is not bound in all patterns + --> $DIR/name-resolution.rs:37:10 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^^^^^^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `y` + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:37:25 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | - ^^^^^^^^^^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:63:28 + | +LL | Some(x if x > 0) | None => {} + | - ^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:10:34 + | +LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:12:25 + | +LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:20:18 + | +LL | (x, y if x) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:22:15 + | +LL | (x if y, y) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:29:20 + | +LL | (x @ (y if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:37:20 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:37:36 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `nonexistent` in this scope + --> $DIR/name-resolution.rs:44:15 + | +LL | let (_ if nonexistent) = true; + | ^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:46:22 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:46:33 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:49:25 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:49:36 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:52:19 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:52:30 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:57:13 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:57:23 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `y` + +error[E0308]: mismatched types + --> $DIR/name-resolution.rs:75:18 + | +LL | local if local => 0, + | ^^^^^ expected `bool`, found `({integer}, {integer})` + | + = note: expected type `bool` + found tuple `({integer}, {integer})` + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0308, E0408, E0425. +For more information about an error, try `rustc --explain E0308`.