|  | use std::fmt::Debug; | 
|  | use std::ops::ControlFlow; | 
|  |  | 
|  | use derive_where::derive_where; | 
|  | use rustc_type_ir::inherent::*; | 
|  | use rustc_type_ir::{ | 
|  | self as ty, InferCtxtLike, Interner, TrivialTypeTraversalImpls, TypeVisitable, | 
|  | TypeVisitableExt, TypeVisitor, | 
|  | }; | 
|  | use tracing::instrument; | 
|  |  | 
|  | /// Whether we do the orphan check relative to this crate or to some remote crate. | 
|  | #[derive(Copy, Clone, Debug)] | 
|  | pub enum InCrate { | 
|  | Local { mode: OrphanCheckMode }, | 
|  | Remote, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, Debug)] | 
|  | pub enum OrphanCheckMode { | 
|  | /// Proper orphan check. | 
|  | Proper, | 
|  | /// Improper orphan check for backward compatibility. | 
|  | /// | 
|  | /// In this mode, type params inside projections are considered to be covered | 
|  | /// even if the projection may normalize to a type that doesn't actually cover | 
|  | /// them. This is unsound. See also [#124559] and [#99554]. | 
|  | /// | 
|  | /// [#124559]: https://github.com/rust-lang/rust/issues/124559 | 
|  | /// [#99554]: https://github.com/rust-lang/rust/issues/99554 | 
|  | Compat, | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Copy, Clone)] | 
|  | pub enum Conflict { | 
|  | Upstream, | 
|  | Downstream, | 
|  | } | 
|  |  | 
|  | /// Returns whether all impls which would apply to the `trait_ref` | 
|  | /// e.g. `Ty: Trait<Arg>` are already known in the local crate. | 
|  | /// | 
|  | /// This both checks whether any downstream or sibling crates could | 
|  | /// implement it and whether an upstream crate can add this impl | 
|  | /// without breaking backwards compatibility. | 
|  | #[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)] | 
|  | pub fn trait_ref_is_knowable<Infcx, I, E>( | 
|  | infcx: &Infcx, | 
|  | trait_ref: ty::TraitRef<I>, | 
|  | mut lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>, | 
|  | ) -> Result<Result<(), Conflict>, E> | 
|  | where | 
|  | Infcx: InferCtxtLike<Interner = I>, | 
|  | I: Interner, | 
|  | E: Debug, | 
|  | { | 
|  | if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() | 
|  | { | 
|  | // A downstream or cousin crate is allowed to implement some | 
|  | // generic parameters of this trait-ref. | 
|  | return Ok(Err(Conflict::Downstream)); | 
|  | } | 
|  |  | 
|  | if trait_ref_is_local_or_fundamental(infcx.cx(), trait_ref) { | 
|  | // This is a local or fundamental trait, so future-compatibility | 
|  | // is no concern. We know that downstream/cousin crates are not | 
|  | // allowed to implement a generic parameter of this trait ref, | 
|  | // which means impls could only come from dependencies of this | 
|  | // crate, which we already know about. | 
|  | return Ok(Ok(())); | 
|  | } | 
|  |  | 
|  | // This is a remote non-fundamental trait, so if another crate | 
|  | // can be the "final owner" of the generic parameters of this trait-ref, | 
|  | // they are allowed to implement it future-compatibly. | 
|  | // | 
|  | // However, if we are a final owner, then nobody else can be, | 
|  | // and if we are an intermediate owner, then we don't care | 
|  | // about future-compatibility, which means that we're OK if | 
|  | // we are an owner. | 
|  | if orphan_check_trait_ref( | 
|  | infcx, | 
|  | trait_ref, | 
|  | InCrate::Local { mode: OrphanCheckMode::Proper }, | 
|  | &mut lazily_normalize_ty, | 
|  | )? | 
|  | .is_ok() | 
|  | { | 
|  | Ok(Ok(())) | 
|  | } else { | 
|  | Ok(Err(Conflict::Upstream)) | 
|  | } | 
|  | } | 
|  |  | 
|  | pub fn trait_ref_is_local_or_fundamental<I: Interner>(tcx: I, trait_ref: ty::TraitRef<I>) -> bool { | 
|  | trait_ref.def_id.is_local() || tcx.trait_is_fundamental(trait_ref.def_id) | 
|  | } | 
|  |  | 
|  | TrivialTypeTraversalImpls! { IsFirstInputType, } | 
|  |  | 
|  | #[derive(Debug, Copy, Clone)] | 
|  | pub enum IsFirstInputType { | 
|  | No, | 
|  | Yes, | 
|  | } | 
|  |  | 
|  | impl From<bool> for IsFirstInputType { | 
|  | fn from(b: bool) -> IsFirstInputType { | 
|  | match b { | 
|  | false => IsFirstInputType::No, | 
|  | true => IsFirstInputType::Yes, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive_where(Debug; I: Interner, T: Debug)] | 
|  | pub enum OrphanCheckErr<I: Interner, T> { | 
|  | NonLocalInputType(Vec<(I::Ty, IsFirstInputType)>), | 
|  | UncoveredTyParams(UncoveredTyParams<I, T>), | 
|  | } | 
|  |  | 
|  | #[derive_where(Debug; I: Interner, T: Debug)] | 
|  | pub struct UncoveredTyParams<I: Interner, T> { | 
|  | pub uncovered: T, | 
|  | pub local_ty: Option<I::Ty>, | 
|  | } | 
|  |  | 
|  | /// Checks whether a trait-ref is potentially implementable by a crate. | 
|  | /// | 
|  | /// The current rule is that a trait-ref orphan checks in a crate C: | 
|  | /// | 
|  | /// 1. Order the parameters in the trait-ref in generic parameters order | 
|  | /// - Self first, others linearly (e.g., `<U as Foo<V, W>>` is U < V < W). | 
|  | /// 2. Of these type parameters, there is at least one type parameter | 
|  | ///    in which, walking the type as a tree, you can reach a type local | 
|  | ///    to C where all types in-between are fundamental types. Call the | 
|  | ///    first such parameter the "local key parameter". | 
|  | ///     - e.g., `Box<LocalType>` is OK, because you can visit LocalType | 
|  | ///       going through `Box`, which is fundamental. | 
|  | ///     - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for | 
|  | ///       the same reason. | 
|  | ///     - but (knowing that `Vec<T>` is non-fundamental, and assuming it's | 
|  | ///       not local), `Vec<LocalType>` is bad, because `Vec<->` is between | 
|  | ///       the local type and the type parameter. | 
|  | /// 3. Before this local type, no generic type parameter of the impl must | 
|  | ///    be reachable through fundamental types. | 
|  | ///     - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental. | 
|  | ///     - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is | 
|  | ///       reachable through the fundamental type `Box`. | 
|  | /// 4. Every type in the local key parameter not known in C, going | 
|  | ///    through the parameter's type tree, must appear only as a subtree of | 
|  | ///    a type local to C, with only fundamental types between the type | 
|  | ///    local to C and the local key parameter. | 
|  | ///     - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`) | 
|  | ///     is bad, because the only local type with `T` as a subtree is | 
|  | ///     `LocalType<T>`, and `Vec<->` is between it and the type parameter. | 
|  | ///     - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because | 
|  | ///     the second occurrence of `T` is not a subtree of *any* local type. | 
|  | ///     - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of | 
|  | ///     `LocalType<Vec<T>>`, which is local and has no types between it and | 
|  | ///     the type parameter. | 
|  | /// | 
|  | /// The orphan rules actually serve several different purposes: | 
|  | /// | 
|  | /// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where | 
|  | ///    every type local to one crate is unknown in the other) can't implement | 
|  | ///    the same trait-ref. This follows because it can be seen that no such | 
|  | ///    type can orphan-check in 2 such crates. | 
|  | /// | 
|  | ///    To check that a local impl follows the orphan rules, we check it in | 
|  | ///    InCrate::Local mode, using type parameters for the "generic" types. | 
|  | /// | 
|  | ///    In InCrate::Local mode the orphan check succeeds if the current crate | 
|  | ///    is definitely allowed to implement the given trait (no false positives). | 
|  | /// | 
|  | /// 2. They ground negative reasoning for coherence. If a user wants to | 
|  | ///    write both a conditional blanket impl and a specific impl, we need to | 
|  | ///    make sure they do not overlap. For example, if we write | 
|  | ///    ```ignore (illustrative) | 
|  | ///    impl<T> IntoIterator for Vec<T> | 
|  | ///    impl<T: Iterator> IntoIterator for T | 
|  | ///    ``` | 
|  | ///    We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. | 
|  | ///    We can observe that this holds in the current crate, but we need to make | 
|  | ///    sure this will also hold in all unknown crates (both "independent" crates, | 
|  | ///    which we need for link-safety, and also child crates, because we don't want | 
|  | ///    child crates to get error for impl conflicts in a *dependency*). | 
|  | /// | 
|  | ///    For that, we only allow negative reasoning if, for every assignment to the | 
|  | ///    inference variables, every unknown crate would get an orphan error if they | 
|  | ///    try to implement this trait-ref. To check for this, we use InCrate::Remote | 
|  | ///    mode. That is sound because we already know all the impls from known crates. | 
|  | /// | 
|  | ///    In InCrate::Remote mode the orphan check succeeds if a foreign crate | 
|  | ///    *could* implement the given trait (no false negatives). | 
|  | /// | 
|  | /// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can | 
|  | ///    add "non-blanket" impls without breaking negative reasoning in dependent | 
|  | ///    crates. This is the "rebalancing coherence" (RFC 1023) restriction. | 
|  | /// | 
|  | ///    For that, we only allow a crate to perform negative reasoning on | 
|  | ///    non-local-non-`#[fundamental]` if there's a local key parameter as per (2). | 
|  | /// | 
|  | ///    Because we never perform negative reasoning generically (coherence does | 
|  | ///    not involve type parameters), this can be interpreted as doing the full | 
|  | ///    orphan check (using InCrate::Local mode), instantiating non-local known | 
|  | ///    types for all inference variables. | 
|  | /// | 
|  | ///    This allows for crates to future-compatibly add impls as long as they | 
|  | ///    can't apply to types with a key parameter in a child crate - applying | 
|  | ///    the rules, this basically means that every type parameter in the impl | 
|  | ///    must appear behind a non-fundamental type (because this is not a | 
|  | ///    type-system requirement, crate owners might also go for "semantic | 
|  | ///    future-compatibility" involving things such as sealed traits, but | 
|  | ///    the above requirement is sufficient, and is necessary in "open world" | 
|  | ///    cases). | 
|  | /// | 
|  | /// Note that this function is never called for types that have both type | 
|  | /// parameters and inference variables. | 
|  | #[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)] | 
|  | pub fn orphan_check_trait_ref<Infcx, I, E: Debug>( | 
|  | infcx: &Infcx, | 
|  | trait_ref: ty::TraitRef<I>, | 
|  | in_crate: InCrate, | 
|  | lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>, | 
|  | ) -> Result<Result<(), OrphanCheckErr<I, I::Ty>>, E> | 
|  | where | 
|  | Infcx: InferCtxtLike<Interner = I>, | 
|  | I: Interner, | 
|  | E: Debug, | 
|  | { | 
|  | if trait_ref.has_param() { | 
|  | panic!("orphan check only expects inference variables: {trait_ref:?}"); | 
|  | } | 
|  |  | 
|  | let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty); | 
|  | Ok(match trait_ref.visit_with(&mut checker) { | 
|  | ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), | 
|  | ControlFlow::Break(residual) => match residual { | 
|  | OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err), | 
|  | OrphanCheckEarlyExit::UncoveredTyParam(ty) => { | 
|  | // Does there exist some local type after the `ParamTy`. | 
|  | checker.search_first_local_ty = true; | 
|  | let local_ty = match trait_ref.visit_with(&mut checker) { | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), | 
|  | _ => None, | 
|  | }; | 
|  | Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { | 
|  | uncovered: ty, | 
|  | local_ty, | 
|  | })) | 
|  | } | 
|  | OrphanCheckEarlyExit::LocalTy(_) => Ok(()), | 
|  | }, | 
|  | }) | 
|  | } | 
|  |  | 
|  | struct OrphanChecker<'a, Infcx, I: Interner, F> { | 
|  | infcx: &'a Infcx, | 
|  | in_crate: InCrate, | 
|  | in_self_ty: bool, | 
|  | lazily_normalize_ty: F, | 
|  | /// Ignore orphan check failures and exclusively search for the first local type. | 
|  | search_first_local_ty: bool, | 
|  | non_local_tys: Vec<(I::Ty, IsFirstInputType)>, | 
|  | } | 
|  |  | 
|  | impl<'a, Infcx, I, F, E> OrphanChecker<'a, Infcx, I, F> | 
|  | where | 
|  | Infcx: InferCtxtLike<Interner = I>, | 
|  | I: Interner, | 
|  | F: FnOnce(I::Ty) -> Result<I::Ty, E>, | 
|  | { | 
|  | fn new(infcx: &'a Infcx, in_crate: InCrate, lazily_normalize_ty: F) -> Self { | 
|  | OrphanChecker { | 
|  | infcx, | 
|  | in_crate, | 
|  | in_self_ty: true, | 
|  | lazily_normalize_ty, | 
|  | search_first_local_ty: false, | 
|  | non_local_tys: Vec::new(), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn found_non_local_ty(&mut self, t: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> { | 
|  | self.non_local_tys.push((t, self.in_self_ty.into())); | 
|  | ControlFlow::Continue(()) | 
|  | } | 
|  |  | 
|  | fn found_uncovered_ty_param(&mut self, ty: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> { | 
|  | if self.search_first_local_ty { | 
|  | return ControlFlow::Continue(()); | 
|  | } | 
|  |  | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) | 
|  | } | 
|  |  | 
|  | fn def_id_is_local(&mut self, def_id: impl DefId<I>) -> bool { | 
|  | match self.in_crate { | 
|  | InCrate::Local { .. } => def_id.is_local(), | 
|  | InCrate::Remote => false, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | enum OrphanCheckEarlyExit<I: Interner, E> { | 
|  | NormalizationFailure(E), | 
|  | UncoveredTyParam(I::Ty), | 
|  | LocalTy(I::Ty), | 
|  | } | 
|  |  | 
|  | impl<'a, Infcx, I, F, E> TypeVisitor<I> for OrphanChecker<'a, Infcx, I, F> | 
|  | where | 
|  | Infcx: InferCtxtLike<Interner = I>, | 
|  | I: Interner, | 
|  | F: FnMut(I::Ty) -> Result<I::Ty, E>, | 
|  | { | 
|  | type Result = ControlFlow<OrphanCheckEarlyExit<I, E>>; | 
|  |  | 
|  | fn visit_region(&mut self, _r: I::Region) -> Self::Result { | 
|  | ControlFlow::Continue(()) | 
|  | } | 
|  |  | 
|  | fn visit_ty(&mut self, ty: I::Ty) -> Self::Result { | 
|  | let ty = self.infcx.shallow_resolve(ty); | 
|  | let ty = match (self.lazily_normalize_ty)(ty) { | 
|  | Ok(norm_ty) if norm_ty.is_ty_var() => ty, | 
|  | Ok(norm_ty) => norm_ty, | 
|  | Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), | 
|  | }; | 
|  |  | 
|  | let result = match ty.kind() { | 
|  | ty::Bool | 
|  | | ty::Char | 
|  | | ty::Int(..) | 
|  | | ty::Uint(..) | 
|  | | ty::Float(..) | 
|  | | ty::Str | 
|  | | ty::FnDef(..) | 
|  | | ty::Pat(..) | 
|  | | ty::FnPtr(..) | 
|  | | ty::Array(..) | 
|  | | ty::Slice(..) | 
|  | | ty::RawPtr(..) | 
|  | | ty::Never | 
|  | | ty::Tuple(..) | 
|  | // FIXME(unsafe_binders): Non-local? | 
|  | | ty::UnsafeBinder(_) => self.found_non_local_ty(ty), | 
|  |  | 
|  | ty::Param(..) => panic!("unexpected ty param"), | 
|  |  | 
|  | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { | 
|  | match self.in_crate { | 
|  | InCrate::Local { .. } => self.found_uncovered_ty_param(ty), | 
|  | // The inference variable might be unified with a local | 
|  | // type in that remote crate. | 
|  | InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), | 
|  | } | 
|  | } | 
|  |  | 
|  | // A rigid alias may normalize to anything. | 
|  | // * If it references an infer var, placeholder or bound ty, it may | 
|  | //   normalize to that, so we have to treat it as an uncovered ty param. | 
|  | // * Otherwise it may normalize to any non-type-generic type | 
|  | //   be it local or non-local. | 
|  | ty::Alias(kind, _) => { | 
|  | if ty.has_type_flags( | 
|  | ty::TypeFlags::HAS_TY_PLACEHOLDER | 
|  | | ty::TypeFlags::HAS_TY_BOUND | 
|  | | ty::TypeFlags::HAS_TY_INFER, | 
|  | ) { | 
|  | match self.in_crate { | 
|  | InCrate::Local { mode } => match kind { | 
|  | ty::Projection => { | 
|  | if let OrphanCheckMode::Compat = mode { | 
|  | ControlFlow::Continue(()) | 
|  | } else { | 
|  | self.found_uncovered_ty_param(ty) | 
|  | } | 
|  | } | 
|  | _ => self.found_uncovered_ty_param(ty), | 
|  | }, | 
|  | InCrate::Remote => { | 
|  | // The inference variable might be unified with a local | 
|  | // type in that remote crate. | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // Regarding *opaque types* specifically, we choose to treat them as non-local, | 
|  | // even those that appear within the same crate. This seems somewhat surprising | 
|  | // at first, but makes sense when you consider that opaque types are supposed | 
|  | // to hide the underlying type *within the same crate*. When an opaque type is | 
|  | // used from outside the module where it is declared, it should be impossible to | 
|  | // observe anything about it other than the traits that it implements. | 
|  | // | 
|  | // The alternative would be to look at the underlying type to determine whether | 
|  | // or not the opaque type itself should be considered local. | 
|  | // | 
|  | // However, this could make it a breaking change to switch the underlying hidden | 
|  | // type from a local type to a remote type. This would violate the rule that | 
|  | // opaque types should be completely opaque apart from the traits that they | 
|  | // implement, so we don't use this behavior. | 
|  | // Addendum: Moreover, revealing the underlying type is likely to cause cycle | 
|  | // errors as we rely on coherence / the specialization graph during typeck. | 
|  |  | 
|  | self.found_non_local_ty(ty) | 
|  | } | 
|  | } | 
|  |  | 
|  | // For fundamental types, we just look inside of them. | 
|  | ty::Ref(_, ty, _) => ty.visit_with(self), | 
|  | ty::Adt(def, args) => { | 
|  | if self.def_id_is_local(def.def_id()) { | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) | 
|  | } else if def.is_fundamental() { | 
|  | args.visit_with(self) | 
|  | } else { | 
|  | self.found_non_local_ty(ty) | 
|  | } | 
|  | } | 
|  | ty::Foreign(def_id) => { | 
|  | if self.def_id_is_local(def_id) { | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) | 
|  | } else { | 
|  | self.found_non_local_ty(ty) | 
|  | } | 
|  | } | 
|  | ty::Dynamic(tt, ..) => { | 
|  | let principal = tt.principal().map(|p| p.def_id()); | 
|  | if principal.is_some_and(|p| self.def_id_is_local(p)) { | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) | 
|  | } else { | 
|  | self.found_non_local_ty(ty) | 
|  | } | 
|  | } | 
|  | ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), | 
|  | ty::Closure(did, ..) => { | 
|  | if self.def_id_is_local(did) { | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) | 
|  | } else { | 
|  | self.found_non_local_ty(ty) | 
|  | } | 
|  | } | 
|  | ty::CoroutineClosure(did, ..) => { | 
|  | if self.def_id_is_local(did) { | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) | 
|  | } else { | 
|  | self.found_non_local_ty(ty) | 
|  | } | 
|  | } | 
|  | ty::Coroutine(did, ..) => { | 
|  | if self.def_id_is_local(did) { | 
|  | ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) | 
|  | } else { | 
|  | self.found_non_local_ty(ty) | 
|  | } | 
|  | } | 
|  | // This should only be created when checking whether we have to check whether some | 
|  | // auto trait impl applies. There will never be multiple impls, so we can just | 
|  | // act as if it were a local type here. | 
|  | ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), | 
|  | }; | 
|  | // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so | 
|  | // the first type we visit is always the self type. | 
|  | self.in_self_ty = false; | 
|  | result | 
|  | } | 
|  |  | 
|  | /// All possible values for a constant parameter already exist | 
|  | /// in the crate defining the trait, so they are always non-local[^1]. | 
|  | /// | 
|  | /// Because there's no way to have an impl where the first local | 
|  | /// generic argument is a constant, we also don't have to fail | 
|  | /// the orphan check when encountering a parameter or a generic constant. | 
|  | /// | 
|  | /// This means that we can completely ignore constants during the orphan check. | 
|  | /// | 
|  | /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples. | 
|  | /// | 
|  | /// [^1]: This might not hold for function pointers or trait objects in the future. | 
|  | /// As these should be quite rare as const arguments and especially rare as impl | 
|  | /// parameters, allowing uncovered const parameters in impls seems more useful | 
|  | /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. | 
|  | fn visit_const(&mut self, _c: I::Const) -> Self::Result { | 
|  | ControlFlow::Continue(()) | 
|  | } | 
|  | } |