Skip to content

Commit 3dbb4da

Browse files
committed
Auto merge of #117301 - saethlin:finish-rmeta-encoding, r=WaffleLapkin
Call FileEncoder::finish in rmeta encoding Fixes #117254 The bug here was that rmeta encoding never called FileEncoder::finish. Now it does. Most of the changes here are needed to support that, since rmeta encoding wants to finish _then_ access the File in the encoder, so finish can't move out. I tried adding a `cfg(debug_assertions)` exploding Drop impl to FileEncoder that checked for finish being called before dropping, but fatal errors cause unwinding so this isn't really possible. If we encounter a fatal error with a dirty FileEncoder, the Drop impl ICEs even though the implementation is correct. If we try to paper over that by wrapping FileEncoder in ManuallyDrop then that just erases the fact that Drop automatically checks that we call finish on all paths. I also changed the name of DepGraph::encode to DepGraph::finish_encoding, because that's what it does and it makes the fact that it is the path to FileEncoder::finish less confusing. r? `@WaffleLapkin`
2 parents 0b8a61b + fbaa24e commit 3dbb4da

File tree

14 files changed

+82
-41
lines changed

14 files changed

+82
-41
lines changed

Diff for: Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4038,6 +4038,7 @@ dependencies = [
40384038
"rustc_query_impl",
40394039
"rustc_query_system",
40404040
"rustc_resolve",
4041+
"rustc_serialize",
40414042
"rustc_session",
40424043
"rustc_span",
40434044
"rustc_symbol_mangling",

Diff for: compiler/rustc_codegen_ssa/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ impl CodegenResults {
224224
encoder.emit_raw_bytes(&RLINK_VERSION.to_be_bytes());
225225
encoder.emit_str(sess.cfg_version);
226226
Encodable::encode(codegen_results, &mut encoder);
227-
encoder.finish()
227+
encoder.finish().map_err(|(_path, err)| err)
228228
}
229229

230230
pub fn deserialize_rlink(sess: &Session, data: Vec<u8>) -> Result<Self, CodegenErrors> {

Diff for: compiler/rustc_incremental/src/persist/file_format.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ where
8080
);
8181
debug!("save: data written to disk successfully");
8282
}
83-
Err(err) => {
84-
sess.emit_err(errors::WriteNew { name, path: path_buf, err });
83+
Err((path, err)) => {
84+
sess.emit_err(errors::WriteNew { name, path, err });
8585
}
8686
}
8787
}

Diff for: compiler/rustc_incremental/src/persist/save.rs

