Basic compiler infra
This commit is contained in:
parent
5e6bb83268
commit
aa115eba12
7 changed files with 129 additions and 25 deletions
|
@ -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);
|
||||||
|
|
||||||
|
|
44
compiler/rustc_builtin_macros/src/assert/context.rs
Normal file
44
compiler/rustc_builtin_macros/src/assert/context.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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() {
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue