about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-06-02 15:26:58 +0200
committerGitHub <noreply@github.com>2022-06-02 15:26:58 +0200
commit15bf62b475caa2e24f0186e8e2d6cd104cd72370 (patch)
treef858be19cb2f67c7acf6ebab533fe0d7b3de56be
parent0b2d48e5af11ca20878ccb463fceba2c180a6a4a (diff)
parentaa115eba12d6a12bfa4c51e3b6c187f28c42a889 (diff)
downloadrust-15bf62b475caa2e24f0186e8e2d6cd104cd72370.tar.gz
rust-15bf62b475caa2e24f0186e8e2d6cd104cd72370.zip
Rollup merge of #97450 - c410-f3r:assert-compiler, r=oli-obk
[RFC 2011] Basic compiler infrastructure

Splitting https://github.com/rust-lang/rust/pull/96496 into smaller pieces as was done in https://github.com/rust-lang/rust/pull/97233. Hope review will be easier.

This PR practically contains no logic and only serves as a building ground for the actual code that will be placed in a posterior step.

* Adds `context.rs` to place the new `assert!` logic. Has a lot of unused elements but all of them are used by the implementation.
* Creates an unstable flag because the feature is not yet complete and also to allow external feedback.
* Creates the necessary `sym` identifiers that are mostly based on the library elements -> https://github.com/rust-lang/rust/blob/master/library/core/src/asserting.rs
* Modifies `assert.rs` to branch to `context.rs` if the unstable flag is enabled.
* Adds a test to satisfy tidy but the test does nothing in reality.
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs70
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs44
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs5
-rw-r--r--compiler/rustc_expand/src/build.rs6
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs26
7 files changed, 129 insertions, 25 deletions
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index a984980dea9..0c75187193c 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<ast::Expr>,
+    cond_expr: P<Expr>,
     custom_message: Option<TokenStream>,
 }
 
+// if !{ ... } { ... } else { ... }
+fn expr_if_not(
+    cx: &ExtCtxt<'_>,
+    span: Span,
+    cond: P<Expr>,
+    then: P<Expr>,
+    els: Option<P<Expr>>,
+) -> P<Expr> {
+    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 00000000000..8d187a4be8a
--- /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<Expr>, _panic_path: Path) -> P<Expr> {
+        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 0c9e3c22bcf..48b1470ced5 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 56d0263269b..e73c31c98fe 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<ast::Local>, 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 5a02661513c..1466e8dfc92 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 16162f1cc89..f02465fe7dd 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 00000000000..f70ca87e304
--- /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() {
+}