Perform indexing during lowering.

Do not access DefId<->HirId maps before they are initialized.
This commit is contained in:
Camille GILLOT 2021-09-12 03:19:18 +02:00
parent c09eaea484
commit 1c7f85f17c
12 changed files with 118 additions and 170 deletions

View File

@ -1,5 +1,3 @@
use crate::hir::map::Map;
use crate::hir::{IndexedHir, OwnerNodes, ParentedNode};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
@ -12,16 +10,16 @@ use rustc_span::source_map::SourceMap;
use rustc_span::{Span, DUMMY_SP};
use std::iter::repeat;
use tracing::debug;
/// A visitor that walks over the HIR and collects `Node`s into a HIR map.
pub(super) struct NodeCollector<'a, 'hir> {
/// The crate
krate: &'hir Crate<'hir>,
/// Source map
source_map: &'a SourceMap,
bodies: &'a IndexVec<ItemLocalId, Option<&'hir Body<'hir>>>,
nodes: OwnerNodes<'hir>,
/// Outputs
nodes: IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>,
parenting: FxHashMap<LocalDefId, ItemLocalId>,
/// The parent of this node
@ -42,28 +40,21 @@ fn insert_vec_map<K: Idx, V: Clone>(map: &mut IndexVec<K, Option<V>>, k: K, v: V
map[k] = Some(v);
}
pub(super) fn collect<'a, 'hir: 'a>(
sess: &'a Session,
krate: &'hir Crate<'hir>,
definitions: &'a definitions::Definitions,
owner: LocalDefId,
) -> Option<IndexedHir<'hir>> {
let info = krate.owners.get(owner)?.as_ref()?;
let item = info.node;
pub(super) fn index_hir<'hir>(
sess: &Session,
definitions: &definitions::Definitions,
item: hir::OwnerNode<'hir>,
bodies: &IndexVec<ItemLocalId, Option<&'hir Body<'hir>>>,
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) {
let mut nodes = IndexVec::new();
nodes.push(Some(ParentedNode { parent: ItemLocalId::new(0), node: item.into() }));
let mut collector = NodeCollector {
krate,
source_map: sess.source_map(),
owner,
parent_node: ItemLocalId::new(0),
definitions,
nodes: OwnerNodes {
hash: info.hash,
node_hash: info.node_hash,
nodes,
bodies: &info.bodies,
},
owner: item.def_id(),
parent_node: ItemLocalId::new(0),
nodes,
bodies,
parenting: FxHashMap::default(),
};
@ -75,7 +66,7 @@ pub(super) fn collect<'a, 'hir: 'a>(
OwnerNode::ForeignItem(item) => collector.visit_foreign_item(item),
};
Some(IndexedHir { nodes: collector.nodes, parenting: collector.parenting })
(collector.nodes, collector.parenting)
}
impl<'a, 'hir> NodeCollector<'a, 'hir> {
@ -87,17 +78,11 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
// owner of that node.
if cfg!(debug_assertions) {
if hir_id.owner != self.owner {
let node_str = match self.definitions.opt_hir_id_to_local_def_id(hir_id) {
Some(def_id) => self.definitions.def_path(def_id).to_string_no_crate_verbose(),
None => format!("{:?}", node),
};
span_bug!(
span,
"inconsistent DepNode at `{:?}` for `{}`: \
panic!(
"inconsistent DepNode at `{:?}` for `{:?}`: \
current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})",
self.source_map.span_to_diagnostic_string(span),
node_str,
node,
self.definitions.def_path(self.owner).to_string_no_crate_verbose(),
self.owner,
self.definitions.def_path(hir_id.owner).to_string_no_crate_verbose(),
@ -107,7 +92,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
}
insert_vec_map(
&mut self.nodes.nodes,
&mut self.nodes,
hir_id.local_id,
ParentedNode { parent: self.parent_node, node: node },
);
@ -122,18 +107,12 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
}
fn insert_nested(&mut self, item: LocalDefId) {
let dk_parent = self.definitions.def_key(item).parent.unwrap();
let dk_parent = LocalDefId { local_def_index: dk_parent };
let dk_parent = self.definitions.local_def_id_to_hir_id(dk_parent);
debug_assert_eq!(dk_parent.owner, self.owner, "Different parents for {:?}", item);
if dk_parent.local_id != self.parent_node {
self.parenting.insert(item, self.parent_node);
}
self.parenting.insert(item, self.parent_node);
}
}
impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
type Map = Map<'hir>;
type Map = !;
/// Because we want to track parent items and so forth, enable
/// deep walking so that we walk nested items in the context of
@ -161,8 +140,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
}
fn visit_nested_body(&mut self, id: BodyId) {
let body = self.krate.body(id);
debug_assert_eq!(id.hir_id.owner, self.owner);
let body = self.bodies[id.hir_id.local_id].unwrap();
self.visit_body(body);
}

