"Aborted (core dumped)" on typo in attribute
dwrensha opened this issue · 12 comments
rustc crashes ungracefully when given the following input (found by fuzz-rustc):
fn main() {
let _ = || {
#[cfg(any(target_arc+ = "arm"))]
fn _foo() {}
};
}
$ rustc -vV
rustc 1.44.0-nightly (74bd074ee 2020-04-03)
binary: rustc
commit-hash: 74bd074eefcf4915c73d1ab91bc90859664729e6
commit-date: 2020-04-03
host: x86_64-unknown-linux-gnu
release: 1.44.0-nightly
LLVM version: 9.0
$ rustc main.rs
error: expected one of `(`, `)`, `,`, `::`, or `=`, found `+`
--> main.rs:3:28
|
3 | #[cfg(any(target_arc+ = "arm"))]
| ^ expected one of `(`, `)`, `,`, `::`, or `=`
Aborted (core dumped)
According to gdb
, the abort happens in rustc_ast::mut_visit::visit_clobber::{{closure}}
, as it did with #62894.
Here's a somewhat cuter test case that hits the same failure:
fn main() {
let _ = || {
#[cfg(any(target_arch == "arm"))]
let _ = ();
};
}
$ rustc main.rs
error: expected one of `(`, `)`, `,`, `::`, or `=`, found `==`
--> main.rs:3:30
|
3 | #[cfg(any(target_arch == "arm"))]
| ^^ expected one of `(`, `)`, `,`, `::`, or `=`
Aborted (core dumped)
Simplified:
const _: _ = [#[cfg(ident(ident ==))] 0];
Backtrace:
centril@centrilnas2:~/programming/rust/rust-gamma$ rustc +gamma-stage1 foo.rs
error: expected one of `(`, `)`, `,`, `::`, or `=`, found `==`
--> foo.rs:1:33
|
1 | const _: _ = [#[cfg(ident(ident == "arm"))] 0];
| ^^ expected one of `(`, `)`, `,`, `::`, or `=`
0: rustc_ast::mut_visit::visit_clobber::{{closure}}
at ./src/librustc_ast/mut_visit.rs:316
1: core::result::Result<T,E>::unwrap_or_else
at ./src/libcore/result.rs:851
rustc_ast::mut_visit::visit_clobber
at ./src/librustc_ast/mut_visit.rs:315
2: <rustc_expand::expand::InvocationCollector as rustc_ast::mut_visit::MutVisitor>::visit_expr
at src/librustc_expand/expand.rs:1171
rustc_ast::mut_visit::noop_visit_item_kind::{{closure}}
at ./src/librustc_ast/mut_visit.rs:887
rustc_ast::mut_visit::visit_opt
at ./src/librustc_ast/mut_visit.rs:341
rustc_ast::mut_visit::noop_visit_item_kind
at ./src/librustc_ast/mut_visit.rs:887
3: <rustc_expand::expand::InvocationCollector as rustc_ast::mut_visit::MutVisitor>::visit_item_kind
at src/librustc_expand/expand.rs:1642
rustc_ast::mut_visit::noop_flat_map_item
at ./src/librustc_ast/mut_visit.rs:1017
4: <rustc_expand::expand::InvocationCollector as rustc_ast::mut_visit::MutVisitor>::flat_map_item
at src/librustc_expand/expand.rs:1521
5: rustc_ast::mut_visit::noop_visit_mod::{{closure}}
at ./src/librustc_ast/mut_visit.rs:976
<alloc::vec::Vec<T> as rustc_ast::util::map_in_place::MapInPlace<T>>::flat_map_in_place
at ./src/librustc_ast/util/map_in_place.rs:36
6: rustc_ast::mut_visit::noop_visit_mod
at ./src/librustc_ast/mut_visit.rs:976
rustc_ast::mut_visit::MutVisitor::visit_mod
at ./src/librustc_ast/mut_visit.rs:166
rustc_ast::mut_visit::noop_visit_item_kind
at ./src/librustc_ast/mut_visit.rs:894
7: <rustc_expand::expand::InvocationCollector as rustc_ast::mut_visit::MutVisitor>::visit_item_kind
at src/librustc_expand/expand.rs:1642
rustc_ast::mut_visit::noop_flat_map_item
at ./src/librustc_ast/mut_visit.rs:1017
8: <rustc_expand::expand::InvocationCollector as rustc_ast::mut_visit::MutVisitor>::flat_map_item
at src/librustc_expand/expand.rs:1521
9: rustc_expand::expand::AstFragment::mut_visit_with::{{closure}}
at src/librustc_expand/expand.rs:121
<smallvec::SmallVec<A> as rustc_ast::util::map_in_place::MapInPlace<T>>::flat_map_in_place
at ./src/librustc_ast/util/map_in_place.rs:82
10: rustc_expand::expand::AstFragment::mut_visit_with
at src/librustc_expand/expand.rs:121
rustc_expand::expand::MacroExpander::collect_invocations
at src/librustc_expand/expand.rs:577
11: rustc_expand::expand::MacroExpander::fully_expand_fragment
at src/librustc_expand/expand.rs:396
12: rustc_expand::expand::MacroExpander::expand_crate
at src/librustc_expand/expand.rs:363
13: rustc_interface::passes::configure_and_expand_inner::{{closure}}::{{closure}}
at src/librustc_interface/passes.rs:302
rustc_data_structures::profiling::VerboseTimingGuard::run
at ./src/librustc_data_structures/profiling.rs:569
rustc_session::utils::<impl rustc_session::session::Session>::time
at ./src/librustc_session/utils.rs:9
rustc_interface::passes::configure_and_expand_inner::{{closure}}
at src/librustc_interface/passes.rs:302
rustc_data_structures::profiling::VerboseTimingGuard::run
at ./src/librustc_data_structures/profiling.rs:569
rustc_session::utils::<impl rustc_session::session::Session>::time
at ./src/librustc_session/utils.rs:9
14: rustc_interface::passes::configure_and_expand_inner
at src/librustc_interface/passes.rs:256
15: rustc_interface::passes::configure_and_expand::{{closure}}
at src/librustc_interface/passes.rs:115
16: alloc::boxed::<impl core::ops::generator::Generator<R> for core::pin::Pin<alloc::boxed::Box<G>>>::resume
at ./src/liballoc/boxed.rs:1115
rustc_data_structures::box_region::PinnedGenerator<I,A,R>::new
at ./src/librustc_data_structures/box_region.rs:34
17: rustc_interface::passes::BoxedResolver::new
at ./<::rustc_data_structures::box_region::declare_box_region_type macros>:12
rustc_interface::passes::configure_and_expand
at src/librustc_interface/passes.rs:112
18: rustc_interface::queries::Queries::expansion::{{closure}}
at src/librustc_interface/queries.rs:176
rustc_interface::queries::Query<T>::compute
at src/librustc_interface/queries.rs:34
rustc_interface::queries::Queries::expansion
at src/librustc_interface/queries.rs:172
19: rustc_driver::run_compiler::{{closure}}::{{closure}}
at src/librustc_driver/lib.rs:337
rustc_interface::queries::<impl rustc_interface::interface::Compiler>::enter
at ./src/librustc_interface/queries.rs:385
rustc_driver::run_compiler::{{closure}}
at src/librustc_driver/lib.rs:286
rustc_interface::interface::run_compiler_in_existing_thread_pool
at ./src/librustc_interface/interface.rs:199
20: rustc_interface::interface::run_compiler::{{closure}}
at ./src/librustc_interface/interface.rs:213
rustc_interface::util::spawn_thread_pool::{{closure}}::{{closure}}::{{closure}}
at ./src/librustc_interface/util.rs:154
scoped_tls::ScopedKey<T>::set
at /home/centril/.cargo/registry/src/github.com-1ecc6299db9ec823/scoped-tls-1.0.0/src/lib.rs:137
rustc_interface::util::spawn_thread_pool::{{closure}}::{{closure}}
at ./src/librustc_interface/util.rs:150
scoped_tls::ScopedKey<T>::set
at /home/centril/.cargo/registry/src/github.com-1ecc6299db9ec823/scoped-tls-1.0.0/src/lib.rs:137
rustc_ast::attr::with_globals::{{closure}}
at ./src/librustc_ast/attr/mod.rs:44
scoped_tls::ScopedKey<T>::set
at /home/centril/.cargo/registry/src/github.com-1ecc6299db9ec823/scoped-tls-1.0.0/src/lib.rs:137
rustc_ast::attr::with_globals
at ./src/librustc_ast/attr/mod.rs:44
21: rustc_interface::util::spawn_thread_pool::{{closure}}
at ./src/librustc_interface/util.rs:149
rustc_interface::util::scoped_thread::{{closure}}
at ./src/librustc_interface/util.rs:124
std::sys_common::backtrace::__rust_begin_short_backtrace
at ./src/libstd/sys_common/backtrace.rs:130
22: std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}}
at ./src/libstd/thread/mod.rs:475
<std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
at ./src/libstd/panic.rs:318
std::panicking::try::do_call
at ./src/libstd/panicking.rs:331
23: std::panicking::try::do_try
at src/libstd/panicking.rs:298
24: std::panicking::try
at ./src/libstd/panicking.rs:274
std::panic::catch_unwind
at ./src/libstd/panic.rs:394
std::thread::Builder::spawn_unchecked::{{closure}}
at ./src/libstd/thread/mod.rs:474
core::ops::function::FnOnce::call_once{{vtable.shim}}
at ./src/libcore/ops/function.rs:232
25: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
at ./src/liballoc/boxed.rs:1008
26: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
at ./src/liballoc/boxed.rs:1008
std::sys_common::thread::start_thread
at src/libstd/sys_common/thread.rs:13
27: std::sys::unix::thread::Thread::new::thread_start
at src/libstd/sys/unix/thread.rs:80
28: start_thread
at /build/glibc-KRRWSm/glibc-2.29/nptl/pthread_create.c:486
29: __clone
Aborted (core dumped)
Something in the closure here is causing a panic:
impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
self.cfg.configure_expr(expr);
visit_clobber(expr.deref_mut(), |mut expr| {
self.cfg.configure_expr_kind(&mut expr.kind);
// ignore derives so they remain unused
let (attr, after_derive) = self.classify_nonitem(&mut expr);
if attr.is_some() {
// Collect the invoc regardless of whether or not attributes are permitted here
// expansion will eat the attribute so it won't error later.
attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
// AstFragmentKind::Expr requires the macro to emit an expression.
return self
.collect_attr(
attr,
vec![],
Annotatable::Expr(P(expr)),
AstFragmentKind::Expr,
after_derive,
)
.make_expr()
.into_inner();
}
if let ast::ExprKind::MacCall(mac) = expr.kind {
self.check_attributes(&expr.attrs);
self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr().into_inner()
} else {
noop_visit_expr(&mut expr, self);
expr
}
});
}
With the definition:
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
/// when using a `flat_map_*` or `filter_map_*` method within a `visit_`
/// method. Abort the program if the closure panics.
//
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_clobber<T, F>(t: &mut T, f: F)
where
F: FnOnce(T) -> T,
{
unsafe {
// Safe because `t` is used in a read-only fashion by `read()` before
// being overwritten by `write()`.
let old_t = ptr::read(t);
let new_t =
panic::catch_unwind(panic::AssertUnwindSafe(|| f(old_t))).unwrap_or_else(|_| {
println!("{}", std::backtrace::Backtrace::force_capture());
process::abort()
});
ptr::write(t, new_t);
}
}
The problem seems to occur in the call to noop_visit_expr(&mut expr, self);
, which is a fairly useless observation.
Digging further, unsurprisingly, the panic happens in the configure!
in:
fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
let expr = configure!(self, expr);
expr.filter_map(|mut expr| {
Further down the rabbit hole, we have:
pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
which calls validate_attr::parse_meta(self.sess, attr)
which in turn has:
let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
which calls:
fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
which has:
match self.parse_meta_item() {
so we will end up in:
crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
Ok(if false && self.eat(&token::Eq) {
ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
} else if self.check(&token::OpenDelim(token::Paren)) {
// Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
ast::MetaItemKind::List(list)
} else {
ast::MetaItemKind::Word
})
}
and reach the else {
.
However, this was nested, so we are in the self.parse_paren_comma_seq(
call here, and so we end up in fn parse_seq_to_before_tokens<T>(
which has match self.expect(t) {
so we end up calling self.expect_one_of(slice::from_ref(t), &[])
which ends up in:
} else if self.last_unexpected_token_span == Some(self.token.span) {
FatalError.raise();
}
and there we have our panic. Making that line not trigger removes the abort here.
Unfortunately, removing the FatalError.raise()
in favor of Err(...)
causes a bunch of knock-on errors all over the place, and to remove with_clobber
we'd need to expr.clone()
.
Assigning P-low
and removing nomination. This was discussed as part of pre-triage meeting in Zulip.
Latest stable does not crash any more, errors gracefully.