diff options
| author | lcnr <rust@lcnr.de> | 2024-11-19 20:10:42 +0100 |
|---|---|---|
| committer | lcnr <rust@lcnr.de> | 2024-11-19 21:36:23 +0100 |
| commit | 7a90e84f4d760cb49035f1446d4c6cfb3993aad2 (patch) | |
| tree | bdb6b4df31aab75b58021f6e6d228db0ec3ae6fa /compiler/rustc_const_eval/src/interpret | |
| parent | b9dea31ea96b6eef91ab26307b0870783d4931ef (diff) | |
| download | rust-7a90e84f4d760cb49035f1446d4c6cfb3993aad2.tar.gz rust-7a90e84f4d760cb49035f1446d4c6cfb3993aad2.zip | |
`InterpCx` store `TypingEnv` instead of a `ParamEnv`
Diffstat (limited to 'compiler/rustc_const_eval/src/interpret')
11 files changed, 72 insertions, 74 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 6cfe4b21907..ed4a1a9e647 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -215,7 +215,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Even if `ty` is normalized, the search for the unsized tail will project // to fields, which can yield non-normalized types. So we need to provide a // normalization function. - let normalize = |ty| self.tcx.normalize_erasing_regions(self.typing_env(), ty); + let normalize = |ty| self.tcx.normalize_erasing_regions(self.typing_env, ty); ty.ptr_metadata_ty(*self.tcx, normalize) }; return interp_ok(meta_ty(caller) == meta_ty(callee)); @@ -652,35 +652,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Obtain the underlying trait we are working on, and the adjusted receiver argument. - let (trait_, dyn_ty, adjusted_recv) = - if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { - let recv = self.unpack_dyn_star(&receiver_place, data)?; + let (trait_, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) = + receiver_place.layout.ty.kind() + { + let recv = self.unpack_dyn_star(&receiver_place, data)?; - (data.principal(), recv.layout.ty, recv.ptr()) - } else { - // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. - // (For that reason we also cannot use `unpack_dyn_trait`.) - let receiver_tail = self - .tcx - .struct_tail_for_codegen(receiver_place.layout.ty, self.typing_env()); - let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { - span_bug!( - self.cur_span(), - "dynamic call on non-`dyn` type {}", - receiver_tail - ) - }; - assert!(receiver_place.layout.is_unsized()); - - // Get the required information from the vtable. - let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?; - let dyn_ty = self.get_ptr_vtable_ty(vptr, Some(receiver_trait))?; - - // It might be surprising that we use a pointer as the receiver even if this - // is a by-val case; this works because by-val passing of an unsized `dyn - // Trait` to a function is actually desugared to a pointer. - (receiver_trait.principal(), dyn_ty, receiver_place.ptr()) + (data.principal(), recv.layout.ty, recv.ptr()) + } else { + // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. + // (For that reason we also cannot use `unpack_dyn_trait`.) + let receiver_tail = + self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.typing_env); + let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { + span_bug!( + self.cur_span(), + "dynamic call on non-`dyn` type {}", + receiver_tail + ) }; + assert!(receiver_place.layout.is_unsized()); + + // Get the required information from the vtable. + let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?; + let dyn_ty = self.get_ptr_vtable_ty(vptr, Some(receiver_trait))?; + + // It might be surprising that we use a pointer as the receiver even if this + // is a by-val case; this works because by-val passing of an unsized `dyn + // Trait` to a function is actually desugared to a pointer. + (receiver_trait.principal(), dyn_ty, receiver_place.ptr()) + }; // Now determine the actual method to call. Usually we use the easy way of just // looking up the method at index `idx`. @@ -704,7 +704,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let concrete_method = Instance::expect_resolve_for_vtable( tcx, - self.typing_env(), + self.typing_env, def_id, instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args), self.cur_span(), diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 2d1bb5c9551..c95e51f0a1f 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -83,7 +83,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ty::FnDef(def_id, args) => { let instance = ty::Instance::resolve_for_fn_ptr( *self.tcx, - self.typing_env(), + self.typing_env, def_id, args, ) @@ -384,7 +384,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { // A<Struct> -> A<Trait> conversion let (src_pointee_ty, dest_pointee_ty) = - self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.typing_env()); + self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.typing_env); match (src_pointee_ty.kind(), dest_pointee_ty.kind()) { (&ty::Array(_, length), &ty::Slice(_)) => { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 31ce1600ce7..42fdf32e91e 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,10 +1,12 @@ +use std::assert_matches::debug_assert_matches; + use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::at::ToTrace; -use rustc_infer::traits::{ObligationCause, Reveal}; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ @@ -36,8 +38,9 @@ pub struct InterpCx<'tcx, M: Machine<'tcx>> { /// we are evaluating (if this is CTFE). pub tcx: TyCtxtAt<'tcx>, - /// Bounds in scope for polymorphic evaluations. - pub(crate) param_env: ty::ParamEnv<'tcx>, + /// The current context in case we're evaluating in a + /// polymorphic context. This always uses `ty::TypingMode::PostAnalysis` + pub typing_env: ty::TypingEnv<'tcx>, /// The virtual memory system. pub memory: Memory<'tcx, M>, @@ -68,7 +71,7 @@ where M: Machine<'tcx>, { fn typing_env(&self) -> ty::TypingEnv<'tcx> { - self.typing_env() + self.typing_env } } @@ -189,25 +192,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn new( tcx: TyCtxt<'tcx>, root_span: Span, - param_env: ty::ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, machine: M, ) -> Self { + // Const eval always happens in post analysis mode in order to be able to use the hidden types of + // opaque types. This is needed for trivial things like `size_of`, but also for using associated + // types that are not specified in the opaque type. We also use MIR bodies whose opaque types have + // already been revealed, so we'd be able to at least partially observe the hidden types anyways. + debug_assert_matches!(typing_env.typing_mode, ty::TypingMode::PostAnalysis); InterpCx { machine, tcx: tcx.at(root_span), - param_env, + typing_env, memory: Memory::new(), recursion_limit: tcx.recursion_limit(), } } - /// During CTFE we're always in `PostAnalysis` mode. - #[inline(always)] - pub fn typing_env(&self) -> ty::TypingEnv<'tcx> { - debug_assert_eq!(self.param_env.reveal(), Reveal::All); - ty::TypingEnv { typing_mode: ty::TypingMode::PostAnalysis, param_env: self.param_env } - } - /// Returns the span of the currently executed statement/terminator. /// This is the span typically used for error reporting. #[inline(always)] @@ -250,7 +251,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { #[inline] pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { - ty.is_freeze(*self.tcx, self.typing_env()) + ty.is_freeze(*self.tcx, self.typing_env) } pub fn load_mir( @@ -296,7 +297,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .instance .try_instantiate_mir_and_normalize_erasing_regions( *self.tcx, - self.typing_env(), + self.typing_env, ty::EarlyBinder::bind(value), ) .map_err(|_| ErrorHandled::TooGeneric(self.cur_span())) @@ -309,9 +310,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { args: GenericArgsRef<'tcx>, ) -> InterpResult<'tcx, ty::Instance<'tcx>> { trace!("resolve: {:?}, {:#?}", def, args); - trace!("param_env: {:#?}", self.param_env); + trace!("typing_env: {:#?}", self.typing_env); trace!("args: {:#?}", args); - match ty::Instance::try_resolve(*self.tcx, self.typing_env(), def, args) { + match ty::Instance::try_resolve(*self.tcx, self.typing_env, def, args) { Ok(Some(instance)) => interp_ok(instance), Ok(None) => throw_inval!(TooGeneric), @@ -332,7 +333,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return true; } // Slow path: spin up an inference context to check if these traits are sufficiently equal. - let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env()); + let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); let ocx = ObligationCtxt::new(&infcx); let cause = ObligationCause::dummy_with_span(self.cur_span()); // equate the two trait refs after normalization @@ -564,10 +565,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let val = if self.tcx.is_static(gid.instance.def_id()) { let alloc_id = self.tcx.reserve_and_set_static_alloc(gid.instance.def_id()); - let ty = instance.ty(self.tcx.tcx, self.typing_env()); + let ty = instance.ty(self.tcx.tcx, self.typing_env); mir::ConstAlloc { alloc_id, ty } } else { - self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.param_env.and(gid)))? + self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.typing_env.as_query_input(gid)))? }; self.raw_const_to_mplace(val) } @@ -579,7 +580,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| { - let const_val = val.eval(*ecx.tcx, ecx.typing_env(), span).map_err(|err| { + let const_val = val.eval(*ecx.tcx, ecx.typing_env, span).map_err(|err| { if M::ALL_CONSTS_ARE_PRECHECKED { match err { ErrorHandled::TooGeneric(..) => {}, diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 2d695688ae5..0cbb3b157f3 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -165,7 +165,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval InternKind::Static(Mutability::Not) => { ( // Outermost allocation is mutable if `!Freeze`. - if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env()) { + if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) { Mutability::Not } else { Mutability::Mut diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c8859ab3e88..a79923e8555 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -34,13 +34,12 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll /// inside an `InterpCx` and instead have their value computed directly from rustc internal info. pub(crate) fn eval_nullary_intrinsic<'tcx>( tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + typing_env: ty::TypingEnv<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>, ) -> InterpResult<'tcx, ConstValue<'tcx>> { let tp_ty = args.type_at(0); let name = tcx.item_name(def_id); - let typing_env = ty::TypingEnv { typing_mode: ty::TypingMode::PostAnalysis, param_env }; interp_ok(match name { sym::type_name => { ensure_monomorphic_enough(tcx, tp_ty)?; @@ -152,8 +151,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::type_name => Ty::new_static_str(self.tcx.tcx), _ => bug!(), }; - let val = - self.ctfe_query(|tcx| tcx.const_eval_global_id(self.param_env, gid, tcx.span))?; + let val = self + .ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?; let val = self.const_val_to_op(val, ty, Some(dest.layout))?; self.copy_op(&val, dest)?; } @@ -358,7 +357,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let should_panic = !self .tcx - .check_validity_requirement((requirement, self.typing_env().as_query_input(ty))) + .check_validity_requirement((requirement, self.typing_env.as_query_input(ty))) .map_err(|_| err_inval!(TooGeneric))?; if should_panic { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 9bae9c4b310..277d293597a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -859,8 +859,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # Global allocations if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) { - let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env()); - let mutbl = global_alloc.mutability(*self.tcx, self.typing_env()); + let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env); + let mutbl = global_alloc.mutability(*self.tcx, self.typing_env); let kind = match global_alloc { GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData, GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"), diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 201f1b5dc62..1fa5dcbd24f 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -533,7 +533,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } OffsetOf(fields) => { let val = - self.tcx.offset_of_subfield(self.typing_env(), layout, fields.iter()).bytes(); + self.tcx.offset_of_subfield(self.typing_env, layout, fields.iter()).bytes(); ImmTy::from_uint(val, usize_layout()) } UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx), diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 13fcccca76b..2beec544fad 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -540,7 +540,7 @@ where )?; if !mir_assign_valid_types( *self.tcx, - self.typing_env(), + self.typing_env, self.layout_of(normalized_place_ty)?, place.layout, ) { @@ -871,7 +871,7 @@ where // We do NOT compare the types for equality, because well-typed code can // actually "transmute" `&mut T` to `&T` in an assignment without a cast. let layout_compat = - mir_assign_valid_types(*self.tcx, self.typing_env(), src.layout(), dest.layout()); + mir_assign_valid_types(*self.tcx, self.typing_env, src.layout(), dest.layout()); if !allow_transmute && !layout_compat { span_bug!( self.cur_span(), diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 037c1a233ee..8ce11c71b8b 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -379,7 +379,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { for &const_ in body.required_consts() { let c = self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; - c.eval(*self.tcx, self.typing_env(), const_.span).map_err(|err| { + c.eval(*self.tcx, self.typing_env, const_.span).map_err(|err| { err.emit_note(*self.tcx); err })?; @@ -596,7 +596,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return interp_ok(layout); } - let layout = from_known_layout(self.tcx, self.typing_env(), layout, || { + let layout = from_known_layout(self.tcx, self.typing_env, layout, || { let local_ty = frame.body.local_decls[local].ty; let local_ty = self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index d4525243642..98aca37e73e 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -418,8 +418,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .collect::<InterpResult<'tcx, Vec<_>>>()?; let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); - let fn_sig = - self.tcx.normalize_erasing_late_bound_regions(self.typing_env(), fn_sig_binder); + let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, fn_sig_binder); let extra_args = &args[fn_sig.inputs().len()..]; let extra_args = self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 9ff24a01689..273eaf42d87 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -448,7 +448,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { meta: MemPlaceMeta<M::Provenance>, pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { - let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.typing_env()); + let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.typing_env); match tail.kind() { ty::Dynamic(data, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; @@ -568,7 +568,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind }); }; let (size, _align) = - global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env()); + global_alloc.size_and_align(*self.ecx.tcx, self.ecx.typing_env); if let GlobalAlloc::Static(did) = global_alloc { let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { @@ -619,7 +619,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { }; // Determine what it actually points to. let alloc_actual_mutbl = - global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env()); + global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env); // Mutable pointer to immutable memory is no good. if ptr_expected_mutbl == Mutability::Mut && alloc_actual_mutbl == Mutability::Not @@ -848,7 +848,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) { let tcx = *self.ecx.tcx; // Everything must be already interned. - let mutbl = tcx.global_alloc(alloc_id).mutability(tcx, self.ecx.typing_env()); + let mutbl = tcx.global_alloc(alloc_id).mutability(tcx, self.ecx.typing_env); if let Some((_, alloc)) = self.ecx.memory.alloc_map.get(alloc_id) { assert_eq!(alloc.mutability, mutbl); } @@ -955,7 +955,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ) -> Cow<'e, RangeSet> { assert!(layout.ty.is_union()); assert!(layout.is_sized(), "there are no unsized unions"); - let layout_cx = LayoutCx::new(*ecx.tcx, ecx.typing_env()); + let layout_cx = LayoutCx::new(*ecx.tcx, ecx.typing_env); return M::cached_union_data_range(ecx, layout.ty, || { let mut out = RangeSet(Vec::new()); union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out); @@ -1085,8 +1085,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, ) -> InterpResult<'tcx> { // Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory. if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) { - if !val.layout.is_zst() - && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.typing_env()) + if !val.layout.is_zst() && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.typing_env) { if !self.in_mutable_memory(val) { throw_validation_failure!(self.path, UnsafeCellInImmutable); |