View File

@ -101,7 +101,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let old_len = self.in_scope_lifetimes.len();
let parent_generics =
match self.owners[parent_hir_id].as_ref().unwrap().node.expect_item().kind {
match self.owners[parent_hir_id].as_ref().unwrap().node().expect_item().kind {
hir::ItemKind::Impl(hir::Impl { ref generics, .. })
| hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params,
_ => &[],

View File

@ -33,6 +33,7 @@
#![feature(crate_visibility_modifier)]
#![feature(box_patterns)]
#![feature(iter_zip)]
#![feature(never_type)]
#![recursion_limit = "256"]
use rustc_ast::token::{self, Token};
@ -78,6 +79,7 @@ macro_rules! arena_vec {
mod asm;
mod block;
mod expr;
mod index;
mod item;
mod pat;
mod path;
@ -434,6 +436,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.local_node_ids.push(owner);
let item = f(self);
debug_assert_eq!(def_id, item.def_id());
let info = self.make_owner_info(item);
self.attrs = current_attrs;
@ -470,8 +473,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
let (hash, node_hash) = self.hash_body(node, &bodies);
let (nodes, parenting) =
index::index_hir(self.sess, self.resolver.definitions(), node, &bodies);
let nodes = hir::OwnerNodes { hash, node_hash, nodes, bodies };
hir::OwnerInfo { hash, node_hash, node, attrs, bodies, trait_map }
hir::OwnerInfo { nodes, parenting, attrs, trait_map }
}
/// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate

View File

@ -37,6 +37,7 @@ macro_rules! arena_types {
[few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>,
[] local: rustc_hir::Local<$tcx>,
[few] mod_: rustc_hir::Mod<$tcx>,
[] owner_info: rustc_hir::OwnerInfo<$tcx>,
[] param: rustc_hir::Param<$tcx>,
[] pat: rustc_hir::Pat<$tcx>,
[] path: rustc_hir::Path<$tcx>,

View File

@ -1,5 +1,5 @@
use crate::def::{CtorKind, DefKind, Res};
use crate::def_id::{DefId, CRATE_DEF_ID};
use crate::def_id::DefId;
crate use crate::hir_id::{HirId, ItemLocalId};
use crate::LangItem;
@ -663,18 +663,49 @@ pub struct WhereEqPredicate<'hir> {
pub rhs_ty: &'hir Ty<'hir>,
}
/// HIR node coupled with its parent's id in the same HIR owner.
///
/// The parent is trash when the node is a HIR owner.
#[derive(Clone, Debug)]
pub struct ParentedNode<'tcx> {
pub parent: ItemLocalId,
pub node: Node<'tcx>,
}
#[derive(Debug)]
pub struct OwnerInfo<'hir> {
pub node: OwnerNode<'hir>,
pub attrs: BTreeMap<ItemLocalId, &'hir [Attribute]>,
pub bodies: IndexVec<ItemLocalId, Option<&'hir Body<'hir>>>,
/// Map indicating what traits are in scope for places where this
/// is relevant; generated by resolve.
pub trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>,
pub struct OwnerNodes<'tcx> {
/// Pre-computed hash of the full HIR.
pub hash: Fingerprint,
/// Pre-computed hash of the top node.
pub node_hash: Fingerprint,
/// Full HIR for the current owner.
// The zeroth node's parent is trash, but is never accessed.
pub nodes: IndexVec<ItemLocalId, Option<ParentedNode<'tcx>>>,
/// Content of local bodies.
pub bodies: IndexVec<ItemLocalId, Option<&'tcx Body<'tcx>>>,
}
#[derive(Debug)]
pub struct OwnerInfo<'hir> {
/// Contents of the HIR.
pub nodes: OwnerNodes<'hir>,
/// Map from each nested owner to its parent's local id.
pub parenting: FxHashMap<LocalDefId, ItemLocalId>,
pub attrs: BTreeMap<ItemLocalId, &'hir [Attribute]>,
/// Map indicating what traits are in scope for places where this
/// is relevant; generated by resolve.
pub trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>,
}
impl<'tcx> OwnerInfo<'tcx> {
#[inline]
pub fn node(&self) -> OwnerNode<'tcx> {
use rustc_index::vec::Idx;
let node = self.nodes.nodes[ItemLocalId::new(0)].as_ref().unwrap().node;
let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode.
node
}
}
/// The top-level data structure that stores the entire contents of
@ -688,39 +719,6 @@ pub struct Crate<'hir> {
pub owners: IndexVec<LocalDefId, Option<OwnerInfo<'hir>>>,
}
impl Crate<'hir> {
pub fn module(&self) -> &'hir Mod<'hir> {
let i = self.owners[CRATE_DEF_ID].as_ref().unwrap().node;
if let OwnerNode::Crate(m) = i { m } else { panic!() }
}
pub fn item(&self, id: ItemId) -> &'hir Item<'hir> {
self.owners[id.def_id].as_ref().unwrap().node.expect_item()
}
pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> {
self.owners[id.def_id].as_ref().unwrap().node.expect_trait_item()
}
pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> {
self.owners[id.def_id].as_ref().unwrap().node.expect_impl_item()
}
pub fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> {
self.owners[id.def_id].as_ref().unwrap().node.expect_foreign_item()
}
pub fn body(&self, id: BodyId) -> &'hir Body<'hir> {
let HirId { owner, local_id } = id.hir_id;
self.owners[owner].as_ref().unwrap().bodies[local_id].unwrap()
}
pub fn attrs(&self, id: HirId) -> &'hir [Attribute] {
let HirId { owner, local_id } = id;
&self.owners[owner].as_ref().unwrap().attrs.get(&local_id).map(|la| *la).unwrap_or(&[])
}
}
/// A block of statements `{ .. }`, which may have a label (in this case the
/// `targeted_by_break` field will be `true`) and may be `unsafe` by means of
/// the `rules` being anything but `DefaultBlock`.

