Skip to content

Commit b663c0f

Browse files
committed
Auto merge of rust-lang#85698 - ehuss:incremental-session-panic, r=estebank
Don't panic when failing to initialize incremental directory. This removes a panic when rustc fails to initialize the incremental directory. This can commonly happen on various filesystems that don't support locking (often various network filesystems). Panics can be confusing and scary, and there are already plenty of issues reporting this. This has been panicking since 1.22 due to I think rust-lang#44502 which was a major rework of how things work. Previously, things were simpler and the [`load_dep_graph`](https://github.com/rust-lang/rust/blob/1.21.0/src/librustc_incremental/persist/load.rs#L43-L65) function would emit an error and then continue on without panicking. With 1.22, [`load_dep_graph`](https://github.com/rust-lang/rust/blob/1.22.0/src/librustc_incremental/persist/load.rs#L44) was changed so that it assumes it can load the data without errors. Today, the problem is that it calls [`prepare_session_directory`](https://github.com/rust-lang/rust/blob/fbf1b1a7193cda17008ab590e06ad28d9924023b/compiler/rustc_interface/src/passes.rs#L175-L179) and then immediately calls `garbage_collect_session_directories` which will panic since the session is `IncrCompSession::NotInitialized`. The solution here is to have `prepare_session_directory` return an error that must be handled so that compilation stops if it fails. Some other options: * Ignore directory lock failures. * Print a warning on directory lock failure, but otherwise continue with incremental enabled. * Print a warning on directory lock failure, and disable incremental. * Provide a different locking mechanism. Cargo ignores lock errors if locking is not supported, so that would be a precedent for the first option. These options would require quite a bit more changes, but I'm happy to entertain any of them, as I think they all have valid justifications. There is more discussion on the many issues where this is reported: rust-lang#49773, rust-lang#59224, rust-lang#66513, rust-lang#76251. I'm not sure if this can be considered closing any of those, though, since I think there is some value in discussing if there is a way to avoid the error altogether. But I think it would make sense to at least close all but one to consolidate them.
2 parents ff5522f + 4c550bc commit b663c0f

File tree

9 files changed

+77
-27
lines changed

9 files changed

+77
-27
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3876,6 +3876,7 @@ dependencies = [
38763876
"rand 0.7.3",
38773877
"rustc_ast",
38783878
"rustc_data_structures",
3879+
"rustc_errors",
38793880
"rustc_fs_util",
38803881
"rustc_graphviz",
38813882
"rustc_hir",

compiler/rustc_data_structures/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ tempfile = "3.2"
3434
version = "0.11"
3535

3636
[target.'cfg(windows)'.dependencies]
37-
winapi = { version = "0.3", features = ["fileapi", "psapi"] }
37+
winapi = { version = "0.3", features = ["fileapi", "psapi", "winerror"] }
3838

3939
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
4040
memmap2 = "0.2.1"

compiler/rustc_data_structures/src/flock.rs

+13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ cfg_if! {
5454
Ok(Lock { _file: file })
5555
}
5656
}
57+
58+
pub fn error_unsupported(err: &io::Error) -> bool {
59+
matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS))
60+
}
5761
}
5862

5963
// Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by
@@ -103,6 +107,10 @@ cfg_if! {
103107
Ok(Lock { file })
104108
}
105109
}
110+
111+
pub fn error_unsupported(err: &io::Error) -> bool {
112+
matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS))
113+
}
106114
}
107115

108116
impl Drop for Lock {
@@ -122,6 +130,7 @@ cfg_if! {
122130
use std::mem;
123131
use std::os::windows::prelude::*;
124132

133+
use winapi::shared::winerror::ERROR_INVALID_FUNCTION;
125134
use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK};
126135
use winapi::um::fileapi::LockFileEx;
127136
use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};
@@ -194,6 +203,10 @@ cfg_if! {
194203
Ok(Lock { _file: file })
195204
}
196205
}
206+
207+
pub fn error_unsupported(err: &io::Error) -> bool {
208+
err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32)
209+
}
197210
}
198211