-3
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
5050
join(
5151
move || {
5252
sess.time("incr_comp_persist_dep_graph", || {
53-
if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) {
54-
sess.emit_err(errors::WriteDepGraph { path: &staging_dep_graph_path, err });
55-
}
5653
if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) {
5754
sess.emit_err(errors::MoveDepGraph {
5855
from: &staging_dep_graph_path,

Diff for: compiler/rustc_interface/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ rustc_privacy = { path = "../rustc_privacy" }
4040
rustc_query_impl = { path = "../rustc_query_impl" }
4141
rustc_query_system = { path = "../rustc_query_system" }
4242
rustc_resolve = { path = "../rustc_resolve" }
43+
rustc_serialize = { path = "../rustc_serialize" }
4344
rustc_session = { path = "../rustc_session" }
4445
rustc_span = { path = "../rustc_span" }
4546
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }

Diff for: compiler/rustc_interface/src/queries.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::errors::{FailedWritingFile, RustcErrorFatal, RustcErrorUnexpectedAnnotation};
22
use crate::interface::{Compiler, Result};
3-
use crate::{passes, util};
3+
use crate::{errors, passes, util};
44

55
use rustc_ast as ast;
66
use rustc_codegen_ssa::traits::CodegenBackend;
@@ -15,6 +15,7 @@ use rustc_metadata::creader::CStore;
1515
use rustc_middle::arena::Arena;
1616
use rustc_middle::dep_graph::DepGraph;
1717
use rustc_middle::ty::{GlobalCtxt, TyCtxt};
18+
use rustc_serialize::opaque::FileEncodeResult;
1819
use rustc_session::config::{self, CrateType, OutputFilenames, OutputType};
1920
use rustc_session::cstore::Untracked;
2021
use rustc_session::output::find_crate_name;
@@ -102,6 +103,10 @@ impl<'tcx> Queries<'tcx> {
102103
}
103104
}
104105

106+
pub fn finish(&self) -> FileEncodeResult {
107+
if let Some(gcx) = self.gcx_cell.get() { gcx.finish() } else { Ok(0) }
108+
}
109+
105110
pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> {
106111
self.parse.compute(|| {
107112
passes::parse(&self.compiler.sess).map_err(|mut parse_error| parse_error.emit())
@@ -317,6 +322,9 @@ impl Compiler {
317322
// The timer's lifetime spans the dropping of `queries`, which contains
318323
// the global context.
319324
_timer = Some(self.sess.timer("free_global_ctxt"));
325+
if let Err((path, error)) = queries.finish() {
326+
self.sess.emit_err(errors::FailedWritingFile { path: &path, error });
327+
}
320328

321329
ret
322330
}

Diff for: compiler/rustc_metadata/messages.ftl

+1-4
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,8 @@ metadata_extern_location_not_file =
6363
metadata_fail_create_file_encoder =
6464
failed to create file encoder: {$err}
6565
66-
metadata_fail_seek_file =
67-
failed to seek the file: {$err}
68-
6966
metadata_fail_write_file =
70-
failed to write to the file: {$err}
67+
failed to write to `{$path}`: {$err}
7168
7269
metadata_failed_copy_to_stdout =
7370
failed to copy {$filename} to stdout: {$err}

Diff for: compiler/rustc_metadata/src/errors.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -307,15 +307,10 @@ pub struct FailCreateFileEncoder {
307307
pub err: Error,
308308
}
309309

310-
#[derive(Diagnostic)]
311-
#[diag(metadata_fail_seek_file)]
312-
pub struct FailSeekFile {
313-
pub err: Error,
314-
}
315-
316310
#[derive(Diagnostic)]
317311
#[diag(metadata_fail_write_file)]
318-
pub struct FailWriteFile {
312+
pub struct FailWriteFile<'a> {
313+
pub path: &'a Path,
319314
pub err: Error,
320315
}
321316

Diff for: compiler/rustc_metadata/src/rmeta/encoder.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::errors::{FailCreateFileEncoder, FailSeekFile, FailWriteFile};
1+
use crate::errors::{FailCreateFileEncoder, FailWriteFile};
22
use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
33
use crate::rmeta::table::TableBuilder;
44
use crate::rmeta::*;
@@ -42,6 +42,7 @@ use rustc_span::symbol::{sym, Symbol};
4242
use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SpanData, SyntaxContext};
4343
use std::borrow::Borrow;
4444
use std::collections::hash_map::Entry;
45+
use std::fs::File;
4546
use std::hash::Hash;
4647
use std::io::{Read, Seek, Write};
4748
use std::num::NonZeroUsize;
@@ -2255,25 +2256,34 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
22552256
// culminating in the `CrateRoot` which points to all of it.
22562257
let root = ecx.encode_crate_root();
22572258

2258-
ecx.opaque.flush();
2259+
// Make sure we report any errors from writing to the file.
2260+
// If we forget this, compilation can succeed with an incomplete rmeta file,
2261+
// causing an ICE when the rmeta file is read by another compilation.
2262+
if let Err((path, err)) = ecx.opaque.finish() {
2263+
tcx.sess.emit_err(FailWriteFile { path: &path, err });
2264+
}
2265+
2266+
let file = ecx.opaque.file();
2267+
if let Err(err) = encode_root_position(file, root.position.get()) {
2268+
tcx.sess.emit_err(FailWriteFile { path: ecx.opaque.path(), err });
2269+
}
2270+
2271+
// Record metadata size for self-profiling
2272+
tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len());
2273+
}
22592274

2260-
let mut file = ecx.opaque.file();
2275+
fn encode_root_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> {
22612276
// We will return to this position after writing the root position.
22622277
let pos_before_seek = file.stream_position().unwrap();
22632278

22642279
// Encode the root position.
22652280
let header = METADATA_HEADER.len();
2266-
file.seek(std::io::SeekFrom::Start(header as u64))
2267-
.unwrap_or_else(|err| tcx.sess.emit_fatal(FailSeekFile { err }));
2268-
let pos = root.position.get();
2269-
file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8])
2270-
.unwrap_or_else(|err| tcx.sess.emit_fatal(FailWriteFile { err }));
2281+
file.seek(std::io::SeekFrom::Start(header as u64))?;
2282+
file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8])?;
22712283

