diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index 9873a58cfe99f..095d65b164151 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -346,7 +346,11 @@ impl Definitions { } /// Adds a definition with a parent definition. - pub fn create_def(&mut self, parent: LocalDefId, data: DefPathData) -> LocalDefId { + pub fn create_def( + &mut self, + parent: LocalDefId, + data: DefPathData, + ) -> (LocalDefId, DefPathHash) { // We can't use `Debug` implementation for `LocalDefId` here, since it tries to acquire a // reference to `Definitions` and we're already holding a mutable reference. debug!( @@ -375,7 +379,7 @@ impl Definitions { debug!("create_def: after disambiguation, key = {:?}", key); // Create the definition. - LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) } + (LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) }, def_path_hash) } #[inline(always)] diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 87fd4de26a5af..675f9d3b10bb8 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -172,6 +172,15 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } }); + // Make sure we actually have a value for static items, as they aren't cached in incremental. + // While we could just wait for codegen to invoke this, the definitions freeze below will cause + // that to ICE, because evaluating statics can create more items. + tcx.hir().par_body_owners(|item_def_id| { + if let DefKind::Static { .. } = tcx.def_kind(item_def_id) { + let _ = tcx.eval_static_initializer(item_def_id); + } + }); + // FIXME: Remove this when we implement creating `DefId`s // for anon constants during their parents' typeck. // Typeck all body owners in parallel will produce queries diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index f66b9eb3a2856..af63f275ae059 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -41,8 +41,9 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { tls::with_context_opt(|icx| { if let Some(icx) = icx { - if let Some(diagnostics) = icx.diagnostics { - diagnostics.lock().extend(Some(diagnostic.clone())); + if let Some(side_effects) = icx.side_effects { + let diagnostic = diagnostic.clone(); + side_effects.lock().diagnostics.push(diagnostic); } // Diagnostics are tracked, we can ignore the dependency. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a3976c3dda119..da3d92227f6ed 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1113,7 +1113,6 @@ rustc_queries! { "evaluating initializer of static `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } separate_provide_extern feedable } @@ -1841,6 +1840,7 @@ rustc_queries! { desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } separate_provide_extern feedable + cache_on_disk_if { def_id.is_local() } } query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 2841470d24878..93ee170928566 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -39,7 +39,7 @@ use rustc_hir::{HirId, Node, TraitCandidate}; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::cache::WithDepNode; -use rustc_query_system::dep_graph::DepNodeIndex; +use rustc_query_system::dep_graph::{DepNodeIndex, TaskDepsRef}; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::CrateType; @@ -50,10 +50,8 @@ use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{DUMMY_SP, Span}; use rustc_type_ir::TyKind::*; -use rustc_type_ir::fold::TypeFoldable; -use rustc_type_ir::lang_items::TraitSolverLangItem; -pub use rustc_type_ir::lift::Lift; -use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, search_graph}; +use rustc_type_ir::WithCachedTypeInfo; +use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; use tracing::{debug, instrument}; use crate::arena::Arena; @@ -1818,6 +1816,7 @@ impl<'tcx> TyCtxtAt<'tcx> { impl<'tcx> TyCtxt<'tcx> { /// `tcx`-dependent operations performed for every created definition. + #[instrument(level = "trace", skip(self))] pub fn create_def( self, parent: LocalDefId, @@ -1825,27 +1824,63 @@ impl<'tcx> TyCtxt<'tcx> { def_kind: DefKind, ) -> TyCtxtFeed<'tcx, LocalDefId> { let data = def_kind.def_path_data(name); - // The following call has the side effect of modifying the tables inside `definitions`. + // The following create_def calls have the side effect of modifying the tables inside `definitions`. // These very tables are relied on by the incr. comp. engine to decode DepNodes and to // decode the on-disk cache. // // Any LocalDefId which is used within queries, either as key or result, either: - // - has been created before the construction of the TyCtxt; + // - has been created before the construction of the TyCtxt, + // - has been created when marking a query as green (recreating definitions it created in the actual run), // - has been created by this call to `create_def`. - // As a consequence, this LocalDefId is always re-created before it is needed by the incr. - // comp. engine itself. // // This call also writes to the value of `source_span` and `expn_that_defined` queries. // This is fine because: // - those queries are `eval_always` so we won't miss their result changing; // - this write will have happened before these queries are called. - let def_id = self.untracked.definitions.write().create_def(parent, data); - - // This function modifies `self.definitions` using a side-effect. - // We need to ensure that these side effects are re-run by the incr. comp. engine. - // Depending on the forever-red node will tell the graph that the calling query - // needs to be re-evaluated. - self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE); + let def_id = tls::with_context(|icx| { + match icx.task_deps { + // Always gets rerun anyway, so nothing to replay + TaskDepsRef::EvalAlways => { + let def_id = self.untracked.definitions.write().create_def(parent, data).0; + trace!(?def_id, "eval always"); + def_id + } + // Top-level queries like the resolver get rerun every time anyway + TaskDepsRef::Ignore => { + let def_id = self.untracked.definitions.write().create_def(parent, data).0; + trace!(?def_id, "ignore"); + def_id + } + TaskDepsRef::Forbid => bug!( + "cannot create definition {parent:?}, {name:?}, {def_kind:?} without being able to register task dependencies" + ), + TaskDepsRef::Allow(_) => { + let (def_id, hash) = + self.untracked.definitions.write().create_def(parent, data); + trace!(?def_id, "record side effects"); + + icx.side_effects.as_ref().unwrap().lock().definitions.push(DefIdInfo { + parent, + data, + hash, + }); + def_id + } + TaskDepsRef::Replay { prev_side_effects, created_def_ids } => { + trace!(?created_def_ids, "replay side effects"); + trace!("num_defs : {}", prev_side_effects.definitions.len()); + let index = created_def_ids.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let prev_info = &prev_side_effects.definitions[index]; + let def_id = self.untracked.definitions.read().local_def_path_hash_to_def_id( + prev_info.hash, + &"should have already recreated def id in try_mark_green", + ); + assert_eq!(prev_info.data, data); + assert_eq!(prev_info.parent, parent); + def_id + } + } + }); let feed = TyCtxtFeed { tcx: self, key: def_id }; feed.def_kind(def_kind); diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index eaab8474dd20c..820a98e51a18b 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -1,8 +1,7 @@ use std::{mem, ptr}; use rustc_data_structures::sync::{self, Lock}; -use rustc_errors::DiagInner; -use thin_vec::ThinVec; +use rustc_query_system::query::QuerySideEffects; use super::{GlobalCtxt, TyCtxt}; use crate::dep_graph::TaskDepsRef; @@ -24,7 +23,7 @@ pub struct ImplicitCtxt<'a, 'tcx> { /// Where to store diagnostics for the current query job, if any. /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query. - pub diagnostics: Option<&'a Lock>>, + pub side_effects: Option<&'a Lock>, /// Used to prevent queries from calling too deeply. pub query_depth: usize, @@ -40,7 +39,7 @@ impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { ImplicitCtxt { tcx, query: None, - diagnostics: None, + side_effects: None, query_depth: 0, task_deps: TaskDepsRef::Ignore, } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index f72f656b2f8f9..95058c168a2af 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -7,7 +7,6 @@ use std::num::NonZero; use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; use rustc_data_structures::sync::Lock; use rustc_data_structures::unord::UnordMap; -use rustc_errors::DiagInner; use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::dep_graph::{ @@ -24,14 +23,13 @@ use rustc_middle::ty::{self, TyCtxt, TyEncoder}; use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext}; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffects, QueryStackFrame, - force_query, + DefIdInfo, QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffects, + QueryStackFrame, force_query, }; use rustc_query_system::{LayoutOfDepth, QueryOverflow}; use rustc_serialize::{Decodable, Encodable}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; -use thin_vec::ThinVec; use crate::QueryConfigRestored; @@ -127,7 +125,7 @@ impl QueryContext for QueryCtxt<'_> { self, token: QueryJobId, depth_limit: bool, - diagnostics: Option<&Lock>>, + side_effects: Option<&Lock>, compute: impl FnOnce() -> R, ) -> R { // The `TyCtxt` stored in TLS has the same global interner lifetime @@ -142,7 +140,7 @@ impl QueryContext for QueryCtxt<'_> { let new_icx = ImplicitCtxt { tcx: self.tcx, query: Some(token), - diagnostics, + side_effects, query_depth: current_icx.query_depth + depth_limit as usize, task_deps: current_icx.task_deps, }; @@ -174,6 +172,21 @@ impl QueryContext for QueryCtxt<'_> { crate_name: self.crate_name(LOCAL_CRATE), }); } + + #[tracing::instrument(level = "trace", skip(self, side_effects))] + fn apply_side_effects(self, side_effects: QuerySideEffects) { + let dcx = self.dep_context().sess().dcx(); + let QuerySideEffects { diagnostics, definitions } = side_effects; + + for diagnostic in diagnostics { + dcx.emit_diagnostic(diagnostic); + } + + for DefIdInfo { parent, data, hash } in definitions { + let (_def_id, h) = self.tcx.untracked().definitions.write().create_def(parent, data); + debug_assert_eq!(h, hash); + } + } } pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool { diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 4b47ce8389c3d..b7be32f7869e5 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::{QueryInvocationId, SelfProfilerRef}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc}; +use rustc_data_structures::sync::{AtomicU32, AtomicU64, AtomicUsize, Lock, Lrc}; use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable}; @@ -224,6 +224,15 @@ impl DepGraph { D::with_deps(TaskDepsRef::Ignore, op) } + pub(crate) fn with_replay( + &self, + prev_side_effects: &QuerySideEffects, + created_def_ids: &AtomicUsize, + op: impl FnOnce() -> R, + ) -> R { + D::with_deps(TaskDepsRef::Replay { prev_side_effects, created_def_ids }, op) + } + /// Used to wrap the deserialization of a query result from disk, /// This method enforces that no new `DepNodes` are created during /// query result deserialization. @@ -278,6 +287,7 @@ impl DepGraph { } #[inline(always)] + /// A helper for `codegen_cranelift`. pub fn with_task, A: Debug, R>( &self, key: DepNode, @@ -477,6 +487,12 @@ impl DepGraph { return; } TaskDepsRef::Ignore => return, + // We don't need to record dependencies when rerunning a query + // because we have no disk cache entry to load. The dependencies + // are preserved. + // FIXME: assert that the dependencies don't change instead of + // recording them. + TaskDepsRef::Replay { .. } => return, TaskDepsRef::Forbid => { // Reading is forbidden in this context. ICE with a useful error message. panic_on_forbidden_read(data, dep_node_index) @@ -583,6 +599,7 @@ impl DepGraph { edges.push(DepNodeIndex::FOREVER_RED_NODE); } TaskDepsRef::Ignore => {} + TaskDepsRef::Replay { .. } => {} TaskDepsRef::Forbid => { panic!("Cannot summarize when dependencies are not recorded.") } @@ -892,7 +909,7 @@ impl DepGraphData { Some(dep_node_index) } - /// Atomically emits some loaded diagnostics. + /// Atomically emits some loaded side effects. /// This may be called concurrently on multiple threads for the same dep node. #[cold] #[inline(never)] @@ -908,14 +925,10 @@ impl DepGraphData { // We were the first to insert the node in the set so this thread // must process side effects - // Promote the previous diagnostics to the current session. + // Promote the previous side effects to the current session. qcx.store_side_effects(dep_node_index, side_effects.clone()); - let dcx = qcx.dep_context().sess().dcx(); - - for diagnostic in side_effects.diagnostics { - dcx.emit_diagnostic(diagnostic); - } + qcx.apply_side_effects(side_effects); } } } @@ -1288,6 +1301,18 @@ pub enum TaskDepsRef<'a> { /// to ensure that the decoding process doesn't itself /// require the execution of any queries. Forbid, + /// Side effects from the previous run made available to + /// queries when they are reexecuted because their result was not + /// available in the cache. Whenever the query creates a new `DefId`, + /// it is checked against the entries in `QuerySideEffects::definitions` + /// to ensure that the new `DefId`s are the same as the ones that were + /// created the last time the query was executed. + Replay { + prev_side_effects: &'a QuerySideEffects, + /// Every new `DefId` is pushed here so we can check + /// that they match the cached ones. + created_def_ids: &'a AtomicUsize, + }, } #[derive(Debug)] diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index b81386f06ec7a..482aebef11d4e 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -15,9 +15,10 @@ use rustc_data_structures::stable_hasher::Hash64; use rustc_data_structures::sync::Lock; use rustc_errors::DiagInner; use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefPathHash; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; -use rustc_span::def_id::DefId; +use rustc_span::def_id::{DefId, LocalDefId}; use thin_vec::ThinVec; pub use self::config::{HashResult, QueryConfig}; @@ -75,21 +76,32 @@ pub struct QuerySideEffects { /// Stores any diagnostics emitted during query execution. /// These diagnostics will be re-emitted if we mark /// the query as green. - pub(super) diagnostics: ThinVec, + pub diagnostics: ThinVec, + /// Stores any `DefId`s that were created during query execution. + /// These `DefId`s will be re-created when we mark the query as green. + pub definitions: ThinVec, +} + +#[derive(Debug, Clone, Encodable, Decodable, PartialEq)] +pub struct DefIdInfo { + pub parent: LocalDefId, + pub data: rustc_hir::definitions::DefPathData, + pub hash: DefPathHash, } impl QuerySideEffects { /// Returns true if there might be side effects. #[inline] pub fn maybe_any(&self) -> bool { - let QuerySideEffects { diagnostics } = self; + let QuerySideEffects { diagnostics, definitions } = self; // Use `has_capacity` so that the destructor for `self.diagnostics` can be skipped // if `maybe_any` is known to be false. - diagnostics.has_capacity() + diagnostics.has_capacity() || definitions.has_capacity() } pub fn append(&mut self, other: QuerySideEffects) { - let QuerySideEffects { diagnostics } = self; + let QuerySideEffects { diagnostics, definitions } = self; diagnostics.extend(other.diagnostics); + definitions.extend(other.definitions); } } @@ -107,6 +119,9 @@ pub trait QueryContext: HasDepContext { /// Register diagnostics for the given node, for use in next session. fn store_side_effects(self, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects); + /// Actually execute the side effects + fn apply_side_effects(self, side_effects: QuerySideEffects); + /// Register diagnostics for the given node, for use in next session. fn store_side_effects_for_anon_node( self, @@ -121,7 +136,7 @@ pub trait QueryContext: HasDepContext { self, token: QueryJobId, depth_limit: bool, - diagnostics: Option<&Lock>>, + side_effects: Option<&Lock>, compute: impl FnOnce() -> R, ) -> R; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 6fb5e37d2d066..68d30cb97ebfe 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -12,11 +12,10 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sharded::Sharded; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lock; +use rustc_data_structures::sync::{AtomicUsize, Lock}; use rustc_data_structures::{outline, sync}; use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; -use thin_vec::ThinVec; use tracing::instrument; use super::QueryConfig; @@ -505,7 +504,7 @@ where let dep_node = dep_node_opt.get_or_insert_with(|| query.construct_dep_node(*qcx.dep_context(), &key)); - // The diagnostics for this query will be promoted to the current session during + // The side_effects for this query will be promoted to the current session during // `try_mark_green()`, so we can ignore them here. if let Some(ret) = qcx.start_query(job_id, false, None, || { try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node) @@ -515,10 +514,10 @@ where } let prof_timer = qcx.dep_context().profiler().query_provider(); - let diagnostics = Lock::new(ThinVec::new()); + let side_effects = Lock::new(QuerySideEffects::default()); let (result, dep_node_index) = - qcx.start_query(job_id, query.depth_limit(), Some(&diagnostics), || { + qcx.start_query(job_id, query.depth_limit(), Some(&side_effects), || { if query.anon() { return dep_graph_data.with_anon_task_inner( *qcx.dep_context(), @@ -542,7 +541,7 @@ where prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - let side_effects = QuerySideEffects { diagnostics: diagnostics.into_inner() }; + let side_effects = side_effects.into_inner(); if std::intrinsics::unlikely(side_effects.maybe_any()) { if query.anon() { @@ -625,8 +624,19 @@ where // recompute. let prof_timer = qcx.dep_context().profiler().query_provider(); + let prev_side_effects = qcx.load_side_effects(prev_dep_node_index); + let created_def_ids = AtomicUsize::new(0); // The dep-graph for this computation is already in-place. - let result = qcx.dep_context().dep_graph().with_ignore(|| query.compute(qcx, *key)); + let result = + qcx.dep_context() + .dep_graph() + .with_replay(&prev_side_effects, &created_def_ids, || query.compute(qcx, *key)); + + // We want to verify that the `DefId`s created by the call to `query.compute` are consistent with + // those from the previous compilation. We already checked at `DefId` creation time, that the + // created `DefId`s have the same parent and `DefPathData` as the cached ones. + // We check here that we have not missed any. + assert_eq!(created_def_ids.into_inner(), prev_side_effects.definitions.len()); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); diff --git a/tests/incremental/rpitit-feeding.rs b/tests/incremental/rpitit-feeding.rs new file mode 100644 index 0000000000000..0786879808f57 --- /dev/null +++ b/tests/incremental/rpitit-feeding.rs @@ -0,0 +1,27 @@ +//@ revisions: cpass cpass2 cpass3 + +// This test checks that creating a new `DefId` from within a query `A` +// recreates that `DefId` before reexecuting queries that depend on query `A`. +// Otherwise we'd end up referring to a `DefId` that doesn't exist. +// At present this is handled by always marking all queries as red if they create +// a new `DefId` and thus subsequently rerunning the query. + +trait Foo { + fn foo() -> impl Sized; +} + +#[cfg(any(cpass, cpass3))] +impl Foo for String { + fn foo() -> i32 { + 22 + } +} + +#[cfg(cpass2)] +impl Foo for String { + fn foo() -> u32 { + 22 + } +} + +fn main() {} diff --git a/tests/incremental/static_cycle/feed_nested.rs b/tests/incremental/static_cycle/feed_nested.rs new file mode 100644 index 0000000000000..4a2a10de49a1b --- /dev/null +++ b/tests/incremental/static_cycle/feed_nested.rs @@ -0,0 +1,28 @@ +//@ revisions:rpass1 rpass2 + +//! Test that the following order of instructions will not create duplicate +//! `DefId`s for the nested static items. +// ensure(eval_static_initializer(FOO)) +// -> try_mark_green(eval_static_initializer(FOO)) +// -> green +// -> replay side effects +// -> create some definitions. +// +// get(eval_static_initializer(FOO)) +// -> graph in place +// -> replay +// -> eval_static_initializer.compute +// -> how do we skip re-creating the same definitions ? + +#![feature(const_mut_refs)] +#![cfg_attr(rpass2, warn(dead_code))] + +pub static mut FOO: &mut i32 = &mut 42; + +pub static mut BAR: &mut i32 = unsafe { FOO }; + +fn main() { + unsafe { + assert_eq!(BAR as *mut i32, FOO as *mut i32); + } +}