diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
commit | 4547b622d8d29df964fa2914213088b148c498fc (patch) | |
tree | 9fc6b25f3c3add6b745be9a2400a6e96140046e9 /compiler/rustc_codegen_cranelift/src | |
parent | Releasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz rustc-4547b622d8d29df964fa2914213088b148c498fc.zip |
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/abi/mod.rs | 25 | ||||
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/allocator.rs | 9 | ||||
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/archive.rs | 224 | ||||
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/config.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/constant.rs | 77 | ||||
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/main_shim.rs | 8 | ||||
-rw-r--r-- | compiler/rustc_codegen_cranelift/src/value_and_place.rs | 10 |
8 files changed, 68 insertions, 289 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 99059e788..1e22537c2 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -22,7 +22,19 @@ fn clif_sig_from_fn_abi<'tcx>( default_call_conv: CallConv, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> Signature { - let call_conv = match fn_abi.conv { + let call_conv = conv_to_call_conv(fn_abi.conv, default_call_conv); + + let inputs = fn_abi.args.iter().map(|arg_abi| arg_abi.get_abi_param(tcx).into_iter()).flatten(); + + let (return_ptr, returns) = fn_abi.ret.get_abi_return(tcx); + // Sometimes the first param is an pointer to the place where the return value needs to be stored. + let params: Vec<_> = return_ptr.into_iter().chain(inputs).collect(); + + Signature { params, returns, call_conv } +} + +pub(crate) fn conv_to_call_conv(c: Conv, default_call_conv: CallConv) -> CallConv { + match c { Conv::Rust | Conv::C => default_call_conv, Conv::RustCold => CallConv::Cold, Conv::X86_64SysV => CallConv::SystemV, @@ -38,15 +50,8 @@ fn clif_sig_from_fn_abi<'tcx>( | Conv::X86VectorCall | Conv::AmdGpuKernel | Conv::AvrInterrupt - | Conv::AvrNonBlockingInterrupt => todo!("{:?}", fn_abi.conv), - }; - let inputs = fn_abi.args.iter().map(|arg_abi| arg_abi.get_abi_param(tcx).into_iter()).flatten(); - - let (return_ptr, returns) = fn_abi.ret.get_abi_return(tcx); - // Sometimes the first param is an pointer to the place where the return value needs to be stored. - let params: Vec<_> = return_ptr.into_iter().chain(inputs).collect(); - - Signature { params, returns, call_conv } + | Conv::AvrNonBlockingInterrupt => todo!("{:?}", c), + } } pub(crate) fn get_function_sig<'tcx>( diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index bad8a87b9..12bb00d34 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -5,6 +5,7 @@ use crate::prelude::*; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_session::config::OomStrategy; +use rustc_span::symbol::sym; /// Returns whether an allocator shim was created pub(crate) fn codegen( @@ -23,7 +24,7 @@ pub(crate) fn codegen( module, unwind_context, kind, - tcx.lang_items().oom().is_some(), + tcx.alloc_error_handler_kind(()).unwrap(), tcx.sess.opts.unstable_opts.oom, ); true @@ -36,7 +37,7 @@ fn codegen_inner( module: &mut impl Module, unwind_context: &mut UnwindContext, kind: AllocatorKind, - has_alloc_error_handler: bool, + alloc_error_handler_kind: AllocatorKind, oom_strategy: OomStrategy, ) { let usize_ty = module.target_config().pointer_type(); @@ -108,12 +109,12 @@ fn codegen_inner( returns: vec![], }; - let callee_name = if has_alloc_error_handler { "__rg_oom" } else { "__rdl_oom" }; + let callee_name = alloc_error_handler_kind.fn_name(sym::oom); let func_id = module.declare_function("__rust_alloc_error_handler", Linkage::Export, &sig).unwrap(); - let callee_func_id = module.declare_function(callee_name, Linkage::Import, &sig).unwrap(); + let callee_func_id = module.declare_function(&callee_name, Linkage::Import, &sig).unwrap(); let mut ctx = Context::new(); ctx.func.signature = sig; diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index f2e3bf16e..5a29bc18d 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -1,35 +1,15 @@ -//! Creation of ar archives like for the lib and staticlib crate type - -use std::collections::BTreeMap; -use std::fs::File; -use std::io::{self, Read, Seek}; use std::path::{Path, PathBuf}; -use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; +use rustc_codegen_ssa::back::archive::{ + get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, +}; use rustc_session::Session; -use object::read::archive::ArchiveFile; -use object::{Object, ObjectSymbol, ReadCache}; - -#[derive(Debug)] -enum ArchiveEntry { - FromArchive { archive_index: usize, file_range: (u64, u64) }, - File(PathBuf), -} - pub(crate) struct ArArchiveBuilderBuilder; impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a> { - Box::new(ArArchiveBuilder { - sess, - use_gnu_style_archive: sess.target.archive_format == "gnu", - // FIXME fix builtin ranlib on macOS - no_builtin_ranlib: sess.target.is_like_osx, - - src_archives: vec![], - entries: vec![], - }) + Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols)) } fn create_dll_import_lib( @@ -40,200 +20,6 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { _tmpdir: &Path, _is_direct_dependency: bool, ) -> PathBuf { - bug!("creating dll imports is not supported"); - } -} - -pub(crate) struct ArArchiveBuilder<'a> { - sess: &'a Session, - use_gnu_style_archive: bool, - no_builtin_ranlib: bool, - - src_archives: Vec<File>, - // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at - // the end of an archive for linkers to not get confused. - entries: Vec<(Vec<u8>, ArchiveEntry)>, -} - -impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { - fn add_file(&mut self, file: &Path) { - self.entries.push(( - file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(), - ArchiveEntry::File(file.to_owned()), - )); - } - - fn add_archive( - &mut self, - archive_path: &Path, - mut skip: Box<dyn FnMut(&str) -> bool + 'static>, - ) -> std::io::Result<()> { - let read_cache = ReadCache::new(std::fs::File::open(&archive_path)?); - let archive = ArchiveFile::parse(&read_cache).unwrap(); - let archive_index = self.src_archives.len(); - - for entry in archive.members() { - let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - let file_name = String::from_utf8(entry.name().to_vec()) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - if !skip(&file_name) { - self.entries.push(( - file_name.into_bytes(), - ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() }, - )); - } - } - - self.src_archives.push(read_cache.into_inner()); - Ok(()) - } - - fn build(mut self: Box<Self>, output: &Path) -> bool { - enum BuilderKind { - Bsd(ar::Builder<File>), - Gnu(ar::GnuBuilder<File>), - } - - let sess = self.sess; - - let mut symbol_table = BTreeMap::new(); - - let mut entries = Vec::new(); - - for (mut entry_name, entry) in self.entries { - // FIXME only read the symbol table of the object files to avoid having to keep all - // object files in memory at once, or read them twice. - let data = match entry { - ArchiveEntry::FromArchive { archive_index, file_range } => { - // FIXME read symbols from symtab - let src_read_cache = &mut self.src_archives[archive_index]; - - src_read_cache.seek(io::SeekFrom::Start(file_range.0)).unwrap(); - let mut data = std::vec::from_elem(0, usize::try_from(file_range.1).unwrap()); - src_read_cache.read_exact(&mut data).unwrap(); - - data - } - ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| { - sess.fatal(&format!( - "error while reading object file during archive building: {}", - err - )); - }), - }; - - if !self.no_builtin_ranlib { - if symbol_table.contains_key(&entry_name) { - // The ar crate can't handle creating a symbol table in case of multiple archive - // members with the same name. Work around this by prepending a number until we - // get a unique name. - for i in 1.. { - let new_name = format!("{}_", i) - .into_bytes() - .into_iter() - .chain(entry_name.iter().copied()) - .collect::<Vec<_>>(); - if !symbol_table.contains_key(&new_name) { - entry_name = new_name; - break; - } - } - } - - match object::File::parse(&*data) { - Ok(object) => { - symbol_table.insert( - entry_name.to_vec(), - object - .symbols() - .filter_map(|symbol| { - if symbol.is_undefined() || symbol.is_local() { - None - } else { - symbol.name().map(|name| name.as_bytes().to_vec()).ok() - } - }) - .collect::<Vec<_>>(), - ); - } - Err(err) => { - let err = err.to_string(); - if err == "Unknown file magic" { - // Not an object file; skip it. - } else if object::read::archive::ArchiveFile::parse(&*data).is_ok() { - // Nested archive file; skip it. - } else { - sess.fatal(&format!( - "error parsing `{}` during archive creation: {}", - String::from_utf8_lossy(&entry_name), - err - )); - } - } - } - } - - entries.push((entry_name, data)); - } - - let mut builder = if self.use_gnu_style_archive { - BuilderKind::Gnu( - ar::GnuBuilder::new( - File::create(output).unwrap_or_else(|err| { - sess.fatal(&format!( - "error opening destination during archive building: {}", - err - )); - }), - entries.iter().map(|(name, _)| name.clone()).collect(), - ar::GnuSymbolTableFormat::Size32, - symbol_table, - ) - .unwrap(), - ) - } else { - BuilderKind::Bsd( - ar::Builder::new( - File::create(output).unwrap_or_else(|err| { - sess.fatal(&format!( - "error opening destination during archive building: {}", - err - )); - }), - symbol_table, - ) - .unwrap(), - ) - }; - - let any_members = !entries.is_empty(); - - // Add all files - for (entry_name, data) in entries.into_iter() { - let header = ar::Header::new(entry_name, data.len() as u64); - match builder { - BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(), - BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(), - } - } - - // Finalize archive - std::mem::drop(builder); - - if self.no_builtin_ranlib { - let ranlib = crate::toolchain::get_toolchain_binary(self.sess, "ranlib"); - - // Run ranlib to be able to link the archive - let status = std::process::Command::new(ranlib) - .arg(output) - .status() - .expect("Couldn't run ranlib"); - - if !status.success() { - self.sess.fatal(&format!("Ranlib exited with code {:?}", status.code())); - } - } - - any_members + unimplemented!("creating dll imports is not yet supported"); } } diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs index e59a0cb0a..45522fb1a 100644 --- a/compiler/rustc_codegen_cranelift/src/config.rs +++ b/compiler/rustc_codegen_cranelift/src/config.rs @@ -2,7 +2,7 @@ use std::env; use std::str::FromStr; fn bool_env_var(key: &str) -> bool { - env::var(key).as_ref().map(|val| &**val) == Ok("1") + env::var(key).as_deref() == Ok("1") } /// The mode to use for compilation. diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 148b66d95..a6bde8840 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -38,22 +38,8 @@ impl ConstantCx { pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { let mut all_constants_ok = true; for constant in &fx.mir.required_consts { - let unevaluated = match fx.monomorphize(constant.literal) { - ConstantKind::Ty(_) => unreachable!(), - ConstantKind::Unevaluated(uv, _) => uv, - ConstantKind::Val(..) => continue, - }; - - if let Err(err) = fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { + if eval_mir_constant(fx, constant).is_none() { all_constants_ok = false; - match err { - ErrorHandled::Reported(_) | ErrorHandled::Linted => { - fx.tcx.sess.span_err(constant.span, "erroneous constant encountered"); - } - ErrorHandled::TooGeneric => { - span_bug!(constant.span, "codegen encountered polymorphic constant: {:?}", err); - } - } } } all_constants_ok @@ -80,15 +66,15 @@ pub(crate) fn codegen_tls_ref<'tcx>( } pub(crate) fn eval_mir_constant<'tcx>( - fx: &mut FunctionCx<'_, '_, 'tcx>, + fx: &FunctionCx<'_, '_, 'tcx>, constant: &Constant<'tcx>, -) -> (ConstValue<'tcx>, Ty<'tcx>) { +) -> Option<(ConstValue<'tcx>, Ty<'tcx>)> { let constant_kind = fx.monomorphize(constant.literal); let uv = match constant_kind { ConstantKind::Ty(const_) => match const_.kind() { ty::ConstKind::Unevaluated(uv) => uv.expand(), ty::ConstKind::Value(val) => { - return (fx.tcx.valtree_to_const_val((const_.ty(), val)), const_.ty()); + return Some((fx.tcx.valtree_to_const_val((const_.ty(), val)), const_.ty())); } err => span_bug!( constant.span, @@ -102,22 +88,31 @@ pub(crate) fn eval_mir_constant<'tcx>( span_bug!(constant.span, "MIR constant refers to static"); } ConstantKind::Unevaluated(uv, _) => uv, - ConstantKind::Val(val, _) => return (val, constant_kind.ty()), + ConstantKind::Val(val, _) => return Some((val, constant_kind.ty())), }; - ( - fx.tcx.const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None).unwrap_or_else(|_err| { - span_bug!(constant.span, "erroneous constant not captured by required_consts"); - }), - constant_kind.ty(), - ) + let val = fx + .tcx + .const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None) + .map_err(|err| match err { + ErrorHandled::Reported(_) => { + fx.tcx.sess.span_err(constant.span, "erroneous constant encountered"); + } + ErrorHandled::TooGeneric => { + span_bug!(constant.span, "codegen encountered polymorphic constant: {:?}", err); + } + }) + .ok(); + val.map(|val| (val, constant_kind.ty())) } pub(crate) fn codegen_constant_operand<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, constant: &Constant<'tcx>, ) -> CValue<'tcx> { - let (const_val, ty) = eval_mir_constant(fx, constant); + let (const_val, ty) = eval_mir_constant(fx, constant).unwrap_or_else(|| { + span_bug!(constant.span, "erroneous constant not captured by required_consts") + }); codegen_const_value(fx, const_val, ty) } @@ -128,7 +123,7 @@ pub(crate) fn codegen_const_value<'tcx>( ty: Ty<'tcx>, ) -> CValue<'tcx> { let layout = fx.layout_of(ty); - assert!(!layout.is_unsized(), "sized const value"); + assert!(layout.is_sized(), "unsized const value"); if layout.is_zst() { return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout); @@ -262,9 +257,9 @@ pub(crate) fn data_id_for_alloc_id( mutability: rustc_hir::Mutability, ) -> DataId { cx.todo.push(TodoItem::Alloc(alloc_id)); - *cx.anon_allocs.entry(alloc_id).or_insert_with(|| { - module.declare_anonymous_data(mutability == rustc_hir::Mutability::Mut, false).unwrap() - }) + *cx.anon_allocs + .entry(alloc_id) + .or_insert_with(|| module.declare_anonymous_data(mutability.is_mut(), false).unwrap()) } fn data_id_for_static( @@ -348,12 +343,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant } }; let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| { - module - .declare_anonymous_data( - alloc.inner().mutability == rustc_hir::Mutability::Mut, - false, - ) - .unwrap() + module.declare_anonymous_data(alloc.inner().mutability.is_mut(), false).unwrap() }); (data_id, alloc, None) } @@ -398,7 +388,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec(); data_ctx.define(bytes.into_boxed_slice()); - for &(offset, alloc_id) in alloc.provenance().iter() { + for &(offset, alloc_id) in alloc.provenance().ptrs().iter() { let addend = { let endianness = tcx.data_layout.endian; let offset = offset.bytes() as usize; @@ -431,7 +421,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant { tcx.sess.fatal(&format!( "Allocation {:?} contains reference to TLS value {:?}", - alloc, def_id + alloc_id, def_id )); } @@ -453,20 +443,13 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant assert!(cx.todo.is_empty(), "{:?}", cx.todo); } +/// Used only for intrinsic implementations that need a compile-time constant pub(crate) fn mir_operand_get_const_val<'tcx>( fx: &FunctionCx<'_, '_, 'tcx>, operand: &Operand<'tcx>, ) -> Option<ConstValue<'tcx>> { match operand { - Operand::Constant(const_) => match fx.monomorphize(const_.literal) { - ConstantKind::Ty(const_) => Some( - const_.eval_for_mir(fx.tcx, ParamEnv::reveal_all()).try_to_value(fx.tcx).unwrap(), - ), - ConstantKind::Val(val, _) => Some(val), - ConstantKind::Unevaluated(uv, _) => { - Some(fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), uv, None).unwrap()) - } - }, + Operand::Constant(const_) => Some(eval_mir_constant(fx, const_).unwrap().0), // FIXME(rust-lang/rust#85105): Casts like `IMM8 as u32` result in the const being stored // inside a temporary before being passed to the intrinsic requiring the const argument. // This code tries to find a single constant defining definition of the referenced local. diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index c55db2017..2ba012a77 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -59,7 +59,7 @@ impl DebugContext { let producer = format!( "cg_clif (rustc {}, cranelift {})", - rustc_interface::util::version_str().unwrap_or("unknown version"), + rustc_interface::util::rustc_version_str().unwrap_or("unknown version"), cranelift_codegen::VERSION, ); let comp_dir = tcx diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index cae6312a6..f7434633e 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -63,10 +63,14 @@ pub(crate) fn maybe_create_entry_wrapper( AbiParam::new(m.target_config().pointer_type()), ], returns: vec![AbiParam::new(m.target_config().pointer_type() /*isize*/)], - call_conv: CallConv::triple_default(m.isa().triple()), + call_conv: crate::conv_to_call_conv( + tcx.sess.target.options.entry_abi, + CallConv::triple_default(m.isa().triple()), + ), }; - let cmain_func_id = m.declare_function("main", Linkage::Export, &cmain_sig).unwrap(); + let entry_name = tcx.sess.target.options.entry_name.as_ref(); + let cmain_func_id = m.declare_function(entry_name, Linkage::Export, &cmain_sig).unwrap(); let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index c3dfbd372..34746ff6b 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -19,7 +19,7 @@ fn codegen_field<'tcx>( }; if let Some(extra) = extra { - if !field_layout.is_unsized() { + if field_layout.is_sized() { return simple(fx); } match field_layout.ty.kind() { @@ -108,8 +108,8 @@ impl<'tcx> CValue<'tcx> { } // FIXME remove - // Forces the data value of a dyn* value to the stack and returns a pointer to it as well as the - // vtable pointer. + /// Forces the data value of a dyn* value to the stack and returns a pointer to it as well as the + /// vtable pointer. pub(crate) fn dyn_star_force_data_on_stack( self, fx: &mut FunctionCx<'_, '_, 'tcx>, @@ -364,7 +364,7 @@ impl<'tcx> CPlace<'tcx> { fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { - assert!(!layout.is_unsized()); + assert!(layout.is_sized()); if layout.size.bytes() == 0 { return CPlace { inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None), @@ -825,7 +825,7 @@ impl<'tcx> CPlace<'tcx> { fx: &FunctionCx<'_, '_, 'tcx>, variant: VariantIdx, ) -> Self { - assert!(!self.layout().is_unsized()); + assert!(self.layout().is_sized()); let layout = self.layout().for_variant(fx, variant); CPlace { inner: self.inner, layout } } |