22722284
// Return to the position where we are before writing the root position.
2273-
file.seek(std::io::SeekFrom::Start(pos_before_seek)).unwrap();
2274-
2275-
// Record metadata size for self-profiling
2276-
tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len());
2285+
file.seek(std::io::SeekFrom::Start(pos_before_seek))?;
2286+
Ok(())
22772287
}
22782288

22792289
pub fn provide(providers: &mut Providers) {

Diff for: compiler/rustc_middle/src/query/on_disk_cache.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use rustc_span::source_map::{SourceMap, StableSourceFileId};
2525
use rustc_span::{BytePos, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span};
2626
use rustc_span::{CachingSourceMapView, Symbol};
2727
use std::collections::hash_map::Entry;
28-
use std::io;
2928
use std::mem;
3029

3130
const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE;
@@ -862,7 +861,7 @@ impl<'a, 'tcx> CacheEncoder<'a, 'tcx> {
862861
}
863862

864863
#[inline]
865-
fn finish(self) -> Result<usize, io::Error> {
864+
fn finish(mut self) -> FileEncodeResult {
866865
self.encoder.finish()
867866
}
868867
}

Diff for: compiler/rustc_middle/src/ty/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,10 @@ impl<'tcx> GlobalCtxt<'tcx> {
629629
let icx = tls::ImplicitCtxt::new(self);
630630
tls::enter_context(&icx, || f(icx.tcx))
631631
}
632+
633+
pub fn finish(&self) -> FileEncodeResult {
634+
self.dep_graph.finish_encoding(&self.sess.prof)
635+
}
632636
}
633637

634638
impl<'tcx> TyCtxt<'tcx> {

Diff for: compiler/rustc_query_system/src/dep_graph/graph.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ impl<D: Deps> DepGraph<D> {
982982
}
983983
}
984984

