Skip to content

[generic_assert] Avoid constant environments #134126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1715,6 +1715,7 @@ pub enum ClosureBinder {
/// is being invoked, and the `args` are arguments passed to it.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct MacCall {
pub is_in_const_env: bool,
pub path: Path,
pub args: P<DelimArgs>,
}
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
@@ -714,7 +714,7 @@ fn walk_attribute<T: MutVisitor>(vis: &mut T, attr: &mut Attribute) {
}

fn walk_mac<T: MutVisitor>(vis: &mut T, mac: &mut MacCall) {
let MacCall { path, args } = mac;
let MacCall { is_in_const_env: _, path, args } = mac;
vis.visit_path(path);
visit_delim_args(vis, args);
}
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
@@ -1002,7 +1002,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V:
}

pub fn walk_mac<'a, V: Visitor<'a>>(visitor: &mut V, mac: &'a MacCall) -> V::Result {
let MacCall { path, args: _ } = mac;
let MacCall { is_in_const_env: _, path, args: _ } = mac;
visitor.visit_path(path, DUMMY_NODE_ID)
}

3 changes: 3 additions & 0 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
@@ -814,6 +814,7 @@ fn expand_preparsed_asm(

pub(super) fn expand_asm<'cx>(
ecx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
@@ -843,6 +844,7 @@ pub(super) fn expand_asm<'cx>(

pub(super) fn expand_naked_asm<'cx>(
ecx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
@@ -873,6 +875,7 @@ pub(super) fn expand_naked_asm<'cx>(

pub(super) fn expand_global_asm<'cx>(
ecx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
6 changes: 4 additions & 2 deletions compiler/rustc_builtin_macros/src/assert.rs
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ use crate::errors;

pub(crate) fn expand_assert<'cx>(
cx: &'cx mut ExtCtxt<'_>,
is_in_const_env: bool,
span: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
@@ -56,6 +57,7 @@ pub(crate) fn expand_assert<'cx>(
let then = cx.expr(
call_site_span,
ExprKind::MacCall(P(MacCall {
is_in_const_env,
path: panic_path(),
args: P(DelimArgs {
dspan: DelimSpan::from_single(call_site_span),
@@ -69,8 +71,8 @@ pub(crate) fn expand_assert<'cx>(
// If `generic_assert` is enabled, generates rich captured outputs
//
// FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
else if cx.ecfg.features.generic_assert() {
context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
else if cx.ecfg.features.generic_assert() && !is_in_const_env {
context::Context::new(cx, call_site_span).build(cond_expr, is_in_const_env, panic_path())
}
// If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
// string
12 changes: 9 additions & 3 deletions compiler/rustc_builtin_macros/src/assert/context.rs
Original file line number Diff line number Diff line change
@@ -71,11 +71,16 @@ impl<'cx, 'a> Context<'cx, 'a> {
/// }
/// }
/// ```
pub(super) fn build(mut self, mut cond_expr: P<Expr>, panic_path: Path) -> P<Expr> {
pub(super) fn build(
mut self,
mut cond_expr: P<Expr>,
is_in_const_env: bool,
panic_path: Path,
) -> P<Expr> {
let expr_str = pprust::expr_to_string(&cond_expr);
self.manage_cond_expr(&mut cond_expr);
let initial_imports = self.build_initial_imports();
let panic = self.build_panic(&expr_str, panic_path);
let panic = self.build_panic(&expr_str, is_in_const_env, panic_path);
let cond_expr_with_unlikely = self.build_unlikely(cond_expr);

let Self { best_case_captures, capture_decls, cx, local_bind_decls, span, .. } = self;
@@ -147,7 +152,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
/// __capture0,
/// ...
/// );
fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> {
fn build_panic(&self, expr_str: &str, is_in_const_env: bool, panic_path: Path) -> P<Expr> {
let escaped_expr_str = escape_to_fmt(expr_str);
let initial = [
TokenTree::token_joint(
@@ -179,6 +184,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
self.cx.expr(
self.span,
ExprKind::MacCall(P(MacCall {
is_in_const_env,
path: panic_path,
args: P(DelimArgs {
dspan: DelimSpan::from_single(self.span),
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/cfg.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ use crate::errors;

pub(crate) fn expand_cfg(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/compile_error.rs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ use crate::util::get_single_str_from_tts;

pub(crate) fn expand_compile_error<'cx>(
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/concat.rs
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ use crate::util::get_exprs_from_tts;

pub(crate) fn expand_concat(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: rustc_span::Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/concat_bytes.rs
Original file line number Diff line number Diff line change
@@ -112,6 +112,7 @@ fn handle_array_element(

pub(crate) fn expand_concat_bytes(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/concat_idents.rs
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ use crate::errors;

pub(crate) fn expand_concat_idents<'cx>(
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
8 changes: 6 additions & 2 deletions compiler/rustc_builtin_macros/src/edition_panic.rs
Original file line number Diff line number Diff line change
@@ -18,11 +18,12 @@ use rustc_span::symbol::sym;
/// one we're expanding from.
pub(crate) fn expand_panic<'cx>(
cx: &'cx mut ExtCtxt<'_>,
is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
let mac = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
expand(mac, cx, sp, tts)
expand(is_in_const_env, mac, cx, sp, tts)
}

/// This expands to either
@@ -31,14 +32,16 @@ pub(crate) fn expand_panic<'cx>(
/// depending on the edition.
pub(crate) fn expand_unreachable<'cx>(
cx: &'cx mut ExtCtxt<'_>,
is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
let mac = if use_panic_2021(sp) { sym::unreachable_2021 } else { sym::unreachable_2015 };
expand(mac, cx, sp, tts)
expand(is_in_const_env, mac, cx, sp, tts)
}

fn expand<'cx>(
is_in_const_env: bool,
mac: rustc_span::Symbol,
cx: &'cx ExtCtxt<'_>,
sp: Span,
@@ -50,6 +53,7 @@ fn expand<'cx>(
cx.expr(
sp,
ExprKind::MacCall(P(MacCall {
is_in_const_env,
path: Path {
span: sp,
segments: cx
2 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/src/env.rs
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result<Symbol, VarError

pub(crate) fn expand_option_env<'cx>(
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
@@ -89,6 +90,7 @@ pub(crate) fn expand_option_env<'cx>(

pub(crate) fn expand_env<'cx>(
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
2 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/src/format.rs
Original file line number Diff line number Diff line change
@@ -1015,6 +1015,7 @@ fn expand_format_args_impl<'cx>(

pub(crate) fn expand_format_args<'cx>(
ecx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
@@ -1023,6 +1024,7 @@ pub(crate) fn expand_format_args<'cx>(

pub(crate) fn expand_format_args_nl<'cx>(
ecx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/log_syntax.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult

pub(crate) fn expand_log_syntax<'cx>(
_cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: rustc_span::Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/pattern_type.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ use rustc_span::{Span, sym};

pub(crate) fn expand<'cx>(
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
8 changes: 8 additions & 0 deletions compiler/rustc_builtin_macros/src/source_util.rs
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ use crate::util::{
/// line!(): expands to the current line number
pub(crate) fn expand_line(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
@@ -47,6 +48,7 @@ pub(crate) fn expand_line(
/* column!(): expands to the current column number */
pub(crate) fn expand_column(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
@@ -64,6 +66,7 @@ pub(crate) fn expand_column(
/// out if we wanted.
pub(crate) fn expand_file(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
@@ -85,6 +88,7 @@ pub(crate) fn expand_file(

pub(crate) fn expand_stringify(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
@@ -95,6 +99,7 @@ pub(crate) fn expand_stringify(

pub(crate) fn expand_mod(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
@@ -111,6 +116,7 @@ pub(crate) fn expand_mod(
/// unhygienically.
pub(crate) fn expand_include<'cx>(
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
@@ -192,6 +198,7 @@ pub(crate) fn expand_include<'cx>(
/// `include_str!`: read the given file, insert it as a literal string expr
pub(crate) fn expand_include_str(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
@@ -221,6 +228,7 @@ pub(crate) fn expand_include_str(

pub(crate) fn expand_include_bytes(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'static> {
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/trace_macros.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ use crate::errors;

pub(crate) fn expand_trace_macros(
cx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
tt: TokenStream,
) -> MacroExpanderResult<'static> {
11 changes: 8 additions & 3 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
@@ -280,6 +280,7 @@ pub trait BangProcMacro {
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
is_in_const_env: bool,
span: Span,
ts: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed>;
@@ -292,6 +293,7 @@ where
fn expand<'cx>(
&self,
_ecx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
_span: Span,
ts: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
@@ -331,6 +333,7 @@ pub trait TTMacroExpander {
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
is_in_const_env: bool,
span: Span,
input: TokenStream,
) -> MacroExpanderResult<'cx>;
@@ -339,19 +342,20 @@ pub trait TTMacroExpander {
pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>;

pub type MacroExpanderFn =
for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>;
for<'cx> fn(&'cx mut ExtCtxt<'_>, bool, Span, TokenStream) -> MacroExpanderResult<'cx>;

impl<F> TTMacroExpander for F
where
F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>,
F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, bool, Span, TokenStream) -> MacroExpanderResult<'cx>,
{
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
is_in_const_env: bool,
span: Span,
input: TokenStream,
) -> MacroExpanderResult<'cx> {
self(ecx, span, input)
self(ecx, is_in_const_env, span, input)
}
}

@@ -901,6 +905,7 @@ impl SyntaxExtension {
pub fn dummy_bang(edition: Edition) -> SyntaxExtension {
fn expander<'cx>(
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
span: Span,
_: TokenStream,
) -> MacroExpanderResult<'cx> {
3 changes: 3 additions & 0 deletions compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
@@ -53,12 +53,14 @@ impl<'a> ExtCtxt<'a> {

pub fn macro_call(
&self,
is_in_const_env: bool,
span: Span,
path: ast::Path,
delim: ast::token::Delimiter,
tokens: ast::tokenstream::TokenStream,
) -> P<ast::MacCall> {
P(ast::MacCall {
is_in_const_env,
path,
args: P(ast::DelimArgs {
dspan: ast::tokenstream::DelimSpan { open: span, close: span },
@@ -435,6 +437,7 @@ impl<'a> ExtCtxt<'a> {
self.expr_macro_call(
span,
self.macro_call(
false,
span,
self.path_global(
span,
14 changes: 12 additions & 2 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
@@ -676,15 +676,25 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
ExpandResult::Ready(match invoc.kind {
InvocationKind::Bang { mac, span } => match ext {
SyntaxExtensionKind::Bang(expander) => {
match expander.expand(self.cx, span, mac.args.tokens.clone()) {
match expander.expand(
self.cx,
mac.is_in_const_env,
span,
mac.args.tokens.clone(),
) {
Ok(tok_result) => {
self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
}
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
}
}
SyntaxExtensionKind::LegacyBang(expander) => {
let tok_result = match expander.expand(self.cx, span, mac.args.tokens.clone()) {
let tok_result = match expander.expand(
self.cx,
mac.is_in_const_env,
span,
mac.args.tokens.clone(),
) {
ExpandResult::Ready(tok_result) => tok_result,
ExpandResult::Retry(_) => {
// retry the original
2 changes: 2 additions & 0 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
@@ -111,6 +111,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
fn expand<'cx>(
&self,
cx: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
sp: Span,
input: TokenStream,
) -> MacroExpanderResult<'cx> {
@@ -134,6 +135,7 @@ impl TTMacroExpander for DummyExpander {
fn expand<'cx>(
&self,
_: &'cx mut ExtCtxt<'_>,
_is_in_const_env: bool,
span: Span,
_: TokenStream,
) -> ExpandResult<Box<dyn MacResult + 'cx>, ()> {
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/placeholders.rs
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ pub(crate) fn placeholder(
) -> AstFragment {
fn mac_placeholder() -> P<ast::MacCall> {
P(ast::MacCall {
is_in_const_env: false,
path: ast::Path { span: DUMMY_SP, segments: ThinVec::new(), tokens: None },
args: P(ast::DelimArgs {
dspan: ast::tokenstream::DelimSpan::dummy(),
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/proc_macro.rs
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@ impl base::BangProcMacro for BangProcMacro {
fn expand(
&self,
ecx: &mut ExtCtxt<'_>,
_is_in_const_env: bool,
span: Span,
input: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
6 changes: 5 additions & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
@@ -1614,7 +1614,11 @@ impl<'a> Parser<'a> {
self.dcx().emit_err(errors::MacroInvocationWithQualifiedPath(path.span));
}
let lo = path.span;
let mac = P(MacCall { path, args: self.parse_delim_args()? });
let mac = P(MacCall {
is_in_const_env: self.is_in_const_env,
path,
args: self.parse_delim_args()?,
});
(lo.to(self.prev_token.span), ExprKind::MacCall(mac))
} else if self.check(&token::OpenDelim(Delimiter::Brace))
&& let Some(expr) = self.maybe_parse_struct_expr(&qself, &path)
24 changes: 19 additions & 5 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
@@ -241,11 +241,14 @@ impl<'a> Parser<'a> {
// CONST ITEM
if self.token.is_keyword(kw::Impl) {
// recover from `const impl`, suggest `impl const`
self.recover_const_impl(const_span, attrs, def_())?
self.with_const_management(|this| {
this.recover_const_impl(const_span, attrs, def_())
})?
} else {
self.recover_const_mut(const_span);
self.recover_missing_kw_before_item()?;
let (ident, generics, ty, expr) = self.parse_const_item()?;
let tuple = self.with_const_management(|this| this.parse_const_item())?;
let (ident, generics, ty, expr) = tuple;
(
ident,
ItemKind::Const(Box::new(ConstItem {
@@ -486,7 +489,7 @@ impl<'a> Parser<'a> {
Ok(args) => {
self.eat_semi_for_macro_if_needed(&args);
self.complain_if_pub_macro(vis, false);
Ok(MacCall { path, args })
Ok(MacCall { is_in_const_env: self.is_in_const_env, path, args })
}

Err(mut err) => {
@@ -2383,8 +2386,19 @@ impl<'a> Parser<'a> {

let mut sig_hi = self.prev_token.span;
// Either `;` or `{ ... }`.
let body =
self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?;
let body = if let Const::Yes(_) = header.constness {
self.with_const_management(|this| {
this.parse_fn_body(
attrs,
&ident,
&mut sig_hi,
fn_parse_mode.req_body,
fn_params_end,
)
})?
} else {
self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?
};
let fn_sig_span = sig_lo.to(sig_hi);
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
}
14 changes: 12 additions & 2 deletions compiler/rustc_parse/src/parser/mod.rs
Copy link
Member

@fmease fmease Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this doesn't account for nested non-const items. For example:

// false positive: `assert!(…)` is actually NOT in a const context 
const F: fn() = || { let x = true; assert!(!x); };
fn main() { F() }

// false postive: `assert!(…)` is actually NOT in a const context
const F: fn() = { fn f() { let x = true; assert!(!x); } f };
fn main() { F() }

// false postive: `assert!(…)` is actually NOT in a const context
const F: fn() = { struct S; impl S { fn f() { let x = true; assert!(!x); } } S::f };
fn main() { F() }

// false positive: `assert!(…)` is actually NOT in a const context
const fn f() -> fn() { fn g() { let x = true; assert!(!x); } g }
fn main() { f()() }

// false positive (low priority, not "exploitable" atm): `assert!(…)` is actually NOT in a const context
struct Ty<const N: u8>;
fn main() { _ = Ty::<{ fn _f() { let x = true; assert!(!x) } 0 }>; }

Other issues:

// false negative (you missed `parse_static_item`): `assert!(…)` is actually in a const context
static X: () = { let x = true; assert!(!x) };

// false negative: `assert!(…)` is actually in a const context
#![feature(const_trait_impl)]
#[const_trait]
trait Trait { fn f() { let x = true; assert!(!x); } }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the parser is the right place for this.

For example, you would never be able to catch this in the parser I believe:

#![feature(const_trait_impl)]
#[cfg_attr(SOME_CFG_SPEC, const_trait)]
trait Trait { fn f() { let x = true; assert!(!x); } }

Copy link
Member

@fmease fmease Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does your current approach catch this (it might actually, I'm not sure rn (sth. sth. interpolated AST nodes))?

macro_rules! constify { ($expr:expr) => { const { $expr } } }

fn main() {
    constify! {{ let x = false; assert!(!x); }}
}

Copy link
Contributor Author

@c410-f3r c410-f3r Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review, @fmease.

I also share your concerns about uncovered use-cases as well as possible edge-cases. If somehow it were possible to call something like the is_inside_const_context method defined in HIR things would be much easier.

The feeling of thinking that the parser is probably not the right place is also shared, unfortunately, I am not aware of any other alternative.

So the plan is to cover as many cases as possible (your snippets certainly help in that direction), do a crater run and hope for the best. Without a strategy to avoid constant environments, generic_assert is likely going to remain unstable for a long time.

Original file line number Diff line number Diff line change
@@ -185,12 +185,14 @@ pub struct Parser<'a> {
/// Whether the parser is allowed to do recovery.
/// This is disabled when parsing macro arguments, see #103534
recovery: Recovery,
/// If the current parsing is within a constant environment
is_in_const_env: bool,
}

// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
// it doesn't unintentionally get bigger.
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(Parser<'_>, 288);
rustc_data_structures::static_assert_size!(Parser<'_>, 296);

/// Stores span information about a closure.
#[derive(Clone, Debug)]
@@ -481,6 +483,7 @@ impl<'a> Parser<'a> {
},
current_closure: None,
recovery: Recovery::Allowed,
is_in_const_env: false,
};

// Make parser point to the first token.
@@ -1320,7 +1323,7 @@ impl<'a> Parser<'a> {
self.psess.gated_spans.gate(sym::inline_const_pat, span);
}
self.expect_keyword(kw::Const)?;
let (attrs, blk) = self.parse_inner_attrs_and_block()?;
let (attrs, blk) = self.with_const_management(|this| this.parse_inner_attrs_and_block())?;
let anon_const = AnonConst {
id: DUMMY_NODE_ID,
value: self.mk_expr(blk.span, ExprKind::Block(blk, None)),
@@ -1650,6 +1653,13 @@ impl<'a> Parser<'a> {
pub fn approx_token_stream_pos(&self) -> u32 {
self.num_bump_calls
}

fn with_const_management<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
self.is_in_const_env = true;
let rslt = f(self);
self.is_in_const_env = false;
rslt
}
}

pub(crate) fn make_unclosed_delims_error(
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
@@ -1098,7 +1098,7 @@ impl<'a> Parser<'a> {
fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
self.bump();
let args = self.parse_delim_args()?;
let mac = P(MacCall { path, args });
let mac = P(MacCall { is_in_const_env: self.is_in_const_env, path, args });
Ok(PatKind::MacCall(mac))
}

12 changes: 7 additions & 5 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
@@ -843,11 +843,13 @@ impl<'a> Parser<'a> {
/// the caller.
pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> {
// Parse const argument.
let value = if let token::OpenDelim(Delimiter::Brace) = self.token.kind {
self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?
} else {
self.handle_unambiguous_unbraced_const_arg()?
};
let value = self.with_const_management(|this| {
if let token::OpenDelim(Delimiter::Brace) = this.token.kind {
this.parse_expr_block(None, this.token.span, BlockCheckMode::Default)
} else {
this.handle_unambiguous_unbraced_const_arg()
}
})?;
Ok(AnonConst { id: ast::DUMMY_NODE_ID, value })
}

2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
@@ -226,7 +226,7 @@ impl<'a> Parser<'a> {
_ => MacStmtStyle::NoBraces,
};

let mac = P(MacCall { path, args });
let mac = P(MacCall { is_in_const_env: self.is_in_const_env, path, args });

let kind = if (style == MacStmtStyle::Braces
&& self.token != token::Dot
6 changes: 5 additions & 1 deletion compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
@@ -758,7 +758,11 @@ impl<'a> Parser<'a> {
let path = self.parse_path_inner(PathStyle::Type, ty_generics)?;
if self.eat(&token::Not) {
// Macro invocation in type position
Ok(TyKind::MacCall(P(MacCall { path, args: self.parse_delim_args()? })))
Ok(TyKind::MacCall(P(MacCall {
is_in_const_env: self.is_in_const_env,
path,
args: self.parse_delim_args()?,
})))
} else if allow_plus == AllowPlus::Yes && self.check_plus() {
// `Trait1 + Trait2 + 'a`
self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true)
10 changes: 0 additions & 10 deletions tests/crashes/123629.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#![feature(core_intrinsics, generic_assert)]

const _: () = {
let foo = 1;
assert!(foo == 3);
//~^ERROR: evaluation of constant value failed
};

fn main() {
const {
let foo = 1;
assert!(foo == 3);
}

const fn bar() {
let foo = 1;
assert!(foo == 3);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0080]: evaluation of constant value failed
--> $DIR/const-environments.rs:5:5
|
LL | assert!(foo == 3);
| ^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: foo == 3', $DIR/const-environments.rs:5:5
|
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.