Basic compiler infra

This commit is contained in:
Caio 2022-06-02 09:00:04 -03:00
parent 5e6bb83268
commit aa115eba12
7 changed files with 129 additions and 25 deletions

View file

@ -1,11 +1,13 @@
mod context;
use crate::edition_panic::use_panic_2021; use crate::edition_panic::use_panic_2021;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::tokenstream::{DelimSpan, TokenStream}; 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_ast_pretty::pprust;
use rustc_errors::{Applicability, PResult}; use rustc_errors::{Applicability, PResult};
use rustc_expand::base::*; use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use rustc_parse::parser::Parser; use rustc_parse::parser::Parser;
use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP}; 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 // `core::panic` and `std::panic` are different macros, so we use call-site
// context to pick up whichever is currently in scope. // 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 panic_path = || {
let path = if use_panic_2021(span) { if use_panic_2021(span) {
// On edition 2021, we always call `$crate::panic::panic_2021!()`. // On edition 2021, we always call `$crate::panic::panic_2021!()`.
Path { Path {
span: sp, span: call_site_span,
segments: cx segments: cx
.std_path(&[sym::panic, sym::panic_2021]) .std_path(&[sym::panic, sym::panic_2021])
.into_iter() .into_iter()
@ -42,27 +44,40 @@ pub fn expand_assert<'cx>(
} else { } else {
// Before edition 2021, we call `panic!()` unqualified, // Before edition 2021, we call `panic!()` unqualified,
// such that it calls either `std::panic!()` or `core::panic!()`. // such that it calls either `std::panic!()` or `core::panic!()`.
Path::from_ident(Ident::new(sym::panic, sp)) Path::from_ident(Ident::new(sym::panic, call_site_span))
}
}; };
// Pass the custom message to panic!().
cx.expr( // Simply uses the user provided message instead of generating custom outputs
sp, let expr = if let Some(tokens) = custom_message {
let then = cx.expr(
call_site_span,
ExprKind::MacCall(MacCall { ExprKind::MacCall(MacCall {
path, path: panic_path(),
args: P(MacArgs::Delimited( args: P(MacArgs::Delimited(
DelimSpan::from_single(sp), DelimSpan::from_single(call_site_span),
MacDelimiter::Parenthesis, MacDelimiter::Parenthesis,
tokens, tokens,
)), )),
prior_type_ascription: None, 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(), // Pass our own message directly to $crate::panicking::panic(),
// because it might contain `{` and `}` that should always be // because it might contain `{` and `}` that should always be
// passed literally. // passed literally.
cx.expr_call_global( let then = cx.expr_call_global(
sp, call_site_span,
cx.std_path(&[sym::panicking, sym::panic]), cx.std_path(&[sym::panicking, sym::panic]),
vec![cx.expr_str( vec![cx.expr_str(
DUMMY_SP, DUMMY_SP,
@ -71,18 +86,29 @@ pub fn expand_assert<'cx>(
pprust::expr_to_string(&cond_expr).escape_debug() 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(expr)
MacEager::expr(if_expr)
} }
struct Assert { struct Assert {
cond_expr: P<ast::Expr>, cond_expr: P<Expr>,
custom_message: Option<TokenStream>, 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> { fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
let mut parser = cx.new_parser_from_tts(stream); let mut parser = cx.new_parser_from_tts(stream);

View file

@ -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))
}
}

View file

@ -1,17 +1,18 @@
//! This crate contains implementations of built-in macros and other code generating facilities //! This crate contains implementations of built-in macros and other code generating facilities
//! injecting code into the crate before it is lowered to HIR. //! 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/")] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(array_windows)] #![feature(array_windows)]
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(decl_macro)] #![feature(decl_macro)]
#![feature(is_sorted)] #![feature(is_sorted)]
#![feature(nll)] #![feature(let_chains)]
#![feature(let_else)] #![feature(let_else)]
#![feature(nll)]
#![feature(proc_macro_internals)] #![feature(proc_macro_internals)]
#![feature(proc_macro_quote)] #![feature(proc_macro_quote)]
#![recursion_limit = "256"] #![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
extern crate proc_macro; extern crate proc_macro;

View file

@ -160,7 +160,7 @@ impl<'a> ExtCtxt<'a> {
attrs: AttrVec::new(), attrs: AttrVec::new(),
tokens: None, 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. // Generates `let _: Type;`, which is usually used for type assertions.
@ -174,6 +174,10 @@ impl<'a> ExtCtxt<'a> {
attrs: AttrVec::new(), attrs: AttrVec::new(),
tokens: None, 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 } ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span }
} }

View file

@ -150,6 +150,8 @@ declare_features! (
(active, allow_internal_unstable, "1.0.0", None, None), (active, allow_internal_unstable, "1.0.0", None, None),
/// Allows identifying the `compiler_builtins` crate. /// Allows identifying the `compiler_builtins` crate.
(active, compiler_builtins, "1.13.0", None, None), (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". /// Allows using the `rust-intrinsic`'s "ABI".
(active, intrinsics, "1.0.0", None, None), (active, intrinsics, "1.0.0", None, None),
/// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.

View file

@ -733,6 +733,7 @@ symbols! {
generator_state, generator_state,
generators, generators,
generic_arg_infer, generic_arg_infer,
generic_assert,
generic_associated_types, generic_associated_types,
generic_associated_types_extended, generic_associated_types_extended,
generic_const_exprs, generic_const_exprs,

View file

@ -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() {
}