]> git.lizzy.rs Git - rust.git/commitdiff
Don't ICE on errors in function returning impl trait
authorJoshua Nelson <jyn514@gmail.com>
Fri, 3 Jul 2020 22:41:23 +0000 (18:41 -0400)
committerJoshua Nelson <jyn514@gmail.com>
Wed, 15 Jul 2020 14:54:05 +0000 (10:54 -0400)
Instead, report the error.

This emits the errors on-demand, without special-casing `impl Trait`, so
it should catch all ICEs of this kind, including ones that haven't been
found yet.

Since the error is emitted during type-checking there is less info about
the error; see comments in the code for details.

- Add test case for -> impl Trait
- Add test for impl trait with alias
- Move EmitIgnoredResolutionErrors to rustdoc

This makes `fn typeck_item_bodies` public, which is not desired behavior.
That change should be removed once
https://github.com/rust-lang/rust/pull/74070 is merged.

- Don't visit nested closures twice

src/librustc_typeck/check/mod.rs
src/librustc_typeck/lib.rs
src/librustdoc/core.rs
src/librustdoc/lib.rs
src/test/rustdoc-ui/error-in-impl-trait.rs [new file with mode: 0644]
src/test/rustdoc-ui/error-in-impl-trait.stderr [new file with mode: 0644]
src/test/rustdoc/impl-trait-alias.rs [new file with mode: 0644]

index bc01da324b66fd4dab9457d5a892e4efb84a4b84..514600b4733d49e82e77e65e4875bdb7fa6374c5 100644 (file)
@@ -955,7 +955,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
     val.fold_with(&mut FixupFolder { tcx })
 }
 
-fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckTables<'tcx> {
+pub fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckTables<'tcx> {
     let fallback = move || tcx.type_of(def_id.to_def_id());
     typeck_tables_of_with_fallback(tcx, def_id, fallback)
 }
index 9ba2545ba63cbc8d98d512826a4679d9667f3f01..79e1585ce3cdb7366331921227f5c0d02b8f844a 100644 (file)
@@ -78,7 +78,7 @@
 pub mod expr_use_visitor;
 
 mod astconv;
-mod check;
+pub mod check;
 mod check_unused;
 mod coherence;
 mod collect;
index 061d2d21ec927798ac7d2b8718660af051188225..3d0da0e9157f7fcc4edc312ad1de1f234bfe123a 100644 (file)
@@ -375,6 +375,15 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
         override_queries: Some(|_sess, local_providers, external_providers| {
             local_providers.lint_mod = |_, _| {};
             external_providers.lint_mod = |_, _| {};
+            //let old_typeck = local_providers.typeck_tables_of;
+            local_providers.typeck_tables_of = move |tcx, def_id| {
+                let hir = tcx.hir();
+                let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id)));
+                debug!("visiting body for {:?}", def_id);
+                EmitIgnoredResolutionErrors::new(&tcx.sess).visit_body(body);
+                rustc_typeck::check::typeck_tables_of(tcx, def_id)
+                //DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id))
+            };
         }),
         registry: rustc_driver::diagnostics_registry(),
     };
@@ -572,6 +581,75 @@ fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) {
     })
 }
 
+use rustc_hir::def::Res;
+use rustc_hir::{
+    intravisit::{NestedVisitorMap, Visitor},
+    Path,
+};
+use rustc_middle::hir::map::Map;
+
+/*
+thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(rustc_middle::ty::TyCtxt<'tcx>, rustc_span::def_id::LocalDefId) -> &'tcx rustc_middle::ty::TypeckTables<'tcx> = {
+    let mut providers = rustc_middle::ty::query::Providers::default();
+    rustc_typeck::provide(&mut providers);
+    providers.typeck_tables_of
+});
+*/
+
+/// Due to https://github.com/rust-lang/rust/pull/73566,
+/// the name resolution pass may find errors that are never emitted.
+/// If typeck is called after this happens, then we'll get an ICE:
+/// 'Res::Error found but not reported'. To avoid this, emit the errors now.
+struct EmitIgnoredResolutionErrors<'a> {
+    session: &'a Session,
+}
+
+impl<'a> EmitIgnoredResolutionErrors<'a> {
+    fn new(session: &'a Session) -> Self {
+        Self { session }
+    }
+}
+
+impl<'a> Visitor<'a> for EmitIgnoredResolutionErrors<'_> {
+    type Map = Map<'a>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        // If we visit nested bodies, then we will report errors twice for e.g. nested closures
+        NestedVisitorMap::None
+    }
+
+    fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) {
+        log::debug!("visiting path {:?}", path);
+        if path.res == Res::Err {
+            // We have less context here than in rustc_resolve,
+            // so we can only emit the name and span.
+            // However we can give a hint that rustc_resolve will have more info.
+            // NOTE: this is a very rare case (only 4 out of several hundred thousand crates in a crater run)
+            // NOTE: so it's ok for it to be slow
+            let label = format!(
+                "could not resolve path `{}`",
+                path.segments
+                    .iter()
+                    .map(|segment| segment.ident.as_str().to_string())
+                    .collect::<Vec<_>>()
+                    .join("::")
+            );
+            let mut err = rustc_errors::struct_span_err!(
+                self.session,
+                path.span,
+                E0433,
+                "failed to resolve: {}",
+                label
+            );
+            err.span_label(path.span, label);
+            err.note("this error was originally ignored because you are running `rustdoc`");
+            err.note("try running again with `rustc` and you may get a more detailed error");
+            err.emit();
+        }
+        // NOTE: this does _not_ visit the path segments
+    }
+}
+
 /// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter
 /// for `impl Trait` in argument position.
 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
