diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index d44faad017ee5..8716acb09da6c 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -197,7 +197,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const { - anon_const: self.lower_const_block(anon_const), + anon_const: self.lower_const_block(anon_const, &[]), }, InlineAsmOperand::Sym { sym } => { let static_def_id = self diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c8a311443a585..326f58f09ebaa 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -106,7 +106,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_const_block(c)), + ExprKind::ConstBlock(c) => { + hir::ExprKind::ConstBlock(self.lower_const_block(c, attrs)) + } ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); let count = self.lower_array_length_to_const_arg(count); @@ -391,12 +393,20 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { + pub(crate) fn lower_const_block( + &mut self, + c: &AnonConst, + attrs: &'hir [hir::Attribute], + ) -> hir::ConstBlock { self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); + let hir_id = this.lower_node_id(c.id); + if !attrs.is_empty() { + this.attrs.insert(hir_id.local_id, attrs); + } hir::ConstBlock { def_id, - hir_id: this.lower_node_id(c.id), + hir_id, body: this.lower_const_body(c.value.span, Some(&c.value)), } }) diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 3571fd6523974..54b6a7390d349 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -399,7 +399,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ExprKind::Lit(lit) => { hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false } } - ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)), + ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c, &[])), ExprKind::IncludedBytes(byte_sym) => hir::PatExprKind::Lit { lit: respan(span, LitKind::ByteStr(*byte_sym, StrStyle::Cooked)), negated: false, diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 7323db06a8f1f..6e5ff0d290432 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -63,6 +63,7 @@ impl CombineAttributeParser for AllowConstFnUnstableParser { Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Expression), // FIXME: should only allow inline consts ]); const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]); diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6d4f77ef1751b..02ddf9bfef122 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -243,7 +243,22 @@ impl AttributeParser for ConstStabilityParser { this.promotable = true; }), ]; - const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Use), // FIXME I don't think this does anything? + Allow(Target::Const), + Allow(Target::AssocConst), + Allow(Target::Trait), + Allow(Target::Static), + Allow(Target::Expression), // FIXME: we really only want to allow inline consts + Allow(Target::Crate), + Allow(Target::MacroDef), // FIXME(oli-obk): remove this and eliminate the manual check for it + ]); fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option { if self.promotable { diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index e9824400ab7a1..5f595bb9937cf 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -54,9 +54,11 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { pub fn enforce_recursive_const_stability(&self) -> bool { // We can skip this if neither `staged_api` nor `-Zforce-unstable-if-unmarked` are enabled, // since in such crates `lookup_const_stability` will always be `None`. - self.const_kind == Some(hir::ConstContext::ConstFn) - && (self.tcx.features().staged_api() - || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) + matches!( + self.const_kind, + Some(hir::ConstContext::ConstFn | hir::ConstContext::Const { inline: true }) + ) && (self.tcx.features().staged_api() + || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) && is_fn_or_trait_safe_to_expose_on_stable(self.tcx, self.def_id().to_def_id()) } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 21191085253bd..9a8b04e17e4ea 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -576,6 +576,11 @@ passes_unstable_attr_for_already_stable_feature = .item = the stability attribute annotates this item .help = consider removing the attribute +passes_unstable_inline_const_in_const = + const stability of inline consts must match const stability of containing item + .help = did you mean to use `rustc_allow_const_fn_unstable`? + .note = stability marker of containing item defined here + passes_unsupported_attributes_in_where = most attributes are not supported in `where` clauses .help = only `#[cfg]` and `#[cfg_attr]` are supported diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 7abd2c703aeb4..2f0991bc1f6c2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1065,6 +1065,16 @@ pub(crate) struct MissingConstErr { pub fn_sig_span: Span, } +#[derive(Diagnostic)] +#[diag(passes_unstable_inline_const_in_const)] +pub(crate) struct UnstableInlineConstInConst { + #[primary_span] + #[help] + pub span: Span, + #[note] + pub parent_span: Option, +} + #[derive(Diagnostic)] #[diag(passes_const_stable_not_stable)] pub(crate) struct ConstStableNotStable { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 9d94c4cc62256..0b80f6eb7601b 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -334,9 +334,12 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { macro_rules! find_attr_span { ($name:ident) => {{ - let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); + find_attr_span!($name, def_id) + }}; + ($name:ident, $id: expr) => {{ + let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id($id)); find_attr!(attrs, AttributeKind::$name { span, .. } => *span) - }} + }}; } if stab.is_none() @@ -398,6 +401,16 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); } + if let DefKind::InlineConst = self.tcx.def_kind(def_id) + && const_stab.is_some() + && let parent = self.tcx.local_parent(def_id) + && self.tcx.lookup_const_stability(parent) != const_stab + && let Some(span) = find_attr_span!(ConstStability) + { + let parent_span = find_attr_span!(ConstStability, parent); + self.tcx.dcx().emit_err(errors::UnstableInlineConstInConst { span, parent_span }); + } + // If this is marked const *stable*, it must also be regular-stable. if let Some(const_stab) = const_stab && let Some(fn_sig) = fn_sig @@ -478,6 +491,11 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { intravisit::walk_item(self, i) } + fn visit_inline_const(&mut self, c: &'tcx rustc_hir::ConstBlock) -> Self::Result { + self.check_compatible_stability(c.def_id); + intravisit::walk_inline_const(self, c) + } + fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { self.check_compatible_stability(ti.owner_id.def_id); self.check_missing_stability(ti.owner_id.def_id); @@ -601,7 +619,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let attrs = self.tcx.hir_attrs(item.hir_id()); let stab = find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span)); - // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem let const_stab = find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); let unstable_feature_stab = diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 1cce36606d2c0..539f584006014 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -321,7 +321,12 @@ impl WithHeader { // of the header, past the padding, so the assigned type makes sense. // It also ensures that the address at the end of the header is sufficiently // aligned for T. - let alloc: &::Metadata = const { + // We generate the vtable in a const block instead of having the compiler + // generate it for us. The basics to allocate new memory during ctfe are + // unstable, but we can always change the intrinsic logic to support the + // needs of new_unsize_zst in the future. + let alloc: &::Metadata = #[rustc_allow_const_fn_unstable(const_heap)] + const { // FIXME: just call `WithHeader::alloc_layout` with size reset to 0. // Currently that's blocked on `Layout::extend` not being `const fn`. diff --git a/library/core/src/any.rs b/library/core/src/any.rs index ff55793340bd0..dcfd9957f7e46 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -864,7 +864,10 @@ impl fmt::Debug for TypeId { #[stable(feature = "type_name", since = "1.38.0")] #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub const fn type_name() -> &'static str { - const { intrinsics::type_name::() } + #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] + const { + intrinsics::type_name::() + } } /// Returns the type name of the pointed-to value as a string slice. diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 1c6137191324c..17792dca583d2 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -31,7 +31,6 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { } /// See [`Drain::new`]; this is our fake iterator. -#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] pub(super) struct Drain<'l, 'f, T, const N: usize, F> { // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 722e16ab0fa49..76f30053a137f 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2881,6 +2881,7 @@ pub const fn type_name() -> &'static str; #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] +#[rustc_intrinsic_const_stable_indirect] pub const fn type_id() -> crate::any::TypeId; /// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index ad5fda0cfe4db..033a006d5ef4a 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1229,7 +1229,10 @@ pub const fn discriminant(v: &T) -> Discriminant { #[rustc_const_unstable(feature = "variant_count", issue = "73662")] #[rustc_diagnostic_item = "mem_variant_count"] pub const fn variant_count() -> usize { - const { intrinsics::variant_count::() } + #[rustc_const_unstable(feature = "variant_count", issue = "73662")] + const { + intrinsics::variant_count::() + } } /// Provides associated constants for various useful properties of types, diff --git a/tests/ui/consts/min_const_fn/const-fn-lang-feature-inline-const.rs b/tests/ui/consts/min_const_fn/const-fn-lang-feature-inline-const.rs new file mode 100644 index 0000000000000..e412b60ee89aa --- /dev/null +++ b/tests/ui/consts/min_const_fn/const-fn-lang-feature-inline-const.rs @@ -0,0 +1,47 @@ +//! Ensure we reject invalid combinations of feature gating inline consts + +#![feature(staged_api, abi_unadjusted)] +#![stable(feature = "rust_test", since = "1.0.0")] + +#[unstable(feature = "abi_unadjusted", issue = "42")] +#[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] +const fn my_fun() {} + +#[stable(feature = "asdf", since = "99.0.0")] +#[rustc_const_stable(feature = "asdf", since = "99.0.0")] +const fn my_fun2() { + #[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] + //~^ ERROR: must match const stability of containing item + const { + my_fun() + } +} + +// Check that const stable const blocks can only call const stable things +#[stable(feature = "asdf", since = "99.0.0")] +#[rustc_const_stable(feature = "asdf", since = "99.0.0")] +const fn my_fun3() { + #[rustc_const_stable(feature = "asdf", since = "99.0.0")] + const { + my_fun() + //~^ ERROR: (indirectly) exposed to stable + } +} + +// Check that const stable const blocks can only call const stable things +#[stable(feature = "asdf", since = "99.0.0")] +#[rustc_const_stable(feature = "asdf", since = "99.0.0")] +const fn my_fun4() { + const { + my_fun() + //~^ ERROR: (indirectly) exposed to stable + } +} + +fn main() { + #[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] + //~^ ERROR: must match const stability of containing item + const { + my_fun2() + }; +} diff --git a/tests/ui/consts/min_const_fn/const-fn-lang-feature-inline-const.stderr b/tests/ui/consts/min_const_fn/const-fn-lang-feature-inline-const.stderr new file mode 100644 index 0000000000000..8a300fccec688 --- /dev/null +++ b/tests/ui/consts/min_const_fn/const-fn-lang-feature-inline-const.stderr @@ -0,0 +1,55 @@ +error: const stability of inline consts must match const stability of containing item. + --> $DIR/const-fn-lang-feature-inline-const.rs:13:5 + | +LL | #[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use `rustc_allow_const_fn_unstable`? + --> $DIR/const-fn-lang-feature-inline-const.rs:13:5 + | +LL | #[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: stability marker of containing item defined here + --> $DIR/const-fn-lang-feature-inline-const.rs:11:1 + | +LL | #[rustc_const_stable(feature = "asdf", since = "99.0.0")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: const stability of inline consts must match const stability of containing item. + --> $DIR/const-fn-lang-feature-inline-const.rs:42:5 + | +LL | #[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use `rustc_allow_const_fn_unstable`? + --> $DIR/const-fn-lang-feature-inline-const.rs:42:5 + | +LL | #[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(abi_unadjusted)]` + --> $DIR/const-fn-lang-feature-inline-const.rs:26:9 + | +LL | my_fun() + | ^^^^^^^^ + | + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` + | +LL | const #[rustc_const_unstable(feature = "...", issue = "...")] + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(abi_unadjusted)]` + --> $DIR/const-fn-lang-feature-inline-const.rs:36:9 + | +LL | my_fun() + | ^^^^^^^^ + | + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features +help: if the caller is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]` + | +LL | const #[rustc_const_unstable(feature = "...", issue = "...")] + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/consts/min_const_fn/const-fn-lang-feature.rs b/tests/ui/consts/min_const_fn/const-fn-lang-feature.rs index 63ef12085f12d..9262af79d8f31 100644 --- a/tests/ui/consts/min_const_fn/const-fn-lang-feature.rs +++ b/tests/ui/consts/min_const_fn/const-fn-lang-feature.rs @@ -2,7 +2,7 @@ //! enabling the feature gate actually lets us call the function. //@ check-pass -#![feature(staged_api, abi_unadjusted)] +#![feature(staged_api, abi_unadjusted, rustc_allow_const_fn_unstable)] #![stable(feature = "rust_test", since = "1.0.0")] #[unstable(feature = "abi_unadjusted", issue = "42")] @@ -15,6 +15,39 @@ const fn my_fun2() { my_fun() } +// Check that we can call unstable things in unstable const blocks +// in unstable fns. +#[unstable(feature = "abi_unadjusted", issue = "42")] +#[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] +const fn my_fun3() { + #[rustc_const_unstable(feature = "abi_unadjusted", issue = "42")] + const { + my_fun() + } +} + +#[stable(feature = "asdf", since = "99.0.0")] +#[rustc_const_stable(feature = "asdf", since = "99.0.0")] +const fn stable_thing() {} + +#[stable(feature = "asdf", since = "99.0.0")] +#[rustc_const_stable(feature = "asdf", since = "99.0.0")] +const fn my_fun4() { + #[rustc_const_stable(feature = "asdf", since = "99.0.0")] + const { + stable_thing() + } +} + +#[stable(feature = "asdf", since = "99.0.0")] +#[rustc_const_stable(feature = "asdf", since = "99.0.0")] +const fn my_fun5() { + const { stable_thing() } +} + fn main() { - const { my_fun2() }; + #[rustc_allow_const_fn_unstable(abi_unadjusted)] + const { + my_fun2() + }; } diff --git a/tests/ui/thir-print/offset_of.stdout b/tests/ui/thir-print/offset_of.stdout index 846817f475288..fe1feb9720cb9 100644 --- a/tests/ui/thir-print/offset_of.stdout +++ b/tests/ui/thir-print/offset_of.stdout @@ -68,7 +68,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).10)) - span: $DIR/offset_of.rs:37:5: 1433:57 (#0) + span: $DIR/offset_of.rs:37:5: 1436:57 (#0) } } Stmt { @@ -117,7 +117,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).20)) - span: $DIR/offset_of.rs:38:5: 1433:57 (#0) + span: $DIR/offset_of.rs:38:5: 1436:57 (#0) } } Stmt { @@ -166,7 +166,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).30)) - span: $DIR/offset_of.rs:39:5: 1433:57 (#0) + span: $DIR/offset_of.rs:39:5: 1436:57 (#0) } } Stmt { @@ -215,7 +215,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).40)) - span: $DIR/offset_of.rs:40:5: 1433:57 (#0) + span: $DIR/offset_of.rs:40:5: 1436:57 (#0) } } Stmt { @@ -264,7 +264,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).50)) - span: $DIR/offset_of.rs:41:5: 1433:57 (#0) + span: $DIR/offset_of.rs:41:5: 1436:57 (#0) } } ] @@ -864,7 +864,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).12)) - span: $DIR/offset_of.rs:45:5: 1433:57 (#0) + span: $DIR/offset_of.rs:45:5: 1436:57 (#0) } } Stmt { @@ -913,7 +913,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).24)) - span: $DIR/offset_of.rs:46:5: 1433:57 (#0) + span: $DIR/offset_of.rs:46:5: 1436:57 (#0) } } Stmt { @@ -962,7 +962,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).36)) - span: $DIR/offset_of.rs:47:5: 1433:57 (#0) + span: $DIR/offset_of.rs:47:5: 1436:57 (#0) } } Stmt { @@ -1011,7 +1011,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).48)) - span: $DIR/offset_of.rs:48:5: 1433:57 (#0) + span: $DIR/offset_of.rs:48:5: 1436:57 (#0) } } ]