diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index d2f743f6c5d8f..badf696606e94 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -20,7 +20,9 @@ use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use thin_vec::ThinVec; use crate::context::{AcceptContext, ShouldEmit, Stage}; -use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser}; +use crate::parser::{ + AllowExprMetavar, ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser, +}; use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg, ParsedDescription, @@ -363,6 +365,7 @@ fn parse_cfg_attr_internal<'a>( let meta = MetaItemOrLitParser::parse_single( parser, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + AllowExprMetavar::Yes, )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 7377159be370a..f3612afe69e5e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -12,7 +12,7 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; -use crate::parser::MetaItemOrLitParser; +use crate::parser::{AllowExprMetavar, MetaItemOrLitParser}; use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry}; #[derive(Clone)] @@ -94,6 +94,7 @@ pub fn parse_cfg_select( let meta = MetaItemOrLitParser::parse_single( p, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + AllowExprMetavar::Yes, ) .map_err(|diag| diag.emit())?; let cfg_span = meta.span(); diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f75f63a0e811a..fa66dec6a1568 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -14,7 +14,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage}; use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState}; -use crate::parser::{ArgParser, PathParser, RefPathParser}; +use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; use crate::{Early, Late, OmitDoc, ShouldEmit}; @@ -139,6 +139,7 @@ impl<'sess> AttributeParser<'sess, Early> { emit_errors: ShouldEmit, parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option, template: &AttributeTemplate, + allow_expr_metavar: AllowExprMetavar, ) -> Option { let ast::AttrKind::Normal(normal_attr) = &attr.kind else { panic!("parse_single called on a doc attr") @@ -152,6 +153,7 @@ impl<'sess> AttributeParser<'sess, Early> { &parts, &sess.psess, emit_errors, + allow_expr_metavar, )?; Self::parse_single_args( sess, @@ -333,6 +335,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { &parts, &self.sess.psess, self.stage.should_emit(), + AllowExprMetavar::No, ) else { continue; }; diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 354fbab9cfcf0..79cb98916ed69 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -109,6 +109,7 @@ impl ArgParser { parts: &[Symbol], psess: &'sess ParseSess, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> Option { Some(match value { AttrArgs::Empty => Self::NoArgs, @@ -122,6 +123,7 @@ impl ArgParser { args.dspan.entire(), psess, ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }, + allow_expr_metavar, ) { Ok(p) => return Some(ArgParser::List(p)), Err(e) => { @@ -147,9 +149,15 @@ impl ArgParser { } Self::List( - MetaItemListParser::new(&args.tokens, args.dspan.entire(), psess, should_emit) - .map_err(|e| should_emit.emit_err(e)) - .ok()?, + MetaItemListParser::new( + &args.tokens, + args.dspan.entire(), + psess, + should_emit, + allow_expr_metavar, + ) + .map_err(|e| should_emit.emit_err(e)) + .ok()?, ) } AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser { @@ -217,8 +225,9 @@ impl MetaItemOrLitParser { pub fn parse_single<'sess>( parser: &mut Parser<'sess>, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> PResult<'sess, MetaItemOrLitParser> { - let mut this = MetaItemListParserContext { parser, should_emit }; + let mut this = MetaItemListParserContext { parser, should_emit, allow_expr_metavar }; this.parse_meta_item_inner() } @@ -404,9 +413,19 @@ fn expr_to_lit<'sess>( } } +/// Whether expansions of `expr` metavariables from decrarative macros +/// are permitted. Used when parsing meta items; currently, only `cfg` predicates +/// enable this option +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum AllowExprMetavar { + No, + Yes, +} + struct MetaItemListParserContext<'a, 'sess> { parser: &'a mut Parser<'sess>, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, } impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { @@ -447,20 +466,44 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { Ok(lit) } - fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> { - if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() { - return if has_meta_form { - let attr_item = self - .parser - .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| { - MetaItemListParserContext { parser: this, should_emit: self.should_emit } - .parse_attr_item() - }) - .unwrap(); - Ok(attr_item) - } else { - self.parser.unexpected_any() - }; + fn parse_meta_item(&mut self) -> PResult<'sess, MetaItemParser> { + if let Some(metavar) = self.parser.token.is_metavar_seq() { + match (metavar, self.allow_expr_metavar) { + (kind @ MetaVarKind::Expr { .. }, AllowExprMetavar::Yes) => { + return self + .parser + .eat_metavar_seq(kind, |this| { + MetaItemListParserContext { + parser: this, + should_emit: self.should_emit, + allow_expr_metavar: AllowExprMetavar::Yes, + } + .parse_meta_item() + }) + .ok_or_else(|| { + self.parser.unexpected_any::().unwrap_err() + }); + } + (MetaVarKind::Meta { has_meta_form }, _) => { + return if has_meta_form { + let attr_item = self + .parser + .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| { + MetaItemListParserContext { + parser: this, + should_emit: self.should_emit, + allow_expr_metavar: self.allow_expr_metavar, + } + .parse_meta_item() + }) + .unwrap(); + Ok(attr_item) + } else { + self.parser.unexpected_any() + }; + } + _ => {} + } } let path = self.parser.parse_path(PathStyle::Mod)?; @@ -469,8 +512,12 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { let args = if self.parser.check(exp!(OpenParen)) { let start = self.parser.token.span; let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| { - MetaItemListParserContext { parser, should_emit: self.should_emit } - .parse_meta_item_inner() + MetaItemListParserContext { + parser, + should_emit: self.should_emit, + allow_expr_metavar: self.allow_expr_metavar, + } + .parse_meta_item_inner() })?; let end = self.parser.prev_token.span; ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) }) @@ -492,7 +539,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?)) } else { let prev_pros = self.parser.approx_token_stream_pos(); - match self.parse_attr_item() { + match self.parse_meta_item() { Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)), Err(err) => { // If `parse_attr_item` made any progress, it likely has a more precise error we should prefer @@ -580,13 +627,15 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { psess: &'sess ParseSess, span: Span, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> PResult<'sess, MetaItemListParser> { let mut parser = Parser::new(psess, tokens, None); if let ShouldEmit::ErrorsAndLints { recovery } = should_emit { parser = parser.recovery(recovery); } - let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; + let mut this = + MetaItemListParserContext { parser: &mut parser, should_emit, allow_expr_metavar }; // Presumably, the majority of the time there will only be one attr. let mut sub_parsers = ThinVec::with_capacity(1); @@ -618,8 +667,15 @@ impl MetaItemListParser { span: Span, psess: &'sess ParseSess, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> Result> { - MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit) + MetaItemListParserContext::parse( + tokens.clone(), + psess, + span, + should_emit, + allow_expr_metavar, + ) } /// Lets you pick and choose as what you want to parse each element in the list diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 3ebde949b99bc..c4a458089f2d2 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -4,10 +4,9 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrStyle, token}; -use rustc_attr_parsing as attr; -use rustc_attr_parsing::parser::MetaItemOrLitParser; +use rustc_attr_parsing::parser::{AllowExprMetavar, MetaItemOrLitParser}; use rustc_attr_parsing::{ - AttributeParser, CFG_TEMPLATE, ParsedDescription, ShouldEmit, parse_cfg_entry, + self as attr, AttributeParser, CFG_TEMPLATE, ParsedDescription, ShouldEmit, parse_cfg_entry, }; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_hir::attrs::CfgEntry; @@ -44,6 +43,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result StripUnconfigured<'a> { emit_errors, parse_cfg, &CFG_TEMPLATE, + AllowExprMetavar::Yes, ) else { // Cfg attribute was not parsable, give up return EvalConfigResult::True; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 76a9a6f9d03d9..640d0746fe1a2 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -13,6 +13,7 @@ use rustc_ast::{ TyKind, token, }; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::parser::AllowExprMetavar; use rustc_attr_parsing::{ AttributeParser, CFG_TEMPLATE, Early, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg, validate_attr, @@ -2224,6 +2225,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, parse_cfg, &CFG_TEMPLATE, + AllowExprMetavar::Yes, ) else { // Cfg attribute was not parsable, give up return EvalConfigResult::True; diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 3f06c9bd1d774..e1d9d80c97b62 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -62,25 +62,7 @@ pub macro thread_local_inner { // by translating it into a `cfg`ed block and recursing. // https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate - (@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { - #[cfg(true)] - { - $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); - } - - $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? - }, - - (@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { - #[cfg(false)] - { - $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); - } - - $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? - }, - - (@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + (@align $final_align:ident, cfg_attr($cfg_pred:expr, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { #[cfg($cfg_pred)] { $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 1318d8dc27809..5de001838faff 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -198,41 +198,10 @@ pub macro thread_local_process_attrs { ); ), - // it's a nested `cfg_attr(true, ...)`; recurse into RHS - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - // it's a nested `cfg_attr(false, ...)`; recurse into RHS - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - // it's a nested `cfg_attr(..., ...)`; recurse into RHS ( [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:expr, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; $($rest:tt)* ) => ( $crate::thread::local_impl::thread_local_process_attrs!( @@ -268,28 +237,8 @@ pub macro thread_local_process_attrs { ); ), - // `cfg_attr(true, ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - - // `cfg_attr(false, ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - // `cfg_attr(..., ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:expr, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( $crate::thread::local_impl::thread_local_process_attrs!( [] []; @processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] }; diff --git a/tests/ui/macros/attr-expr.rs b/tests/ui/macros/attr-expr.rs new file mode 100644 index 0000000000000..a2bee8728ac57 --- /dev/null +++ b/tests/ui/macros/attr-expr.rs @@ -0,0 +1,19 @@ +macro_rules! foo { + ($e:expr) => { + #[$e] + //~^ ERROR expected identifier, found metavariable + fn foo() {} + }; +} +foo!(inline); + +macro_rules! bar { + ($e:expr) => { + #[inline($e)] + //~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable + fn bar() {} + }; +} +bar!(always); + +fn main() {} diff --git a/tests/ui/macros/attr-expr.stderr b/tests/ui/macros/attr-expr.stderr new file mode 100644 index 0000000000000..eaad41c44fb9a --- /dev/null +++ b/tests/ui/macros/attr-expr.stderr @@ -0,0 +1,24 @@ +error: expected identifier, found metavariable + --> $DIR/attr-expr.rs:3:11 + | +LL | #[$e] + | ^^ expected identifier, found metavariable +... +LL | foo!(inline); + | ------------ in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable + --> $DIR/attr-expr.rs:12:18 + | +LL | #[inline($e)] + | ^^ +... +LL | bar!(always); + | ------------ in this macro invocation + | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/cfg-expr.rs b/tests/ui/macros/cfg-expr.rs new file mode 100644 index 0000000000000..e9ca6e3d7b10c --- /dev/null +++ b/tests/ui/macros/cfg-expr.rs @@ -0,0 +1,44 @@ +//@ run-pass +macro_rules! foo { + ($e:expr, $n:ident) => { + #[cfg($e)] + macro_rules! $n { + () => {} + } + + #[cfg_attr($e, allow(non_snake_case))] + #[cfg($e)] + fn $n() { + #[cfg($e)] + $n!(); + } + + #[cfg_attr(not($e), allow(unused))] + #[cfg(not($e))] + fn $n() { + panic!() + } + } +} +foo!(true, BAR); +foo!(any(true, unix, target_pointer_width = "64"), baz); +foo!(target_pointer_width = "64", quux); +foo!(false, haha); + +fn main() { + BAR(); + BAR!(); + baz(); + baz!(); + #[cfg(target_pointer_width = "64")] + quux(); + #[cfg(target_pointer_width = "64")] + quux!(); + #[cfg(panic = "unwind")] + { + let result = std::panic::catch_unwind(|| { + haha(); + }); + assert!(result.is_err()); + } +} diff --git a/tests/ui/macros/cfg_attr-expr.rs b/tests/ui/macros/cfg_attr-expr.rs new file mode 100644 index 0000000000000..1dab2cae59fa7 --- /dev/null +++ b/tests/ui/macros/cfg_attr-expr.rs @@ -0,0 +1,9 @@ +macro_rules! foo { + ($e:expr) => { + #[cfg_attr(true, $e)] + //~^ ERROR expected identifier, found metavariable + fn foo() {} + } +} +foo!(inline); +fn main() {} diff --git a/tests/ui/macros/cfg_attr-expr.stderr b/tests/ui/macros/cfg_attr-expr.stderr new file mode 100644 index 0000000000000..a46ea104b9398 --- /dev/null +++ b/tests/ui/macros/cfg_attr-expr.stderr @@ -0,0 +1,17 @@ +error: expected identifier, found metavariable + --> $DIR/cfg_attr-expr.rs:3:26 + | +LL | #[cfg_attr(true, $e)] + | -----------------^^-- + | | | + | | expected identifier, found metavariable + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` +... +LL | foo!(inline); + | ------------ in this macro invocation + | + = note: for more information, visit + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/tests/ui/macros/cfg_select-expr.rs b/tests/ui/macros/cfg_select-expr.rs new file mode 100644 index 0000000000000..90e894562a16b --- /dev/null +++ b/tests/ui/macros/cfg_select-expr.rs @@ -0,0 +1,57 @@ +//@ run-pass +#![allow(unreachable_cfg_select_predicates)] + +macro_rules! foo { + ($e:expr, $n:ident) => { + cfg_select! { + $e => { + macro_rules! $n { + () => {} + } + } + _ => {} + } + + cfg_select! { + $e => { + #[cfg_attr($e, allow(non_snake_case))] + fn $n() { + cfg_select! { + $e => { + $n!(); + } + _ => {} + } + } + } + not($e) => { + #[cfg_attr(not($e), allow(unused))] + fn $n() { + panic!() + } + } + } + } +} +foo!(true, BAR); +foo!(any(true, unix, target_pointer_width = "64"), baz); +foo!(target_pointer_width = "64", quux); +foo!(false, haha); + +fn main() { + BAR(); + BAR!(); + baz(); + baz!(); + #[cfg(target_pointer_width = "64")] + quux(); + #[cfg(target_pointer_width = "64")] + quux!(); + #[cfg(panic = "unwind")] + { + let result = std::panic::catch_unwind(|| { + haha(); + }); + assert!(result.is_err()); + } +}