index 57151e2b200023ddcada5ed6ec7410a8f9fec42d..4bd6b1260ccef1b05ee26dd500a0f66e3977f050 100644 (file)
@@ -94,6 +94,7 @@ pub fn main() {
         32_000_000 // 32MB on other platforms
     };
     rustc_driver::set_sigpipe_handler();
+    rustc_driver::install_ice_hook();
     env_logger::init_from_env("RUSTDOC_LOG");
     let res = std::thread::Builder::new()
         .stack_size(thread_stack_size)
diff --git a/src/test/rustdoc-ui/error-in-impl-trait.rs b/src/test/rustdoc-ui/error-in-impl-trait.rs
new file mode 100644 (file)
index 0000000..fbe663a
--- /dev/null
@@ -0,0 +1,28 @@
+// edition:2018
+#![feature(type_alias_impl_trait)]
+
+pub trait ValidTrait {}
+type ImplTrait = impl ValidTrait;
+
+/// This returns impl trait
+pub fn g() -> impl ValidTrait {
+    error::_in::impl_trait()
+    //~^ ERROR failed to resolve
+}
+
+/// This returns impl trait, but using a type alias
+pub fn h() -> ImplTrait {
+    error::_in::impl_trait::alias();
+    //~^ ERROR failed to resolve
+    (|| error::_in::impl_trait::alias::nested::closure())()
+    //~^ ERROR failed to resolve
+}
+
+/// This used to work with ResolveBodyWithLoop.
+/// However now that we ignore type checking instead of modifying the function body,
+/// the return type is seen as `impl Future<Output = u32>`, not a `u32`.
+/// So it no longer allows errors in the function body.
+pub async fn a() -> u32 {
+    error::_in::async_fn()
+    //~^ ERROR failed to resolve
+}
diff --git a/src/test/rustdoc-ui/error-in-impl-trait.stderr b/src/test/rustdoc-ui/error-in-impl-trait.stderr
new file mode 100644 (file)
index 0000000..4df40da
--- /dev/null
@@ -0,0 +1,39 @@
+error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait`
+  --> $DIR/error-in-impl-trait.rs:9:5
+   |
+LL |     error::_in::impl_trait()
+   |     ^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait`
+   |
+   = note: this error was originally ignored because you are running `rustdoc`
+   = note: try running again with `rustc` and you may get a more detailed error
+
+error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias`
+  --> $DIR/error-in-impl-trait.rs:15:5
+   |
+LL |     error::_in::impl_trait::alias();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias`
+   |
+   = note: this error was originally ignored because you are running `rustdoc`
+   = note: try running again with `rustc` and you may get a more detailed error
+
+error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure`
+  --> $DIR/error-in-impl-trait.rs:17:9
+   |
+LL |     (|| error::_in::impl_trait::alias::nested::closure())()
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure`
+   |
+   = note: this error was originally ignored because you are running `rustdoc`
+   = note: try running again with `rustc` and you may get a more detailed error
+
+error[E0433]: failed to resolve: could not resolve path `error::_in::async_fn`
+  --> $DIR/error-in-impl-trait.rs:26:5
+   |
+LL |     error::_in::async_fn()
+   |     ^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::async_fn`
+   |
+   = note: this error was originally ignored because you are running `rustdoc`
+   = note: try running again with `rustc` and you may get a more detailed error
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/src/test/rustdoc/impl-trait-alias.rs b/src/test/rustdoc/impl-trait-alias.rs
new file mode 100644 (file)
index 0000000..54c3f85
--- /dev/null
@@ -0,0 +1,14 @@
+#![feature(type_alias_impl_trait)]
+
+trait MyTrait {}
+impl MyTrait for i32 {}
+
+// @has impl_trait_alias/type.Foo.html 'Foo'
+/// debug type
+pub type Foo = impl MyTrait;
+
+// @has impl_trait_alias/fn.foo.html 'foo'
+/// debug function
+pub fn foo() -> Foo {
+    1
+}