1
Fork 0

Don't ICE on errors in function returning impl trait

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
This commit is contained in:
Joshua Nelson 2020-07-03 18:41:23 -04:00
parent 14a8707cde
commit 768d6a4950
7 changed files with 162 additions and 2 deletions

View file

@ -955,7 +955,7 @@ where
val.fold_with(&mut FixupFolder { 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()); let fallback = move || tcx.type_of(def_id.to_def_id());
typeck_tables_of_with_fallback(tcx, def_id, fallback) typeck_tables_of_with_fallback(tcx, def_id, fallback)
} }

View file

@ -78,7 +78,7 @@ extern crate rustc_middle;
pub mod expr_use_visitor; pub mod expr_use_visitor;
mod astconv; mod astconv;
mod check; pub mod check;
mod check_unused; mod check_unused;
mod coherence; mod coherence;
mod collect; mod collect;

View file

@ -375,6 +375,15 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
override_queries: Some(|_sess, local_providers, external_providers| { override_queries: Some(|_sess, local_providers, external_providers| {
local_providers.lint_mod = |_, _| {}; local_providers.lint_mod = |_, _| {};
external_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(), registry: rustc_driver::diagnostics_registry(),
}; };
@ -572,6 +581,75 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
}) })
} }
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 /// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter
/// for `impl Trait` in argument position. /// for `impl Trait` in argument position.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]

View file

@ -94,6 +94,7 @@ pub fn main() {
32_000_000 // 32MB on other platforms 32_000_000 // 32MB on other platforms
}; };
rustc_driver::set_sigpipe_handler(); rustc_driver::set_sigpipe_handler();
rustc_driver::install_ice_hook();
env_logger::init_from_env("RUSTDOC_LOG"); env_logger::init_from_env("RUSTDOC_LOG");
let res = std::thread::Builder::new() let res = std::thread::Builder::new()
.stack_size(thread_stack_size) .stack_size(thread_stack_size)

View file

@ -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
}

View file

@ -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`.

View file

@ -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
}