985-
pub fn encode(&self, profiler: &SelfProfilerRef) -> FileEncodeResult {
985+
pub fn finish_encoding(&self, profiler: &SelfProfilerRef) -> FileEncodeResult {
986986
if let Some(data) = &self.data {
987987
data.current.encoder.steal().finish(profiler)
988988
} else {

Diff for: compiler/rustc_serialize/src/opaque.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use std::io::{self, Write};
55
use std::marker::PhantomData;
66
use std::ops::Range;
77
use std::path::Path;
8+
use std::path::PathBuf;
89

910
// -----------------------------------------------------------------------------
1011
// Encoder
1112
// -----------------------------------------------------------------------------
1213

13-
pub type FileEncodeResult = Result<usize, io::Error>;
14+
pub type FileEncodeResult = Result<usize, (PathBuf, io::Error)>;
1415

1516
/// The size of the buffer in `FileEncoder`.
1617
const BUF_SIZE: usize = 8192;
@@ -34,21 +35,28 @@ pub struct FileEncoder {
3435
// This is used to implement delayed error handling, as described in the
3536
// comment on `trait Encoder`.
3637
res: Result<(), io::Error>,
38+
path: PathBuf,
39+
#[cfg(debug_assertions)]
40+
finished: bool,
3741
}
3842

3943
impl FileEncoder {
4044
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
4145
// File::create opens the file for writing only. When -Zmeta-stats is enabled, the metadata
4246
// encoder rewinds the file to inspect what was written. So we need to always open the file
4347
// for reading and writing.
44-
let file = File::options().read(true).write(true).create(true).truncate(true).open(path)?;
48+
let file =
49+
File::options().read(true).write(true).create(true).truncate(true).open(&path)?;
4550

4651
Ok(FileEncoder {
4752
buf: vec![0u8; BUF_SIZE].into_boxed_slice().try_into().unwrap(),
53+
path: path.as_ref().into(),
4854
buffered: 0,
4955
flushed: 0,
5056
file,
5157
res: Ok(()),
58+
#[cfg(debug_assertions)]
59+
finished: false,
5260
})
5361
}
5462

@@ -63,6 +71,10 @@ impl FileEncoder {
6371
#[cold]
6472
#[inline(never)]
6573
pub fn flush(&mut self) {
74+
#[cfg(debug_assertions)]
75+
{
76+
self.finished = false;
77+
}
6678
if self.res.is_ok() {
6779
self.res = self.file.write_all(&self.buf[..self.buffered]);
6880
}
@@ -74,6 +86,10 @@ impl FileEncoder {
7486
&self.file
7587
}
7688

89+
pub fn path(&self) -> &Path {
90+
&self.path
91+
}
92+
7793
#[inline]
7894
fn buffer_empty(&mut self) -> &mut [u8] {
7995
// SAFETY: self.buffered is inbounds as an invariant of the type
@@ -97,6 +113,10 @@ impl FileEncoder {
97113

98114
#[inline]
99115
fn write_all(&mut self, buf: &[u8]) {
116+
#[cfg(debug_assertions)]
117+
{
118+
self.finished = false;
119+
}
100120
if let Some(dest) = self.buffer_empty().get_mut(..buf.len()) {
101121
dest.copy_from_slice(buf);
102122
self.buffered += buf.len();
@@ -121,6 +141,10 @@ impl FileEncoder {
121141
/// with one instruction, so while this does in some sense do wasted work, we come out ahead.
122142
#[inline]
123143
pub fn write_with<const N: usize>(&mut self, visitor: impl FnOnce(&mut [u8; N]) -> usize) {
144+
#[cfg(debug_assertions)]
145+
{
146+
self.finished = false;
147+
}
124148
let flush_threshold = const { BUF_SIZE.checked_sub(N).unwrap() };
125149
if std::intrinsics::unlikely(self.buffered > flush_threshold) {
126150
self.flush();
@@ -152,20 +176,25 @@ impl FileEncoder {
152176
})
153177
}
154178

155-
pub fn finish(mut self) -> Result<usize, io::Error> {
179+
pub fn finish(&mut self) -> FileEncodeResult {
156180
self.flush();
181+
#[cfg(debug_assertions)]
182+
{
183+
self.finished = true;
184+
}
157185
match std::mem::replace(&mut self.res, Ok(())) {
158186
Ok(()) => Ok(self.position()),
159-
Err(e) => Err(e),
187+
Err(e) => Err((self.path.clone(), e)),
160188
}
161189
}
162190
}
163191

192+
#[cfg(debug_assertions)]
164193
impl Drop for FileEncoder {
165194
fn drop(&mut self) {
166-
// Likely to be a no-op, because `finish` should have been called and
167-
// it also flushes. But do it just in case.
168-
self.flush();
195+
if !std::thread::panicking() {
196+
assert!(self.finished);
197+
}
169198
}
170199
}
171200

Diff for: src/librustdoc/scrape_examples.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ pub(crate) fn run(
325325
// Save output to provided path
326326
let mut encoder = FileEncoder::new(options.output_path).map_err(|e| e.to_string())?;
327327
calls.encode(&mut encoder);
328-
encoder.finish().map_err(|e| e.to_string())?;
328+
encoder.finish().map_err(|(_path, e)| e.to_string())?;
329329

330330
Ok(())
331331
};

0 commit comments

Comments
 (0)