View File

@ -130,6 +130,28 @@ pub trait Map<'hir> {
fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir>;
}
// Used when no map is actually available, forcing manual implementation of nested visitors.
impl Map<'hir> for ! {
fn find(&self, _: HirId) -> Option<Node<'hir>> {
unreachable!()
}
fn body(&self, _: BodyId) -> &'hir Body<'hir> {
unreachable!()
}
fn item(&self, _: ItemId) -> &'hir Item<'hir> {
unreachable!()
}
fn trait_item(&self, _: TraitItemId) -> &'hir TraitItem<'hir> {
unreachable!()
}
fn impl_item(&self, _: ImplItemId) -> &'hir ImplItem<'hir> {
unreachable!()
}
fn foreign_item(&self, _: ForeignItemId) -> &'hir ForeignItem<'hir> {
unreachable!()
}
}
/// An erased version of `Map<'hir>`, using dynamic dispatch.
/// NOTE: This type is effectively only usable with `NestedVisitorMap::None`.
pub struct ErasedMap<'hir>(&'hir dyn Map<'hir>);

View File

@ -6,6 +6,7 @@
#![feature(in_band_lifetimes)]
#![feature(once_cell)]
#![feature(min_specialization)]
#![feature(never_type)]
#![recursion_limit = "256"]
#[macro_use]

View File

@ -1,8 +1,8 @@
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use crate::hir::{
BodyId, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, ItemId, Mod, TraitItem,
TraitItemId, Ty, VisibilityKind,
BodyId, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, ItemId, Mod, OwnerNodes,
TraitItem, TraitItemId, Ty, VisibilityKind,
};
use crate::hir_id::{HirId, ItemLocalId};
use rustc_span::def_id::DefPathHash;
@ -209,3 +209,12 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Item<'_> {
});
}
}
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'tcx> {
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
// We ignore the `nodes` and `bodies` fields since these refer to information included in
// `hash` which is hashed in the collector and used for the crate hash.
let OwnerNodes { hash, node_hash: _, nodes: _, bodies: _ } = *self;
hash.hash_stable(hcx, hasher);
}
}

View File

