diff --git a/Cargo.lock b/Cargo.lock
index 62734bfaf62..5d5083277d3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3876,6 +3876,7 @@ dependencies = [
  "rand 0.7.3",
  "rustc_ast",
  "rustc_data_structures",
+ "rustc_errors",
  "rustc_fs_util",
  "rustc_graphviz",
  "rustc_hir",
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index aa95ecbdaf9..c35a164bb33 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -34,7 +34,7 @@ tempfile = "3.2"
 version = "0.11"
 
 [target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3", features = ["fileapi", "psapi"] }
+winapi = { version = "0.3", features = ["fileapi", "psapi", "winerror"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 memmap2 = "0.2.1"
diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs
index 9383be474fd..4f5d8d7ea48 100644
--- a/compiler/rustc_data_structures/src/flock.rs
+++ b/compiler/rustc_data_structures/src/flock.rs
@@ -54,6 +54,10 @@ cfg_if! {
                     Ok(Lock { _file: file })
                 }
             }
+
+            pub fn error_unsupported(err: &io::Error) -> bool {
+                matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS))
+            }
         }
 
         // Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by
@@ -103,6 +107,10 @@ cfg_if! {
                     Ok(Lock { file })
                 }
             }
+
+            pub fn error_unsupported(err: &io::Error) -> bool {
+                matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS))
+            }
         }
 
         impl Drop for Lock {
@@ -122,6 +130,7 @@ cfg_if! {
         use std::mem;
         use std::os::windows::prelude::*;
 
+        use winapi::shared::winerror::ERROR_INVALID_FUNCTION;
         use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK};
         use winapi::um::fileapi::LockFileEx;
         use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};
@@ -194,6 +203,10 @@ cfg_if! {
                     Ok(Lock { _file: file })
                 }
             }
+
+            pub fn error_unsupported(err: &io::Error) -> bool {
+                err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32)
+            }
         }
 
         // Note that we don't need a Drop impl on the Windows: The file is unlocked
diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml
index 049e5b8b722..85bf4dc176b 100644
--- a/compiler/rustc_incremental/Cargo.toml
+++ b/compiler/rustc_incremental/Cargo.toml
@@ -20,3 +20,4 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_span = { path = "../rustc_span" }
 rustc_fs_util = { path = "../rustc_fs_util" }
 rustc_session = { path = "../rustc_session" }
+rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 30c6c408bc7..a9925832fd8 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -106,6 +106,7 @@
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::{base_n, flock};
+use rustc_errors::ErrorReported;
 use rustc_fs_util::{link_or_copy, LinkOrCopy};
 use rustc_session::{CrateDisambiguator, Session};
 
@@ -189,9 +190,9 @@ pub fn prepare_session_directory(
     sess: &Session,
     crate_name: &str,
     crate_disambiguator: CrateDisambiguator,
-) {
+) -> Result<(), ErrorReported> {
     if sess.opts.incremental.is_none() {
-        return;
+        return Ok(());
     }
 
     let _timer = sess.timer("incr_comp_prepare_session_directory");
@@ -201,9 +202,7 @@ pub fn prepare_session_directory(
     // {incr-comp-dir}/{crate-name-and-disambiguator}
     let crate_dir = crate_path(sess, crate_name, crate_disambiguator);
     debug!("crate-dir: {}", crate_dir.display());
-    if create_dir(sess, &crate_dir, "crate").is_err() {
-        return;
-    }
+    create_dir(sess, &crate_dir, "crate")?;
 
     // Hack: canonicalize the path *after creating the directory*
     // because, on windows, long paths can cause problems;
@@ -217,7 +216,7 @@ pub fn prepare_session_directory(
                 crate_dir.display(),
                 err
             ));
-            return;
+            return Err(ErrorReported);
         }
     };
 
@@ -232,16 +231,11 @@ pub fn prepare_session_directory(
 
         // Lock the new session directory. If this fails, return an
         // error without retrying
-        let (directory_lock, lock_file_path) = match lock_directory(sess, &session_dir) {
-            Ok(e) => e,
-            Err(_) => return,
-        };
+        let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir)?;
 
         // Now that we have the lock, we can actually create the session
         // directory
-        if create_dir(sess, &session_dir, "session").is_err() {
-            return;
-        }
+        create_dir(sess, &session_dir, "session")?;
 
         // Find a suitable source directory to copy from. Ignore those that we
         // have already tried before.
@@ -257,7 +251,7 @@ pub fn prepare_session_directory(
             );
 
             sess.init_incr_comp_session(session_dir, directory_lock, false);
-            return;
+            return Ok(());
         };
 
         debug!("attempting to copy data from source: {}", source_directory.display());
@@ -278,7 +272,7 @@ pub fn prepare_session_directory(
             }
 
             sess.init_incr_comp_session(session_dir, directory_lock, true);
-            return;
+            return Ok(());
         } else {
             debug!("copying failed - trying next directory");
 
@@ -478,7 +472,7 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf {
     directory_path
 }
 
-fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ()> {
+fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorReported> {
     match std_fs::create_dir_all(path) {
         Ok(()) => {
             debug!("{} directory created successfully", dir_tag);
@@ -492,13 +486,16 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ()> {
                 path.display(),
                 err
             ));
-            Err(())
+            Err(ErrorReported)
         }
     }
 }
 
 /// Allocate the lock-file and lock it.
-fn lock_directory(sess: &Session, session_dir: &Path) -> Result<(flock::Lock, PathBuf), ()> {
+fn lock_directory(
+    sess: &Session,
+    session_dir: &Path,
+) -> Result<(flock::Lock, PathBuf), ErrorReported> {
     let lock_file_path = lock_file_path(session_dir);
     debug!("lock_directory() - lock_file: {}", lock_file_path.display());
 
@@ -510,13 +507,23 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> Result<(flock::Lock, Pa
     ) {
         // the lock should be exclusive
         Ok(lock) => Ok((lock, lock_file_path)),
-        Err(err) => {
-            sess.err(&format!(
+        Err(lock_err) => {
+            let mut err = sess.struct_err(&format!(
                 "incremental compilation: could not create \
-                               session directory lock file: {}",
-                err
+                 session directory lock file: {}",
+                lock_err
             ));
-            Err(())
+            if flock::Lock::error_unsupported(&lock_err) {
+                err.note(&format!(
+                    "the filesystem for the incremental path at {} \
+                     does not appear to support locking, consider changing the \
+                     incremental path to a filesystem that supports locking \
+                     or disable incremental compilation",
+                    session_dir.display()
+                ));
+            }
+            err.emit();
+            Err(ErrorReported)
         }
     }
 }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 70ffff1ab99..f99d9290238 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -172,7 +172,7 @@ pub fn register_plugins<'a>(
 
     let disambiguator = util::compute_crate_disambiguator(sess);
     sess.crate_disambiguator.set(disambiguator).expect("not yet initialized");
-    rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator);
+    rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator)?;
 
     if sess.opts.incremental.is_some() {
         sess.time("incr_comp_garbage_collect_session_directories", || {
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 92d05e48068..969b526235b 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -148,7 +148,7 @@ impl<'tcx> Queries<'tcx> {
                 self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
                 krate,
                 &crate_name,
-            );
+            )?;
 
             // Compute the dependency graph (in the background). We want to do
             // this as early as possible, to give the DepGraph maximum time to
@@ -157,7 +157,7 @@ impl<'tcx> Queries<'tcx> {
             // called, which happens within passes::register_plugins().
             self.dep_graph_future().ok();
 
-            result
+            Ok(result)
         })
     }