199212
// Note that we don't need a Drop impl on the Windows: The file is unlocked

compiler/rustc_incremental/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ rustc_macros = { path = "../rustc_macros" }
2020
rustc_span = { path = "../rustc_span" }
2121
rustc_fs_util = { path = "../rustc_fs_util" }
2222
rustc_session = { path = "../rustc_session" }
23+
rustc_errors = { path = "../rustc_errors" }

compiler/rustc_incremental/src/persist/fs.rs

+43-23
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
107107
use rustc_data_structures::svh::Svh;
108108
use rustc_data_structures::{base_n, flock};
109+
use rustc_errors::ErrorReported;
109110
use rustc_fs_util::{link_or_copy, LinkOrCopy};
110111
use rustc_session::{CrateDisambiguator, Session};
111112

@@ -189,9 +190,9 @@ pub fn prepare_session_directory(
189190
sess: &Session,
190191
crate_name: &str,
191192
crate_disambiguator: CrateDisambiguator,
192-
) {
193+
) -> Result<(), ErrorReported> {
193194
if sess.opts.incremental.is_none() {
194-
return;
195+
return Ok(());
195196
}
196197

197198
let _timer = sess.timer("incr_comp_prepare_session_directory");
@@ -201,9 +202,7 @@ pub fn prepare_session_directory(
201202
// {incr-comp-dir}/{crate-name-and-disambiguator}
202203
let crate_dir = crate_path(sess, crate_name, crate_disambiguator);
203204
debug!("crate-dir: {}", crate_dir.display());
204-
if create_dir(sess, &crate_dir, "crate").is_err() {
205-
return;
206-
}
205+
create_dir(sess, &crate_dir, "crate")?;
207206

208207
// Hack: canonicalize the path *after creating the directory*
209208
// because, on windows, long paths can cause problems;
@@ -217,7 +216,7 @@ pub fn prepare_session_directory(
217216
crate_dir.display(),
218217
err
219218
));
220-
return;
219+
return Err(ErrorReported);
221220
}
222221
};
223222

@@ -232,16 +231,11 @@ pub fn prepare_session_directory(
232231

233232
// Lock the new session directory. If this fails, return an
234233
// error without retrying
235-
let (directory_lock, lock_file_path) = match lock_directory(sess, &session_dir) {
236-
Ok(e) => e,
237-
Err(_) => return,
238-
};
234+
let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir)?;
239235

240236
// Now that we have the lock, we can actually create the session
241237
// directory
242-
if create_dir(sess, &session_dir, "session").is_err() {
243-
return;
244-
}
238+
create_dir(sess, &session_dir, "session")?;
245239

246240
// Find a suitable source directory to copy from. Ignore those that we
247241
// have already tried before.
@@ -257,7 +251,7 @@ pub fn prepare_session_directory(
257251
);
258252

259253
sess.init_incr_comp_session(session_dir, directory_lock, false);
260-
return;
254+
return Ok(());
261255
};
262256

263257
debug!("attempting to copy data from source: {}", source_directory.display());
@@ -278,7 +272,7 @@ pub fn prepare_session_directory(
278272
}
279273

