From aa115eba12d6a12bfa4c51e3b6c187f28c42a889 Mon Sep 17 00:00:00 2001 From: Caio Date: Thu, 2 Jun 2022 09:00:04 -0300 Subject: [PATCH] Basic compiler infra --- compiler/rustc_builtin_macros/src/assert.rs | 70 +++++++++++++------ .../src/assert/context.rs | 44 ++++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 5 +- compiler/rustc_expand/src/build.rs | 6 +- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + .../feature-gate-generic_assert.rs | 26 +++++++ 7 files changed, 129 insertions(+), 25 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/assert/context.rs create mode 100644 src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index a984980dea9bf..0c75187193c38 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -1,11 +1,13 @@ +mod context; + use crate::edition_panic::use_panic_2021; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{self as ast, *}; +use rustc_ast::{Expr, ExprKind, MacArgs, MacCall, MacDelimiter, Path, PathSegment, UnOp}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, PResult}; -use rustc_expand::base::*; +use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult}; use rustc_parse::parser::Parser; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -25,13 +27,13 @@ pub fn expand_assert<'cx>( // `core::panic` and `std::panic` are different macros, so we use call-site // context to pick up whichever is currently in scope. - let sp = cx.with_call_site_ctxt(span); + let call_site_span = cx.with_call_site_ctxt(span); - let panic_call = if let Some(tokens) = custom_message { - let path = if use_panic_2021(span) { + let panic_path = || { + if use_panic_2021(span) { // On edition 2021, we always call `$crate::panic::panic_2021!()`. Path { - span: sp, + span: call_site_span, segments: cx .std_path(&[sym::panic, sym::panic_2021]) .into_iter() @@ -42,27 +44,40 @@ pub fn expand_assert<'cx>( } else { // Before edition 2021, we call `panic!()` unqualified, // such that it calls either `std::panic!()` or `core::panic!()`. - Path::from_ident(Ident::new(sym::panic, sp)) - }; - // Pass the custom message to panic!(). - cx.expr( - sp, + Path::from_ident(Ident::new(sym::panic, call_site_span)) + } + }; + + // Simply uses the user provided message instead of generating custom outputs + let expr = if let Some(tokens) = custom_message { + let then = cx.expr( + call_site_span, ExprKind::MacCall(MacCall { - path, + path: panic_path(), args: P(MacArgs::Delimited( - DelimSpan::from_single(sp), + DelimSpan::from_single(call_site_span), MacDelimiter::Parenthesis, tokens, )), prior_type_ascription: None, }), - ) - } else { + ); + expr_if_not(cx, call_site_span, cond_expr, then, None) + } + // If `generic_assert` is enabled, generates rich captured outputs + // + // FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949 + else if let Some(features) = cx.ecfg.features && features.generic_assert { + context::Context::new(cx, call_site_span).build(cond_expr, panic_path()) + } + // If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..." + // string + else { // Pass our own message directly to $crate::panicking::panic(), // because it might contain `{` and `}` that should always be // passed literally. - cx.expr_call_global( - sp, + let then = cx.expr_call_global( + call_site_span, cx.std_path(&[sym::panicking, sym::panic]), vec![cx.expr_str( DUMMY_SP, @@ -71,18 +86,29 @@ pub fn expand_assert<'cx>( pprust::expr_to_string(&cond_expr).escape_debug() )), )], - ) + ); + expr_if_not(cx, call_site_span, cond_expr, then, None) }; - let if_expr = - cx.expr_if(sp, cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)), panic_call, None); - MacEager::expr(if_expr) + + MacEager::expr(expr) } struct Assert { - cond_expr: P, + cond_expr: P, custom_message: Option, } +// if !{ ... } { ... } else { ... } +fn expr_if_not( + cx: &ExtCtxt<'_>, + span: Span, + cond: P, + then: P, + els: Option>, +) -> P { + cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) +} + fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { let mut parser = cx.new_parser_from_tts(stream); diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs new file mode 100644 index 0000000000000..8d187a4be8aee --- /dev/null +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -0,0 +1,44 @@ +use rustc_ast::{ptr::P, Expr, Path}; +use rustc_expand::base::ExtCtxt; +use rustc_span::Span; + +pub(super) struct Context<'cx, 'a> { + cx: &'cx ExtCtxt<'a>, + span: Span, +} + +impl<'cx, 'a> Context<'cx, 'a> { + pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self { + Self { cx, span } + } + + /// Builds the whole `assert!` expression. + /// + /// { + /// use ::core::asserting::{ ... }; + /// + /// let mut __capture0 = Capture::new(); + /// ... + /// ... + /// ... + /// + /// if !{ + /// ... + /// ... + /// ... + /// } { + /// panic!( + /// "Assertion failed: ... \n With expansion: ...", + /// __capture0, + /// ... + /// ... + /// ... + /// ); + /// } + /// } + pub(super) fn build(self, _cond_expr: P, _panic_path: Path) -> P { + let Self { cx, span, .. } = self; + let stmts = Vec::new(); + cx.expr_block(cx.block(span, stmts)) + } +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 0c9e3c22bcf3c..48b1470ced5a0 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -1,17 +1,18 @@ //! This crate contains implementations of built-in macros and other code generating facilities //! injecting code into the crate before it is lowered to HIR. +#![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(is_sorted)] -#![feature(nll)] +#![feature(let_chains)] #![feature(let_else)] +#![feature(nll)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![recursion_limit = "256"] -#![allow(rustc::potential_query_instability)] extern crate proc_macro; diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 56d0263269b53..e73c31c98fe32 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -160,7 +160,7 @@ impl<'a> ExtCtxt<'a> { attrs: AttrVec::new(), tokens: None, }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp } + self.stmt_local(local, sp) } // Generates `let _: Type;`, which is usually used for type assertions. @@ -174,6 +174,10 @@ impl<'a> ExtCtxt<'a> { attrs: AttrVec::new(), tokens: None, }); + self.stmt_local(local, span) + } + + pub fn stmt_local(&self, local: P, span: Span) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span } } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 5a02661513ca7..1466e8dfc92e4 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -150,6 +150,8 @@ declare_features! ( (active, allow_internal_unstable, "1.0.0", None, None), /// Allows identifying the `compiler_builtins` crate. (active, compiler_builtins, "1.13.0", None, None), + /// Outputs useful `assert!` messages + (active, generic_assert, "1.63.0", None, None), /// Allows using the `rust-intrinsic`'s "ABI". (active, intrinsics, "1.0.0", None, None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 16162f1cc898a..f02465fe7dd0b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -733,6 +733,7 @@ symbols! { generator_state, generators, generic_arg_infer, + generic_assert, generic_associated_types, generic_associated_types_extended, generic_const_exprs, diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs b/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs new file mode 100644 index 0000000000000..f70ca87e304a9 --- /dev/null +++ b/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs @@ -0,0 +1,26 @@ +// compile-flags: --test +// run-pass + +// `generic_assert` is completely unimplemented and doesn't generate any logic, thus the +// reason why this test currently passes +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] + +use std::fmt::{Debug, Formatter}; + +#[derive(Clone, Copy, PartialEq)] +struct CopyDebug(i32); + +impl Debug for CopyDebug { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str("With great power comes great electricity bills") + } +} + +#[test] +fn test() { + let _copy_debug = CopyDebug(1); + assert!(_copy_debug == CopyDebug(3)); +} + +fn main() { +}