diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 42ce849c64d..ce26ff62235 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -484,6 +484,7 @@ E0782: include_str!("./error_codes/E0782.md"), E0783: include_str!("./error_codes/E0783.md"), E0784: include_str!("./error_codes/E0784.md"), E0785: include_str!("./error_codes/E0785.md"), +E0786: include_str!("./error_codes/E0786.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/compiler/rustc_error_codes/src/error_codes/E0786.md b/compiler/rustc_error_codes/src/error_codes/E0786.md new file mode 100644 index 00000000000..4a9635bf516 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0786.md @@ -0,0 +1,14 @@ +A metadata file was invalid. + +Erroneous code example: + +```ignore (needs extern files) +use ::foo; // error: found invalid metadata files for crate `foo` +``` + +When loading crates, each crate must have a valid metadata file. +Invalid files could be caused by filesystem corruption, +an IO error while reading the file, or (rarely) a bug in the compiler itself. + +Consider deleting the file and recreating it, +or reporting a bug against the compiler. diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 8db2291dfcf..7cba16e0a9a 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -350,6 +350,7 @@ impl<'a> CrateLocator<'a> { self.crate_rejections.via_kind.clear(); self.crate_rejections.via_version.clear(); self.crate_rejections.via_filename.clear(); + self.crate_rejections.via_invalid.clear(); } crate fn maybe_load_library_crate(&mut self) -> Result, CrateError> { @@ -548,7 +549,17 @@ impl<'a> CrateLocator<'a> { continue; } } - Err(err) => { + Err(MetadataError::LoadFailure(err)) => { + info!("no metadata found: {}", err); + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + self.crate_rejections + .via_invalid + .push(CrateMismatch { path: lib, got: err }); + continue; + } + Err(err @ MetadataError::NotPresent(_)) => { info!("no metadata found: {}", err); continue; } @@ -726,25 +737,28 @@ impl<'a> CrateLocator<'a> { fn get_metadata_section( target: &Target, flavor: CrateFlavor, - filename: &Path, + filename: &'p Path, loader: &dyn MetadataLoader, -) -> Result { +) -> Result> { if !filename.exists() { - return Err(format!("no such file: '{}'", filename.display())); + return Err(MetadataError::NotPresent(filename)); } let raw_bytes: MetadataRef = match flavor { - CrateFlavor::Rlib => loader.get_rlib_metadata(target, filename)?, + CrateFlavor::Rlib => { + loader.get_rlib_metadata(target, filename).map_err(MetadataError::LoadFailure)? + } CrateFlavor::Dylib => { - let buf = loader.get_dylib_metadata(target, filename)?; + let buf = + loader.get_dylib_metadata(target, filename).map_err(MetadataError::LoadFailure)?; // The header is uncompressed let header_len = METADATA_HEADER.len(); debug!("checking {} bytes of metadata-version stamp", header_len); let header = &buf[..cmp::min(header_len, buf.len())]; if header != METADATA_HEADER { - return Err(format!( - "incompatible metadata version found: '{}'", + return Err(MetadataError::LoadFailure(format!( + "invalid metadata version found: {}", filename.display() - )); + ))); } // Header is okay -> inflate the actual metadata @@ -756,17 +770,28 @@ fn get_metadata_section( match FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated) { Ok(_) => rustc_erase_owner!(OwningRef::new(inflated).map_owner_box()), Err(_) => { - return Err(format!("failed to decompress metadata: {}", filename.display())); + return Err(MetadataError::LoadFailure(format!( + "failed to decompress metadata: {}", + filename.display() + ))); } } } CrateFlavor::Rmeta => { // mmap the file, because only a small fraction of it is read. - let file = std::fs::File::open(filename) - .map_err(|_| format!("failed to open rmeta metadata: '{}'", filename.display()))?; + let file = std::fs::File::open(filename).map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to open rmeta metadata: '{}'", + filename.display() + )) + })?; let mmap = unsafe { Mmap::map(file) }; - let mmap = mmap - .map_err(|_| format!("failed to mmap rmeta metadata: '{}'", filename.display()))?; + let mmap = mmap.map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to mmap rmeta metadata: '{}'", + filename.display() + )) + })?; rustc_erase_owner!(OwningRef::new(mmap).map_owner_box()) } @@ -775,7 +800,10 @@ fn get_metadata_section( if blob.is_compatible() { Ok(blob) } else { - Err(format!("incompatible metadata version found: '{}'", filename.display())) + Err(MetadataError::LoadFailure(format!( + "invalid metadata version found: {}", + filename.display() + ))) } } @@ -854,6 +882,7 @@ struct CrateRejections { via_kind: Vec, via_version: Vec, via_filename: Vec, + via_invalid: Vec, } /// Candidate rejection reasons collected during crate search. @@ -883,6 +912,24 @@ crate enum CrateError { NonDylibPlugin(Symbol), } +enum MetadataError<'a> { + /// The file was missing. + NotPresent(&'a Path), + /// The file was present and invalid. + LoadFailure(String), +} + +impl fmt::Display for MetadataError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MetadataError::NotPresent(filename) => { + f.write_str(&format!("no such file: '{}'", filename.display())) + } + MetadataError::LoadFailure(msg) => f.write_str(msg), + } + } +} + impl CrateError { crate fn report(self, sess: &Session, span: Span, missing_core: bool) -> ! { let mut err = match self { @@ -1064,6 +1111,19 @@ impl CrateError { } err.note(&msg); err + } else if !locator.crate_rejections.via_invalid.is_empty() { + let mut err = struct_span_err!( + sess, + span, + E0786, + "found invalid metadata files for crate `{}`{}", + crate_name, + add, + ); + for CrateMismatch { path: _, got } in locator.crate_rejections.via_invalid { + err.note(&got); + } + err } else { let mut err = struct_span_err!( sess, diff --git a/src/test/run-make-fulldeps/invalid-library/Makefile b/src/test/run-make-fulldeps/invalid-library/Makefile index c75713c3ee5..de463a33014 100644 --- a/src/test/run-make-fulldeps/invalid-library/Makefile +++ b/src/test/run-make-fulldeps/invalid-library/Makefile @@ -3,4 +3,4 @@ all: touch $(TMPDIR)/lib.rmeta $(AR) crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/lib.rmeta - $(RUSTC) foo.rs 2>&1 | $(CGREP) "can't find crate for" + $(RUSTC) foo.rs 2>&1 | $(CGREP) "found invalid metadata" diff --git a/src/test/run-make/invalid-so/Makefile b/src/test/run-make/invalid-so/Makefile new file mode 100644 index 00000000000..5b82ecd207d --- /dev/null +++ b/src/test/run-make/invalid-so/Makefile @@ -0,0 +1,7 @@ +include ../../run-make-fulldeps/tools.mk + +DYLIB_NAME := $(shell echo | $(RUSTC) --crate-name foo --crate-type dylib --print file-names -) + +all: + echo >> $(TMPDIR)/$(DYLIB_NAME) + $(RUSTC) --crate-type lib --extern foo=$(TMPDIR)/$(DYLIB_NAME) bar.rs 2>&1 | $(CGREP) 'invalid metadata files for crate `foo`' diff --git a/src/test/run-make/invalid-so/bar.rs b/src/test/run-make/invalid-so/bar.rs new file mode 100644 index 00000000000..49af74e1b74 --- /dev/null +++ b/src/test/run-make/invalid-so/bar.rs @@ -0,0 +1 @@ +extern crate foo; diff --git a/src/test/ui/crate-loading/auxiliary/libfoo.rlib b/src/test/ui/crate-loading/auxiliary/libfoo.rlib new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/test/ui/crate-loading/invalid-rlib.rs b/src/test/ui/crate-loading/invalid-rlib.rs new file mode 100644 index 00000000000..77c29090a3e --- /dev/null +++ b/src/test/ui/crate-loading/invalid-rlib.rs @@ -0,0 +1,8 @@ +// compile-flags: --crate-type lib --extern foo={{src-base}}/crate-loading/auxiliary/libfoo.rlib +// normalize-stderr-test: "failed to mmap file '.*auxiliary/libfoo.rlib':.*" -> "failed to mmap file 'auxiliary/libfoo.rlib'" +// don't emit warn logging, it's basically the same as the errors and it's annoying to normalize +// rustc-env:RUSTC_LOG=error +// edition:2018 +#![no_std] +use ::foo; //~ ERROR invalid metadata files for crate `foo` +//~| NOTE failed to mmap file diff --git a/src/test/ui/crate-loading/invalid-rlib.stderr b/src/test/ui/crate-loading/invalid-rlib.stderr new file mode 100644 index 00000000000..b2c79f742fb --- /dev/null +++ b/src/test/ui/crate-loading/invalid-rlib.stderr @@ -0,0 +1,11 @@ +error[E0786]: found invalid metadata files for crate `foo` + --> $DIR/invalid-rlib.rs:7:7 + | +LL | use ::foo; + | ^^^ + | + = note: failed to mmap file 'auxiliary/libfoo.rlib' + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0786`.