280274
sess.init_incr_comp_session(session_dir, directory_lock, true);
281-
return;
275+
return Ok(());
282276
} else {
283277
debug!("copying failed - trying next directory");
284278

@@ -478,7 +472,7 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf {
478472
directory_path
479473
}
480474

481-
fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ()> {
475+
fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorReported> {
482476
match std_fs::create_dir_all(path) {
483477
Ok(()) => {
484478
debug!("{} directory created successfully", dir_tag);
@@ -492,13 +486,16 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ()> {
492486
path.display(),
493487
err
494488
));
495-
Err(())
489+
Err(ErrorReported)
496490
}
497491
}
498492
}
499493

500494
/// Allocate the lock-file and lock it.
501-
fn lock_directory(sess: &Session, session_dir: &Path) -> Result<(flock::Lock, PathBuf), ()> {
495+
fn lock_directory(
496+
sess: &Session,
497+
session_dir: &Path,
498+
) -> Result<(flock::Lock, PathBuf), ErrorReported> {
502499
let lock_file_path = lock_file_path(session_dir);
503500
debug!("lock_directory() - lock_file: {}", lock_file_path.display());
504501

@@ -510,13 +507,36 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> Result<(flock::Lock, Pa
510507
) {
511508
// the lock should be exclusive
512509
Ok(lock) => Ok((lock, lock_file_path)),
513-
Err(err) => {
514-
sess.err(&format!(
510+
Err(lock_err) => {
511+
let mut err = sess.struct_err(&format!(
515512
"incremental compilation: could not create \
516-
session directory lock file: {}",
517-
err
513+
session directory lock file: {}",
514+
lock_err
518515
));
519-
Err(())
516+
if flock::Lock::error_unsupported(&lock_err) {
517+
err.note(&format!(
518+
"the filesystem for the incremental path at {} \
519+
does not appear to support locking, consider changing the \
520+
incremental path to a filesystem that supports locking \
521+
or disable incremental compilation",
522+
session_dir.display()
523+
));
524+
if std::env::var_os("CARGO").is_some() {
525+
err.help(
526+
"incremental compilation can be disabled by setting the \
527+
environment variable CARGO_INCREMENTAL=0 (see \
528+
https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)",
529+
);
530+
err.help(
531+
"the entire build directory can be changed to a different \
532+
filesystem by setting the environment variable CARGO_TARGET_DIR \
533+
to a different path (see \
534+
https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)",
535+
);
536+
}
537+
}
538+
err.emit();
539+
Err(ErrorReported)
520540
}
521541
}
522542
}

compiler/rustc_interface/src/passes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub fn register_plugins<'a>(
172172

173173
let disambiguator = util::compute_crate_disambiguator(sess);
174174
sess.crate_disambiguator.set(disambiguator).expect("not yet initialized");
175-
rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator);
175+
rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator)?;
176176

177177
if sess.opts.incremental.is_some() {
178178
sess.time("incr_comp_garbage_collect_session_directories", || {

compiler/rustc_interface/src/queries.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<'tcx> Queries<'tcx> {
148148
self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
149149
krate,
150150
&crate_name,
151-
);
151+
)?;
152152

153153
// Compute the dependency graph (in the background). We want to do
154154
// this as early as possible, to give the DepGraph maximum time to
@@ -157,7 +157,7 @@ impl<'tcx> Queries<'tcx> {
157157
// called, which happens within passes::register_plugins().
158158
self.dep_graph_future().ok();
159159

160-
result
160+
Ok(result)
161161
})
162162
}
163163

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
include ../../run-make-fulldeps/tools.mk
2+
3+
SESSION_DIR := $(TMPDIR)/session
4+
OUTPUT_FILE := $(TMPDIR)/build-output
5+
6+
all:
7+
echo $(TMPDIR)
8+
# Make it so that rustc will fail to create a session directory.
9+
touch $(SESSION_DIR)
10+
# Check exit code is 1 for an error, and not 101 for ICE.
11+
$(RUSTC) foo.rs --crate-type=rlib -C incremental=$(SESSION_DIR) > $(OUTPUT_FILE) 2>&1; [ $$? -eq 1 ]
12+
$(CGREP) "Could not create incremental compilation crate directory" < $(OUTPUT_FILE)
13+
# -v tests are fragile, hopefully this text won't change
14+
$(CGREP) -v "internal compiler error" < $(OUTPUT_FILE)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// intentionally empty

0 commit comments

Comments
 (0)