From 281d8cc4ae53cc3ac4e96cd5528200f40086abfa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 2 Sep 2023 12:54:47 +0200 Subject: [PATCH 01/11] document ABI compatibility --- library/core/src/option.rs | 7 +- library/core/src/primitive_docs.rs | 106 ++++++++++++++++++++++++++++- tests/ui/abi/compatibility.rs | 8 +-- 3 files changed, 113 insertions(+), 8 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index acf3dfbdf4cf4..ba1367cde3109 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -119,7 +119,7 @@ //! # Representation //! //! Rust guarantees to optimize the following types `T` such that -//! [`Option`] has the same size and alignment as `T`. In some +//! [`Option`] has the same size, alignment, and [function call ABI] as `T`. In some //! of these cases, Rust further guarantees that //! `transmute::<_, Option>([0u8; size_of::()])` is sound and //! produces `Option::::None`. These cases are identified by the @@ -127,7 +127,7 @@ //! //! | `T` | `transmute::<_, Option>([0u8; size_of::()])` sound? | //! |---------------------------------------------------------------------|----------------------------------------------------------------------| -//! | [`Box`] | when `U: Sized` | +//! | [`Box`] (specifically, only `Box`) | when `U: Sized` | //! | `&U` | when `U: Sized` | //! | `&mut U` | when `U: Sized` | //! | `fn`, `extern "C" fn`[^extern_fn] | always | @@ -135,11 +135,12 @@ //! | [`ptr::NonNull`] | when `U: Sized` | //! | `#[repr(transparent)]` struct around one of the types in this list. | when it holds for the inner type | //! -//! [^extern_fn]: this remains true for any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`) +//! [^extern_fn]: this remains true for any argument/return types and any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`) //! //! [`Box`]: ../../std/boxed/struct.Box.html //! [`num::NonZero*`]: crate::num //! [`ptr::NonNull`]: crate::ptr::NonNull +//! [function call ABI]: ../primitive.fn.html#abi-compatibility //! //! This is called the "null pointer optimization" or NPO. //! diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index f3695d16d7a6b..694ea8c703d96 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1480,7 +1480,7 @@ mod prim_ref {} /// /// ### Casting to and from integers /// -/// You cast function pointers directly to integers: +/// You can cast function pointers directly to integers: /// /// ```rust /// let fnptr: fn(i32) -> i32 = |x| x+2; @@ -1506,6 +1506,110 @@ mod prim_ref {} /// Note that all of this is not portable to platforms where function pointers and data pointers /// have different sizes. /// +/// ### ABI compatibility +/// +/// Generally, when a function is declared with one signature and called via a function pointer with +/// a different signature, the two signatures must be *ABI-compatible* or else calling the function +/// via that function pointer is Undefined Behavior. ABI compatibility is a lot stricter than merely +/// having the same memory layout; for example, even if `i32` and `f32` have the same size and +/// alignment, they might be passed in different registers and hence not be ABI-compatible. +/// +/// ABI compatibility as a concern only arises in code that alters the type of function pointers, +/// and in code that combines `#[target_feature]` with `extern fn`. Altering the type of +/// function pointers is wildly unsafe (as in, a lot more unsafe than even +/// [`transmute_copy`][mem::transmute_copy]), and should only occur in the most exceptional +/// circumstances. `#[target_feature]` is also used rarely. But assuming such circumstances, what +/// are the rules? +/// +/// For two signatures to be considered *ABI-compatible*, they must use a compatible ABI string, +/// must take the same number of arguments, the individual argument types and the return types must +/// be ABI-compatible, and the target feature requirements must be met (see the subsection below for +/// the last point). The ABI string is declared via `extern "ABI" fn(...) -> ...`; note that +/// `fn name(...) -> ...` implicitly uses the `"Rust"` ABI string and `extern fn name(...) -> ...` +/// implicitly uses the `"C"` ABI string. +/// +/// The ABI strings are guaranteed to be compatible if they are the same, or if the caller ABI +/// string is `$X-unwind` and the callee ABI string is `$X`, where `$X` is one of the following: +/// "C", "aapcs", "fastcall", "stdcall", "system", "sysv64", "thiscall", "vectorcall", "win64". +/// +/// The following types are guaranteed to be ABI-compatible: +/// +/// - `*const T`, `*mut T`, `&T`, `&mut T`, `Box` (specifically, only `Box`), +/// `NonNull` are all ABI-compatible with each other for all `T`. Two of these pointer types +/// with different `T` are ABI-compatible if they have the same metadata type (`::Metadata`). +/// - `usize` is ABI-compatible with the `uN` integer type of the same size, and likewise `isize` is +/// ABI-compatible with the `iN` integer type of the same size. +/// - Any two `fn` types are ABI-compatible with each other if they have the same ABI string or the +/// ABI string only differs in a trailing `-unwind`, independent of the rest of their signature. +/// (Note that this is about the case of passing a function pointer as an argument to a function. +/// The two pointers being ABI-compatible here means that the call successfully passes the +/// pointer. When actually calling the pointer, of course the rest of the signature becomes +/// relevant as well, according to the rules in this section.) +/// - Any two types with size 0 and alignment 1 are ABI-compatible. +/// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the +/// unique field that doesn't have size 0 and alignment 1 (if there is such a field). +/// - `i32` is ABI-compatible with `NonZeroI32`, and similar for all other integer types with their +/// matching `NonZero*` type. +/// - If `T` is guaranteed to be subject to the [null pointer +/// optimization](option/index.html#representation), then `T` and `Option` are ABI-compatible. +/// +/// Furthermore, ABI compatibility satisfies the following general properties: +/// +/// - Every type is ABI-compatible with itself. +/// - If `T1` and `T2` are ABI-compatible, then two `repr(C)` types that only differ because one +/// field type was changed from `T1` to `T2` are ABI-compatible. +/// - If `T1` and `T2` are ABI-compatible and `T2` and `T3` are ABI-compatible, then so are `T1` and +/// `T3` (i.e., ABI-compatibility is transitive). +/// - If `T1` and `T2` are ABI-compatible, then so are `T2` and `T1` (i.e., ABI-compatibility is +/// symmetric). +/// +/// More signatures can be ABI-compatible on specific targets, but that should not be relied upon +/// since it is not portable and not a stable guarantee. +/// +/// Noteworthy cases of types *not* being ABI-compatible in general are: +/// * `bool` vs `u8`, and `i32` vs `u32`: on some targets, the calling conventions for these types +/// differ in terms of what they guarantee for the remaining bits in the register that are not +/// used by the value. +/// * `i32` vs `f32` are not compatible either, as has already been mentioned above. +/// * `struct Foo(u32)` and `u32` are not compatible (without `repr(transparent)`) since structs are +/// aggregate types and often passed in a different way than primitives like `i32`. +/// +/// Note that these rules describe when two completely known types are ABI-compatible. When +/// considering ABI compatibility of a type declared in another crate (including the standard +/// library), consider that any type that has a private field or the `#[non_exhaustive]` attribute +/// may change its layout as a non-breaking update unless documented otherwise -- so for instance, +/// even if such a type is a 1-ZST or `repr(transparent)` right now, this might change with any +/// library version bump. +/// +/// If the declared signature and the signature of the function pointer are ABI-compatible, then the +/// function call behaves as if every argument was [`transmute`d][mem::transmute] from the +/// type in the function pointer to the type at the function declaration, and the return value is +/// [`transmute`d][mem::transmute] from the type in the declaration to the type in the +/// pointer. All the usual caveats and concerns around transmutation apply; for instance, if the +/// function expects a `NonNullI32` and the function pointer uses the ABI-compatible type +/// `Option`, and the value used for the argument is `None`, then this call is Undefined +/// Behavior since transmuting `None::` to `NonNullI32` violates the non-null +/// requirement. +/// +/// #### Requirements concerning target features +/// +/// Under some conditions, the signature used by the caller and the callee can be ABI-incompatible +/// even if the exact same ABI string and types are being used. As an example, the +/// `std::arch::x86_64::__m256` type has a different `extern "C"` ABI when the `avx` feature is +/// enabled vs when it is not enabled. +/// +/// Therefore, to ensure ABI compatibility when code using different target features is combined +/// (such as via `#[target_feature]`), we further require that one of the following conditions is +/// met: +/// +/// - The function uses the `"Rust"` ABI string (which is the default without `extern`). +/// - Caller and callee are using the exact same set of target features. For the callee we consider +/// the features enabled (via `#[target_feature]` and `-C target-feature`/`-C target-cpu`) at the +/// declaration site; for the caller we consider the features enabled at the call site. +/// - Neither any argument nor the return value involves a SIMD type (`#[repr(simd)]`) that is not +/// behind a pointer indirection (i.e., `*mut __m256` is fine, but `(i32, __m256)` is not). +/// /// ### Trait implementations /// /// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 0cdf229711ad0..53e1eff9d72b7 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -231,8 +231,7 @@ macro_rules! test_abi_compatible { }; } -// Compatibility of pointers is probably de-facto guaranteed, -// but that does not seem to be documented. +// Compatibility of pointers. test_abi_compatible!(ptr_mut, *const i32, *mut i32); test_abi_compatible!(ptr_pointee, *const i32, *const Vec); test_abi_compatible!(ref_mut, &i32, &mut i32); @@ -241,14 +240,15 @@ test_abi_compatible!(box_ptr, Box, *const i32); test_abi_compatible!(nonnull_ptr, NonNull, *const i32); test_abi_compatible!(fn_fn, fn(), fn(i32) -> i32); -// Some further guarantees we will likely (have to) make. +// Compatibility of 1-ZST. test_abi_compatible!(zst_unit, Zst, ()); #[cfg(not(any(target_arch = "sparc64")))] test_abi_compatible!(zst_array, Zst, [u8; 0]); test_abi_compatible!(nonzero_int, NonZeroI32, i32); // `DispatchFromDyn` relies on ABI compatibility. -// This is interesting since these types are not `repr(transparent)`. +// This is interesting since these types are not `repr(transparent)`. So this is not part of our +// public ABI guarantees, but is relied on by the compiler. test_abi_compatible!(rc, Rc, *mut i32); test_abi_compatible!(arc, Arc, *mut i32); From 044d05769b509fa976ad5b96106ae0c845bda9a3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 Nov 2023 20:33:09 +0100 Subject: [PATCH 02/11] add 'import functions' to the list of situations where ABI compatibility comes up --- library/core/src/primitive_docs.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 694ea8c703d96..87c6c22280c53 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1515,11 +1515,14 @@ mod prim_ref {} /// alignment, they might be passed in different registers and hence not be ABI-compatible. /// /// ABI compatibility as a concern only arises in code that alters the type of function pointers, -/// and in code that combines `#[target_feature]` with `extern fn`. Altering the type of -/// function pointers is wildly unsafe (as in, a lot more unsafe than even -/// [`transmute_copy`][mem::transmute_copy]), and should only occur in the most exceptional -/// circumstances. `#[target_feature]` is also used rarely. But assuming such circumstances, what -/// are the rules? +/// code that imports functions via `extern` blocks, and in code that combines `#[target_feature]` +/// with `extern fn`. Altering the type of function pointers is wildly unsafe (as in, a lot more +/// unsafe than even [`transmute_copy`][mem::transmute_copy]), and should only occur in the most +/// exceptional circumstances. Most Rust code just imports functions via `use`. `#[target_feature]` +/// is also used rarely. So, most likely you do not have to worry about ABI compatibility. +/// +/// But assuming such circumstances, what are the rules? For this section, we are specifically +/// concerned with the case where both the caller and the callee are defined in Rust. /// /// For two signatures to be considered *ABI-compatible*, they must use a compatible ABI string, /// must take the same number of arguments, the individual argument types and the return types must From 52d22eaa2396fc10db79dcec254d69c5cd710630 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 Nov 2023 20:41:09 +0100 Subject: [PATCH 03/11] clarify ABI compatibility of fn ptr types and ptr types and add an and --- library/core/src/primitive_docs.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 87c6c22280c53..8b00719368a7b 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1537,18 +1537,18 @@ mod prim_ref {} /// /// The following types are guaranteed to be ABI-compatible: /// -/// - `*const T`, `*mut T`, `&T`, `&mut T`, `Box` (specifically, only `Box`), -/// `NonNull` are all ABI-compatible with each other for all `T`. Two of these pointer types -/// with different `T` are ABI-compatible if they have the same metadata type (`` (specifically, only `Box`), and +/// `NonNull` are all ABI-compatible with each other for all `T`. They are also ABI-compatible +/// with each other for _different_ `T` if they have the same metadata type (`::Metadata`). /// - `usize` is ABI-compatible with the `uN` integer type of the same size, and likewise `isize` is /// ABI-compatible with the `iN` integer type of the same size. -/// - Any two `fn` types are ABI-compatible with each other if they have the same ABI string or the -/// ABI string only differs in a trailing `-unwind`, independent of the rest of their signature. -/// (Note that this is about the case of passing a function pointer as an argument to a function. -/// The two pointers being ABI-compatible here means that the call successfully passes the -/// pointer. When actually calling the pointer, of course the rest of the signature becomes -/// relevant as well, according to the rules in this section.) +/// - Any two `fn` (function pointer) types are ABI-compatible with each other if they have the same +/// ABI string or the ABI string only differs in a trailing `-unwind`, independent of the rest of +/// their signature. (This means you can pass `fn()` to a function expecting `fn(i32)`, and the +/// call will be valid ABI-wise. The callee receives the result of transmuting the function pointer +/// from `fn()` to `fn(i32)`; that transmutation is itself a well-defined operation, it's just +/// almost certainly UB to later call that function pointer.) /// - Any two types with size 0 and alignment 1 are ABI-compatible. /// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the /// unique field that doesn't have size 0 and alignment 1 (if there is such a field). From e70839ac84aa3dc8b4525a11e64961e375f8f3ba Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 7 Nov 2023 14:07:32 -0800 Subject: [PATCH 04/11] Add more SMIR internal impl and callback return value In cases like Kani, we will invoke the rustc_internal run command directly for now. It would be handly to be able to have a callback that can return a value. We also need extra methods to convert stable constructs into internal ones, so we can break down the transition into finer grain commits. --- .../rustc_smir/src/rustc_internal/internal.rs | 136 +++++++++++++++++- compiler/rustc_smir/src/rustc_internal/mod.rs | 24 ++-- compiler/stable_mir/src/lib.rs | 20 +-- 3 files changed, 159 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 7cfdbbbf70375..5bb3c1a0d4c36 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -6,11 +6,23 @@ // Prefer importing stable_mir over internal rustc constructs to make this file more readable. use crate::rustc_smir::Tables; use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy}; -use stable_mir::ty::{Const, GenericArgKind, GenericArgs, Region, Ty}; -use stable_mir::DefId; +use rustc_span::Symbol; +use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; +use stable_mir::ty::{ + Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, GenericArgKind, + GenericArgs, Region, TraitRef, Ty, +}; +use stable_mir::{AllocId, CrateItem, DefId}; use super::RustcInternal; +impl<'tcx> RustcInternal<'tcx> for CrateItem { + type T = rustc_span::def_id::DefId; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.0.internal(tables) + } +} + impl<'tcx> RustcInternal<'tcx> for DefId { type T = rustc_span::def_id::DefId; fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { @@ -38,8 +50,9 @@ impl<'tcx> RustcInternal<'tcx> for GenericArgKind { impl<'tcx> RustcInternal<'tcx> for Region { type T = rustc_ty::Region<'tcx>; - fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { - todo!() + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + // Cannot recover region. Use erased instead. + tables.tcx.lifetimes.re_erased } } @@ -65,3 +78,118 @@ impl<'tcx> RustcInternal<'tcx> for Const { tables.constants[self.id] } } + +impl<'tcx> RustcInternal<'tcx> for MonoItem { + type T = rustc_middle::mir::mono::MonoItem<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + use rustc_middle::mir::mono as rustc_mono; + match self { + MonoItem::Fn(instance) => rustc_mono::MonoItem::Fn(instance.internal(tables)), + MonoItem::Static(def) => rustc_mono::MonoItem::Static(def.internal(tables)), + MonoItem::GlobalAsm(_) => { + unimplemented!() + } + } + } +} + +impl<'tcx> RustcInternal<'tcx> for Instance { + type T = rustc_ty::Instance<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.instances[self.def] + } +} + +impl<'tcx> RustcInternal<'tcx> for StaticDef { + type T = rustc_span::def_id::DefId; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.0.internal(tables) + } +} + +#[allow(rustc::usage_of_qualified_ty)] +impl<'tcx, T> RustcInternal<'tcx> for Binder +where + T: RustcInternal<'tcx>, + T::T: rustc_ty::TypeVisitable>, +{ + type T = rustc_ty::Binder<'tcx, T::T>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + rustc_ty::Binder::bind_with_vars( + self.value.internal(tables), + tables.tcx.mk_bound_variable_kinds_from_iter( + self.bound_vars.iter().map(|bound| bound.internal(tables)), + ), + ) + } +} + +impl<'tcx> RustcInternal<'tcx> for BoundVariableKind { + type T = rustc_ty::BoundVariableKind; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self { + BoundVariableKind::Ty(kind) => rustc_ty::BoundVariableKind::Ty(match kind { + BoundTyKind::Anon => rustc_ty::BoundTyKind::Anon, + BoundTyKind::Param(def, symbol) => { + rustc_ty::BoundTyKind::Param(def.0.internal(tables), Symbol::intern(&symbol)) + } + }), + BoundVariableKind::Region(kind) => rustc_ty::BoundVariableKind::Region(match kind { + BoundRegionKind::BrAnon => rustc_ty::BoundRegionKind::BrAnon, + BoundRegionKind::BrNamed(def, symbol) => rustc_ty::BoundRegionKind::BrNamed( + def.0.internal(tables), + Symbol::intern(&symbol), + ), + BoundRegionKind::BrEnv => rustc_ty::BoundRegionKind::BrEnv, + }), + BoundVariableKind::Const => rustc_ty::BoundVariableKind::Const, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for TraitRef { + type T = rustc_ty::TraitRef<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + rustc_ty::TraitRef::new( + tables.tcx, + self.def_id.0.internal(tables), + self.args().internal(tables), + ) + } +} + +impl<'tcx> RustcInternal<'tcx> for AllocId { + type T = rustc_middle::mir::interpret::AllocId; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.alloc_ids[*self] + } +} + +impl<'tcx> RustcInternal<'tcx> for ClosureKind { + type T = rustc_ty::ClosureKind; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + ClosureKind::Fn => rustc_ty::ClosureKind::Fn, + ClosureKind::FnMut => rustc_ty::ClosureKind::FnMut, + ClosureKind::FnOnce => rustc_ty::ClosureKind::FnOnce, + } + } +} + +impl<'tcx, T> RustcInternal<'tcx> for &T +where + T: RustcInternal<'tcx>, +{ + type T = T::T; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + (*self).internal(tables) + } +} diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index f0b368bec39c4..c82f948f195e6 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -13,6 +13,7 @@ use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::Span; use scoped_tls::scoped_thread_local; use stable_mir::ty::IndexedVal; +use stable_mir::Error; use std::cell::Cell; use std::cell::RefCell; use std::fmt::Debug; @@ -21,11 +22,11 @@ use std::ops::Index; mod internal; -pub fn stable<'tcx, S: Stable<'tcx>>(item: &S) -> S::T { +pub fn stable<'tcx, S: Stable<'tcx>>(item: S) -> S::T { with_tables(|tables| item.stable(tables)) } -pub fn internal<'tcx, S: RustcInternal<'tcx>>(item: &S) -> S::T { +pub fn internal<'tcx, S: RustcInternal<'tcx>>(item: S) -> S::T { with_tables(|tables| item.internal(tables)) } @@ -144,12 +145,13 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum { // datastructures and stable MIR datastructures scoped_thread_local! (static TLV: Cell<*const ()>); -pub(crate) fn init<'tcx>(tables: &TablesWrapper<'tcx>, f: impl FnOnce()) { +pub(crate) fn init<'tcx, F, T>(tables: &TablesWrapper<'tcx>, f: F) -> T +where + F: FnOnce() -> T, +{ assert!(!TLV.is_set()); let ptr = tables as *const _ as *const (); - TLV.set(&Cell::new(ptr), || { - f(); - }); + TLV.set(&Cell::new(ptr), || f()) } /// Loads the current context and calls a function with it. @@ -165,7 +167,10 @@ pub(crate) fn with_tables<'tcx, R>(f: impl FnOnce(&mut Tables<'tcx>) -> R) -> R }) } -pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { +pub fn run(tcx: TyCtxt<'_>, f: F) -> Result +where + F: FnOnce() -> T, +{ let tables = TablesWrapper(RefCell::new(Tables { tcx, def_ids: IndexMap::default(), @@ -175,7 +180,7 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { instances: IndexMap::default(), constants: IndexMap::default(), })); - stable_mir::run(&tables, || init(&tables, f)); + stable_mir::run(&tables, || init(&tables, f)) } #[macro_export] @@ -241,7 +246,8 @@ macro_rules! run { queries.global_ctxt().unwrap().enter(|tcx| { rustc_internal::run(tcx, || { self.result = Some((self.callback)(tcx)); - }); + }) + .unwrap(); if self.result.as_ref().is_some_and(|val| val.is_continue()) { Compilation::Continue } else { diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index f316671b278ea..63e9d54544bc8 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -47,7 +47,7 @@ pub type Symbol = String; pub type CrateNum = usize; /// A unique identification number for each item accessible for the current compilation unit. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct DefId(usize); impl Debug for DefId { @@ -240,12 +240,16 @@ pub trait Context { // datastructures and stable MIR datastructures scoped_thread_local! (static TLV: Cell<*const ()>); -pub fn run(context: &dyn Context, f: impl FnOnce()) { - assert!(!TLV.is_set()); - let ptr: *const () = &context as *const &_ as _; - TLV.set(&Cell::new(ptr), || { - f(); - }); +pub fn run(context: &dyn Context, f: F) -> Result +where + F: FnOnce() -> T, +{ + if TLV.is_set() { + Err(Error::from("StableMIR already running")) + } else { + let ptr: *const () = &context as *const &_ as _; + TLV.set(&Cell::new(ptr), || Ok(f())) + } } /// Loads the current context and calls a function with it. @@ -260,7 +264,7 @@ pub fn with(f: impl FnOnce(&dyn Context) -> R) -> R { } /// A type that provides internal information but that can still be used for debug purpose. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct Opaque(String); impl std::fmt::Display for Opaque { From 3f87dac9a255aaf26a3481575444b67b46cf913f Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 7 Nov 2023 14:11:15 -0800 Subject: [PATCH 05/11] Fix bug on MIRVisitor We were not iterating over all local variables due to a typo. --- compiler/stable_mir/src/mir/visit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index 475d6e9763d91..d6304d3ea398d 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -142,7 +142,7 @@ pub trait MirVisitor { } let local_start = arg_count + 1; - for (idx, arg) in body.arg_locals().iter().enumerate() { + for (idx, arg) in body.inner_locals().iter().enumerate() { self.visit_local_decl(idx + local_start, arg) } } @@ -417,7 +417,7 @@ pub trait MirVisitor { fn visit_opaque(_: &Opaque) {} /// The location of a statement / terminator in the code and the CFG. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Location(Span); impl Location { From 6515ac9d3f500bfa1d221a0985e29c5bab66b24c Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 16 Nov 2023 12:01:10 -0800 Subject: [PATCH 06/11] Add more APIs and fix `Instance::body` Add more APIs to retrieve information about types, and add more instance resolution options. Make `Instance::body()` return an Option, since not every instance might have an available body. For example, foreign instances, virtual instances, dependencies. --- compiler/rustc_smir/src/rustc_smir/builder.rs | 14 ++ compiler/rustc_smir/src/rustc_smir/mod.rs | 165 +++++++++++++++--- compiler/stable_mir/src/lib.rs | 41 ++++- compiler/stable_mir/src/mir/mono.rs | 68 +++++++- compiler/stable_mir/src/ty.rs | 149 +++++++++++++++- .../ui-fulldeps/stable-mir/check_instance.rs | 14 +- tests/ui-fulldeps/stable-mir/crate-info.rs | 8 +- tests/ui-fulldeps/stable-mir/projections.rs | 8 +- tests/ui-fulldeps/stable-mir/smir_visitor.rs | 2 +- 9 files changed, 422 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 8ff3958da7bd9..4aaa29ccebcd3 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -49,6 +49,20 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { *ty = self.monomorphize(*ty); } + fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { + let const_ = self.monomorphize(constant.const_); + let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), None) { + Ok(v) => v, + Err(mir::interpret::ErrorHandled::Reported(..)) => return, + Err(mir::interpret::ErrorHandled::TooGeneric(..)) => { + unreachable!("Failed to evaluate instance constant: {:?}", const_) + } + }; + let ty = constant.ty(); + constant.const_ = mir::Const::Val(val, ty); + self.super_constant(constant, location); + } + fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 8845320ca8b7d..059d39fee4fed 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -8,9 +8,9 @@ //! For now, we are developing everything inside `rustc`, thus, we keep this module private. use crate::rustc_internal::{IndexMap, RustcInternal}; -use crate::rustc_smir::hir::def::DefKind; -use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyParamRegion, Region}; +use crate::rustc_smir::stable_mir::ty::{BoundRegion, Region}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::interpret::{alloc_range, AllocId}; use rustc_middle::mir::mono::MonoItem; @@ -20,10 +20,11 @@ use rustc_target::abi::FieldIdx; use stable_mir::mir::mono::InstanceDef; use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx}; use stable_mir::ty::{ - Const, ConstId, ConstantKind, FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, - Span, TyKind, UintTy, + AdtDef, AdtKind, ClosureDef, ClosureKind, Const, ConstId, ConstantKind, EarlyParamRegion, + FloatTy, FnDef, GenericArgs, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, Span, + TyKind, UintTy, }; -use stable_mir::{self, opaque, Context, Filename}; +use stable_mir::{self, opaque, Context, CrateItem, Filename, ItemKind}; use std::cell::RefCell; use tracing::debug; @@ -85,9 +86,23 @@ impl<'tcx> Context for TablesWrapper<'tcx> { LineInfo { start_line: lines.1, start_col: lines.2, end_line: lines.3, end_col: lines.4 } } - fn def_kind(&self, def_id: stable_mir::DefId) -> stable_mir::DefKind { + fn item_kind(&self, item: CrateItem) -> ItemKind { + let tables = self.0.borrow(); + new_item_kind(tables.tcx.def_kind(tables[item.0])) + } + + fn adt_kind(&self, def: AdtDef) -> AdtKind { + let mut tables = self.0.borrow_mut(); + let ty = tables.tcx.type_of(def.0.internal(&mut *tables)).instantiate_identity().kind(); + let ty::TyKind::Adt(def, _) = ty else { + panic!("Expected an ADT definition, but found: {ty:?}") + }; + def.adt_kind().stable(&mut *tables) + } + + fn def_ty(&self, item: stable_mir::DefId) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); - tables.tcx.def_kind(tables[def_id]).stable(&mut *tables) + tables.tcx.type_of(item.internal(&mut *tables)).instantiate_identity().stable(&mut *tables) } fn span_of_an_item(&self, def_id: stable_mir::DefId) -> Span { @@ -198,10 +213,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn instance_body(&self, def: InstanceDef) -> Body { + fn instance_body(&self, def: InstanceDef) -> Option { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; - builder::BodyBuilder::new(tables.tcx, instance).build(&mut *tables) + tables + .has_body(instance) + .then(|| builder::BodyBuilder::new(tables.tcx, instance).build(&mut *tables)) } fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty { @@ -249,6 +266,42 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Ok(None) | Err(_) => None, } } + + fn resolve_drop_in_place( + &self, + ty: stable_mir::ty::Ty, + ) -> Option { + let mut tables = self.0.borrow_mut(); + let internal_ty = ty.internal(&mut *tables); + let instance = Instance::resolve_drop_in_place(tables.tcx, internal_ty); + matches!(instance.def, ty::InstanceDef::DropGlue(_, Some(_))) + .then(|| instance.stable(&mut *tables)) + } + + fn resolve_for_fn_ptr( + &self, + def: FnDef, + args: &GenericArgs, + ) -> Option { + let mut tables = self.0.borrow_mut(); + let def_id = def.0.internal(&mut *tables); + let args_ref = args.internal(&mut *tables); + Instance::resolve_for_fn_ptr(tables.tcx, ParamEnv::reveal_all(), def_id, args_ref) + .stable(&mut *tables) + } + + fn resolve_closure( + &self, + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Option { + let mut tables = self.0.borrow_mut(); + let def_id = def.0.internal(&mut *tables); + let args_ref = args.internal(&mut *tables); + let closure_kind = kind.internal(&mut *tables); + Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind).stable(&mut *tables) + } } pub(crate) struct TablesWrapper<'tcx>(pub(crate) RefCell>); @@ -271,6 +324,16 @@ impl<'tcx> Tables<'tcx> { fn intern_const(&mut self, constant: mir::Const<'tcx>) -> ConstId { self.constants.create_or_fetch(constant) } + + fn has_body(&self, instance: Instance<'tcx>) -> bool { + let def_id = instance.def_id(); + !self.tcx.is_foreign_item(def_id) + && self.tcx.is_mir_available(def_id) + && !matches!( + instance.def, + ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(..) + ) + } } /// Build a stable mir crate from a given crate number. @@ -281,6 +344,43 @@ fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate { stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local } } +fn new_item_kind(kind: DefKind) -> ItemKind { + match kind { + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm => { + unreachable!("Not a valid item kind: {kind:?}"); + } + DefKind::Closure + | DefKind::Coroutine + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::Impl { .. } + | DefKind::Fn => ItemKind::Fn, + DefKind::Const | DefKind::InlineConst | DefKind::AssocConst | DefKind::AnonConst => { + ItemKind::Const + } + DefKind::Static(_) => ItemKind::Static, + } +} + /// Trait used to convert between an internal MIR type to a Stable MIR type. pub trait Stable<'tcx> { /// The stable representation of the type implementing Stable. @@ -926,6 +1026,18 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { } } +impl<'tcx> Stable<'tcx> for ty::AdtKind { + type T = AdtKind; + + fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + ty::AdtKind::Struct => AdtKind::Struct, + ty::AdtKind::Union => AdtKind::Union, + ty::AdtKind::Enum => AdtKind::Enum, + } + } +} + impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource { type T = stable_mir::mir::CoroutineSource; fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { @@ -1062,8 +1174,6 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { type T = stable_mir::ty::GenericArgs; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - use stable_mir::ty::GenericArgs; - GenericArgs(self.iter().map(|arg| arg.unpack().stable(tables)).collect()) } } @@ -1486,7 +1596,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitRef<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::TraitRef; - TraitRef { def_id: tables.trait_def(self.def_id), args: self.args.stable(tables) } + TraitRef::try_new(tables.trait_def(self.def_id), self.args.stable(tables)).unwrap() } } @@ -1762,15 +1872,6 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { } } -impl<'tcx> Stable<'tcx> for DefKind { - type T = stable_mir::DefKind; - - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - // FIXME: add a real implementation of stable DefKind - opaque(self) - } -} - impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { type T = stable_mir::mir::mono::Instance; @@ -1805,3 +1906,25 @@ impl<'tcx> Stable<'tcx> for MonoItem<'tcx> { } } } + +impl<'tcx, T> Stable<'tcx> for &T +where + T: Stable<'tcx>, +{ + type T = T::T; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + (*self).stable(tables) + } +} + +impl<'tcx, T> Stable<'tcx> for Option +where + T: Stable<'tcx>, +{ + type T = Option; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.as_ref().map(|value| value.stable(tables)) + } +} diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 63e9d54544bc8..9873e7e3eedc3 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -36,6 +36,7 @@ pub mod mir; pub mod ty; pub mod visitor; +use crate::ty::{AdtDef, AdtKind, ClosureDef, ClosureKind}; pub use error::*; use mir::mono::Instance; use ty::{FnDef, GenericArgs}; @@ -99,7 +100,13 @@ pub struct Crate { pub is_local: bool, } -pub type DefKind = Opaque; +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum ItemKind { + Fn, + Static, + Const, +} + pub type Filename = Opaque; /// Holds information about an item in the crate. @@ -119,13 +126,17 @@ impl CrateItem { with(|cx| cx.name_of_def_id(self.0)) } - pub fn kind(&self) -> DefKind { - with(|cx| cx.def_kind(self.0)) + pub fn kind(&self) -> ItemKind { + with(|cx| cx.item_kind(*self)) } pub fn requires_monomorphization(&self) -> bool { with(|cx| cx.requires_monomorphization(self.0)) } + + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } } /// Return the function where execution starts if the current @@ -204,7 +215,13 @@ pub trait Context { fn get_lines(&self, span: &Span) -> LineInfo; /// Returns the `kind` of given `DefId` - fn def_kind(&self, def_id: DefId) -> DefKind; + fn item_kind(&self, item: CrateItem) -> ItemKind; + + /// Returns the kind of a given algebraic data type + fn adt_kind(&self, def: AdtDef) -> AdtKind; + + /// Returns the type of given crate item. + fn def_ty(&self, item: DefId) -> Ty; /// `Span` of an item fn span_of_an_item(&self, def_id: DefId) -> Span; @@ -214,7 +231,7 @@ pub trait Context { /// Get the body of an Instance. /// FIXME: Monomorphize the body. - fn instance_body(&self, instance: InstanceDef) -> Body; + fn instance_body(&self, instance: InstanceDef) -> Option; /// Get the instance type with generic substitutions applied and lifetimes erased. fn instance_ty(&self, instance: InstanceDef) -> Ty; @@ -234,6 +251,20 @@ pub trait Context { /// Resolve an instance from the given function definition and generic arguments. fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option; + + /// Resolve an instance for drop_in_place for the given type. + fn resolve_drop_in_place(&self, ty: Ty) -> Option; + + /// Resolve instance for a function pointer. + fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option; + + /// Resolve instance for a closure with the requested type. + fn resolve_closure( + &self, + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Option; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 8f533349848e2..5d197a32dbd6c 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -1,16 +1,16 @@ use crate::mir::Body; -use crate::ty::{FnDef, GenericArgs, IndexedVal, Ty}; -use crate::{with, CrateItem, DefId, Error, Opaque}; +use crate::ty::{ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; +use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque}; use std::fmt::Debug; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum MonoItem { Fn(Instance), Static(StaticDef), GlobalAsm(Opaque), } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Instance { /// The type of instance. pub kind: InstanceKind, @@ -19,7 +19,7 @@ pub struct Instance { pub def: InstanceDef, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum InstanceKind { /// A user defined item. Item, @@ -33,7 +33,7 @@ pub enum InstanceKind { impl Instance { /// Get the body of an Instance. The body will be eagerly monomorphized. - pub fn body(&self) -> Body { + pub fn body(&self) -> Option { with(|context| context.instance_body(self.def)) } @@ -54,6 +54,34 @@ impl Instance { }) }) } + + /// Resolve the drop in place for a given type. + /// Return `None` if the drop is a no-op. + pub fn resolve_drop_in_place(ty: Ty) -> Option { + with(|cx| cx.resolve_drop_in_place(ty)) + } + + /// Resolve the drop in place for a given type. + pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result { + with(|context| { + context.resolve_for_fn_ptr(def, args).ok_or_else(|| { + crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) + }) + }) + } + + /// Resolve a closure (do we need kind? -- most cases use FnOnce) + pub fn resolve_closure( + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Result { + with(|context| { + context.resolve_closure(def, args, kind).ok_or_else(|| { + crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) + }) + }) + } } /// Try to convert a crate item into an instance. @@ -86,12 +114,36 @@ impl TryFrom for CrateItem { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +impl From for MonoItem { + fn from(value: Instance) -> Self { + MonoItem::Fn(value) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct InstanceDef(usize); -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub struct StaticDef(pub DefId); +impl TryFrom for StaticDef { + type Error = crate::Error; + + fn try_from(value: CrateItem) -> Result { + if matches!(value.kind(), ItemKind::Static | ItemKind::Const) { + Ok(StaticDef(value.0)) + } else { + Err(Error::new(format!("Expected a static item, but found: {value:?}"))) + } + } +} + +impl StaticDef { + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } +} + impl IndexedVal for InstanceDef { fn to_val(index: usize) -> Self { InstanceDef(index) diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index e95c09abe7882..fa932c5d81a78 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -4,7 +4,7 @@ use super::{ with, AllocId, DefId, Symbol, }; use crate::{Filename, Opaque}; -use std::fmt::{self, Debug, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Ty(pub usize); @@ -135,6 +135,46 @@ pub enum TyKind { Bound(usize, BoundTy), } +impl TyKind { + pub fn rigid(&self) -> Option<&RigidTy> { + if let TyKind::RigidTy(inner) = self { Some(inner) } else { None } + } + + pub fn is_unit(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Tuple(data)) if data.len() == 0) + } + + pub fn is_trait(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn))) + } + + pub fn is_enum(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Enum) + } + + pub fn is_struct(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Struct) + } + + pub fn is_union(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Union) + } + + pub fn trait_principal(&self) -> Option> { + if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self { + if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) = + predicates.first() + { + Some(Binder { value: trait_ref.clone(), bound_vars: bound_vars.clone() }) + } else { + None + } + } else { + None + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum RigidTy { Bool, @@ -218,6 +258,43 @@ pub struct BrNamedDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct AdtDef(pub DefId); +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub enum AdtKind { + Enum, + Union, + Struct, +} + +impl AdtDef { + pub fn kind(&self) -> AdtKind { + with(|cx| cx.adt_kind(*self)) + } +} + +impl Display for AdtKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match self { + AdtKind::Enum => "enum", + AdtKind::Union => "union", + AdtKind::Struct => "struct", + }) + } +} + +impl AdtKind { + pub fn is_enum(&self) -> bool { + matches!(self, AdtKind::Enum) + } + + pub fn is_struct(&self) -> bool { + matches!(self, AdtKind::Struct) + } + + pub fn is_union(&self) -> bool { + matches!(self, AdtKind::Union) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct AliasDef(pub DefId); @@ -355,6 +432,30 @@ pub struct Binder { pub bound_vars: Vec, } +impl Binder { + pub fn skip_binder(self) -> T { + self.value + } + + pub fn map_bound_ref(&self, f: F) -> Binder + where + F: FnOnce(&T) -> U, + { + let Binder { value, bound_vars } = self; + let new_value = f(value); + Binder { value: new_value, bound_vars: bound_vars.clone() } + } + + pub fn map_bound(self, f: F) -> Binder + where + F: FnOnce(T) -> U, + { + let Binder { value, bound_vars } = self; + let new_value = f(value); + Binder { value: new_value, bound_vars } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct EarlyBinder { pub value: T, @@ -393,12 +494,27 @@ pub enum ExistentialPredicate { AutoTrait(TraitDef), } +/// An existential reference to a trait where `Self` is not included. +/// +/// The `generic_args` will include any other known argument. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExistentialTraitRef { pub def_id: TraitDef, pub generic_args: GenericArgs, } +impl Binder { + pub fn with_self_ty(&self, self_ty: Ty) -> Binder { + self.map_bound_ref(|trait_ref| trait_ref.with_self_ty(self_ty)) + } +} + +impl ExistentialTraitRef { + pub fn with_self_ty(&self, self_ty: Ty) -> TraitRef { + TraitRef::new(self.def_id, self_ty, &self.generic_args) + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExistentialProjection { pub def_id: TraitDef, @@ -504,10 +620,39 @@ impl TraitDecl { pub type ImplTrait = EarlyBinder; +/// A complete reference to a trait, i.e., one where `Self` is known. #[derive(Clone, Debug, Eq, PartialEq)] pub struct TraitRef { pub def_id: TraitDef, - pub args: GenericArgs, + /// The generic arguments for this definition. + /// The first element must always be type, and it represents `Self`. + args: GenericArgs, +} + +impl TraitRef { + pub fn new(def_id: TraitDef, self_ty: Ty, gen_args: &GenericArgs) -> TraitRef { + let mut args = vec![GenericArgKind::Type(self_ty)]; + args.extend_from_slice(&gen_args.0); + TraitRef { def_id, args: GenericArgs(args) } + } + + pub fn try_new(def_id: TraitDef, args: GenericArgs) -> Result { + match &args.0[..] { + [GenericArgKind::Type(_), ..] => Ok(TraitRef { def_id, args }), + _ => Err(()), + } + } + + pub fn args(&self) -> &GenericArgs { + &self.args + } + + pub fn self_ty(&self) -> Ty { + let GenericArgKind::Type(self_ty) = self.args.0[0] else { + panic!("Self must be a type, but found: {:?}", self.args.0[0]) + }; + self_ty + } } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index a340877752d8f..e5a480cd61de4 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -49,7 +49,7 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { assert!(generic.iter().all(|item| mir::mono::Instance::try_from(*item).is_err())); for instance in instances { - test_body(instance.body()) + test_body(instance.body().unwrap()) } ControlFlow::Continue(()) } @@ -61,8 +61,10 @@ fn test_body(body: mir::Body) { Call { func, .. } => { let TyKind::RigidTy(ty) = func.ty(body.locals()).kind() else { unreachable!() }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; - let result = Instance::resolve(def, &args); - assert!(result.is_ok()); + let instance = Instance::resolve(def, &args).unwrap(); + let mangled_name = instance.mangled_name(); + let body = instance.body(); + assert!(body.is_some() || mangled_name == "setpwent", "Failed: {func:?}"); } Goto { .. } | Assert { .. } | SwitchInt { .. } | Return | Drop { .. } => { /* Do nothing */ @@ -105,10 +107,16 @@ fn generate_input(path: &str) -> std::io::Result<()> { LEN > 0 && a[0] }} + extern "C" {{ + // Body should not be available. + fn setpwent(); + }} + pub fn monomorphic() {{ let v = vec![10]; let dup = ty_param(&v); assert_eq!(v, dup); + unsafe {{ setpwent() }}; }} pub mod foo {{ diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index ed6b786f5e1de..025ed1b6a952e 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -22,6 +22,7 @@ extern crate stable_mir; use rustc_hir::def::DefKind; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; +use stable_mir::ItemKind; use stable_mir::mir::mono::Instance; use stable_mir::ty::{RigidTy, TyKind}; use std::assert_matches::assert_matches; @@ -120,13 +121,13 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { let monomorphic = get_item(&items, (DefKind::Fn, "monomorphic")).unwrap(); let instance = Instance::try_from(monomorphic.clone()).unwrap(); - for block in instance.body().blocks { + for block in instance.body().unwrap().blocks { match &block.terminator.kind { stable_mir::mir::TerminatorKind::Call { func, .. } => { let TyKind::RigidTy(ty) = func.ty(&body.locals()).kind() else { unreachable!() }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; let next_func = Instance::resolve(def, &args).unwrap(); - match next_func.body().locals()[1].ty.kind() { + match next_func.body().unwrap().locals()[1].ty.kind() { TyKind::RigidTy(RigidTy::Uint(_)) | TyKind::RigidTy(RigidTy::Tuple(_)) => {} other => panic!("{other:?}"), } @@ -172,7 +173,8 @@ fn get_item<'a>( item: (DefKind, &str), ) -> Option<&'a stable_mir::CrateItem> { items.iter().find(|crate_item| { - crate_item.kind().to_string() == format!("{:?}", item.0) && crate_item.name() == item.1 + matches!((item.0, crate_item.kind()), (DefKind::Fn, ItemKind::Fn) | (DefKind::Const, + ItemKind::Const)) && crate_item.name() == item.1 }) } diff --git a/tests/ui-fulldeps/stable-mir/projections.rs b/tests/ui-fulldeps/stable-mir/projections.rs index 9c649a2effc75..d00f17d206b95 100644 --- a/tests/ui-fulldeps/stable-mir/projections.rs +++ b/tests/ui-fulldeps/stable-mir/projections.rs @@ -19,11 +19,11 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_hir::def::DefKind; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::{ProjectionElem, Rvalue, StatementKind}; use stable_mir::ty::{RigidTy, TyKind}; +use stable_mir::ItemKind; use std::assert_matches::assert_matches; use std::io::Write; use std::ops::ControlFlow; @@ -33,7 +33,7 @@ const CRATE_NAME: &str = "input"; /// Tests projections within Place objects fn test_place_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { let items = stable_mir::all_local_items(); - let body = get_item(&items, (DefKind::Fn, "projections")).unwrap().body(); + let body = get_item(&items, (ItemKind::Fn, "projections")).unwrap().body(); assert_eq!(body.blocks.len(), 4); // The first statement assigns `&s.c` to a local. The projections include a deref for `s`, since // `s` is passed as a reference argument, and a field access for field `c`. @@ -131,10 +131,10 @@ fn test_place_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { // Use internal API to find a function in a crate. fn get_item<'a>( items: &'a stable_mir::CrateItems, - item: (DefKind, &str), + item: (ItemKind, &str), ) -> Option<&'a stable_mir::CrateItem> { items.iter().find(|crate_item| { - crate_item.kind().to_string() == format!("{:?}", item.0) && crate_item.name() == item.1 + crate_item.kind() == item.0 && crate_item.name() == item.1 }) } diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs index de5148bb5f420..3ec63efcc06bf 100644 --- a/tests/ui-fulldeps/stable-mir/smir_visitor.rs +++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs @@ -40,7 +40,7 @@ fn test_visitor(_tcx: TyCtxt<'_>) -> ControlFlow<()> { let exit_fn = main_visitor.calls.last().unwrap(); assert!(exit_fn.mangled_name().contains("exit_fn"), "Unexpected last function: {exit_fn:?}"); - let exit_body = exit_fn.body(); + let exit_body = exit_fn.body().unwrap(); let exit_visitor = TestVisitor::collect(&exit_body); assert!(exit_visitor.ret_val.is_some()); assert_eq!(exit_visitor.args.len(), 1); From 08036a8005d1eb9f2b6bbb61d0a380c2a725f179 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 10 Nov 2023 13:32:37 -0800 Subject: [PATCH 07/11] Address PR comments --- compiler/rustc_smir/src/rustc_smir/mod.rs | 3 +-- compiler/stable_mir/src/mir/mono.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 059d39fee4fed..8662a9c7ec15b 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -327,8 +327,7 @@ impl<'tcx> Tables<'tcx> { fn has_body(&self, instance: Instance<'tcx>) -> bool { let def_id = instance.def_id(); - !self.tcx.is_foreign_item(def_id) - && self.tcx.is_mir_available(def_id) + self.tcx.is_mir_available(def_id) && !matches!( instance.def, ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(..) diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 5d197a32dbd6c..3524295f597d0 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -61,7 +61,7 @@ impl Instance { with(|cx| cx.resolve_drop_in_place(ty)) } - /// Resolve the drop in place for a given type. + /// Resolve an instance for a given function pointer. pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result { with(|context| { context.resolve_for_fn_ptr(def, args).ok_or_else(|| { @@ -70,7 +70,7 @@ impl Instance { }) } - /// Resolve a closure (do we need kind? -- most cases use FnOnce) + /// Resolve a closure with the expected kind. pub fn resolve_closure( def: ClosureDef, args: &GenericArgs, From 4c00aa3d740e3520e64a43c210e0f0d9a5ee19f4 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 14 Nov 2023 07:10:58 -0800 Subject: [PATCH 08/11] Always run builder to evaluate constants We were previously skipping it for non-generic functions, but this was leaving some constants unevaluated. --- compiler/rustc_smir/src/rustc_smir/builder.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 4aaa29ccebcd3..c133adfdeded7 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -21,10 +21,7 @@ impl<'tcx> BodyBuilder<'tcx> { pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { let mut body = self.tcx.instance_mir(self.instance.def).clone(); - let generics = self.tcx.generics_of(self.instance.def_id()); - if generics.requires_monomorphization(self.tcx) { - self.visit_body(&mut body); - } + self.visit_body(&mut body); body.stable(tables) } From 8e81fc00879b640ed6acc6a9ca870814189901e5 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 16 Nov 2023 12:04:25 -0800 Subject: [PATCH 09/11] Fix has_body() and change resolve_drop_in_place() sig Fixed the `has_body()` function operator. Before that, this function was returning false for all shims. Change resolve_drop_in_place() to also return an instance for empty shims, since they may still be required for vtable construction. --- compiler/rustc_smir/src/rustc_smir/builder.rs | 10 +++++++- compiler/rustc_smir/src/rustc_smir/mod.rs | 23 ++++++++----------- compiler/stable_mir/src/lib.rs | 2 +- compiler/stable_mir/src/mir/mono.rs | 3 +-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index c133adfdeded7..7e74a1d92c76f 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -19,9 +19,17 @@ impl<'tcx> BodyBuilder<'tcx> { BodyBuilder { tcx, instance } } + /// Build a stable monomorphic body for a given instance based on the MIR body. + /// + /// Note that we skip instantiation for static and constants. Trying to do so can cause ICE. + /// + /// We do monomorphize non-generic functions to eval unevaluated constants. pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { let mut body = self.tcx.instance_mir(self.instance.def).clone(); - self.visit_body(&mut body); + if self.tcx.def_kind(self.instance.def_id()).is_fn_like() || !self.instance.args.is_empty() + { + self.visit_body(&mut body); + } body.stable(tables) } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 8662a9c7ec15b..3df09cef1c7dc 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -267,15 +267,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn resolve_drop_in_place( - &self, - ty: stable_mir::ty::Ty, - ) -> Option { + fn resolve_drop_in_place(&self, ty: stable_mir::ty::Ty) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let internal_ty = ty.internal(&mut *tables); let instance = Instance::resolve_drop_in_place(tables.tcx, internal_ty); - matches!(instance.def, ty::InstanceDef::DropGlue(_, Some(_))) - .then(|| instance.stable(&mut *tables)) + instance.stable(&mut *tables) } fn resolve_for_fn_ptr( @@ -328,9 +324,11 @@ impl<'tcx> Tables<'tcx> { fn has_body(&self, instance: Instance<'tcx>) -> bool { let def_id = instance.def_id(); self.tcx.is_mir_available(def_id) - && !matches!( + || !matches!( instance.def, - ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(..) + ty::InstanceDef::Virtual(..) + | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::Item(..) ) } } @@ -364,15 +362,12 @@ fn new_item_kind(kind: DefKind) -> ItemKind { | DefKind::OpaqueTy | DefKind::Field | DefKind::LifetimeParam + | DefKind::Impl { .. } + | DefKind::Ctor(_, _) | DefKind::GlobalAsm => { unreachable!("Not a valid item kind: {kind:?}"); } - DefKind::Closure - | DefKind::Coroutine - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::Impl { .. } - | DefKind::Fn => ItemKind::Fn, + DefKind::Closure | DefKind::Coroutine | DefKind::AssocFn | DefKind::Fn => ItemKind::Fn, DefKind::Const | DefKind::InlineConst | DefKind::AssocConst | DefKind::AnonConst => { ItemKind::Const } diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 9873e7e3eedc3..0262fb536e785 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -253,7 +253,7 @@ pub trait Context { fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option; /// Resolve an instance for drop_in_place for the given type. - fn resolve_drop_in_place(&self, ty: Ty) -> Option; + fn resolve_drop_in_place(&self, ty: Ty) -> Instance; /// Resolve instance for a function pointer. fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option; diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 3524295f597d0..8562bfd390570 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -56,8 +56,7 @@ impl Instance { } /// Resolve the drop in place for a given type. - /// Return `None` if the drop is a no-op. - pub fn resolve_drop_in_place(ty: Ty) -> Option { + pub fn resolve_drop_in_place(ty: Ty) -> Instance { with(|cx| cx.resolve_drop_in_place(ty)) } From 5c3e01a340699069e1e8d9303fff0563fdbd09e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 17 Nov 2023 00:55:55 +0000 Subject: [PATCH 10/11] On resolve error of `[rest..]`, suggest `[rest @ ..]` When writing a pattern to collect multiple entries of a slice in a single binding, it is easy to misremember or typo the appropriate syntax to do so, instead writing the experimental `X..` pattern syntax. When we encounter a resolve error because `X` isn't available, we suggest `X @ ..` as an alternative. ``` error[E0425]: cannot find value `rest` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13 | LL | [1, rest..] => println!("{rest:?}"), | ^^^^ not found in this scope | help: if you meant to collect the rest of the slice in `rest`, use the at operator | LL | [1, rest @ ..] => println!("{rest:?}"), | + ``` Fix #88404. --- compiler/rustc_resolve/src/late.rs | 8 +++++ .../rustc_resolve/src/late/diagnostics.rs | 27 +++++++++++++++++ tests/ui/match/issue-92100.stderr | 5 ++++ ...-pattern-meant-to-be-slice-rest-pattern.rs | 9 ++++++ ...tern-meant-to-be-slice-rest-pattern.stderr | 30 +++++++++++++++++++ tests/ui/typeck/issue-105946.stderr | 5 ++++ 6 files changed, 84 insertions(+) create mode 100644 tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.rs create mode 100644 tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3be962dab90b9..9b7897d69e787 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -603,6 +603,8 @@ struct DiagnosticMetadata<'ast> { /// Only used for better errors on `let : ;`. current_let_binding: Option<(Span, Option, Option)>, + current_pat: Option<&'ast Pat>, + /// Used to detect possible `if let` written without `let` and to provide structured suggestion. in_if_condition: Option<&'ast Expr>, @@ -703,6 +705,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, fn visit_expr(&mut self, expr: &'ast Expr) { self.resolve_expr(expr, None); } + fn visit_pat(&mut self, p: &'ast Pat) { + let prev = self.diagnostic_metadata.current_pat; + self.diagnostic_metadata.current_pat = Some(p); + visit::walk_pat(self, p); + self.diagnostic_metadata.current_pat = prev; + } fn visit_local(&mut self, local: &'ast Local) { let local_spans = match local.pat.kind { // We check for this to avoid tuple struct fields. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index fd5d6fabf021d..6aecd3610c6e7 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -431,6 +431,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { code, ); + self.suggest_at_operator_in_slice_pat_with_range(&mut err, path); self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); if let Some((span, label)) = base_error.span_label { @@ -1063,6 +1064,32 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { true } + fn suggest_at_operator_in_slice_pat_with_range( + &mut self, + err: &mut Diagnostic, + path: &[Segment], + ) { + if let Some(pat) = self.diagnostic_metadata.current_pat + && let ast::PatKind::Range(Some(start), None, range) = &pat.kind + && let ExprKind::Path(None, range_path) = &start.kind + && let [segment] = &range_path.segments[..] + && let [s] = path + && segment.ident == s.ident + { + // We've encountered `[first, rest..]` where the user might have meant + // `[first, rest @ ..]` (#88404). + err.span_suggestion_verbose( + segment.ident.span.between(range.span), + format!( + "if you meant to collect the rest of the slice in `{}`, use the at operator", + segment.ident, + ), + " @ ", + Applicability::MaybeIncorrect, + ); + } + } + fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, err: &mut Diagnostic, diff --git a/tests/ui/match/issue-92100.stderr b/tests/ui/match/issue-92100.stderr index 0f694c587fcb4..d0e50f3ae16b3 100644 --- a/tests/ui/match/issue-92100.stderr +++ b/tests/ui/match/issue-92100.stderr @@ -3,6 +3,11 @@ error[E0425]: cannot find value `a` in this scope | LL | [a.., a] => {} | ^ not found in this scope + | +help: if you meant to collect the rest of the slice in `a`, use the at operator + | +LL | [a @ .., a] => {} + | + error: aborting due to previous error diff --git a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.rs b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.rs new file mode 100644 index 0000000000000..a619fcafc8614 --- /dev/null +++ b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.rs @@ -0,0 +1,9 @@ +fn main() { + match &[1, 2, 3][..] { + [1, rest..] => println!("{rest:?}"), + //~^ ERROR cannot find value `rest` in this scope + //~| ERROR cannot find value `rest` in this scope + //~| ERROR `X..` patterns in slices are experimental + _ => {} + } +} diff --git a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr new file mode 100644 index 0000000000000..cddd01212798e --- /dev/null +++ b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr @@ -0,0 +1,30 @@ +error[E0425]: cannot find value `rest` in this scope + --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13 + | +LL | [1, rest..] => println!("{rest:?}"), + | ^^^^ not found in this scope + | +help: if you meant to collect the rest of the slice in `rest`, use the at operator + | +LL | [1, rest @ ..] => println!("{rest:?}"), + | + + +error[E0425]: cannot find value `rest` in this scope + --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35 + | +LL | [1, rest..] => println!("{rest:?}"), + | ^^^^ not found in this scope + +error[E0658]: `X..` patterns in slices are experimental + --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13 + | +LL | [1, rest..] => println!("{rest:?}"), + | ^^^^^^ + | + = note: see issue #67264 for more information + = help: add `#![feature(half_open_range_patterns_in_slices)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/typeck/issue-105946.stderr b/tests/ui/typeck/issue-105946.stderr index 26c3b7fbc841d..2220271e58122 100644 --- a/tests/ui/typeck/issue-105946.stderr +++ b/tests/ui/typeck/issue-105946.stderr @@ -3,6 +3,11 @@ error[E0425]: cannot find value `_y` in this scope | LL | let [_y..] = [Box::new(1), Box::new(2)]; | ^^ not found in this scope + | +help: if you meant to collect the rest of the slice in `_y`, use the at operator + | +LL | let [_y @ ..] = [Box::new(1), Box::new(2)]; + | + error[E0658]: `X..` patterns in slices are experimental --> $DIR/issue-105946.rs:6:10 From 8f03a55566a0b382b5c8506b153e2850af9c280e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 17 Nov 2023 08:02:28 +0100 Subject: [PATCH 11/11] linking in general has more pitfalls than just call ABI --- library/core/src/primitive_docs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 8b00719368a7b..53dec5bee8f5f 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1521,8 +1521,9 @@ mod prim_ref {} /// exceptional circumstances. Most Rust code just imports functions via `use`. `#[target_feature]` /// is also used rarely. So, most likely you do not have to worry about ABI compatibility. /// -/// But assuming such circumstances, what are the rules? For this section, we are specifically -/// concerned with the case where both the caller and the callee are defined in Rust. +/// But assuming such circumstances, what are the rules? For this section, we are only considering +/// the ABI of direct Rust-to-Rust calls, not linking in general -- once functions are imported via +/// `extern` blocks, there are more things to consider that we do not go into here. /// /// For two signatures to be considered *ABI-compatible*, they must use a compatible ABI string, /// must take the same number of arguments, the individual argument types and the return types must