From 51e22be682e4ed5864e36400cbbc10836ee52c7e Mon Sep 17 00:00:00 2001 From: "Alexis (Poliorcetics) Bourget" Date: Sat, 1 Jul 2023 19:13:00 +0200 Subject: [PATCH 1/7] feat: render Object Safety informations non-object safe traits --- src/librustdoc/clean/types.rs | 3 +++ src/librustdoc/html/render/print_item.rs | 14 ++++++++++++++ src/librustdoc/html/render/sidebar.rs | 8 ++++++++ 3 files changed, 25 insertions(+) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 455228d04efa7..b773977eb9ca9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1455,6 +1455,9 @@ impl Trait { pub(crate) fn unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety { tcx.trait_def(self.def_id).unsafety } + pub(crate) fn is_object_safe(&self, tcx: TyCtxt<'_>) -> bool { + tcx.check_is_object_safe(self.def_id) + } } #[derive(Clone, Debug)] diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index fdf4556906172..4d518a3e20b9c 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -959,6 +959,20 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let cloned_shared = Rc::clone(&cx.shared); let cache = &cloned_shared.cache; let mut extern_crates = FxHashSet::default(); + + if !t.is_object_safe(cx.tcx()) { + write_small_section_header( + w, + "object-safety", + "Object Safety", + &format!("
This trait is not \ + \ + object safe.
", + base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL + ), + ); + } + if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) { // The DefId is for the first Type found with that name. The bool is // if any Types with the same name but different DefId have been found. diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 4e8d88c55b64f..ba4aaaff5a749 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -218,6 +218,14 @@ fn sidebar_trait<'a>( .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)) .collect(); sidebar_assoc_items(cx, it, &mut blocks); + + if !t.is_object_safe(cx.tcx()) { + blocks.push(LinkBlock::forced( + Link::new("object-safety", "Object Safety"), + "object-safety-note", + )); + } + blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl")); if t.is_auto(cx.tcx()) { blocks.push(LinkBlock::forced( From 40556b968ca044108d5f28d7a8810821c8be7d9d Mon Sep 17 00:00:00 2001 From: "Alexis (Poliorcetics) Bourget" Date: Sat, 8 Jul 2023 17:29:05 +0200 Subject: [PATCH 2/7] feat: Add 'object-safety' ID to init_id_map() in rustdoc --- src/librustdoc/html/markdown.rs | 1 + src/librustdoc/html/render/print_item.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index d24e6e5faf521..aa728c26afcf0 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -2024,6 +2024,7 @@ fn init_id_map() -> FxHashMap, usize> { map.insert("required-associated-consts".into(), 1); map.insert("required-methods".into(), 1); map.insert("provided-methods".into(), 1); + map.insert("object-safety".into(), 1); map.insert("implementors".into(), 1); map.insert("synthetic-implementors".into(), 1); map.insert("implementations-list".into(), 1); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 4d518a3e20b9c..c852f01f450a0 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -965,7 +965,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w, "object-safety", "Object Safety", - &format!("
This trait is not \ + &format!( + "
This trait is not \ \ object safe.
", base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL From a119158eb39a5e251d0d309a261c11743b996ce0 Mon Sep 17 00:00:00 2001 From: "Alexis (Poliorcetics) Bourget" Date: Sat, 1 Jul 2023 19:13:26 +0200 Subject: [PATCH 3/7] tests: object-safety section in traits --- tests/rustdoc/sidebar-items.rs | 7 +++++++ tests/rustdoc/trait-object-safe.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/rustdoc/trait-object-safe.rs diff --git a/tests/rustdoc/sidebar-items.rs b/tests/rustdoc/sidebar-items.rs index 6f7afa59bddc7..b746f6982644a 100644 --- a/tests/rustdoc/sidebar-items.rs +++ b/tests/rustdoc/sidebar-items.rs @@ -14,6 +14,7 @@ // @has - '//*[@class="sidebar-elems"]//section//a' 'Output' // @has - '//div[@class="sidebar-elems"]//h3/a[@href="#provided-associated-types"]' 'Provided Associated Types' // @has - '//*[@class="sidebar-elems"]//section//a' 'Extra' +// @has - '//div[@class="sidebar-elems"]//h3/a[@href="#object-safety"]' 'Object Safety' pub trait Foo { const FOO: usize; const BAR: u32 = 0; @@ -24,6 +25,12 @@ pub trait Foo { fn bar() -> Self::Output; } +// @has foo/trait.Safe.html +// @!has - '//div[@class="sidebar-elems"]//h3/a[@href="#object-safety"]' '' +pub trait Safe { + fn access(&self); +} + // @has foo/struct.Bar.html // @has - '//div[@class="sidebar-elems"]//h3/a[@href="#fields"]' 'Fields' // @has - '//*[@class="sidebar-elems"]//section//a[@href="#structfield.f"]' 'f' diff --git a/tests/rustdoc/trait-object-safe.rs b/tests/rustdoc/trait-object-safe.rs new file mode 100644 index 0000000000000..818843f75583f --- /dev/null +++ b/tests/rustdoc/trait-object-safe.rs @@ -0,0 +1,27 @@ +#![crate_name = "foo"] + +// @has 'foo/trait.Unsafe.html' +// @has - '//*[@class="object-safety-info"]' 'This trait is not object safe.' +// @has - '//*[@id="object-safety"]' 'Object Safety' +pub trait Unsafe { + fn foo() -> Self; +} + +// @has 'foo/trait.Unsafe2.html' +// @has - '//*[@class="object-safety-info"]' 'This trait is not object safe.' +// @has - '//*[@id="object-safety"]' 'Object Safety' +pub trait Unsafe2 { + fn foo(i: T); +} + +// @has 'foo/trait.Safe.html' +// @!has - '//*[@class="object-safety-info"]' '' +// @!has - '//*[@id="object-safety"]' '' +pub trait Safe { + fn foo(&self); +} + +// @has 'foo/struct.Foo.html' +// @!has - '//*[@class="object-safety-info"]' '' +// @!has - '//*[@id="object-safety"]' '' +pub struct Foo; From af7472ecbc40693680669c7667c81811a3eb949a Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 30 Oct 2023 13:07:02 -0700 Subject: [PATCH 4/7] Add a stable MIR visitor Add a few utility functions as well and extend most `mir` and `ty` ADTs to implement `PartialEq` and `Eq`. --- compiler/rustc_smir/src/rustc_smir/mod.rs | 6 + compiler/stable_mir/src/lib.rs | 7 +- compiler/stable_mir/src/mir.rs | 2 + compiler/stable_mir/src/mir/body.rs | 74 ++-- compiler/stable_mir/src/mir/mono.rs | 4 + compiler/stable_mir/src/mir/visit.rs | 414 +++++++++++++++++++ compiler/stable_mir/src/ty.rs | 92 ++--- tests/ui-fulldeps/stable-mir/smir_visitor.rs | 148 +++++++ 8 files changed, 662 insertions(+), 85 deletions(-) create mode 100644 compiler/stable_mir/src/mir/visit.rs create mode 100644 tests/ui-fulldeps/stable-mir/smir_visitor.rs diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 5ab5a048ffafa..b619ce6e35f99 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -216,6 +216,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { tables.create_def_id(def_id) } + fn instance_mangled_name(&self, def: InstanceDef) -> String { + let tables = self.0.borrow_mut(); + let instance = tables.instances[def]; + tables.tcx.symbol_name(instance).name.to_string() + } + fn mono_instance(&self, item: stable_mir::CrateItem) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let def_id = tables[item.0]; diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 38915afaa0c84..f316671b278ea 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -103,8 +103,6 @@ pub type DefKind = Opaque; pub type Filename = Opaque; /// Holds information about an item in the crate. -/// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to -/// use this item. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct CrateItem(pub DefId); @@ -224,6 +222,9 @@ pub trait Context { /// Get the instance. fn instance_def_id(&self, instance: InstanceDef) -> DefId; + /// Get the instance mangled name. + fn instance_mangled_name(&self, instance: InstanceDef) -> String; + /// Convert a non-generic crate item into an instance. /// This function will panic if the item is generic. fn mono_instance(&self, item: CrateItem) -> Instance; @@ -259,7 +260,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)] +#[derive(Clone, Eq, PartialEq)] pub struct Opaque(String); impl std::fmt::Display for Opaque { diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs index 3138bb1ec832c..2e1714b49c184 100644 --- a/compiler/stable_mir/src/mir.rs +++ b/compiler/stable_mir/src/mir.rs @@ -1,4 +1,6 @@ mod body; pub mod mono; +pub mod visit; pub use body::*; +pub use visit::MirVisitor; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 9f69e61d6fe7b..804f5e3d9d0c4 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,6 +1,6 @@ -use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region}; +use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, Ty}; use crate::Opaque; -use crate::{ty::Ty, Span}; +use crate::Span; /// The SMIR representation of a single function. #[derive(Clone, Debug)] @@ -12,10 +12,10 @@ pub struct Body { // The first local is the return value pointer, followed by `arg_count` // locals for the function arguments, followed by any user-declared // variables and temporaries. - locals: LocalDecls, + pub(super) locals: LocalDecls, // The number of arguments this function takes. - arg_count: usize, + pub(super) arg_count: usize, } impl Body { @@ -35,7 +35,7 @@ impl Body { /// Return local that holds this function's return value. pub fn ret_local(&self) -> &LocalDecl { - &self.locals[0] + &self.locals[RETURN_LOCAL] } /// Locals in `self` that correspond to this function's arguments. @@ -60,7 +60,7 @@ impl Body { type LocalDecls = Vec; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct LocalDecl { pub ty: Ty, pub span: Span, @@ -72,13 +72,13 @@ pub struct BasicBlock { pub terminator: Terminator, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Terminator { pub kind: TerminatorKind, pub span: Span, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum TerminatorKind { Goto { target: usize, @@ -122,7 +122,7 @@ pub enum TerminatorKind { }, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct InlineAsmOperand { pub in_value: Option, pub out_place: Option, @@ -131,7 +131,7 @@ pub struct InlineAsmOperand { pub raw_rpr: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum UnwindAction { Continue, Unreachable, @@ -139,7 +139,7 @@ pub enum UnwindAction { Cleanup(usize), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum AssertMessage { BoundsCheck { len: Operand, index: Operand }, Overflow(BinOp, Operand, Operand), @@ -151,7 +151,7 @@ pub enum AssertMessage { MisalignedPointerDereference { required: Operand, found: Operand }, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum BinOp { Add, AddUnchecked, @@ -177,20 +177,20 @@ pub enum BinOp { Offset, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum UnOp { Not, Neg, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum CoroutineKind { Async(CoroutineSource), Coroutine, Gen(CoroutineSource), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum CoroutineSource { Block, Closure, @@ -204,7 +204,7 @@ pub(crate) type LocalDefId = Opaque; pub(crate) type Coverage = Opaque; /// The FakeReadCause describes the type of pattern why a FakeRead statement exists. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum FakeReadCause { ForMatchGuard, ForMatchedPlace(LocalDefId), @@ -214,7 +214,7 @@ pub enum FakeReadCause { } /// Describes what kind of retag is to be performed -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum RetagKind { FnEntry, TwoPhase, @@ -222,7 +222,7 @@ pub enum RetagKind { Default, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum Variance { Covariant, Invariant, @@ -230,26 +230,26 @@ pub enum Variance { Bivariant, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CopyNonOverlapping { pub src: Operand, pub dst: Operand, pub count: Operand, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum NonDivergingIntrinsic { Assume(Operand), CopyNonOverlapping(CopyNonOverlapping), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Statement { pub kind: StatementKind, pub span: Span, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum StatementKind { Assign(Place, Rvalue), FakeRead(FakeReadCause, Place), @@ -266,7 +266,7 @@ pub enum StatementKind { Nop, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum Rvalue { /// Creates a pointer with the indicated mutability to the place. /// @@ -378,7 +378,7 @@ pub enum Rvalue { Use(Operand), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum AggregateKind { Array(Ty), Tuple, @@ -387,21 +387,21 @@ pub enum AggregateKind { Coroutine(CoroutineDef, GenericArgs, Movability), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum Operand { Copy(Place), Move(Place), Constant(Constant), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Place { pub local: Local, /// projection out of a place (access a field, deref a pointer, etc) pub projection: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct UserTypeProjection { pub base: UserTypeAnnotationIndex, pub projection: String, @@ -409,6 +409,8 @@ pub struct UserTypeProjection { pub type Local = usize; +pub const RETURN_LOCAL: Local = 0; + type FieldIdx = usize; /// The source-order index of a variant in a type. @@ -416,20 +418,20 @@ pub type VariantIdx = usize; type UserTypeAnnotationIndex = usize; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Constant { pub span: Span, pub user_ty: Option, pub literal: Const, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct SwitchTarget { pub value: u128, pub target: usize, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum BorrowKind { /// Data must be immutable and is aliasable. Shared, @@ -446,26 +448,26 @@ pub enum BorrowKind { }, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum MutBorrowKind { Default, TwoPhaseBorrow, ClosureCapture, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Mutability { Not, Mut, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Safety { Unsafe, Normal, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum PointerCoercion { /// Go from a fn-item type to a fn-pointer type. ReifyFnPointer, @@ -492,7 +494,7 @@ pub enum PointerCoercion { Unsize, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum CastKind { PointerExposeAddress, PointerFromExposedAddress, @@ -507,7 +509,7 @@ pub enum CastKind { Transmute, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum NullOp { /// Returns the size of a value of that type. SizeOf, diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 997576fc7cbf0..8f533349848e2 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -42,6 +42,10 @@ impl Instance { with(|context| context.instance_ty(self.def)) } + pub fn mangled_name(&self) -> String { + with(|context| context.instance_mangled_name(self.def)) + } + /// Resolve an instance starting from a function definition and generic arguments. pub fn resolve(def: FnDef, args: &GenericArgs) -> Result { with(|context| { diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs new file mode 100644 index 0000000000000..806dced71ff3e --- /dev/null +++ b/compiler/stable_mir/src/mir/visit.rs @@ -0,0 +1,414 @@ +//! # The Stable MIR Visitor +//! +//! ## Overview +//! +//! We currently only support an immutable visitor. +//! The structure of this visitor is similar to the ones internal to `rustc`, +//! and it follows the following conventions: +//! +//! For every mir item, the trait has a `visit_` and a `super_` method. +//! - `visit_`, by default, calls `super_` +//! - `super_`, by default, destructures the `` and calls `visit_` for +//! all sub-items that compose the original item. +//! +//! In order to implement a visitor, override the `visit_*` methods for the types you are +//! interested in analyzing, and invoke (within that method call) +//! `self.super_*` to continue to the traverse. +//! Avoid calling `super` methods in other circumstances. +//! +//! For the most part, we do not destructure things external to the +//! MIR, e.g., types, spans, etc, but simply visit them and stop. +//! This avoids duplication with other visitors like `TypeFoldable`. +//! +//! ## Updating +//! +//! The code is written in a very deliberate style intended to minimize +//! the chance of things being overlooked. +//! +//! Use pattern matching to reference fields and ensure that all +//! matches are exhaustive. +//! +//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. +//! That means you never write `..` to skip over fields, nor do you write `_` +//! to skip over variants in a `match`. +//! +//! The only place that `_` is acceptable is to match a field (or +//! variant argument) that does not require visiting. + +use crate::mir::*; +use crate::ty::{Const, GenericArgs, Region, Ty}; +use crate::{Opaque, Span}; + +pub trait MirVisitor { + fn visit_body(&mut self, body: &Body) { + self.super_body(body) + } + + fn visit_basic_block(&mut self, bb: &BasicBlock) { + self.super_basic_block(bb) + } + + fn visit_ret_decl(&mut self, local: Local, decl: &LocalDecl) { + self.super_ret_decl(local, decl) + } + + fn visit_arg_decl(&mut self, local: Local, decl: &LocalDecl) { + self.super_arg_decl(local, decl) + } + + fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) { + self.super_local_decl(local, decl) + } + + fn visit_statement(&mut self, stmt: &Statement, location: Location) { + self.super_statement(stmt, location) + } + + fn visit_terminator(&mut self, term: &Terminator, location: Location) { + self.super_terminator(term, location) + } + + fn visit_span(&mut self, span: &Span) { + self.super_span(span) + } + + fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { + self.super_place(place, ptx, location) + } + + fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) { + let _ = (local, ptx, location); + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) { + self.super_rvalue(rvalue, location) + } + + fn visit_operand(&mut self, operand: &Operand, location: Location) { + self.super_operand(operand, location) + } + + fn visit_user_type_projection(&mut self, projection: &UserTypeProjection) { + self.super_user_type_projection(projection) + } + + fn visit_ty(&mut self, ty: &Ty, location: Location) { + let _ = location; + self.super_ty(ty) + } + + fn visit_constant(&mut self, constant: &Constant, location: Location) { + self.super_constant(constant, location) + } + + fn visit_const(&mut self, constant: &Const, location: Location) { + self.super_const(constant, location) + } + + fn visit_region(&mut self, region: &Region, location: Location) { + let _ = location; + self.super_region(region) + } + + fn visit_args(&mut self, args: &GenericArgs, location: Location) { + let _ = location; + self.super_args(args) + } + + fn visit_assert_msg(&mut self, msg: &AssertMessage, location: Location) { + self.super_assert_msg(msg, location) + } + + fn super_body(&mut self, body: &Body) { + let Body { blocks, locals: _, arg_count } = body; + + for bb in blocks { + self.visit_basic_block(bb); + } + + self.visit_ret_decl(RETURN_LOCAL, body.ret_local()); + + for (idx, arg) in body.arg_locals().iter().enumerate() { + self.visit_arg_decl(idx + 1, arg) + } + + let local_start = arg_count + 1; + for (idx, arg) in body.arg_locals().iter().enumerate() { + self.visit_local_decl(idx + local_start, arg) + } + } + + fn super_basic_block(&mut self, bb: &BasicBlock) { + let BasicBlock { statements, terminator } = bb; + for stmt in statements { + self.visit_statement(stmt, Location(stmt.span)); + } + self.visit_terminator(terminator, Location(terminator.span)); + } + + fn super_local_decl(&mut self, local: Local, decl: &LocalDecl) { + let _ = local; + let LocalDecl { ty, span } = decl; + self.visit_ty(ty, Location(*span)); + } + + fn super_ret_decl(&mut self, local: Local, decl: &LocalDecl) { + self.super_local_decl(local, decl) + } + + fn super_arg_decl(&mut self, local: Local, decl: &LocalDecl) { + self.super_local_decl(local, decl) + } + + fn super_statement(&mut self, stmt: &Statement, location: Location) { + let Statement { kind, span } = stmt; + self.visit_span(span); + match kind { + StatementKind::Assign(place, rvalue) => { + self.visit_place(place, PlaceContext::MUTATING, location); + self.visit_rvalue(rvalue, location); + } + StatementKind::FakeRead(_, place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location); + } + StatementKind::SetDiscriminant { place, .. } => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + StatementKind::Deinit(place) => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + StatementKind::StorageLive(local) => { + self.visit_local(local, PlaceContext::NON_USE, location); + } + StatementKind::StorageDead(local) => { + self.visit_local(local, PlaceContext::NON_USE, location); + } + StatementKind::Retag(_, place) => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + StatementKind::PlaceMention(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location); + } + StatementKind::AscribeUserType { place, projections, variance: _ } => { + self.visit_place(place, PlaceContext::NON_USE, location); + self.visit_user_type_projection(projections); + } + StatementKind::Coverage(coverage) => visit_opaque(coverage), + StatementKind::Intrinsic(intrisic) => match intrisic { + NonDivergingIntrinsic::Assume(operand) => { + self.visit_operand(operand, location); + } + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src, + dst, + count, + }) => { + self.visit_operand(src, location); + self.visit_operand(dst, location); + self.visit_operand(count, location); + } + }, + StatementKind::ConstEvalCounter => {} + StatementKind::Nop => {} + } + } + + fn super_terminator(&mut self, term: &Terminator, location: Location) { + let Terminator { kind, span } = term; + self.visit_span(&span); + match kind { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Unreachable + | TerminatorKind::CoroutineDrop => {} + TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { + self.visit_operand(cond, location); + self.visit_assert_msg(msg, location); + } + TerminatorKind::Drop { place, target: _, unwind: _ } => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => { + self.visit_operand(func, location); + for arg in args { + self.visit_operand(arg, location); + } + self.visit_place(destination, PlaceContext::MUTATING, location); + } + TerminatorKind::InlineAsm { operands, .. } => { + for op in operands { + let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op; + if let Some(input) = in_value { + self.visit_operand(input, location); + } + if let Some(output) = out_place { + self.visit_place(output, PlaceContext::MUTATING, location); + } + } + } + TerminatorKind::Return => { + let local = RETURN_LOCAL; + self.visit_local(&local, PlaceContext::NON_MUTATING, location); + } + TerminatorKind::SwitchInt { discr, targets: _, otherwise: _ } => { + self.visit_operand(discr, location); + } + } + } + + fn super_span(&mut self, span: &Span) { + let _ = span; + } + + fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { + let _ = location; + let _ = ptx; + visit_opaque(&Opaque(place.projection.clone())); + } + + fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) { + match rvalue { + Rvalue::AddressOf(mutability, place) => { + let pcx = PlaceContext { is_mut: *mutability == Mutability::Mut }; + self.visit_place(place, pcx, location); + } + Rvalue::Aggregate(_, operands) => { + for op in operands { + self.visit_operand(op, location); + } + } + Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + self.visit_operand(lhs, location); + self.visit_operand(rhs, location); + } + Rvalue::Cast(_, op, ty) => { + self.visit_operand(op, location); + self.visit_ty(ty, location); + } + Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location); + } + Rvalue::Ref(region, kind, place) => { + self.visit_region(region, location); + let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) }; + self.visit_place(place, pcx, location); + } + Rvalue::Repeat(op, constant) => { + self.visit_operand(op, location); + self.visit_const(constant, location); + } + Rvalue::ShallowInitBox(op, ty) => { + self.visit_ty(ty, location); + self.visit_operand(op, location) + } + Rvalue::ThreadLocalRef(_) => {} + Rvalue::NullaryOp(_, ty) => { + self.visit_ty(ty, location); + } + Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => { + self.visit_operand(op, location); + } + } + } + + fn super_operand(&mut self, operand: &Operand, location: Location) { + match operand { + Operand::Copy(place) | Operand::Move(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location) + } + Operand::Constant(constant) => { + self.visit_constant(constant, location); + } + } + } + + fn super_user_type_projection(&mut self, projection: &UserTypeProjection) { + // This is a no-op on mir::Visitor. + let _ = projection; + } + + fn super_ty(&mut self, ty: &Ty) { + let _ = ty; + } + + fn super_constant(&mut self, constant: &Constant, location: Location) { + let Constant { span, user_ty: _, literal } = constant; + self.visit_span(span); + self.visit_const(literal, location); + } + + fn super_const(&mut self, constant: &Const, location: Location) { + let Const { kind: _, ty, id: _ } = constant; + self.visit_ty(ty, location); + } + + fn super_region(&mut self, region: &Region) { + let _ = region; + } + + fn super_args(&mut self, args: &GenericArgs) { + let _ = args; + } + + fn super_assert_msg(&mut self, msg: &AssertMessage, location: Location) { + match msg { + AssertMessage::BoundsCheck { len, index } => { + self.visit_operand(len, location); + self.visit_operand(index, location); + } + AssertMessage::Overflow(_, left, right) => { + self.visit_operand(left, location); + self.visit_operand(right, location); + } + AssertMessage::OverflowNeg(op) + | AssertMessage::DivisionByZero(op) + | AssertMessage::RemainderByZero(op) => { + self.visit_operand(op, location); + } + AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { //nothing to visit + } + AssertMessage::MisalignedPointerDereference { required, found } => { + self.visit_operand(required, location); + self.visit_operand(found, location); + } + } + } +} + +/// This function is a no-op that gets used to ensure this visitor is kept up-to-date. +/// +/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail +/// when trying to invoke `visit_opaque`. +/// +/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()` +/// by a `visit_` for your construct. +fn visit_opaque(_: &Opaque) {} + +/// The location of a statement / terminator in the code and the CFG. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Location(Span); + +impl Location { + pub fn span(&self) -> Span { + self.0 + } +} + +/// Information about a place's usage. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct PlaceContext { + /// Whether the access is mutable or not. Keep this private so we can increment the type in a + /// backward compatible manner. + is_mut: bool, +} + +impl PlaceContext { + const MUTATING: Self = PlaceContext { is_mut: true }; + const NON_MUTATING: Self = PlaceContext { is_mut: false }; + const NON_USE: Self = PlaceContext { is_mut: false }; + + pub fn is_mutating(&self) -> bool { + self.is_mut + } +} diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index e7440cc439b16..5dfaa0fd89158 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -22,12 +22,12 @@ impl Ty { } /// Represents a constant in MIR or from the Type system. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Const { /// The constant kind. - kind: ConstantKind, + pub(crate) kind: ConstantKind, /// The constant type. - ty: Ty, + pub(crate) ty: Ty, /// Used for internal tracking of the internal constant. pub id: ConstId, } @@ -54,12 +54,12 @@ pub struct ConstId(pub usize); type Ident = Opaque; -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Region { pub kind: RegionKind, } -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum RegionKind { ReEarlyBound(EarlyBoundRegion), ReLateBound(DebruijnIndex, BoundRegion), @@ -70,7 +70,7 @@ pub enum RegionKind { pub(crate) type DebruijnIndex = u32; -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct EarlyBoundRegion { pub def_id: RegionDef, pub index: u32, @@ -79,7 +79,7 @@ pub struct EarlyBoundRegion { pub(crate) type BoundVar = u32; -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct BoundRegion { pub var: BoundVar, pub kind: BoundRegionKind, @@ -87,7 +87,7 @@ pub struct BoundRegion { pub(crate) type UniverseIndex = u32; -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Placeholder { pub universe: UniverseIndex, pub bound: T, @@ -127,7 +127,7 @@ pub struct LineInfo { pub end_col: usize, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum TyKind { RigidTy(RigidTy), Alias(AliasKind, AliasTy), @@ -135,7 +135,7 @@ pub enum TyKind { Bound(usize, BoundTy), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum RigidTy { Bool, Char, @@ -236,7 +236,7 @@ pub struct ImplDef(pub DefId); pub struct RegionDef(pub DefId); /// A list of generic arguments. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct GenericArgs(pub Vec); impl std::ops::Index for GenericArgs { @@ -255,7 +255,7 @@ impl std::ops::Index for GenericArgs { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum GenericArgKind { Lifetime(Region), Type(Ty), @@ -284,13 +284,13 @@ impl GenericArgKind { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum TermKind { Type(Ty), Const(Const), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum AliasKind { Projection, Inherent, @@ -298,7 +298,7 @@ pub enum AliasKind { Weak, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct AliasTy { pub def_id: AliasDef, pub args: GenericArgs, @@ -306,7 +306,7 @@ pub struct AliasTy { pub type PolyFnSig = Binder; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct FnSig { pub inputs_and_output: Vec, pub c_variadic: bool, @@ -345,18 +345,18 @@ pub enum Abi { RiscvInterruptS, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Binder { pub value: T, pub bound_vars: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct EarlyBinder { pub value: T, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum BoundVariableKind { Ty(BoundTyKind), Region(BoundRegionKind), @@ -369,46 +369,46 @@ pub enum BoundTyKind { Param(ParamDef, String), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum BoundRegionKind { BrAnon, BrNamed(BrNamedDef, String), BrEnv, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum DynKind { Dyn, DynStar, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ExistentialPredicate { Trait(ExistentialTraitRef), Projection(ExistentialProjection), AutoTrait(TraitDef), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ExistentialTraitRef { pub def_id: TraitDef, pub generic_args: GenericArgs, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ExistentialProjection { pub def_id: TraitDef, pub generic_args: GenericArgs, pub term: TermKind, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ParamTy { pub index: u32, pub name: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct BoundTy { pub var: usize, pub kind: BoundTyKind, @@ -424,14 +424,14 @@ pub type Promoted = u32; pub type InitMaskMaterialized = Vec; /// Stores the provenance information of pointers stored in memory. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ProvenanceMap { /// Provenance in this map applies from the given offset for an entire pointer-size worth of /// bytes. Two entries in this map are always at least a pointer size apart. pub ptrs: Vec<(Size, Prov)>, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Allocation { pub bytes: Bytes, pub provenance: ProvenanceMap, @@ -439,7 +439,7 @@ pub struct Allocation { pub mutability: Mutability, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ConstantKind { Allocated(Allocation), Unevaluated(UnevaluatedConst), @@ -449,13 +449,13 @@ pub enum ConstantKind { ZeroSized, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ParamConst { pub index: u32, pub name: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct UnevaluatedConst { pub def: ConstDef, pub args: GenericArgs, @@ -469,7 +469,7 @@ pub enum TraitSpecializationKind { AlwaysApplicable, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct TraitDecl { pub def_id: TraitDef, pub unsafety: Safety, @@ -500,13 +500,13 @@ impl TraitDecl { pub type ImplTrait = EarlyBinder; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct TraitRef { pub def_id: TraitDef, pub args: GenericArgs, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Generics { pub parent: Option, pub parent_count: usize, @@ -517,14 +517,14 @@ pub struct Generics { pub host_effect_index: Option, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum GenericParamDefKind { Lifetime, Type { has_default: bool, synthetic: bool }, Const { has_default: bool }, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct GenericParamDef { pub name: super::Symbol, pub def_id: GenericDef, @@ -538,7 +538,7 @@ pub struct GenericPredicates { pub predicates: Vec<(PredicateKind, Span)>, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum PredicateKind { Clause(ClauseKind), ObjectSafe(TraitDef), @@ -550,7 +550,7 @@ pub enum PredicateKind { AliasRelate(TermKind, TermKind, AliasRelationDirection), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ClauseKind { Trait(TraitPredicate), RegionOutlives(RegionOutlivesPredicate), @@ -561,50 +561,50 @@ pub enum ClauseKind { ConstEvaluatable(Const), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ClosureKind { Fn, FnMut, FnOnce, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct SubtypePredicate { pub a: Ty, pub b: Ty, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CoercePredicate { pub a: Ty, pub b: Ty, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum AliasRelationDirection { Equate, Subtype, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct TraitPredicate { pub trait_ref: TraitRef, pub polarity: ImplPolarity, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct OutlivesPredicate(pub A, pub B); pub type RegionOutlivesPredicate = OutlivesPredicate; pub type TypeOutlivesPredicate = OutlivesPredicate; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ProjectionPredicate { pub projection_ty: AliasTy, pub term: TermKind, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ImplPolarity { Positive, Negative, diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs new file mode 100644 index 0000000000000..de5148bb5f420 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs @@ -0,0 +1,148 @@ +// run-pass +//! Sanity check Stable MIR Visitor + +// ignore-stage1 +// ignore-cross-compile +// ignore-remote +// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +// edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +extern crate rustc_middle; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use std::collections::HashSet; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::*; +use stable_mir::mir::MirVisitor; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +fn test_visitor(_tcx: TyCtxt<'_>) -> ControlFlow<()> { + let main_fn = stable_mir::entry_fn(); + let main_body = main_fn.unwrap().body(); + let main_visitor = TestVisitor::collect(&main_body); + assert!(main_visitor.ret_val.is_some()); + assert!(main_visitor.args.is_empty()); + assert!(main_visitor.tys.contains(&main_visitor.ret_val.unwrap().ty)); + assert!(!main_visitor.calls.is_empty()); + + 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_visitor = TestVisitor::collect(&exit_body); + assert!(exit_visitor.ret_val.is_some()); + assert_eq!(exit_visitor.args.len(), 1); + assert!(exit_visitor.tys.contains(&exit_visitor.ret_val.unwrap().ty)); + assert!(exit_visitor.tys.contains(&exit_visitor.args[0].ty)); + ControlFlow::Continue(()) +} + +struct TestVisitor<'a> { + pub body: &'a mir::Body, + pub tys: HashSet, + pub ret_val: Option, + pub args: Vec, + pub calls: Vec +} + +impl<'a> TestVisitor<'a> { + fn collect(body: &'a mir::Body) -> TestVisitor<'a> { + let mut visitor = TestVisitor { + body: &body, + tys: Default::default(), + ret_val: None, + args: vec![], + calls: vec![], + }; + visitor.visit_body(&body); + visitor + } +} + +impl<'a> mir::MirVisitor for TestVisitor<'a> { + fn visit_ty(&mut self, ty: &ty::Ty, _location: mir::visit::Location) { + self.tys.insert(*ty); + self.super_ty(ty) + } + + fn visit_ret_decl(&mut self, local: mir::Local, decl: &mir::LocalDecl) { + assert!(local == mir::RETURN_LOCAL); + assert!(self.ret_val.is_none()); + self.ret_val = Some(decl.clone()); + self.super_ret_decl(local, decl); + } + + fn visit_arg_decl(&mut self, local: mir::Local, decl: &mir::LocalDecl) { + self.args.push(decl.clone()); + assert_eq!(local, self.args.len()); + self.super_arg_decl(local, decl); + } + + fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) { + if let mir::TerminatorKind::Call { func, .. } = &term.kind { + let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).kind() else { unreachable! + () }; + let ty::RigidTy::FnDef(def, args) = ty else { unreachable!() }; + self.calls.push(mir::mono::Instance::resolve(def, &args).unwrap()); + } + self.super_terminator(term, location); + } +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "sim_visitor_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "-Cpanic=abort".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, tcx, test_visitor(tcx)).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + fn main() -> std::process::ExitCode {{ + let inputs = Inputs::new(); + let total = inputs.values.iter().sum(); + exit_fn(total) + }} + + fn exit_fn(code: u8) -> std::process::ExitCode {{ + std::process::ExitCode::from(code) + }} + + struct Inputs {{ + values: [u8; 3], + }} + + impl Inputs {{ + fn new() -> Inputs {{ + Inputs {{ values: [0, 1, 2] }} + }} + }} + "# + )?; + Ok(()) +} From 078144e3e893ef1e52eb6bfa909777ece444dedf Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 31 Oct 2023 11:50:30 +0100 Subject: [PATCH 5/7] prepopulate opaque ty storage before using it :> --- compiler/rustc_borrowck/src/type_check/mod.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8477686d4f577..9f30d9d8ba12b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -188,11 +188,7 @@ pub(crate) fn type_check<'mir, 'tcx>( &mut borrowck_context, ); - // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering - // predefined opaques in the typeck root. - if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) { - checker.register_predefined_opaques_in_new_solver(); - } + checker.check_user_type_annotations(); let mut verifier = TypeVerifier::new(&mut checker, promoted); verifier.visit_body(&body); @@ -1021,7 +1017,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { borrowck_context, reported_errors: Default::default(), }; - checker.check_user_type_annotations(); + + // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering + // predefined opaques in the typeck root. + if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) { + checker.register_predefined_opaques_in_new_solver(); + } + checker } From a8ece1190bf6b340175bc5b688e52bd29924f483 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 31 Oct 2023 15:05:06 +0100 Subject: [PATCH 6/7] Add support for pre-unix-epoch file dates on Apple platforms (#108277) Time in UNIX system calls counts from the epoch, 1970-01-01. The timespec struct used in various system calls represents this as a number of seconds and a number of nanoseconds. Nanoseconds are required to be between 0 and 999_999_999, because the portion outside that range should be represented in the seconds field; if nanoseconds were larger than 999_999_999, the seconds field should go up instead. Suppose you ask for the time 1969-12-31, what time is that? On UNIX systems that support times before the epoch, that's seconds=-86400, one day before the epoch. But now, suppose you ask for the time 1969-12-31 23:59:00.1. In other words, a tenth of a second after one minute before the epoch. On most UNIX systems, that's represented as seconds=-60, nanoseconds=100_000_000. The macOS bug is that it returns seconds=-59, nanoseconds=-900_000_000. While that's in some sense an accurate description of the time (59.9 seconds before the epoch), that violates the invariant of the timespec data structure: nanoseconds must be between 0 and 999999999. This causes this assertion in the Rust standard library. So, on macOS, if we get a Timespec value with seconds less than or equal to zero, and nanoseconds between -999_999_999 and -1 (inclusive), we can add 1_000_000_000 to the nanoseconds and subtract 1 from the seconds, and then convert. The resulting timespec value is still accepted by macOS, and when fed back into the OS, produces the same results. (If you set a file's mtime with that timestamp, then read it back, you get back the one with negative nanoseconds again.) Co-authored-by: Josh Triplett --- library/std/src/fs/tests.rs | 42 ++++++++++++++++++++++++++++++++ library/std/src/sys/unix/time.rs | 24 ++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 736b495343e91..547a7b7052f43 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1708,6 +1708,48 @@ fn test_file_times() { } } +#[test] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +fn test_file_times_pre_epoch_with_nanos() { + #[cfg(target_os = "ios")] + use crate::os::ios::fs::FileTimesExt; + #[cfg(target_os = "macos")] + use crate::os::macos::fs::FileTimesExt; + #[cfg(target_os = "tvos")] + use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "watchos")] + use crate::os::watchos::fs::FileTimesExt; + + let tmp = tmpdir(); + let file = File::create(tmp.join("foo")).unwrap(); + + for (accessed, modified, created) in [ + // The first round is to set filetimes to something we know works, but this time + // it's validated with nanoseconds as well which probe the numeric boundary. + ( + SystemTime::UNIX_EPOCH + Duration::new(12345, 1), + SystemTime::UNIX_EPOCH + Duration::new(54321, 100_000_000), + SystemTime::UNIX_EPOCH + Duration::new(32123, 999_999_999), + ), + // The second rounds uses pre-epoch dates along with nanoseconds that probe + // the numeric boundary. + ( + SystemTime::UNIX_EPOCH - Duration::new(1, 1), + SystemTime::UNIX_EPOCH - Duration::new(60, 100_000_000), + SystemTime::UNIX_EPOCH - Duration::new(3600, 999_999_999), + ), + ] { + let mut times = FileTimes::new(); + times = times.set_accessed(accessed).set_modified(modified).set_created(created); + file.set_times(times).unwrap(); + + let metadata = file.metadata().unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + assert_eq!(metadata.created().unwrap(), created); + } +} + #[test] #[cfg(windows)] fn windows_unix_socket_exists() { diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index e4540b99413bf..f2e86a4fb2b1b 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -76,6 +76,30 @@ impl Timespec { } const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + // On Apple OS, dates before epoch are represented differently than on other + // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1` + // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and + // `nanoseconds=-900_000_000` on Apple OS. + // + // To compensate, we first detect this special case by checking if both + // seconds and nanoseconds are in range, and then correct the value for seconds + // and nanoseconds to match the common unix representation. + // + // Please note that Apple OS nonetheless accepts the standard unix format when + // setting file times, which makes this compensation round-trippable and generally + // transparent. + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos" + ))] + let (tv_sec, tv_nsec) = + if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) { + (tv_sec - 1, tv_nsec + 1_000_000_000) + } else { + (tv_sec, tv_nsec) + }; assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); // SAFETY: The assert above checks tv_nsec is within the valid range Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } From 77174d3f29a44d2473a924a350862bd2824d06d6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 30 Oct 2023 09:58:54 +0000 Subject: [PATCH 7/7] Turn const_caller_location from a query to a hook --- compiler/rustc_const_eval/src/lib.rs | 2 +- .../rustc_const_eval/src/util/caller_location.rs | 13 ++++++++----- compiler/rustc_middle/src/hooks/mod.rs | 3 +++ compiler/rustc_middle/src/mir/consts.rs | 4 ++-- compiler/rustc_middle/src/query/mod.rs | 4 ---- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 7fc87ed888102..7d36e2eaefed6 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -49,7 +49,7 @@ pub fn provide(providers: &mut Providers) { const_eval::provide(providers); providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; - providers.const_caller_location = util::caller_location::const_caller_location_provider; + providers.hooks.const_caller_location = util::caller_location::const_caller_location_provider; providers.eval_to_valtree = |tcx, param_env_and_value| { let (param_env, raw) = param_env_and_value.into_parts(); const_eval::eval_to_valtree(tcx, param_env, raw) diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index d67863d75d668..4a3cfd50b4414 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -1,8 +1,9 @@ use rustc_hir::LangItem; use rustc_middle::mir; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; +use rustc_span::symbol::Symbol; use rustc_type_ir::Mutability; use crate::const_eval::{mk_eval_cx, CanAccessStatics, CompileTimeEvalContext}; @@ -49,11 +50,13 @@ fn alloc_caller_location<'mir, 'tcx>( } pub(crate) fn const_caller_location_provider( - tcx: TyCtxt<'_>, - (file, line, col): (Symbol, u32, u32), + tcx: TyCtxtAt<'_>, + file: Symbol, + line: u32, + col: u32, ) -> mir::ConstValue<'_> { trace!("const_caller_location: {}:{}:{}", file, line, col); - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No); + let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessStatics::No); let loc_place = alloc_caller_location(&mut ecx, file, line, col); if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 572751d951189..34838ca4302f3 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -67,4 +67,7 @@ declare_hooks! { /// Tries to destructure an `mir::Const` ADT or array into its variant index /// and its field values. This should only be used for pretty printing. hook try_destructure_mir_constant_for_diagnostics(val: mir::ConstValue<'tcx>, ty: Ty<'tcx>) -> Option>; + + /// Getting a &core::panic::Location referring to a span. + hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>; } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 92218f6948f47..a9d09709e8427 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -590,12 +590,12 @@ impl<'tcx> TyCtxt<'tcx> { pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let caller = self.sess.source_map().lookup_char_pos(topmost.lo()); - self.const_caller_location(( + self.const_caller_location( rustc_span::symbol::Symbol::intern( &caller.file.name.for_codegen(&self.sess).to_string_lossy(), ), caller.line as u32, caller.col_display as u32 + 1, - )) + ) } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 062b03e71fdc1..f9ec368361c59 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1101,10 +1101,6 @@ rustc_queries! { desc { "destructuring type level constant"} } - query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> mir::ConstValue<'tcx> { - desc { "getting a &core::panic::Location referring to a span" } - } - // FIXME get rid of this with valtrees query lit_to_const( key: LitToConstInput<'tcx>