@ -92,9 +92,6 @@ macro_rules! arena_types {
[] tys: rustc_middle::ty::TyS<$tcx>,
[] predicates: rustc_middle::ty::PredicateInner<$tcx>,
// HIR query types
[] indexed_hir: rustc_middle::hir::IndexedHir<$tcx>,
// Note that this deliberately duplicates items in the `rustc_hir::arena`,
// since we need to allocate this type on both the `rustc_hir` arena
// (during lowering) and the `librustc_middle` arena (for decoding MIR)

View File

@ -1,4 +1,4 @@
use crate::hir::{IndexedHir, ModuleItems, Owner};
use crate::hir::{ModuleItems, Owner};
use crate::ty::TyCtxt;
use rustc_ast as ast;
use rustc_data_structures::fingerprint::Fingerprint;
@ -21,7 +21,6 @@ use rustc_target::spec::abi::Abi;
use std::collections::VecDeque;
pub mod blocks;
mod collector;
fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> {
match node {
@ -164,7 +163,7 @@ impl<'hir> Map<'hir> {
pub fn items(&self) -> impl Iterator<Item = &'hir Item<'hir>> + 'hir {
let krate = self.krate();
krate.owners.iter().filter_map(|owner| match owner.as_ref()?.node {
krate.owners.iter().filter_map(|owner| match owner.as_ref()?.node() {
OwnerNode::Item(item) => Some(item),
_ => None,
})
@ -497,7 +496,7 @@ impl<'hir> Map<'hir> {
.owners
.iter_enumerated()
.flat_map(move |(owner, owner_info)| {
let bodies = &owner_info.as_ref()?.bodies;
let bodies = &owner_info.as_ref()?.nodes.bodies;
Some(bodies.iter_enumerated().filter_map(move |(local_id, body)| {
if body.is_none() {
return None;
@ -518,7 +517,7 @@ impl<'hir> Map<'hir> {
par_iter(&self.krate().owners.raw).enumerate().for_each(|(owner, owner_info)| {
let owner = LocalDefId::new(owner);
if let Some(owner_info) = owner_info {
par_iter(&owner_info.bodies.raw).enumerate().for_each(|(local_id, body)| {
par_iter(&owner_info.nodes.bodies.raw).enumerate().for_each(|(local_id, body)| {
if body.is_some() {
let local_id = ItemLocalId::new(local_id);
let hir_id = HirId { owner, local_id };
@ -605,7 +604,7 @@ impl<'hir> Map<'hir> {
{
let krate = self.krate();
for owner in krate.owners.iter().filter_map(Option::as_ref) {
match owner.node {
match owner.node() {
OwnerNode::Item(item) => visitor.visit_item(item),
OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item),
OwnerNode::ImplItem(item) => visitor.visit_impl_item(item),
@ -621,7 +620,7 @@ impl<'hir> Map<'hir> {
V: itemlikevisit::ParItemLikeVisitor<'hir> + Sync + Send,
{
let krate = self.krate();
par_for_each_in(&krate.owners.raw, |owner| match owner.as_ref().map(|o| o.node) {
par_for_each_in(&krate.owners.raw, |owner| match owner.as_ref().map(OwnerInfo::node) {
Some(OwnerNode::Item(item)) => visitor.visit_item(item),
Some(OwnerNode::ForeignItem(item)) => visitor.visit_foreign_item(item),
Some(OwnerNode::ImplItem(item)) => visitor.visit_impl_item(item),
@ -1065,20 +1064,6 @@ impl<'hir> intravisit::Map<'hir> for Map<'hir> {
}
}
pub(super) fn index_hir<'tcx>(
tcx: TyCtxt<'tcx>,
owner: LocalDefId,
) -> Option<&'tcx IndexedHir<'tcx>> {
let map = collector::collect(
tcx.sess,
tcx.untracked_crate,
&tcx.untracked_resolutions.definitions,
owner,
)?;
Some(&*tcx.arena.alloc(map))
}
pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
debug_assert_eq!(crate_num, LOCAL_CRATE);
let mut hir_body_nodes: Vec<_> = tcx
@ -1088,7 +1073,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
.all_def_path_hashes_and_def_ids()
.filter_map(|(def_path_hash, local_def_index)| {
let def_id = LocalDefId { local_def_index };
let hash = tcx.index_hir(def_id).as_ref()?.nodes.hash;
let hash = tcx.hir_crate(()).owners[def_id].as_ref()?.nodes.hash;
Some((def_path_hash, hash, def_id))
})
.collect();

View File

@ -10,24 +10,13 @@ use crate::ty::query::Providers;
use crate::ty::TyCtxt;
use rustc_ast::Attribute;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::*;
use rustc_index::vec::{Idx, IndexVec};
use rustc_query_system::ich::StableHashingContext;
use rustc_span::DUMMY_SP;
use std::collections::BTreeMap;
/// Result of HIR indexing for a given HIR owner.
#[derive(Debug, HashStable)]
pub struct IndexedHir<'hir> {
/// Contents of the HIR.
nodes: OwnerNodes<'hir>,
/// Map from each nested owner to its parent's local id.
parenting: FxHashMap<LocalDefId, ItemLocalId>,
}
/// Top-level HIR node for current owner. This only contains the node for which
/// `HirId::local_id == 0`, and excludes bodies.
///
@ -47,38 +36,6 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> {
}
}
/// HIR node coupled with its parent's id in the same HIR owner.
///
/// The parent is trash when the node is a HIR owner.
#[derive(Clone, Debug)]
pub struct ParentedNode<'tcx> {
parent: ItemLocalId,
node: Node<'tcx>,
}
#[derive(Debug)]
pub struct OwnerNodes<'tcx> {
/// Pre-computed hash of the full HIR.
hash: Fingerprint,
/// Pre-computed hash of the top node.
node_hash: Fingerprint,
/// Full HIR for the current owner.
// The zeroth node's parent is trash, but is never accessed.
nodes: IndexVec<ItemLocalId, Option<ParentedNode<'tcx>>>,
/// Content of local bodies.
bodies: &'tcx IndexVec<ItemLocalId, Option<&'tcx Body<'tcx>>>,
}
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for OwnerNodes<'tcx> {
#[inline]
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
// We ignore the `nodes` and `bodies` fields since these refer to information included in
// `hash` which is hashed in the collector and used for the crate hash.
let OwnerNodes { hash, node_hash: _, nodes: _, bodies: _ } = *self;
hash.hash_stable(hcx, hasher);
}
}
/// Attributes owner by a HIR owner.
#[derive(Copy, Clone, Debug, HashStable)]
pub struct AttributeMap<'tcx> {
@ -125,23 +82,23 @@ pub fn provide(providers: &mut Providers) {
hir.local_def_id(hir.get_module_parent_node(hir.local_def_id_to_hir_id(id)))
};
providers.hir_crate = |tcx, ()| tcx.untracked_crate;
providers.index_hir = map::index_hir;
providers.crate_hash = map::crate_hash;
providers.hir_module_items = map::hir_module_items;
providers.hir_owner = |tcx, id| {
let owner = tcx.index_hir(id)?;
let node = owner.nodes.nodes[ItemLocalId::new(0)].as_ref().unwrap().node;
let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode.
let owner = tcx.hir_crate(()).owners[id].as_ref()?;
let node = owner.node();
Some(Owner { node, node_hash: owner.nodes.node_hash })
};
providers.hir_owner_nodes = |tcx, id| tcx.index_hir(id).map(|i| &i.nodes);
providers.hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map(|i| &i.nodes);
providers.hir_owner_parent = |tcx, id| {
let parent = tcx.untracked_resolutions.definitions.def_key(id).parent;
let parent = parent.map_or(CRATE_HIR_ID, |local_def_index| {
let def_id = LocalDefId { local_def_index };
let mut parent_hir_id =
tcx.untracked_resolutions.definitions.local_def_id_to_hir_id(def_id);
if let Some(local_id) = tcx.index_hir(parent_hir_id.owner).unwrap().parenting.get(&id) {
if let Some(local_id) =
tcx.hir_crate(()).owners[parent_hir_id.owner].as_ref().unwrap().parenting.get(&id)
{
parent_hir_id.local_id = *local_id;
}
parent_hir_id

View File

@ -40,13 +40,6 @@ rustc_queries! {
desc { "get the crate HIR" }
}
/// The indexed HIR. This can be conveniently accessed by `tcx.hir()`.
/// Avoid calling this query directly.
query index_hir(_: LocalDefId) -> Option<&'tcx crate::hir::IndexedHir<'tcx>> {
eval_always
desc { "index HIR" }
}
/// The items in a module.
///
/// This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`.
@ -76,7 +69,7 @@ rustc_queries! {
///
/// This can be conveniently accessed by methods on `tcx.hir()`.
/// Avoid calling this query directly.
query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> {
query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> {
desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) }
}