From 645841ea446ed0772835f07fb62aaeecdb06b604 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 25 Jul 2017 17:26:24 +0200 Subject: [PATCH] async-llvm(8): Clean up resource management and drop LLVM modules ASAP. --- src/librustc_driver/driver.rs | 2 - src/librustc_trans/back/write.rs | 315 ++++++++++++++++++------------- src/librustc_trans/base.rs | 27 ++- src/librustc_trans/lib.rs | 63 ++++++- 4 files changed, 256 insertions(+), 151 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 1bc3f59ed04..ff9f666af75 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -237,8 +237,6 @@ pub fn compile_input(sess: &Session, phase5_result); phase5_result?; - write::cleanup_llvm(&trans); - phase_6_link_output(sess, &trans, &outputs); // Now that we won't touch anything in the incremental compilation directory diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 5d9444218c4..83f7f574493 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -18,7 +18,8 @@ use rustc::session::Session; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; use llvm::SMDiagnosticRef; -use {CrateTranslation, OngoingCrateTranslation, ModuleLlvm, ModuleSource, ModuleTranslation}; +use {CrateTranslation, OngoingCrateTranslation, ModuleSource, ModuleTranslation, + CompiledModule, ModuleKind}; use rustc::hir::def_id::CrateNum; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr}; use rustc::util::fs::link_or_copy; @@ -192,7 +193,6 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef { /// Module-specific configuration for `optimize_and_codegen`. -#[derive(Clone)] pub struct ModuleConfig { /// LLVM TargetMachine to use for codegen. tm: TargetMachineRef, @@ -231,9 +231,9 @@ pub struct ModuleConfig { unsafe impl Send for ModuleConfig { } impl ModuleConfig { - fn new(tm: TargetMachineRef, passes: Vec) -> ModuleConfig { + fn new(sess: &Session, passes: Vec) -> ModuleConfig { ModuleConfig { - tm: tm, + tm: create_target_machine(sess), passes: passes, opt_level: None, opt_size: None, @@ -281,6 +281,40 @@ impl ModuleConfig { self.merge_functions = sess.opts.optimize == config::OptLevel::Default || sess.opts.optimize == config::OptLevel::Aggressive; } + + fn clone(&self, sess: &Session) -> ModuleConfig { + ModuleConfig { + tm: create_target_machine(sess), + passes: self.passes.clone(), + opt_level: self.opt_level, + opt_size: self.opt_size, + + emit_no_opt_bc: self.emit_no_opt_bc, + emit_bc: self.emit_bc, + emit_lto_bc: self.emit_lto_bc, + emit_ir: self.emit_ir, + emit_asm: self.emit_asm, + emit_obj: self.emit_obj, + obj_is_bitcode: self.obj_is_bitcode, + + no_verify: self.no_verify, + no_prepopulate_passes: self.no_prepopulate_passes, + no_builtins: self.no_builtins, + time_passes: self.time_passes, + vectorize_loop: self.vectorize_loop, + vectorize_slp: self.vectorize_slp, + merge_functions: self.merge_functions, + inline_threshold: self.inline_threshold, + } + } +} + +impl Drop for ModuleConfig { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustDisposeTargetMachine(self.tm); + } + } } /// Additional resources used by optimize_and_codegen (not module specific) @@ -372,13 +406,17 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo unsafe fn optimize_and_codegen(cgcx: &CodegenContext, diag_handler: &Handler, mtrans: ModuleTranslation, - mllvm: ModuleLlvm, config: ModuleConfig, output_names: OutputFilenames) - -> Result<(), FatalError> + -> Result { - let llmod = mllvm.llmod; - let llcx = mllvm.llcx; + let (llmod, llcx) = match mtrans.source { + ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx), + ModuleSource::Preexisting(_) => { + bug!("optimize_and_codegen: called with ModuleSource::Preexisting") + } + }; + let tm = config.tm; let fv = HandlerFreeVars { @@ -390,7 +428,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); - let module_name = Some(&mtrans.name[..]); + let module_name = mtrans.name.clone(); + let module_name = Some(&module_name[..]); if config.emit_no_opt_bc { let out = output_names.temp_path_ext("no-opt.bc", module_name); @@ -606,30 +645,13 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } } - llvm::LLVMRustDisposeTargetMachine(tm); - Ok(()) + Ok(mtrans.into_compiled_module(config.emit_obj, config.emit_bc)) } - -pub fn cleanup_llvm(trans: &CrateTranslation) { - for module in trans.modules.iter() { - unsafe { - match module.source { - ModuleSource::Translated(llvm) => { - llvm::LLVMDisposeModule(llvm.llmod); - llvm::LLVMContextDispose(llvm.llcx); - } - ModuleSource::Preexisting(_) => { - } - } - } - } -} - -pub struct RunLLVMPassesResult { - pub modules: Vec, - pub metadata_module: ModuleTranslation, - pub allocator_module: Option, +pub struct CompiledModules { + pub modules: Vec, + pub metadata_module: CompiledModule, + pub allocator_module: Option, } pub fn run_passes(sess: &Session, @@ -658,13 +680,11 @@ pub fn run_passes(sess: &Session, !sess.opts.output_types.should_trans() || sess.opts.debugging_opts.no_trans); - let tm = create_target_machine(sess); - // Figure out what we actually need to build. - let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); - let mut metadata_config = ModuleConfig::new(tm, vec![]); - let mut allocator_config = ModuleConfig::new(tm, vec![]); + let mut modules_config = ModuleConfig::new(sess, sess.opts.cg.passes.clone()); + let mut metadata_config = ModuleConfig::new(sess, vec![]); + let mut allocator_config = ModuleConfig::new(sess, vec![]); if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { match *sanitizer { @@ -747,25 +767,22 @@ pub fn run_passes(sess: &Session, let mut work_items = Vec::with_capacity(1 + modules.len()); { - let work = build_work_item(sess, - metadata_module.clone(), - metadata_config.clone(), + let work = build_work_item(metadata_module, + metadata_config.clone(sess), crate_output.clone()); work_items.push(work); } - if let Some(allocator) = allocator_module.clone() { - let work = build_work_item(sess, - allocator, - allocator_config.clone(), + if let Some(allocator) = allocator_module { + let work = build_work_item(allocator, + allocator_config.clone(sess), crate_output.clone()); work_items.push(work); } - for mtrans in modules.iter() { - let work = build_work_item(sess, - mtrans.clone(), - modules_config.clone(), + for mtrans in modules { + let work = build_work_item(mtrans, + modules_config.clone(sess), crate_output.clone()); work_items.push(work); } @@ -778,6 +795,10 @@ pub fn run_passes(sess: &Session, Client::new(num_workers).expect("failed to create jobserver") }); + drop(modules_config); + drop(metadata_config); + drop(allocator_config); + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (trans_worker_send, trans_worker_receive) = channel(); let (coordinator_send, coordinator_receive) = channel(); @@ -813,37 +834,27 @@ pub fn run_passes(sess: &Session, } } - match coordinator_thread.join() { - Ok(()) => {}, - Err(err) => { - panic!("error: {:?}", err); - } - } + let compiled_modules = coordinator_thread.join().unwrap(); // Just in case, check this on the way out. shared_emitter_main.check(sess); sess.diagnostic().abort_if_errors(); // If in incr. comp. mode, preserve the `.o` files for potential re-use - for mtrans in modules.iter() { + for module in compiled_modules.modules.iter() { let mut files = vec![]; - if modules_config.emit_obj { - let path = crate_output.temp_path(OutputType::Object, Some(&mtrans.name)); + if module.emit_obj { + let path = crate_output.temp_path(OutputType::Object, Some(&module.name)); files.push((OutputType::Object, path)); } - if modules_config.emit_bc { - let path = crate_output.temp_path(OutputType::Bitcode, Some(&mtrans.name)); + if module.emit_bc { + let path = crate_output.temp_path(OutputType::Bitcode, Some(&module.name)); files.push((OutputType::Bitcode, path)); } - save_trans_partition(sess, &mtrans.name, mtrans.symbol_name_hash, &files); - } - - // All codegen is finished. - unsafe { - llvm::LLVMRustDisposeTargetMachine(tm); + save_trans_partition(sess, &module.name, module.symbol_name_hash, &files); } let mut user_wants_bitcode = false; @@ -858,10 +869,10 @@ pub fn run_passes(sess: &Session, let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| { - if modules.len() == 1 { + if compiled_modules.modules.len() == 1 { // 1) Only one codegen unit. In this case it's no difficulty // to copy `foo.0.x` to `foo.x`. - let module_name = Some(&modules[0].name[..]); + let module_name = Some(&compiled_modules.modules[0].name[..]); let path = crate_output.temp_path(output_type, module_name); copy_gracefully(&path, &crate_output.path(output_type)); @@ -962,27 +973,30 @@ pub fn run_passes(sess: &Session, let keep_numbered_objects = needs_crate_object || (user_wants_objects && sess.opts.cg.codegen_units > 1); - for module_name in modules.iter().map(|m| Some(&m.name[..])) { - if modules_config.emit_obj && !keep_numbered_objects { + for module in compiled_modules.modules.iter() { + let module_name = Some(&module.name[..]); + + if module.emit_obj && !keep_numbered_objects { let path = crate_output.temp_path(OutputType::Object, module_name); remove(sess, &path); } - if modules_config.emit_bc && !keep_numbered_bitcode { + if module.emit_bc && !keep_numbered_bitcode { let path = crate_output.temp_path(OutputType::Bitcode, module_name); remove(sess, &path); } } - if metadata_config.emit_bc && !user_wants_bitcode { + if compiled_modules.metadata_module.emit_bc && !user_wants_bitcode { let path = crate_output.temp_path(OutputType::Bitcode, - Some(&metadata_module.name)); + Some(&compiled_modules.metadata_module.name)); remove(sess, &path); } - if allocator_config.emit_bc && !user_wants_bitcode { - if let Some(ref module) = allocator_module { + + if let Some(ref allocator_module) = compiled_modules.allocator_module { + if allocator_module.emit_bc && !user_wants_bitcode { let path = crate_output.temp_path(OutputType::Bitcode, - Some(&module.name)); + Some(&allocator_module.name)); remove(sess, &path); } } @@ -1000,21 +1014,14 @@ pub fn run_passes(sess: &Session, unsafe { llvm::LLVMRustPrintPassTimings(); } } - *trans.result.borrow_mut() = Some( - RunLLVMPassesResult { - modules, - metadata_module, - allocator_module, - } - ); + *trans.result.borrow_mut() = Some(compiled_modules); } pub fn dump_incremental_data(trans: &CrateTranslation) { let mut reuse = 0; for mtrans in trans.modules.iter() { - match mtrans.source { - ModuleSource::Preexisting(..) => reuse += 1, - ModuleSource::Translated(..) => (), + if mtrans.pre_existing { + reuse += 1; } } eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len()); @@ -1032,14 +1039,11 @@ impl fmt::Debug for WorkItem { } } -fn build_work_item(sess: &Session, - mtrans: ModuleTranslation, +fn build_work_item(mtrans: ModuleTranslation, config: ModuleConfig, output_names: OutputFilenames) -> WorkItem { - let mut config = config; - config.tm = create_target_machine(sess); WorkItem { mtrans: mtrans, config: config, @@ -1048,54 +1052,65 @@ fn build_work_item(sess: &Session, } fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) - -> Result<(), FatalError> + -> Result { let diag_handler = cgcx.create_diag_handler(); - unsafe { - match work_item.mtrans.source { - ModuleSource::Translated(mllvm) => { - debug!("llvm-optimizing {:?}", work_item.mtrans.name); - optimize_and_codegen(cgcx, - &diag_handler, - work_item.mtrans, - mllvm, - work_item.config, - work_item.output_names)?; - } - ModuleSource::Preexisting(wp) => { - let incr_comp_session_dir = cgcx.incr_comp_session_dir - .as_ref() - .unwrap(); - let name = &work_item.mtrans.name; - for (kind, saved_file) in wp.saved_files { - let obj_out = work_item.output_names.temp_path(kind, Some(name)); - let source_file = in_incr_comp_dir(&incr_comp_session_dir, - &saved_file); - debug!("copying pre-existing module `{}` from {:?} to {}", - work_item.mtrans.name, - source_file, - obj_out.display()); - match link_or_copy(&source_file, &obj_out) { - Ok(_) => { } - Err(err) => { - diag_handler.err(&format!("unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), - err)); - } - } + let module_name = work_item.mtrans.name.clone(); + + let pre_existing = match work_item.mtrans.source { + ModuleSource::Translated(_) => None, + ModuleSource::Preexisting(ref wp) => Some(wp.clone()), + }; + + if let Some(wp) = pre_existing { + let incr_comp_session_dir = cgcx.incr_comp_session_dir + .as_ref() + .unwrap(); + let name = &work_item.mtrans.name; + for (kind, saved_file) in wp.saved_files { + let obj_out = work_item.output_names.temp_path(kind, Some(name)); + let source_file = in_incr_comp_dir(&incr_comp_session_dir, + &saved_file); + debug!("copying pre-existing module `{}` from {:?} to {}", + work_item.mtrans.name, + source_file, + obj_out.display()); + match link_or_copy(&source_file, &obj_out) { + Ok(_) => { } + Err(err) => { + diag_handler.err(&format!("unable to copy {} to {}: {}", + source_file.display(), + obj_out.display(), + err)); } } } - } - Ok(()) + Ok(CompiledModule { + name: module_name, + kind: ModuleKind::Regular, + pre_existing: true, + symbol_name_hash: work_item.mtrans.symbol_name_hash, + emit_bc: work_item.config.emit_bc, + emit_obj: work_item.config.emit_obj, + }) + } else { + debug!("llvm-optimizing {:?}", module_name); + + unsafe { + optimize_and_codegen(cgcx, + &diag_handler, + work_item.mtrans, + work_item.config, + work_item.output_names) + } + } } #[derive(Debug)] pub enum Message { Token(io::Result), - Done { success: bool }, + Done { result: Result }, WorkItem(WorkItem), CheckErrorMessages, } @@ -1115,7 +1130,7 @@ fn start_executing_work(sess: &Session, coordinator_receive: Receiver, jobserver: Client, exported_symbols: Arc) - -> thread::JoinHandle<()> { + -> thread::JoinHandle { // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. Once we've got the helper // thread then request `n-1` tokens because all of our work items are ready @@ -1215,6 +1230,10 @@ fn start_executing_work(sess: &Session, // the jobserver. thread::spawn(move || { + let mut compiled_modules = vec![]; + let mut compiled_metadata_module = None; + let mut compiled_allocator_module = None; + let mut work_items_left = total_work_item_count; let mut work_items = Vec::with_capacity(total_work_item_count); let mut tokens = Vec::new(); @@ -1253,7 +1272,8 @@ fn start_executing_work(sess: &Session, } else { shared_emitter.fatal("failed to acquire jobserver token"); drop(trans_worker_send.send(Message::CheckErrorMessages)); - return + // Exit the coordinator thread + panic!() } } @@ -1269,21 +1289,42 @@ fn start_executing_work(sess: &Session, // // Note that if the thread failed that means it panicked, so we // abort immediately. - Message::Done { success: true } => { + Message::Done { result: Ok(compiled_module) } => { drop(tokens.pop()); running -= 1; drop(trans_worker_send.send(Message::CheckErrorMessages)); + + match compiled_module.kind { + ModuleKind::Regular => { + compiled_modules.push(compiled_module); + } + ModuleKind::Metadata => { + assert!(compiled_metadata_module.is_none()); + compiled_metadata_module = Some(compiled_module); + } + ModuleKind::Allocator => { + assert!(compiled_allocator_module.is_none()); + compiled_allocator_module = Some(compiled_module); + } + } } - Message::Done { success: false } => { + Message::Done { result: Err(()) } => { shared_emitter.fatal("aborting due to worker thread panic"); drop(trans_worker_send.send(Message::CheckErrorMessages)); - return + // Exit the coordinator thread + panic!() } msg @ Message::CheckErrorMessages => { bug!("unexpected message: {:?}", msg); } } } + + CompiledModules { + modules: compiled_modules, + metadata_module: compiled_metadata_module.unwrap(), + allocator_module: compiled_allocator_module, + } }) } @@ -1297,17 +1338,22 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // we exit. struct Bomb { coordinator_send: Sender, - success: bool, + result: Option, } impl Drop for Bomb { fn drop(&mut self) { - drop(self.coordinator_send.send(Message::Done { success: self.success })); + let result = match self.result.take() { + Some(compiled_module) => Ok(compiled_module), + None => Err(()) + }; + + drop(self.coordinator_send.send(Message::Done { result })); } } let mut bomb = Bomb { coordinator_send: cgcx.coordinator_send.clone(), - success: false, + result: None, }; // Execute the work itself, and if it finishes successfully then flag @@ -1323,8 +1369,7 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // we just ignore the result and then send off our message saying that // we're done, which if `execute_work_item` failed is unlikely to be // seen by the main thread, but hey we might as well try anyway. - drop(execute_work_item(&cgcx, work).is_err()); - bomb.success = true; + bomb.result = Some(execute_work_item(&cgcx, work).unwrap()); }); } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 5582079c78f..7ece92ef9dd 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -27,6 +27,7 @@ use super::OngoingCrateTranslation; use super::ModuleLlvm; use super::ModuleSource; use super::ModuleTranslation; +use super::ModuleKind; use assert_module_sources; use back::link; @@ -952,6 +953,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, llcx: metadata_llcx, llmod: metadata_llmod, }), + kind: ModuleKind::Metadata, }; let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); @@ -961,7 +963,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_trans() { let empty_exported_symbols = ExportedSymbols::empty(); let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols); - return OngoingCrateTranslation { + let crate_translation = OngoingCrateTranslation { crate_name: tcx.crate_name(LOCAL_CRATE), link: link_meta, metadata: metadata, @@ -970,12 +972,18 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, linker_info: linker_info, windows_subsystem: None, no_integrated_as: false, - result: ::std::cell::RefCell::new(Some(::back::write::RunLLVMPassesResult { - modules: vec![], - metadata_module: metadata_module, - allocator_module: None, - })), + result: ::std::cell::RefCell::new(None), }; + + ::back::write::run_passes(tcx.sess, + &crate_translation, + vec![], + metadata_module, + None, + &output_filenames.outputs, + output_filenames); + + return crate_translation; } let exported_symbols = Arc::new(ExportedSymbols::compute(tcx, @@ -1047,7 +1055,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let module = ModuleTranslation { name: cgu_name, symbol_name_hash, - source: ModuleSource::Preexisting(buf.clone()) + source: ModuleSource::Preexisting(buf.clone()), + kind: ModuleKind::Regular, }; return (Stats::default(), module); } @@ -1108,7 +1117,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, source: ModuleSource::Translated(ModuleLlvm { llcx: ccx.llcx(), llmod: ccx.llmod(), - }) + }), + kind: ModuleKind::Regular, } }; @@ -1196,6 +1206,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, name: link::ALLOCATOR_MODULE_NAME.to_string(), symbol_name_hash: 0, // we always rebuild allocator shims source: ModuleSource::Translated(modules), + kind: ModuleKind::Allocator, }) } }); diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index e7debe3919b..1df1bd272fd 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -135,7 +135,6 @@ mod type_; mod type_of; mod value; -#[derive(Clone)] pub struct ModuleTranslation { /// The name of the module. When the crate may be saved between /// compilations, incremental compilation requires that name be @@ -145,6 +144,58 @@ pub struct ModuleTranslation { pub name: String, pub symbol_name_hash: u64, pub source: ModuleSource, + pub kind: ModuleKind, +} + +#[derive(Copy, Clone, Debug)] +pub enum ModuleKind { + Regular, + Metadata, + Allocator, +} + +impl ModuleTranslation { + pub fn into_compiled_module(self, emit_obj: bool, emit_bc: bool) -> CompiledModule { + let pre_existing = match self.source { + ModuleSource::Preexisting(_) => true, + ModuleSource::Translated(_) => false, + }; + + CompiledModule { + name: self.name.clone(), + kind: self.kind, + symbol_name_hash: self.symbol_name_hash, + pre_existing, + emit_obj, + emit_bc, + } + } +} + +impl Drop for ModuleTranslation { + fn drop(&mut self) { + match self.source { + ModuleSource::Preexisting(_) => { + // Nothing to dispose. + }, + ModuleSource::Translated(llvm) => { + unsafe { + llvm::LLVMDisposeModule(llvm.llmod); + llvm::LLVMContextDispose(llvm.llcx); + } + }, + } + } +} + +#[derive(Debug)] +pub struct CompiledModule { + pub name: String, + pub kind: ModuleKind, + pub symbol_name_hash: u64, + pub pre_existing: bool, + pub emit_obj: bool, + pub emit_bc: bool, } #[derive(Clone)] @@ -156,7 +207,7 @@ pub enum ModuleSource { Translated(ModuleLlvm), } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct ModuleLlvm { pub llcx: llvm::ContextRef, pub llmod: llvm::ModuleRef, @@ -167,9 +218,9 @@ unsafe impl Sync for ModuleTranslation { } pub struct CrateTranslation { pub crate_name: Symbol, - pub modules: Vec, - pub metadata_module: ModuleTranslation, - pub allocator_module: Option, + pub modules: Vec, + pub metadata_module: CompiledModule, + pub allocator_module: Option, pub link: rustc::middle::cstore::LinkMeta, pub metadata: rustc::middle::cstore::EncodedMetadata, pub exported_symbols: Arc, @@ -189,7 +240,7 @@ pub struct OngoingCrateTranslation { pub no_integrated_as: bool, // This will be replaced by a Future. - pub result: ::std::cell::RefCell>, + pub result: ::std::cell::RefCell>, } impl OngoingCrateTranslation {