Merge from rustc
This commit is contained in:
commit
cc1bf5e471
@ -3,11 +3,12 @@
|
||||
//! Note that HIR pretty printing is layered on top of this crate.
|
||||
|
||||
mod expr;
|
||||
mod fixup;
|
||||
mod item;
|
||||
|
||||
use crate::pp::Breaks::{Consistent, Inconsistent};
|
||||
use crate::pp::{self, Breaks};
|
||||
use crate::pprust::state::expr::FixupContext;
|
||||
use crate::pprust::state::fixup::FixupContext;
|
||||
use ast::TraitBoundModifiers;
|
||||
use rustc_ast::attr::AttrIdGenerator;
|
||||
use rustc_ast::ptr::P;
|
||||
@ -15,7 +16,6 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, To
|
||||
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::comments::{Comment, CommentStyle};
|
||||
use rustc_ast::util::parser;
|
||||
use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind};
|
||||
use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term};
|
||||
use rustc_ast::{GenericArg, GenericBound, SelfKind};
|
||||
@ -1252,22 +1252,14 @@ impl<'a> State<'a> {
|
||||
ast::StmtKind::Item(item) => self.print_item(item),
|
||||
ast::StmtKind::Expr(expr) => {
|
||||
self.space_if_not_bol();
|
||||
self.print_expr_outer_attr_style(
|
||||
expr,
|
||||
false,
|
||||
FixupContext { stmt: true, ..FixupContext::default() },
|
||||
);
|
||||
self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
|
||||
if classify::expr_requires_semi_to_be_stmt(expr) {
|
||||
self.word(";");
|
||||
}
|
||||
}
|
||||
ast::StmtKind::Semi(expr) => {
|
||||
self.space_if_not_bol();
|
||||
self.print_expr_outer_attr_style(
|
||||
expr,
|
||||
false,
|
||||
FixupContext { stmt: true, ..FixupContext::default() },
|
||||
);
|
||||
self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
|
||||
self.word(";");
|
||||
}
|
||||
ast::StmtKind::Empty => {
|
||||
@ -1319,11 +1311,7 @@ impl<'a> State<'a> {
|
||||
ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => {
|
||||
self.maybe_print_comment(st.span.lo());
|
||||
self.space_if_not_bol();
|
||||
self.print_expr_outer_attr_style(
|
||||
expr,
|
||||
false,
|
||||
FixupContext { stmt: true, ..FixupContext::default() },
|
||||
);
|
||||
self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt());
|
||||
self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
|
||||
}
|
||||
_ => self.print_stmt(st),
|
||||
@ -1367,8 +1355,7 @@ impl<'a> State<'a> {
|
||||
self.word_space("=");
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
fixup.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
|
||||
|| parser::needs_par_as_let_scrutinee(expr.precedence().order()),
|
||||
fixup.needs_par_as_let_scrutinee(expr),
|
||||
FixupContext::default(),
|
||||
);
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::pp::Breaks::Inconsistent;
|
||||
use crate::pprust::state::fixup::FixupContext;
|
||||
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
|
||||
use ast::{ForLoopKind, MatchKind};
|
||||
use itertools::{Itertools, Position};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::literal::escape_byte_str_symbol;
|
||||
use rustc_ast::util::parser::{self, AssocOp, Fixity};
|
||||
use rustc_ast::{self as ast, BlockCheckMode};
|
||||
@ -14,78 +14,6 @@ use rustc_ast::{
|
||||
};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct FixupContext {
|
||||
/// Print expression such that it can be parsed back as a statement
|
||||
/// consisting of the original expression.
|
||||
///
|
||||
/// The effect of this is for binary operators in statement position to set
|
||||
/// `leftmost_subexpression_in_stmt` when printing their left-hand operand.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// (match x {}) - 1; // match needs parens when LHS of binary operator
|
||||
///
|
||||
/// match x {}; // not when its own statement
|
||||
/// ```
|
||||
pub stmt: bool,
|
||||
|
||||
/// This is the difference between:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// (match x {}) - 1; // subexpression needs parens
|
||||
///
|
||||
/// let _ = match x {} - 1; // no parens
|
||||
/// ```
|
||||
///
|
||||
/// There are 3 distinguishable contexts in which `print_expr` might be
|
||||
/// called with the expression `$match` as its argument, where `$match`
|
||||
/// represents an expression of kind `ExprKind::Match`:
|
||||
///
|
||||
/// - stmt=false leftmost_subexpression_in_stmt=false
|
||||
///
|
||||
/// Example: `let _ = $match - 1;`
|
||||
///
|
||||
/// No parentheses required.
|
||||
///
|
||||
/// - stmt=false leftmost_subexpression_in_stmt=true
|
||||
///
|
||||
/// Example: `$match - 1;`
|
||||
///
|
||||
/// Must parenthesize `($match)`, otherwise parsing back the output as a
|
||||
/// statement would terminate the statement after the closing brace of
|
||||
/// the match, parsing `-1;` as a separate statement.
|
||||
///
|
||||
/// - stmt=true leftmost_subexpression_in_stmt=false
|
||||
///
|
||||
/// Example: `$match;`
|
||||
///
|
||||
/// No parentheses required.
|
||||
pub leftmost_subexpression_in_stmt: bool,
|
||||
|
||||
/// This is the difference between:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// if let _ = (Struct {}) {} // needs parens
|
||||
///
|
||||
/// match () {
|
||||
/// () if let _ = Struct {} => {} // no parens
|
||||
/// }
|
||||
/// ```
|
||||
pub parenthesize_exterior_struct_lit: bool,
|
||||
}
|
||||
|
||||
/// The default amount of fixing is minimal fixing. Fixups should be turned on
|
||||
/// in a targeted fashion where needed.
|
||||
impl Default for FixupContext {
|
||||
fn default() -> Self {
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
parenthesize_exterior_struct_lit: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
fn print_else(&mut self, els: Option<&ast::Expr>) {
|
||||
if let Some(_else) = els {
|
||||
@ -136,9 +64,7 @@ impl<'a> State<'a> {
|
||||
/// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
|
||||
/// `if cond { ... }`.
|
||||
fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
|
||||
let fixup =
|
||||
FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() };
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), fixup)
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), FixupContext::new_cond())
|
||||
}
|
||||
|
||||
/// Does `expr` need parentheses when printed in a condition position?
|
||||
@ -310,15 +236,7 @@ impl<'a> State<'a> {
|
||||
// because the latter is valid syntax but with the incorrect meaning.
|
||||
// It's a match-expression followed by tuple-expression, not a function
|
||||
// call.
|
||||
self.print_expr_maybe_paren(
|
||||
func,
|
||||
prec,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt,
|
||||
..fixup
|
||||
},
|
||||
);
|
||||
self.print_expr_maybe_paren(func, prec, fixup.leftmost_subexpression());
|
||||
|
||||
self.print_call_post(args)
|
||||
}
|
||||
@ -387,33 +305,17 @@ impl<'a> State<'a> {
|
||||
_ => left_prec,
|
||||
};
|
||||
|
||||
self.print_expr_maybe_paren(
|
||||
lhs,
|
||||
left_prec,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt,
|
||||
..fixup
|
||||
},
|
||||
);
|
||||
self.print_expr_maybe_paren(lhs, left_prec, fixup.leftmost_subexpression());
|
||||
|
||||
self.space();
|
||||
self.word_space(op.node.as_str());
|
||||
|
||||
self.print_expr_maybe_paren(
|
||||
rhs,
|
||||
right_prec,
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
|
||||
);
|
||||
self.print_expr_maybe_paren(rhs, right_prec, fixup.subsequent_subexpression());
|
||||
}
|
||||
|
||||
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
|
||||
self.word(op.as_str());
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_PREFIX,
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
|
||||
);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression());
|
||||
}
|
||||
|
||||
fn print_expr_addr_of(
|
||||
@ -431,11 +333,7 @@ impl<'a> State<'a> {
|
||||
self.print_mutability(mutability, true);
|
||||
}
|
||||
}
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_PREFIX,
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
|
||||
);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression());
|
||||
}
|
||||
|
||||
pub(super) fn print_expr(&mut self, expr: &ast::Expr, fixup: FixupContext) {
|
||||
@ -470,8 +368,7 @@ impl<'a> State<'a> {
|
||||
//
|
||||
// Same applies to a small set of other expression kinds which eagerly
|
||||
// terminate a statement which opens with them.
|
||||
let needs_par =
|
||||
fixup.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr);
|
||||
let needs_par = fixup.would_cause_statement_boundary(expr);
|
||||
if needs_par {
|
||||
self.popen();
|
||||
fixup = FixupContext::default();
|
||||
@ -519,16 +416,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
ast::ExprKind::Cast(expr, ty) => {
|
||||
let prec = AssocOp::As.precedence() as i8;
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
prec,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: fixup.stmt
|
||||
|| fixup.leftmost_subexpression_in_stmt,
|
||||
..fixup
|
||||
},
|
||||
);
|
||||
self.print_expr_maybe_paren(expr, prec, fixup.leftmost_subexpression());
|
||||
self.space();
|
||||
self.word_space("as");
|
||||
self.print_type(ty);
|
||||
@ -660,70 +548,34 @@ impl<'a> State<'a> {
|
||||
self.print_block_with_attrs(blk, attrs);
|
||||
}
|
||||
ast::ExprKind::Await(expr, _) => {
|
||||
// Same fixups as ExprKind::MethodCall.
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.word(".await");
|
||||
}
|
||||
ast::ExprKind::Assign(lhs, rhs, _) => {
|
||||
// Same fixups as ExprKind::Binary.
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_maybe_paren(
|
||||
lhs,
|
||||
prec + 1,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: fixup.stmt
|
||||
|| fixup.leftmost_subexpression_in_stmt,
|
||||
..fixup
|
||||
},
|
||||
);
|
||||
self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression());
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
self.print_expr_maybe_paren(
|
||||
rhs,
|
||||
prec,
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
|
||||
);
|
||||
self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
|
||||
}
|
||||
ast::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
// Same fixups as ExprKind::Binary.
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_maybe_paren(
|
||||
lhs,
|
||||
prec + 1,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: fixup.stmt
|
||||
|| fixup.leftmost_subexpression_in_stmt,
|
||||
..fixup
|
||||
},
|
||||
);
|
||||
self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression());
|
||||
self.space();
|
||||
self.word(op.node.as_str());
|
||||
self.word_space("=");
|
||||
self.print_expr_maybe_paren(
|
||||
rhs,
|
||||
prec,
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
|
||||
);
|
||||
self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
|
||||
}
|
||||
ast::ExprKind::Field(expr, ident) => {
|
||||
// Same fixups as ExprKind::MethodCall.
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.word(".");
|
||||
self.print_ident(*ident);
|
||||
}
|
||||
ast::ExprKind::Index(expr, index, _) => {
|
||||
// Same fixups as ExprKind::Call.
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_POSTFIX,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: fixup.stmt
|
||||
|| fixup.leftmost_subexpression_in_stmt,
|
||||
..fixup
|
||||
},
|
||||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
self.word("[");
|
||||
self.print_expr(index, FixupContext::default());
|
||||
@ -736,31 +588,14 @@ impl<'a> State<'a> {
|
||||
// a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
|
||||
let fake_prec = AssocOp::LOr.precedence() as i8;
|
||||
if let Some(e) = start {
|
||||
self.print_expr_maybe_paren(
|
||||
e,
|
||||
fake_prec,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: fixup.stmt
|
||||
|| fixup.leftmost_subexpression_in_stmt,
|
||||
..fixup
|
||||
},
|
||||
);
|
||||
self.print_expr_maybe_paren(e, fake_prec, fixup.leftmost_subexpression());
|
||||
}
|
||||
match limits {
|
||||
ast::RangeLimits::HalfOpen => self.word(".."),
|
||||
ast::RangeLimits::Closed => self.word("..="),
|
||||
}
|
||||
if let Some(e) = end {
|
||||
self.print_expr_maybe_paren(
|
||||
e,
|
||||
fake_prec,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
..fixup
|
||||
},
|
||||
);
|
||||
self.print_expr_maybe_paren(e, fake_prec, fixup.subsequent_subexpression());
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Underscore => self.word("_"),
|
||||
@ -777,11 +612,7 @@ impl<'a> State<'a> {
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_JUMP,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
..fixup
|
||||
},
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -799,11 +630,7 @@ impl<'a> State<'a> {
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_JUMP,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
..fixup
|
||||
},
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -816,11 +643,7 @@ impl<'a> State<'a> {
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_JUMP,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
..fixup
|
||||
},
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -830,7 +653,7 @@ impl<'a> State<'a> {
|
||||
self.print_expr_maybe_paren(
|
||||
result,
|
||||
parser::PREC_JUMP,
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup },
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
ast::ExprKind::InlineAsm(a) => {
|
||||
@ -884,16 +707,11 @@ impl<'a> State<'a> {
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_JUMP,
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
..fixup
|
||||
},
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Try(e) => {
|
||||
// Same fixups as ExprKind::MethodCall.
|
||||
self.print_expr_maybe_paren(e, parser::PREC_POSTFIX, fixup);
|
||||
self.word("?")
|
||||
}
|
||||
@ -961,7 +779,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
_ => {
|
||||
self.end(); // Close the ibox for the pattern.
|
||||
self.print_expr(body, FixupContext { stmt: true, ..FixupContext::default() });
|
||||
self.print_expr(body, FixupContext::new_stmt());
|
||||
self.word(",");
|
||||
}
|
||||
}
|
||||
|
149
compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
Normal file
149
compiler/rustc_ast_pretty/src/pprust/state/fixup.rs
Normal file
@ -0,0 +1,149 @@
|
||||
use rustc_ast::util::{classify, parser};
|
||||
use rustc_ast::Expr;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct FixupContext {
|
||||
/// Print expression such that it can be parsed back as a statement
|
||||
/// consisting of the original expression.
|
||||
///
|
||||
/// The effect of this is for binary operators in statement position to set
|
||||
/// `leftmost_subexpression_in_stmt` when printing their left-hand operand.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// (match x {}) - 1; // match needs parens when LHS of binary operator
|
||||
///
|
||||
/// match x {}; // not when its own statement
|
||||
/// ```
|
||||
stmt: bool,
|
||||
|
||||
/// This is the difference between:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// (match x {}) - 1; // subexpression needs parens
|
||||
///
|
||||
/// let _ = match x {} - 1; // no parens
|
||||
/// ```
|
||||
///
|
||||
/// There are 3 distinguishable contexts in which `print_expr` might be
|
||||
/// called with the expression `$match` as its argument, where `$match`
|
||||
/// represents an expression of kind `ExprKind::Match`:
|
||||
///
|
||||
/// - stmt=false leftmost_subexpression_in_stmt=false
|
||||
///
|
||||
/// Example: `let _ = $match - 1;`
|
||||
///
|
||||
/// No parentheses required.
|
||||
///
|
||||
/// - stmt=false leftmost_subexpression_in_stmt=true
|
||||
///
|
||||
/// Example: `$match - 1;`
|
||||
///
|
||||
/// Must parenthesize `($match)`, otherwise parsing back the output as a
|
||||
/// statement would terminate the statement after the closing brace of
|
||||
/// the match, parsing `-1;` as a separate statement.
|
||||
///
|
||||
/// - stmt=true leftmost_subexpression_in_stmt=false
|
||||
///
|
||||
/// Example: `$match;`
|
||||
///
|
||||
/// No parentheses required.
|
||||
leftmost_subexpression_in_stmt: bool,
|
||||
|
||||
/// This is the difference between:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// if let _ = (Struct {}) {} // needs parens
|
||||
///
|
||||
/// match () {
|
||||
/// () if let _ = Struct {} => {} // no parens
|
||||
/// }
|
||||
/// ```
|
||||
parenthesize_exterior_struct_lit: bool,
|
||||
}
|
||||
|
||||
/// The default amount of fixing is minimal fixing. Fixups should be turned on
|
||||
/// in a targeted fashion where needed.
|
||||
impl Default for FixupContext {
|
||||
fn default() -> Self {
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
parenthesize_exterior_struct_lit: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FixupContext {
|
||||
/// Create the initial fixup for printing an expression in statement
|
||||
/// position.
|
||||
///
|
||||
/// This is currently also used for printing an expression as a match-arm,
|
||||
/// but this is incorrect and leads to over-parenthesizing.
|
||||
pub fn new_stmt() -> Self {
|
||||
FixupContext { stmt: true, ..FixupContext::default() }
|
||||
}
|
||||
|
||||
/// Create the initial fixup for printing an expression as the "condition"
|
||||
/// of an `if` or `while`. There are a few other positions which are
|
||||
/// grammatically equivalent and also use this, such as the iterator
|
||||
/// expression in `for` and the scrutinee in `match`.
|
||||
pub fn new_cond() -> Self {
|
||||
FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() }
|
||||
}
|
||||
|
||||
/// Transform this fixup into the one that should apply when printing the
|
||||
/// leftmost subexpression of the current expression.
|
||||
///
|
||||
/// The leftmost subexpression is any subexpression that has the same first
|
||||
/// token as the current expression, but has a different last token.
|
||||
///
|
||||
/// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a
|
||||
/// leftmost subexpression.
|
||||
///
|
||||
/// Not every expression has a leftmost subexpression. For example neither
|
||||
/// `-$a` nor `[$a]` have one.
|
||||
pub fn leftmost_subexpression(self) -> Self {
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform this fixup into the one that should apply when printing any
|
||||
/// subexpression that is neither a leftmost subexpression nor surrounded in
|
||||
/// delimiters.
|
||||
///
|
||||
/// This is for any subexpression that has a different first token than the
|
||||
/// current expression, and is not surrounded by a paren/bracket/brace. For
|
||||
/// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or
|
||||
/// `$a.f($b)`.
|
||||
pub fn subsequent_subexpression(self) -> Self {
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..self }
|
||||
}
|
||||
|
||||
/// Determine whether parentheses are needed around the given expression to
|
||||
/// head off an unintended statement boundary.
|
||||
///
|
||||
/// The documentation on `FixupContext::leftmost_subexpression_in_stmt` has
|
||||
/// examples.
|
||||
pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool {
|
||||
self.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr)
|
||||
}
|
||||
|
||||
/// Determine whether parentheses are needed around the given `let`
|
||||
/// scrutinee.
|
||||
///
|
||||
/// In `if let _ = $e {}`, some examples of `$e` that would need parentheses
|
||||
/// are:
|
||||
///
|
||||
/// - `Struct {}.f()`, because otherwise the `{` would be misinterpreted
|
||||
/// as the opening of the if's then-block.
|
||||
///
|
||||
/// - `true && false`, because otherwise this would be misinterpreted as a
|
||||
/// "let chain".
|
||||
pub fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool {
|
||||
self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
|
||||
|| parser::needs_par_as_let_scrutinee(expr.precedence().order())
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::pp::Breaks::Inconsistent;
|
||||
use crate::pprust::state::expr::FixupContext;
|
||||
use crate::pprust::state::fixup::FixupContext;
|
||||
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
|
||||
|
||||
use ast::StaticItem;
|
||||
|
@ -7,7 +7,6 @@
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(let_chains)]
|
||||
|
||||
#[macro_use]
|
||||
|
@ -1320,7 +1320,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
.into_iter()
|
||||
.map(|err| match err.obligation.predicate.kind().skip_binder() {
|
||||
PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
|
||||
match predicate.self_ty().kind() {
|
||||
match *predicate.self_ty().kind() {
|
||||
ty::Param(param_ty) => Ok((
|
||||
generics.type_param(param_ty, tcx),
|
||||
predicate.trait_ref.print_only_trait_path().to_string(),
|
||||
|
@ -1070,7 +1070,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// LL | blk();
|
||||
// | ----- this value implements `FnOnce`, which causes it to be moved when called
|
||||
// ```
|
||||
if let ty::Param(param_ty) = self_ty.kind()
|
||||
if let ty::Param(param_ty) = *self_ty.kind()
|
||||
&& let generics = self.infcx.tcx.generics_of(self.mir_def_id())
|
||||
&& let param = generics.type_param(param_ty, self.infcx.tcx)
|
||||
&& let Some(hir_generics) = self
|
||||
|
@ -1315,7 +1315,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
AggregateKind::Adt(..)
|
||||
| AggregateKind::Array(..)
|
||||
| AggregateKind::Tuple { .. } => (),
|
||||
| AggregateKind::Tuple { .. }
|
||||
| AggregateKind::RawPtr(..) => (),
|
||||
}
|
||||
|
||||
for operand in operands {
|
||||
|
@ -1921,7 +1921,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
AggregateKind::Array(ty) => Ok(ty),
|
||||
AggregateKind::Tuple => {
|
||||
AggregateKind::Tuple | AggregateKind::RawPtr(..) => {
|
||||
unreachable!("This should have been covered in check_rvalues");
|
||||
}
|
||||
}
|
||||
@ -2518,6 +2518,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
AggregateKind::Closure(_, _) => None,
|
||||
AggregateKind::Coroutine(_, _) => None,
|
||||
AggregateKind::CoroutineClosure(_, _) => None,
|
||||
AggregateKind::RawPtr(_, _) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -2539,6 +2540,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
if let AggregateKind::RawPtr(..) = aggregate_kind {
|
||||
bug!("RawPtr should only be in runtime MIR");
|
||||
}
|
||||
|
||||
for (i, operand) in operands.iter_enumerated() {
|
||||
let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
|
||||
Ok(field_ty) => field_ty,
|
||||
@ -2757,7 +2762,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
),
|
||||
),
|
||||
|
||||
AggregateKind::Array(_) | AggregateKind::Tuple => {
|
||||
AggregateKind::Array(_) | AggregateKind::Tuple | AggregateKind::RawPtr(..) => {
|
||||
(CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty())
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,13 @@
|
||||
#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)]
|
||||
#![feature(
|
||||
no_core,
|
||||
lang_items,
|
||||
never_type,
|
||||
linkage,
|
||||
extern_types,
|
||||
thread_local,
|
||||
repr_simd,
|
||||
raw_ref_op
|
||||
)]
|
||||
#![no_core]
|
||||
#![allow(dead_code, non_camel_case_types, internal_features)]
|
||||
|
||||
@ -112,9 +121,7 @@ fn start<T: Termination + 'static>(
|
||||
|
||||
static mut NUM: u8 = 6 * 7;
|
||||
|
||||
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
|
||||
#[allow(static_mut_refs)]
|
||||
static NUM_REF: &'static u8 = unsafe { &NUM };
|
||||
static NUM_REF: &'static u8 = unsafe { &*&raw const NUM };
|
||||
|
||||
unsafe fn zeroed<T>() -> T {
|
||||
let mut uninit = MaybeUninit { uninit: () };
|
||||
|
@ -813,6 +813,19 @@ fn codegen_stmt<'tcx>(
|
||||
);
|
||||
lval.write_cvalue(fx, val);
|
||||
}
|
||||
Rvalue::Aggregate(ref kind, ref operands)
|
||||
if matches!(**kind, AggregateKind::RawPtr(..)) =>
|
||||
{
|
||||
let ty = to_place_and_rval.1.ty(&fx.mir.local_decls, fx.tcx);
|
||||
let layout = fx.layout_of(fx.monomorphize(ty));
|
||||
let [data, meta] = &*operands.raw else {
|
||||
bug!("RawPtr fields: {operands:?}");
|
||||
};
|
||||
let data = codegen_operand(fx, data);
|
||||
let meta = codegen_operand(fx, meta);
|
||||
let ptr_val = CValue::pointer_from_data_and_meta(data, meta, layout);
|
||||
lval.write_cvalue(fx, ptr_val);
|
||||
}
|
||||
Rvalue::Aggregate(ref kind, ref operands) => {
|
||||
let (variant_index, variant_dest, active_field_index) = match **kind {
|
||||
mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
|
||||
|
@ -94,6 +94,23 @@ impl<'tcx> CValue<'tcx> {
|
||||
CValue(CValueInner::ByValPair(value, extra), layout)
|
||||
}
|
||||
|
||||
/// For `AggregateKind::RawPtr`, create a pointer from its parts.
|
||||
///
|
||||
/// Panics if the `layout` is not a raw pointer.
|
||||
pub(crate) fn pointer_from_data_and_meta(
|
||||
data: CValue<'tcx>,
|
||||
meta: CValue<'tcx>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> CValue<'tcx> {
|
||||
assert!(layout.ty.is_unsafe_ptr());
|
||||
let inner = match (data.0, meta.0) {
|
||||
(CValueInner::ByVal(p), CValueInner::ByVal(m)) => CValueInner::ByValPair(p, m),
|
||||
(p @ CValueInner::ByVal(_), CValueInner::ByRef(..)) if meta.1.is_zst() => p,
|
||||
_ => bug!("RawPtr operands {data:?} {meta:?}"),
|
||||
};
|
||||
CValue(inner, layout)
|
||||
}
|
||||
|
||||
pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.1
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#![feature(
|
||||
no_core, unboxed_closures, start, lang_items, never_type, linkage,
|
||||
extern_types, thread_local
|
||||
extern_types, thread_local, raw_ref_op
|
||||
)]
|
||||
#![no_core]
|
||||
#![allow(dead_code, internal_features, non_camel_case_types)]
|
||||
@ -99,9 +99,7 @@ fn start<T: Termination + 'static>(
|
||||
|
||||
static mut NUM: u8 = 6 * 7;
|
||||
|
||||
// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint
|
||||
#[allow(static_mut_refs)]
|
||||
static NUM_REF: &'static u8 = unsafe { &NUM };
|
||||
static NUM_REF: &'static u8 = unsafe { &* &raw const NUM };
|
||||
|
||||
macro_rules! assert {
|
||||
($e:expr) => {
|
||||
|
@ -17,7 +17,7 @@ use rustc_data_structures::small_c_str::SmallCStr;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::ty::layout::{
|
||||
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
|
||||
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout,
|
||||
};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_sanitizers::{cfi, kcfi};
|
||||
@ -1702,4 +1702,128 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
};
|
||||
kcfi_bundle
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_parameters(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
bitmap_bytes: &'ll Value,
|
||||
) -> &'ll Value {
|
||||
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
|
||||
|
||||
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
||||
|
||||
let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, bitmap_bytes];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
// Create condition bitmap named `mcdc.addr`.
|
||||
let mut bx = Builder::with_cx(self.cx);
|
||||
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
|
||||
let cond_bitmap = {
|
||||
let alloca =
|
||||
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), c"mcdc.addr".as_ptr());
|
||||
llvm::LLVMSetAlignment(alloca, 4);
|
||||
alloca
|
||||
};
|
||||
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
|
||||
cond_bitmap
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_tvbitmap_update(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
bitmap_bytes: &'ll Value,
|
||||
bitmap_index: &'ll Value,
|
||||
mcdc_temp: &'ll Value,
|
||||
) {
|
||||
debug!(
|
||||
"mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})",
|
||||
fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp
|
||||
);
|
||||
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
||||
|
||||
let llfn =
|
||||
unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[
|
||||
self.cx.type_ptr(),
|
||||
self.cx.type_i64(),
|
||||
self.cx.type_i32(),
|
||||
self.cx.type_i32(),
|
||||
self.cx.type_ptr(),
|
||||
],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
let i32_align = self.tcx().data_layout.i32_align.abi;
|
||||
self.store(self.const_i32(0), mcdc_temp, i32_align);
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_condbitmap_update(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
cond_loc: &'ll Value,
|
||||
mcdc_temp: &'ll Value,
|
||||
bool_value: &'ll Value,
|
||||
) {
|
||||
debug!(
|
||||
"mcdc_condbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})",
|
||||
fn_name, hash, cond_loc, mcdc_temp, bool_value
|
||||
);
|
||||
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
||||
let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[
|
||||
self.cx.type_ptr(),
|
||||
self.cx.type_i64(),
|
||||
self.cx.type_i32(),
|
||||
self.cx.type_ptr(),
|
||||
self.cx.type_i1(),
|
||||
],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, cond_loc, mcdc_temp, bool_value];
|
||||
self.check_call("call", llty, llfn, args);
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,7 +260,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
|
||||
#[instrument(level = "debug", skip(self, llty))]
|
||||
pub(crate) fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value {
|
||||
if let Some(&g) = self.instances.borrow().get(&Instance::mono(self.tcx, def_id)) {
|
||||
let instance = Instance::mono(self.tcx, def_id);
|
||||
if let Some(&g) = self.instances.borrow().get(&instance) {
|
||||
trace!("used cached value");
|
||||
return g;
|
||||
}
|
||||
@ -273,7 +274,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
statics defined in the same CGU, but did not for `{def_id:?}`"
|
||||
);
|
||||
|
||||
let sym = self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name;
|
||||
let sym = self.tcx.symbol_name(instance).name;
|
||||
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
|
||||
|
||||
debug!(?sym, ?fn_attrs);
|
||||
@ -363,7 +364,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
self.instances.borrow_mut().insert(Instance::mono(self.tcx, def_id), g);
|
||||
self.instances.borrow_mut().insert(instance, g);
|
||||
g
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
use rustc_middle::mir::coverage::{CodeRegion, CounterId, CovTerm, ExpressionId, MappingKind};
|
||||
use rustc_middle::mir::coverage::{
|
||||
CodeRegion, ConditionInfo, CounterId, CovTerm, DecisionInfo, ExpressionId, MappingKind,
|
||||
};
|
||||
|
||||
/// Must match the layout of `LLVMRustCounterKind`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -99,6 +101,86 @@ pub enum RegionKind {
|
||||
/// associated with two counters, each representing the number of times the
|
||||
/// expression evaluates to true or false.
|
||||
BranchRegion = 4,
|
||||
|
||||
/// A DecisionRegion represents a top-level boolean expression and is
|
||||
/// associated with a variable length bitmap index and condition number.
|
||||
MCDCDecisionRegion = 5,
|
||||
|
||||
/// A Branch Region can be extended to include IDs to facilitate MC/DC.
|
||||
MCDCBranchRegion = 6,
|
||||
}
|
||||
|
||||
pub mod mcdc {
|
||||
use rustc_middle::mir::coverage::{ConditionInfo, DecisionInfo};
|
||||
|
||||
/// Must match the layout of `LLVMRustMCDCDecisionParameters`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct DecisionParameters {
|
||||
bitmap_idx: u32,
|
||||
conditions_num: u16,
|
||||
}
|
||||
|
||||
// ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257)
|
||||
type LLVMConditionId = i16;
|
||||
|
||||
/// Must match the layout of `LLVMRustMCDCBranchParameters`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct BranchParameters {
|
||||
condition_id: LLVMConditionId,
|
||||
condition_ids: [LLVMConditionId; 2],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ParameterTag {
|
||||
None = 0,
|
||||
Decision = 1,
|
||||
Branch = 2,
|
||||
}
|
||||
/// Same layout with `LLVMRustMCDCParameters`
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Parameters {
|
||||
tag: ParameterTag,
|
||||
decision_params: DecisionParameters,
|
||||
branch_params: BranchParameters,
|
||||
}
|
||||
|
||||
impl Parameters {
|
||||
pub fn none() -> Self {
|
||||
Self {
|
||||
tag: ParameterTag::None,
|
||||
decision_params: Default::default(),
|
||||
branch_params: Default::default(),
|
||||
}
|
||||
}
|
||||
pub fn decision(decision_params: DecisionParameters) -> Self {
|
||||
Self { tag: ParameterTag::Decision, decision_params, branch_params: Default::default() }
|
||||
}
|
||||
pub fn branch(branch_params: BranchParameters) -> Self {
|
||||
Self { tag: ParameterTag::Branch, decision_params: Default::default(), branch_params }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConditionInfo> for BranchParameters {
|
||||
fn from(value: ConditionInfo) -> Self {
|
||||
Self {
|
||||
condition_id: value.condition_id.as_u32() as LLVMConditionId,
|
||||
condition_ids: [
|
||||
value.false_next_id.as_u32() as LLVMConditionId,
|
||||
value.true_next_id.as_u32() as LLVMConditionId,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DecisionInfo> for DecisionParameters {
|
||||
fn from(value: DecisionInfo) -> Self {
|
||||
Self { bitmap_idx: value.bitmap_idx, conditions_num: value.conditions_num }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
|
||||
@ -122,6 +204,7 @@ pub struct CounterMappingRegion {
|
||||
/// for the false branch of the region.
|
||||
false_counter: Counter,
|
||||
|
||||
mcdc_params: mcdc::Parameters,
|
||||
/// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
|
||||
/// file_id is an index into a function-specific `virtual_file_mapping` array of indexes
|
||||
/// that, in turn, are used to look up the filename for this region.
|
||||
@ -173,6 +256,26 @@ impl CounterMappingRegion {
|
||||
end_line,
|
||||
end_col,
|
||||
),
|
||||
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
|
||||
Self::mcdc_branch_region(
|
||||
Counter::from_term(true_term),
|
||||
Counter::from_term(false_term),
|
||||
mcdc_params,
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
)
|
||||
}
|
||||
MappingKind::MCDCDecision(decision_info) => Self::decision_region(
|
||||
decision_info,
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +290,7 @@ impl CounterMappingRegion {
|
||||
Self {
|
||||
counter,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
@ -209,6 +313,7 @@ impl CounterMappingRegion {
|
||||
Self {
|
||||
counter,
|
||||
false_counter,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
@ -219,6 +324,54 @@ impl CounterMappingRegion {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_branch_region(
|
||||
counter: Counter,
|
||||
false_counter: Counter,
|
||||
condition_info: ConditionInfo,
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
counter,
|
||||
false_counter,
|
||||
mcdc_params: mcdc::Parameters::branch(condition_info.into()),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::MCDCBranchRegion,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn decision_region(
|
||||
decision_info: DecisionInfo,
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
let mcdc_params = mcdc::Parameters::decision(decision_info.into());
|
||||
|
||||
Self {
|
||||
counter: Counter::ZERO,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params,
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::MCDCDecisionRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// This function might be used in the future; the LLVM API is still evolving, as is coverage
|
||||
// support.
|
||||
#[allow(dead_code)]
|
||||
@ -233,6 +386,7 @@ impl CounterMappingRegion {
|
||||
Self {
|
||||
counter: Counter::ZERO,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id,
|
||||
start_line,
|
||||
@ -256,6 +410,7 @@ impl CounterMappingRegion {
|
||||
Self {
|
||||
counter: Counter::ZERO,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
@ -280,6 +435,7 @@ impl CounterMappingRegion {
|
||||
Self {
|
||||
counter,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
|
@ -13,7 +13,7 @@ use rustc_codegen_ssa::traits::{
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_llvm::RustString;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::coverage::CoverageKind;
|
||||
use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo};
|
||||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_target::abi::Align;
|
||||
@ -30,6 +30,7 @@ pub struct CrateCoverageContext<'ll, 'tcx> {
|
||||
pub(crate) function_coverage_map:
|
||||
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
|
||||
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||
@ -37,6 +38,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||
Self {
|
||||
function_coverage_map: Default::default(),
|
||||
pgo_func_name_var_map: Default::default(),
|
||||
mcdc_condition_bitmap_map: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +47,12 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||
) -> FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
|
||||
self.function_coverage_map.replace(FxIndexMap::default())
|
||||
}
|
||||
|
||||
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap.
|
||||
/// This value is named `mcdc.addr` (same as clang) and is a 32-bit integer.
|
||||
fn try_get_mcdc_condition_bitmap(&self, instance: &Instance<'tcx>) -> Option<&'ll llvm::Value> {
|
||||
self.mcdc_condition_bitmap_map.borrow().get(instance).copied()
|
||||
}
|
||||
}
|
||||
|
||||
// These methods used to be part of trait `CoverageInfoMethods`, which no longer
|
||||
@ -90,6 +98,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
return;
|
||||
};
|
||||
|
||||
if function_coverage_info.mcdc_bitmap_bytes > 0 {
|
||||
ensure_mcdc_parameters(bx, instance, function_coverage_info);
|
||||
}
|
||||
|
||||
let Some(coverage_context) = bx.coverage_context() else { return };
|
||||
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
||||
let func_coverage = coverage_map
|
||||
@ -131,10 +143,66 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
CoverageKind::ExpressionUsed { id } => {
|
||||
func_coverage.mark_expression_id_seen(id);
|
||||
}
|
||||
CoverageKind::CondBitmapUpdate { id, value, .. } => {
|
||||
drop(coverage_map);
|
||||
assert_ne!(
|
||||
id.as_u32(),
|
||||
0,
|
||||
"ConditionId of evaluated conditions should never be zero"
|
||||
);
|
||||
let cond_bitmap = coverage_context
|
||||
.try_get_mcdc_condition_bitmap(&instance)
|
||||
.expect("mcdc cond bitmap should have been allocated for updating");
|
||||
let cond_loc = bx.const_i32(id.as_u32() as i32 - 1);
|
||||
let bool_value = bx.const_bool(value);
|
||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value);
|
||||
}
|
||||
CoverageKind::TestVectorBitmapUpdate { bitmap_idx } => {
|
||||
drop(coverage_map);
|
||||
let cond_bitmap = coverage_context
|
||||
.try_get_mcdc_condition_bitmap(&instance)
|
||||
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
|
||||
let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
|
||||
assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
|
||||
assert!(
|
||||
bitmap_bytes <= function_coverage_info.mcdc_bitmap_bytes,
|
||||
"bitmap length disagreement: query says {bitmap_bytes} but function info only has {}",
|
||||
function_coverage_info.mcdc_bitmap_bytes
|
||||
);
|
||||
|
||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let bitmap_bytes = bx.const_u32(bitmap_bytes);
|
||||
let bitmap_index = bx.const_u32(bitmap_idx);
|
||||
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_bytes, bitmap_index, cond_bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_mcdc_parameters<'ll, 'tcx>(
|
||||
bx: &mut Builder<'_, 'll, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage_info: &FunctionCoverageInfo,
|
||||
) {
|
||||
let Some(cx) = bx.coverage_context() else { return };
|
||||
if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
|
||||
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes);
|
||||
bx.coverage_context()
|
||||
.expect("already checked above")
|
||||
.mcdc_condition_bitmap_map
|
||||
.borrow_mut()
|
||||
.insert(instance, cond_bitmap);
|
||||
}
|
||||
|
||||
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
|
||||
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
|
||||
/// containing the function name, with the specific variable name and linkage
|
||||
|
@ -1631,6 +1631,10 @@ extern "C" {
|
||||
|
||||
// Miscellaneous instructions
|
||||
pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value;
|
||||
pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value;
|
||||
pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value;
|
||||
pub fn LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(M: &Module) -> &Value;
|
||||
|
||||
pub fn LLVMRustBuildCall<'a>(
|
||||
B: &Builder<'a>,
|
||||
Ty: &'a Type,
|
||||
|
@ -902,52 +902,45 @@ impl<'a> Linker for MsvcLinker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
|
||||
match strip {
|
||||
Strip::None => {
|
||||
// This will cause the Microsoft linker to generate a PDB file
|
||||
// from the CodeView line tables in the object files.
|
||||
self.cmd.arg("/DEBUG");
|
||||
fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
|
||||
// This will cause the Microsoft linker to generate a PDB file
|
||||
// from the CodeView line tables in the object files.
|
||||
self.cmd.arg("/DEBUG");
|
||||
|
||||
// Default to emitting only the file name of the PDB file into
|
||||
// the binary instead of the full path. Emitting the full path
|
||||
// may leak private information (such as user names).
|
||||
// See https://github.com/rust-lang/rust/issues/87825.
|
||||
//
|
||||
// This default behavior can be overridden by explicitly passing
|
||||
// `-Clink-arg=/PDBALTPATH:...` to rustc.
|
||||
self.cmd.arg("/PDBALTPATH:%_PDB%");
|
||||
// Default to emitting only the file name of the PDB file into
|
||||
// the binary instead of the full path. Emitting the full path
|
||||
// may leak private information (such as user names).
|
||||
// See https://github.com/rust-lang/rust/issues/87825.
|
||||
//
|
||||
// This default behavior can be overridden by explicitly passing
|
||||
// `-Clink-arg=/PDBALTPATH:...` to rustc.
|
||||
self.cmd.arg("/PDBALTPATH:%_PDB%");
|
||||
|
||||
// This will cause the Microsoft linker to embed .natvis info into the PDB file
|
||||
let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
|
||||
if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
|
||||
for entry in natvis_dir {
|
||||
match entry {
|
||||
Ok(entry) => {
|
||||
let path = entry.path();
|
||||
if path.extension() == Some("natvis".as_ref()) {
|
||||
let mut arg = OsString::from("/NATVIS:");
|
||||
arg.push(path);
|
||||
self.cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error });
|
||||
}
|
||||
// This will cause the Microsoft linker to embed .natvis info into the PDB file
|
||||
let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
|
||||
if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
|
||||
for entry in natvis_dir {
|
||||
match entry {
|
||||
Ok(entry) => {
|
||||
let path = entry.path();
|
||||
if path.extension() == Some("natvis".as_ref()) {
|
||||
let mut arg = OsString::from("/NATVIS:");
|
||||
arg.push(path);
|
||||
self.cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
|
||||
for path in natvis_debugger_visualizers {
|
||||
let mut arg = OsString::from("/NATVIS:");
|
||||
arg.push(path);
|
||||
self.cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
Strip::Debuginfo | Strip::Symbols => {
|
||||
self.cmd.arg("/DEBUG:NONE");
|
||||
}
|
||||
// This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
|
||||
for path in natvis_debugger_visualizers {
|
||||
let mut arg = OsString::from("/NATVIS:");
|
||||
arg.push(path);
|
||||
self.cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,6 +363,24 @@ fn exported_symbols_provider_local(
|
||||
},
|
||||
));
|
||||
}
|
||||
MonoItem::Fn(Instance {
|
||||
def: InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)),
|
||||
args,
|
||||
}) => {
|
||||
// A little sanity-check
|
||||
debug_assert_eq!(
|
||||
args.non_erasable_generics(tcx, def_id).skip(1).next(),
|
||||
Some(GenericArgKind::Type(ty))
|
||||
);
|
||||
symbols.push((
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty),
|
||||
SymbolExportInfo {
|
||||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
used: false,
|
||||
},
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
// Any other symbols don't qualify for sharing
|
||||
}
|
||||
@ -385,6 +403,7 @@ fn upstream_monomorphizations_provider(
|
||||
let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
|
||||
|
||||
let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
|
||||
let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn();
|
||||
|
||||
for &cnum in cnums.iter() {
|
||||
for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
|
||||
@ -399,6 +418,18 @@ fn upstream_monomorphizations_provider(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
|
||||
if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id {
|
||||
(
|
||||
async_drop_in_place_fn_def_id,
|
||||
tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]),
|
||||
)
|
||||
} else {
|
||||
// `drop_in_place` in place does not exist, don't try
|
||||
// to use it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ExportedSymbol::NonGeneric(..)
|
||||
| ExportedSymbol::ThreadLocalShim(..)
|
||||
| ExportedSymbol::NoDefId(..) => {
|
||||
@ -534,6 +565,13 @@ pub fn symbol_name_for_instance_in_crate<'tcx>(
|
||||
Instance::resolve_drop_in_place(tcx, ty),
|
||||
instantiating_crate,
|
||||
),
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
|
||||
rustc_symbol_mangling::symbol_name_for_instance_in_crate(
|
||||
tcx,
|
||||
Instance::resolve_async_drop_in_place(tcx, ty),
|
||||
instantiating_crate,
|
||||
)
|
||||
}
|
||||
ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(),
|
||||
}
|
||||
}
|
||||
@ -582,6 +620,9 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
|
||||
// DropGlue always use the Rust calling convention and thus follow the target's default
|
||||
// symbol decoration scheme.
|
||||
ExportedSymbol::DropGlue(..) => None,
|
||||
// AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the
|
||||
// target's default symbol decoration scheme.
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(..) => None,
|
||||
// NoDefId always follow the target's default symbol decoration scheme.
|
||||
ExportedSymbol::NoDefId(..) => None,
|
||||
// ThreadLocalShim always follow the target's default symbol decoration scheme.
|
||||
|
@ -835,7 +835,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
|
||||
let def = instance.map(|i| i.def);
|
||||
|
||||
if let Some(ty::InstanceDef::DropGlue(_, None)) = def {
|
||||
if let Some(
|
||||
ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None),
|
||||
) = def
|
||||
{
|
||||
// Empty drop glue; a no-op.
|
||||
let target = target.unwrap();
|
||||
return helper.funclet_br(self, bx, target, mergeable_succ);
|
||||
|
@ -9,7 +9,7 @@ use crate::MemFlags;
|
||||
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::Operand;
|
||||
use rustc_middle::mir::{AggregateKind, Operand};
|
||||
use rustc_middle::ty::cast::{CastTy, IntTy};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
|
||||
@ -720,6 +720,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
OperandRef { val: OperandValue::Immediate(static_), layout }
|
||||
}
|
||||
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
|
||||
mir::Rvalue::Aggregate(box mir::AggregateKind::RawPtr(..), ref fields) => {
|
||||
let ty = rvalue.ty(self.mir, self.cx.tcx());
|
||||
let layout = self.cx.layout_of(self.monomorphize(ty));
|
||||
let [data, meta] = &*fields.raw else {
|
||||
bug!("RawPtr fields: {fields:?}");
|
||||
};
|
||||
let data = self.codegen_operand(bx, data);
|
||||
let meta = self.codegen_operand(bx, meta);
|
||||
match (data.val, meta.val) {
|
||||
(p @ OperandValue::Immediate(_), OperandValue::ZeroSized) => {
|
||||
OperandRef { val: p, layout }
|
||||
}
|
||||
(OperandValue::Immediate(p), OperandValue::Immediate(m)) => {
|
||||
OperandRef { val: OperandValue::Pair(p, m), layout }
|
||||
}
|
||||
_ => bug!("RawPtr operands {data:?} {meta:?}"),
|
||||
}
|
||||
}
|
||||
mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => {
|
||||
// According to `rvalue_creates_operand`, only ZST
|
||||
// aggregate rvalues are allowed to be operands.
|
||||
@ -1032,6 +1050,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
mir::Rvalue::ThreadLocalRef(_) |
|
||||
mir::Rvalue::Use(..) => // (*)
|
||||
true,
|
||||
// This always produces a `ty::RawPtr`, so will be Immediate or Pair
|
||||
mir::Rvalue::Aggregate(box AggregateKind::RawPtr(..), ..) => true,
|
||||
mir::Rvalue::Repeat(..) |
|
||||
mir::Rvalue::Aggregate(..) => {
|
||||
let ty = rvalue.ty(self.mir, self.cx.tcx());
|
||||
|
@ -9,7 +9,9 @@ use rustc_middle::mir;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
|
||||
|
||||
use super::{ImmTy, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar};
|
||||
use super::{
|
||||
ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar,
|
||||
};
|
||||
use crate::util;
|
||||
|
||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
@ -303,6 +305,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let variant_dest = self.project_downcast(dest, variant_index)?;
|
||||
(variant_index, variant_dest, active_field_index)
|
||||
}
|
||||
mir::AggregateKind::RawPtr(..) => {
|
||||
// Pointers don't have "fields" in the normal sense, so the
|
||||
// projection-based code below would either fail in projection
|
||||
// or in type mismatches. Instead, build an `Immediate` from
|
||||
// the parts and write that to the destination.
|
||||
let [data, meta] = &operands.raw else {
|
||||
bug!("{kind:?} should have 2 operands, had {operands:?}");
|
||||
};
|
||||
let data = self.eval_operand(data, None)?;
|
||||
let data = self.read_pointer(&data)?;
|
||||
let meta = self.eval_operand(meta, None)?;
|
||||
let meta = if meta.layout.is_zst() {
|
||||
MemPlaceMeta::None
|
||||
} else {
|
||||
MemPlaceMeta::Meta(self.read_scalar(&meta)?)
|
||||
};
|
||||
let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self);
|
||||
let ptr = ImmTy::from_immediate(ptr_imm, dest.layout);
|
||||
self.copy_op(&ptr, dest)?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => (FIRST_VARIANT, dest.clone(), None),
|
||||
};
|
||||
if active_field_index.is_some() {
|
||||
|
@ -558,6 +558,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..)
|
||||
| ty::InstanceDef::Item(_) => {
|
||||
// We need MIR for this fn
|
||||
let Some((body, instance)) = M::find_mir_or_eval_fn(
|
||||
|
@ -11,7 +11,6 @@ Rust MIR: a lowered representation of Rust.
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(strict_provenance)]
|
||||
|
@ -923,6 +923,47 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
AggregateKind::RawPtr(pointee_ty, mutability) => {
|
||||
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
|
||||
// It would probably be fine to support this in earlier phases,
|
||||
// but at the time of writing it's only ever introduced from intrinsic lowering,
|
||||
// so earlier things just `bug!` on it.
|
||||
self.fail(location, "RawPtr should be in runtime MIR only");
|
||||
}
|
||||
|
||||
if fields.len() != 2 {
|
||||
self.fail(location, "raw pointer aggregate must have 2 fields");
|
||||
} else {
|
||||
let data_ptr_ty = fields.raw[0].ty(self.body, self.tcx);
|
||||
let metadata_ty = fields.raw[1].ty(self.body, self.tcx);
|
||||
if let ty::RawPtr(in_pointee, in_mut) = data_ptr_ty.kind() {
|
||||
if *in_mut != mutability {
|
||||
self.fail(location, "input and output mutability must match");
|
||||
}
|
||||
|
||||
// FIXME: check `Thin` instead of `Sized`
|
||||
if !in_pointee.is_sized(self.tcx, self.param_env) {
|
||||
self.fail(location, "input pointer must be thin");
|
||||
}
|
||||
} else {
|
||||
self.fail(
|
||||
location,
|
||||
"first operand to raw pointer aggregate must be a raw pointer",
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME: Check metadata more generally
|
||||
if pointee_ty.is_slice() {
|
||||
if !self.mir_assign_valid_types(metadata_ty, self.tcx.types.usize) {
|
||||
self.fail(location, "slice metadata must be usize");
|
||||
}
|
||||
} else if pointee_ty.is_sized(self.tcx, self.param_env) {
|
||||
if metadata_ty != self.tcx.types.unit {
|
||||
self.fail(location, "metadata for pointer-to-thin must be unit");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Rvalue::Ref(_, BorrowKind::Fake, _) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
|
@ -20,7 +20,6 @@
|
||||
#![feature(cfg_match)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(extend_one)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(hash_raw_entry)]
|
||||
#![feature(hasher_prefixfree_extras)]
|
||||
#![feature(lazy_cell)]
|
||||
|
@ -730,7 +730,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
|
||||
} }
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
|
||||
pub fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
|
||||
self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
|
||||
self
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
#![feature(box_patterns)]
|
||||
#![feature(error_reporter)]
|
||||
#![feature(extract_if)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
|
@ -12,7 +12,6 @@
|
||||
//! symbol to the `accepted` or `removed` modules respectively.
|
||||
|
||||
#![allow(internal_features)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(lazy_cell)]
|
||||
|
@ -162,6 +162,18 @@ language_item_table! {
|
||||
Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
|
||||
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
AsyncDestruct, sym::async_destruct, async_destruct_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2);
|
||||
AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3);
|
||||
|
||||
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
|
||||
@ -281,6 +293,7 @@ language_item_table! {
|
||||
|
||||
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
|
||||
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None;
|
||||
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
|
@ -351,7 +351,7 @@ hir_analysis_param_in_ty_of_assoc_const_binding =
|
||||
*[normal] the {$param_def_kind} `{$param_name}` is defined here
|
||||
}
|
||||
|
||||
hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope
|
||||
hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>`
|
||||
.label = {$kind} parameter is implicitly captured by this `impl Trait`
|
||||
.note = currently, all {$kind} parameters are required to be mentioned in the precise captures list
|
||||
|
||||
@ -405,6 +405,10 @@ hir_analysis_self_in_impl_self =
|
||||
`Self` is not valid in the self type of an impl block
|
||||
.note = replace `Self` with a different type
|
||||
|
||||
hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>`
|
||||
.label = `Self` type parameter is implicitly captured by this `impl Trait`
|
||||
.note = currently, all type parameters are required to be mentioned in the precise captures list
|
||||
|
||||
hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code
|
||||
.help = add `#![feature(simd_ffi)]` to the crate attributes to enable
|
||||
|
||||
|
@ -492,6 +492,7 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
|
||||
};
|
||||
|
||||
let mut expected_captures = UnordSet::default();
|
||||
let mut shadowed_captures = UnordSet::default();
|
||||
let mut seen_params = UnordMap::default();
|
||||
let mut prev_non_lifetime_param = None;
|
||||
for arg in precise_capturing_args {
|
||||
@ -530,6 +531,21 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
|
||||
match tcx.named_bound_var(hir_id) {
|
||||
Some(ResolvedArg::EarlyBound(def_id)) => {
|
||||
expected_captures.insert(def_id);
|
||||
|
||||
// Make sure we allow capturing these lifetimes through `Self` and
|
||||
// `T::Assoc` projection syntax, too. These will occur when we only
|
||||
// see lifetimes are captured after hir-lowering -- this aligns with
|
||||
// the cases that were stabilized with the `impl_trait_projection`
|
||||
// feature -- see <https://github.com/rust-lang/rust/pull/115659>.
|
||||
if let DefKind::LifetimeParam = tcx.def_kind(def_id)
|
||||
&& let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
|
||||
| ty::ReLateParam(ty::LateParamRegion {
|
||||
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
|
||||
..
|
||||
}) = *tcx.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local())
|
||||
{
|
||||
shadowed_captures.insert(def_id);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tcx.dcx().span_delayed_bug(
|
||||
@ -555,39 +571,61 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// If a param is shadowed by a early-bound (duplicated) lifetime, then
|
||||
// it may or may not be captured as invariant, depending on if it shows
|
||||
// up through `Self` or `T::Assoc` syntax.
|
||||
if shadowed_captures.contains(¶m.def_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => {
|
||||
let use_span = tcx.def_span(param.def_id);
|
||||
let opaque_span = tcx.def_span(opaque_def_id);
|
||||
// Check if the lifetime param was captured but isn't named in the precise captures list.
|
||||
if variances[param.index as usize] == ty::Invariant {
|
||||
let param_span =
|
||||
if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
|
||||
if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
|
||||
&& let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
|
||||
| ty::ReLateParam(ty::LateParamRegion {
|
||||
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
|
||||
..
|
||||
}) = *tcx
|
||||
.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
|
||||
{
|
||||
Some(tcx.def_span(def_id))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// FIXME(precise_capturing): Structured suggestion for this would be useful
|
||||
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
|
||||
use_span: tcx.def_span(param.def_id),
|
||||
param_span,
|
||||
opaque_span: tcx.def_span(opaque_def_id),
|
||||
});
|
||||
{
|
||||
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
|
||||
opaque_span,
|
||||
use_span,
|
||||
param_span: tcx.def_span(def_id),
|
||||
});
|
||||
} else {
|
||||
// If the `use_span` is actually just the param itself, then we must
|
||||
// have not duplicated the lifetime but captured the original.
|
||||
// The "effective" `use_span` will be the span of the opaque itself,
|
||||
// and the param span will be the def span of the param.
|
||||
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
|
||||
opaque_span,
|
||||
use_span: opaque_span,
|
||||
param_span: use_span,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ty::GenericParamDefKind::Type { .. } => {
|
||||
// FIXME(precise_capturing): Structured suggestion for this would be useful
|
||||
tcx.dcx().emit_err(errors::ParamNotCaptured {
|
||||
param_span: tcx.def_span(param.def_id),
|
||||
opaque_span: tcx.def_span(opaque_def_id),
|
||||
kind: "type",
|
||||
});
|
||||
if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) {
|
||||
// FIXME(precise_capturing): Structured suggestion for this would be useful
|
||||
tcx.dcx().emit_err(errors::SelfTyNotCaptured {
|
||||
trait_span: tcx.def_span(param.def_id),
|
||||
opaque_span: tcx.def_span(opaque_def_id),
|
||||
});
|
||||
} else {
|
||||
// FIXME(precise_capturing): Structured suggestion for this would be useful
|
||||
tcx.dcx().emit_err(errors::ParamNotCaptured {
|
||||
param_span: tcx.def_span(param.def_id),
|
||||
opaque_span: tcx.def_span(opaque_def_id),
|
||||
kind: "type",
|
||||
});
|
||||
}
|
||||
}
|
||||
ty::GenericParamDefKind::Const { .. } => {
|
||||
// FIXME(precise_capturing): Structured suggestion for this would be useful
|
||||
|
@ -128,6 +128,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
|
||||
| sym::variant_count
|
||||
| sym::is_val_statically_known
|
||||
| sym::ptr_mask
|
||||
| sym::aggregate_raw_ptr
|
||||
| sym::ub_checks
|
||||
| sym::fadd_algebraic
|
||||
| sym::fsub_algebraic
|
||||
@ -574,6 +575,10 @@ pub fn check_intrinsic_type(
|
||||
(0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
|
||||
}
|
||||
|
||||
// This type check is not particularly useful, but the `where` bounds
|
||||
// on the definition in `core` do the heavy lifting for checking it.
|
||||
sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)),
|
||||
|
||||
sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool),
|
||||
|
||||
sym::simd_eq
|
||||
|
@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::traits::util;
|
||||
use rustc_middle::ty::GenericArgs;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -214,7 +214,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTyToOpaque<'tcx> {
|
||||
{
|
||||
self.tcx.type_of(projection_ty.def_id).instantiate(self.tcx, projection_ty.args)
|
||||
} else {
|
||||
ty
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,19 +6,29 @@ use rustc_span::{Span, Symbol};
|
||||
#[note]
|
||||
pub struct ParamNotCaptured {
|
||||
#[primary_span]
|
||||
pub param_span: Span,
|
||||
#[label]
|
||||
pub opaque_span: Span,
|
||||
#[label]
|
||||
pub param_span: Span,
|
||||
pub kind: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_self_ty_not_captured)]
|
||||
#[note]
|
||||
pub struct SelfTyNotCaptured {
|
||||
#[primary_span]
|
||||
pub opaque_span: Span,
|
||||
#[label]
|
||||
pub trait_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_lifetime_not_captured)]
|
||||
pub struct LifetimeNotCaptured {
|
||||
#[primary_span]
|
||||
pub use_span: Span,
|
||||
#[label(hir_analysis_param_label)]
|
||||
pub param_span: Option<Span>,
|
||||
pub param_span: Span,
|
||||
#[label]
|
||||
pub opaque_span: Span,
|
||||
}
|
||||
|
@ -228,8 +228,8 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
|
||||
// Check whether this segment takes generic arguments and the user has provided any.
|
||||
let (generic_args, infer_args) = ctx.args_for_def_id(def_id);
|
||||
|
||||
let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter());
|
||||
let mut args_iter = args_iter.clone().peekable();
|
||||
let mut args_iter =
|
||||
generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
|
||||
|
||||
// If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
|
||||
// If we later encounter a lifetime, we know that the arguments were provided in the
|
||||
|
@ -63,7 +63,6 @@ This API is completely unstable and subject to change.
|
||||
#![feature(rustdoc_internals)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(is_sorted)]
|
||||
#![feature(iter_intersperse)]
|
||||
|
@ -38,8 +38,11 @@ pub fn check_legal_trait_for_method_call(
|
||||
receiver: Option<Span>,
|
||||
expr_span: Span,
|
||||
trait_id: DefId,
|
||||
body_id: DefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if tcx.lang_items().drop_trait() == Some(trait_id) {
|
||||
if tcx.lang_items().drop_trait() == Some(trait_id)
|
||||
&& tcx.lang_items().fallback_surface_drop_fn() != Some(body_id)
|
||||
{
|
||||
let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
|
||||
errors::ExplicitDestructorCallSugg::Snippet {
|
||||
lo: expr_span.shrink_to_lo(),
|
||||
|
@ -2697,7 +2697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
fn point_at_param_definition(&self, err: &mut Diag<'_>, param: ty::ParamTy) {
|
||||
let generics = self.tcx.generics_of(self.body_id);
|
||||
let generic_param = generics.type_param(¶m, self.tcx);
|
||||
let generic_param = generics.type_param(param, self.tcx);
|
||||
if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind {
|
||||
return;
|
||||
}
|
||||
|
@ -1131,6 +1131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
None,
|
||||
span,
|
||||
container_id,
|
||||
self.body_id.to_def_id(),
|
||||
) {
|
||||
self.set_tainted_by_errors(e);
|
||||
}
|
||||
|
@ -2144,7 +2144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let callee_ty = callee_ty.peel_refs();
|
||||
match *callee_ty.kind() {
|
||||
ty::Param(param) => {
|
||||
let param = self.tcx.generics_of(self.body_id).type_param(¶m, self.tcx);
|
||||
let param = self.tcx.generics_of(self.body_id).type_param(param, self.tcx);
|
||||
if param.kind.is_synthetic() {
|
||||
// if it's `impl Fn() -> ..` then just fall down to the def-id based logic
|
||||
def_id = param.def_id;
|
||||
|
@ -628,6 +628,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
Some(self.self_expr.span),
|
||||
self.call_expr.span,
|
||||
trait_def_id,
|
||||
self.body_id.to_def_id(),
|
||||
) {
|
||||
self.set_tainted_by_errors(e);
|
||||
}
|
||||
|
@ -125,11 +125,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let Some(arg_ty) = args[0].as_type() else {
|
||||
return false;
|
||||
};
|
||||
let ty::Param(param) = arg_ty.kind() else {
|
||||
let ty::Param(param) = *arg_ty.kind() else {
|
||||
return false;
|
||||
};
|
||||
// Is `generic_param` the same as the arg for this trait predicate?
|
||||
generic_param.index == generics.type_param(¶m, tcx).index
|
||||
generic_param.index == generics.type_param(param, tcx).index
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -156,10 +156,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return false;
|
||||
}
|
||||
|
||||
match ty.peel_refs().kind() {
|
||||
match *ty.peel_refs().kind() {
|
||||
ty::Param(param) => {
|
||||
let generics = self.tcx.generics_of(self.body_id);
|
||||
let generic_param = generics.type_param(¶m, self.tcx);
|
||||
let generic_param = generics.type_param(param, self.tcx);
|
||||
for unsatisfied in unsatisfied_predicates.iter() {
|
||||
// The parameter implements `IntoIterator`
|
||||
// but it has called a method that requires it to implement `Iterator`
|
||||
@ -3232,9 +3232,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.sort_by_key(|&info| (!info.def_id.is_local(), self.tcx.def_path_str(info.def_id)));
|
||||
candidates.dedup();
|
||||
|
||||
let param_type = match rcvr_ty.kind() {
|
||||
let param_type = match *rcvr_ty.kind() {
|
||||
ty::Param(param) => Some(param),
|
||||
ty::Ref(_, ty, _) => match ty.kind() {
|
||||
ty::Ref(_, ty, _) => match *ty.kind() {
|
||||
ty::Param(param) => Some(param),
|
||||
_ => None,
|
||||
},
|
||||
@ -3429,7 +3429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
} else {
|
||||
param_type.map_or_else(
|
||||
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
|
||||
ToString::to_string,
|
||||
|p| p.to_string(),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -162,8 +162,11 @@ pub fn query_cache_path(sess: &Session) -> PathBuf {
|
||||
fn lock_file_path(session_dir: &Path) -> PathBuf {
|
||||
let crate_dir = session_dir.parent().unwrap();
|
||||
|
||||
let directory_name = session_dir.file_name().unwrap().to_string_lossy();
|
||||
assert_no_characters_lost(&directory_name);
|
||||
let directory_name = session_dir
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.expect("malformed session dir name: contains non-Unicode characters");
|
||||
|
||||
let dash_indices: Vec<_> = directory_name.match_indices('-').map(|(idx, _)| idx).collect();
|
||||
if dash_indices.len() != 3 {
|
||||
@ -329,8 +332,11 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
|
||||
|
||||
debug!("finalize_session_directory() - session directory: {}", incr_comp_session_dir.display());
|
||||
|
||||
let old_sub_dir_name = incr_comp_session_dir.file_name().unwrap().to_string_lossy();
|
||||
assert_no_characters_lost(&old_sub_dir_name);
|
||||
let old_sub_dir_name = incr_comp_session_dir
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.expect("malformed session dir name: contains non-Unicode characters");
|
||||
|
||||
// Keep the 's-{timestamp}-{random-number}' prefix, but replace the
|
||||
// '-working' part with the SVH of the crate
|
||||
@ -527,8 +533,10 @@ where
|
||||
for session_dir in iter {
|
||||
debug!("find_source_directory_in_iter - inspecting `{}`", session_dir.display());
|
||||
|
||||
let directory_name = session_dir.file_name().unwrap().to_string_lossy();
|
||||
assert_no_characters_lost(&directory_name);
|
||||
let Some(directory_name) = session_dir.file_name().unwrap().to_str() else {
|
||||
debug!("find_source_directory_in_iter - ignoring");
|
||||
continue;
|
||||
};
|
||||
|
||||
if source_directories_already_tried.contains(&session_dir)
|
||||
|| !is_session_directory(&directory_name)
|
||||
@ -619,12 +627,6 @@ fn crate_path(sess: &Session) -> PathBuf {
|
||||
incr_dir.join(crate_name)
|
||||
}
|
||||
|
||||
fn assert_no_characters_lost(s: &str) {
|
||||
if s.contains('\u{FFFD}') {
|
||||
bug!("Could not losslessly convert '{}'.", s)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_old_enough_to_be_collected(timestamp: SystemTime) -> bool {
|
||||
timestamp < SystemTime::now() - Duration::from_secs(10)
|
||||
}
|
||||
@ -657,14 +659,14 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
|
||||
};
|
||||
|
||||
let entry_name = dir_entry.file_name();
|
||||
let entry_name = entry_name.to_string_lossy();
|
||||
let Some(entry_name) = entry_name.to_str() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if is_session_directory_lock_file(&entry_name) {
|
||||
assert_no_characters_lost(&entry_name);
|
||||
lock_files.insert(entry_name.into_owned());
|
||||
lock_files.insert(entry_name.to_string());
|
||||
} else if is_session_directory(&entry_name) {
|
||||
assert_no_characters_lost(&entry_name);
|
||||
session_directories.insert(entry_name.into_owned());
|
||||
session_directories.insert(entry_name.to_string());
|
||||
} else {
|
||||
// This is something we don't know, leave it alone
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ use crate::traits::{
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::{
|
||||
codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString,
|
||||
ErrorGuaranteed, IntoDiagArg,
|
||||
ErrorGuaranteed, IntoDiagArg, StringPart,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -1917,6 +1917,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
);
|
||||
if !is_simple_error || terr.must_include_note() {
|
||||
diag.note_expected_found(&expected_label, expected, &found_label, found);
|
||||
|
||||
if let Some(ty::Closure(_, args)) =
|
||||
exp_found.map(|expected_type_found| expected_type_found.found.kind())
|
||||
{
|
||||
diag.highlighted_note(vec![
|
||||
StringPart::normal("closure has signature: `"),
|
||||
StringPart::highlighted(
|
||||
self.tcx
|
||||
.signature_unclosure(
|
||||
args.as_closure().sig(),
|
||||
rustc_hir::Unsafety::Normal,
|
||||
)
|
||||
.to_string(),
|
||||
),
|
||||
StringPart::normal("`"),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2354,7 +2371,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
|
||||
// type_param_sugg_span is (span, has_bounds)
|
||||
let (type_scope, type_param_sugg_span) = match bound_kind {
|
||||
GenericKind::Param(ref param) => {
|
||||
GenericKind::Param(param) => {
|
||||
let generics = self.tcx.generics_of(generic_param_scope);
|
||||
let def_id = generics.type_param(param, self.tcx).def_id.expect_local();
|
||||
let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
|
||||
|
@ -28,7 +28,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
|
||||
match err {
|
||||
ArgumentSorts(values, _) | Sorts(values) => {
|
||||
match (values.expected.kind(), values.found.kind()) {
|
||||
match (*values.expected.kind(), *values.found.kind()) {
|
||||
(ty::Closure(..), ty::Closure(..)) => {
|
||||
diag.note("no two closures, even if identical, have the same type");
|
||||
diag.help("consider boxing your closure and/or using it as a trait object");
|
||||
@ -452,7 +452,7 @@ impl<T> Trait<T> for X {
|
||||
}
|
||||
(ty::FnPtr(sig), ty::FnDef(def_id, _))
|
||||
| (ty::FnDef(def_id, _), ty::FnPtr(sig)) => {
|
||||
if tcx.fn_sig(*def_id).skip_binder().unsafety() < sig.unsafety() {
|
||||
if tcx.fn_sig(def_id).skip_binder().unsafety() < sig.unsafety() {
|
||||
diag.note(
|
||||
"unsafe functions cannot be coerced into safe function pointers",
|
||||
);
|
||||
@ -527,7 +527,7 @@ impl<T> Trait<T> for X {
|
||||
diag: &mut Diag<'_>,
|
||||
msg: impl Fn() -> String,
|
||||
body_owner_def_id: DefId,
|
||||
proj_ty: &ty::AliasTy<'tcx>,
|
||||
proj_ty: ty::AliasTy<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
@ -541,7 +541,7 @@ impl<T> Trait<T> for X {
|
||||
};
|
||||
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
|
||||
// This will also work for `impl Trait`.
|
||||
let ty::Param(param_ty) = proj_ty.self_ty().kind() else {
|
||||
let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
|
||||
return false;
|
||||
};
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
@ -598,7 +598,7 @@ impl<T> Trait<T> for X {
|
||||
fn expected_projection(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
proj_ty: &ty::AliasTy<'tcx>,
|
||||
proj_ty: ty::AliasTy<'tcx>,
|
||||
values: ExpectedFound<Ty<'tcx>>,
|
||||
body_owner_def_id: DefId,
|
||||
cause_code: &ObligationCauseCode<'_>,
|
||||
@ -709,7 +709,7 @@ fn foo(&self) -> Self::T { String::new() }
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
msg: impl Fn() -> String,
|
||||
proj_ty: &ty::AliasTy<'tcx>,
|
||||
proj_ty: ty::AliasTy<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
|
@ -1521,15 +1521,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
closure_kind_ty.to_opt_closure_kind()
|
||||
}
|
||||
|
||||
/// Clears the selection, evaluation, and projection caches. This is useful when
|
||||
/// repeatedly attempting to select an `Obligation` while changing only
|
||||
/// its `ParamEnv`, since `FulfillmentContext` doesn't use probing.
|
||||
pub fn clear_caches(&self) {
|
||||
self.selection_cache.clear();
|
||||
self.evaluation_cache.clear();
|
||||
self.inner.borrow_mut().projection_cache().clear();
|
||||
}
|
||||
|
||||
pub fn universe(&self) -> ty::UniverseIndex {
|
||||
self.universe.get()
|
||||
}
|
||||
|
@ -78,11 +78,12 @@ pub struct ProjectionCacheStorage<'tcx> {
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ProjectionCacheKey<'tcx> {
|
||||
ty: ty::AliasTy<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ProjectionCacheKey<'tcx> {
|
||||
pub fn new(ty: ty::AliasTy<'tcx>) -> Self {
|
||||
Self { ty }
|
||||
pub fn new(ty: ty::AliasTy<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
|
||||
Self { ty, param_env }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![feature(decl_macro)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(thread_spawn_unchecked)]
|
||||
|
@ -760,7 +760,7 @@ fn test_unstable_options_tracking_hash() {
|
||||
);
|
||||
tracked!(codegen_backend, Some("abc".to_string()));
|
||||
tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes);
|
||||
tracked!(coverage_options, CoverageOptions { branch: true });
|
||||
tracked!(coverage_options, CoverageOptions { branch: true, mcdc: true });
|
||||
tracked!(crate_attr, vec!["abc".to_string()]);
|
||||
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
|
||||
tracked!(debug_info_for_profiling, true);
|
||||
|
@ -32,7 +32,6 @@
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(extract_if)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_order_by)]
|
||||
#![feature(let_chains)]
|
||||
|
@ -264,7 +264,19 @@ fn impl_trait_ref_has_enough_non_local_candidates<'tcx>(
|
||||
binder: EarlyBinder<TraitRef<'tcx>>,
|
||||
mut did_has_local_parent: impl FnMut(DefId) -> bool,
|
||||
) -> bool {
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let infcx = tcx
|
||||
.infer_ctxt()
|
||||
// We use the new trait solver since the obligation we are trying to
|
||||
// prove here may overflow and those are fatal in the old trait solver.
|
||||
// Which is unacceptable for a lint.
|
||||
//
|
||||
// Thanksfully the part we use here are very similar to the
|
||||
// new-trait-solver-as-coherence, which is in stabilization.
|
||||
//
|
||||
// https://github.com/rust-lang/rust/issues/123573
|
||||
.with_next_trait_solver(true)
|
||||
.build();
|
||||
|
||||
let trait_ref = binder.instantiate(tcx, infcx.fresh_args_for_item(infer_span, trait_def_id));
|
||||
|
||||
let trait_ref = trait_ref.fold_with(&mut ReplaceLocalTypesWithInfer {
|
||||
|
@ -4,8 +4,6 @@
|
||||
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// FFI equivalent of enum `llvm::coverage::Counter::CounterKind`
|
||||
@ -43,6 +41,8 @@ enum class LLVMRustCounterMappingRegionKind {
|
||||
SkippedRegion = 2,
|
||||
GapRegion = 3,
|
||||
BranchRegion = 4,
|
||||
MCDCDecisionRegion = 5,
|
||||
MCDCBranchRegion = 6
|
||||
};
|
||||
|
||||
static coverage::CounterMappingRegion::RegionKind
|
||||
@ -58,15 +58,102 @@ fromRust(LLVMRustCounterMappingRegionKind Kind) {
|
||||
return coverage::CounterMappingRegion::GapRegion;
|
||||
case LLVMRustCounterMappingRegionKind::BranchRegion:
|
||||
return coverage::CounterMappingRegion::BranchRegion;
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion:
|
||||
return coverage::CounterMappingRegion::MCDCDecisionRegion;
|
||||
case LLVMRustCounterMappingRegionKind::MCDCBranchRegion:
|
||||
return coverage::CounterMappingRegion::MCDCBranchRegion;
|
||||
#else
|
||||
case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion:
|
||||
break;
|
||||
case LLVMRustCounterMappingRegionKind::MCDCBranchRegion:
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
report_fatal_error("Bad LLVMRustCounterMappingRegionKind!");
|
||||
}
|
||||
|
||||
enum LLVMRustMCDCParametersTag {
|
||||
None = 0,
|
||||
Decision = 1,
|
||||
Branch = 2,
|
||||
};
|
||||
|
||||
struct LLVMRustMCDCDecisionParameters {
|
||||
uint32_t BitmapIdx;
|
||||
uint16_t NumConditions;
|
||||
};
|
||||
|
||||
struct LLVMRustMCDCBranchParameters {
|
||||
int16_t ConditionID;
|
||||
int16_t ConditionIDs[2];
|
||||
};
|
||||
|
||||
struct LLVMRustMCDCParameters {
|
||||
LLVMRustMCDCParametersTag Tag;
|
||||
LLVMRustMCDCDecisionParameters DecisionParameters;
|
||||
LLVMRustMCDCBranchParameters BranchParameters;
|
||||
};
|
||||
|
||||
// LLVM representations for `MCDCParameters` evolved from LLVM 18 to 19.
|
||||
// Look at representations in 18
|
||||
// https://github.com/rust-lang/llvm-project/blob/66a2881a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L253-L263
|
||||
// and representations in 19
|
||||
// https://github.com/llvm/llvm-project/blob/843cc474faefad1d639f4c44c1cf3ad7dbda76c8/llvm/include/llvm/ProfileData/Coverage/MCDCTypes.h
|
||||
#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
|
||||
static coverage::CounterMappingRegion::MCDCParameters
|
||||
fromRust(LLVMRustMCDCParameters Params) {
|
||||
auto parameter = coverage::CounterMappingRegion::MCDCParameters{};
|
||||
switch (Params.Tag) {
|
||||
case LLVMRustMCDCParametersTag::None:
|
||||
return parameter;
|
||||
case LLVMRustMCDCParametersTag::Decision:
|
||||
parameter.BitmapIdx =
|
||||
static_cast<unsigned>(Params.DecisionParameters.BitmapIdx),
|
||||
parameter.NumConditions =
|
||||
static_cast<unsigned>(Params.DecisionParameters.NumConditions);
|
||||
return parameter;
|
||||
case LLVMRustMCDCParametersTag::Branch:
|
||||
parameter.ID = static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
|
||||
Params.BranchParameters.ConditionID),
|
||||
parameter.FalseID =
|
||||
static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
|
||||
Params.BranchParameters.ConditionIDs[0]),
|
||||
parameter.TrueID =
|
||||
static_cast<coverage::CounterMappingRegion::MCDCConditionID>(
|
||||
Params.BranchParameters.ConditionIDs[1]);
|
||||
return parameter;
|
||||
}
|
||||
report_fatal_error("Bad LLVMRustMCDCParametersTag!");
|
||||
}
|
||||
#elif LLVM_VERSION_GE(19, 0)
|
||||
static coverage::mcdc::Parameters fromRust(LLVMRustMCDCParameters Params) {
|
||||
switch (Params.Tag) {
|
||||
case LLVMRustMCDCParametersTag::None:
|
||||
return std::monostate();
|
||||
case LLVMRustMCDCParametersTag::Decision:
|
||||
return coverage::mcdc::DecisionParameters(
|
||||
Params.DecisionParameters.BitmapIdx,
|
||||
Params.DecisionParameters.NumConditions);
|
||||
case LLVMRustMCDCParametersTag::Branch:
|
||||
return coverage::mcdc::BranchParameters(
|
||||
static_cast<coverage::mcdc::ConditionID>(
|
||||
Params.BranchParameters.ConditionID),
|
||||
{static_cast<coverage::mcdc::ConditionID>(
|
||||
Params.BranchParameters.ConditionIDs[0]),
|
||||
static_cast<coverage::mcdc::ConditionID>(
|
||||
Params.BranchParameters.ConditionIDs[1])});
|
||||
}
|
||||
report_fatal_error("Bad LLVMRustMCDCParametersTag!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// FFI equivalent of struct `llvm::coverage::CounterMappingRegion`
|
||||
// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L211-L304
|
||||
struct LLVMRustCounterMappingRegion {
|
||||
LLVMRustCounter Count;
|
||||
LLVMRustCounter FalseCount;
|
||||
LLVMRustMCDCParameters MCDCParameters;
|
||||
uint32_t FileID;
|
||||
uint32_t ExpandedFileID;
|
||||
uint32_t LineStart;
|
||||
@ -135,7 +222,8 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
|
||||
MappingRegions.emplace_back(
|
||||
fromRust(Region.Count), fromRust(Region.FalseCount),
|
||||
#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
|
||||
coverage::CounterMappingRegion::MCDCParameters{},
|
||||
// LLVM 19 may move this argument to last.
|
||||
fromRust(Region.MCDCParameters),
|
||||
#endif
|
||||
Region.FileID, Region.ExpandedFileID, // File IDs, then region info.
|
||||
Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
|
||||
|
@ -1522,6 +1522,7 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) {
|
||||
delete Bundle;
|
||||
}
|
||||
|
||||
// OpBundlesIndirect is an array of pointers (*not* a pointer to an array).
|
||||
extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
OperandBundleDef **OpBundlesIndirect,
|
||||
@ -1546,6 +1547,33 @@ extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M)
|
||||
unwrap(M), llvm::Intrinsic::instrprof_increment));
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) {
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
return wrap(llvm::Intrinsic::getDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters));
|
||||
#else
|
||||
report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) {
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
return wrap(llvm::Intrinsic::getDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update));
|
||||
#else
|
||||
report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) {
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
return wrap(llvm::Intrinsic::getDeclaration(
|
||||
unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update));
|
||||
#else
|
||||
report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
|
||||
LLVMValueRef Dst, unsigned DstAlign,
|
||||
LLVMValueRef Src, unsigned SrcAlign,
|
||||
@ -1574,6 +1602,7 @@ extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B,
|
||||
unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile));
|
||||
}
|
||||
|
||||
// OpBundlesIndirect is an array of pointers (*not* a pointer to an array).
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
@ -1596,6 +1625,7 @@ LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
Name));
|
||||
}
|
||||
|
||||
// OpBundlesIndirect is an array of pointers (*not* a pointer to an array).
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMBasicBlockRef DefaultDest,
|
||||
|
@ -6,7 +6,6 @@
|
||||
#![feature(error_iter)]
|
||||
#![feature(extract_if)]
|
||||
#![feature(coroutines)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(iter_from_coroutine)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(if_let_guard)]
|
||||
|
@ -35,7 +35,6 @@
|
||||
#![feature(const_type_name)]
|
||||
#![feature(discriminant_kind)]
|
||||
#![feature(coroutines)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(inline_const)]
|
||||
#![feature(iter_from_coroutine)]
|
||||
|
@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> {
|
||||
NonGeneric(DefId),
|
||||
Generic(DefId, GenericArgsRef<'tcx>),
|
||||
DropGlue(Ty<'tcx>),
|
||||
AsyncDropGlueCtorShim(Ty<'tcx>),
|
||||
ThreadLocalShim(DefId),
|
||||
NoDefId(ty::SymbolName<'tcx>),
|
||||
}
|
||||
@ -59,6 +60,9 @@ impl<'tcx> ExportedSymbol<'tcx> {
|
||||
ExportedSymbol::DropGlue(ty) => {
|
||||
tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty))
|
||||
}
|
||||
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
|
||||
tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty))
|
||||
}
|
||||
ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance {
|
||||
def: ty::InstanceDef::ThreadLocalShim(def_id),
|
||||
args: ty::GenericArgs::empty(),
|
||||
|
@ -51,6 +51,25 @@ rustc_index::newtype_index! {
|
||||
pub struct ExpressionId {}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// ID of a mcdc condition. Used by llvm to check mcdc coverage.
|
||||
///
|
||||
/// Note for future: the max limit of 0xFFFF is probably too loose. Actually llvm does not
|
||||
/// support decisions with too many conditions (7 and more at LLVM 18 while may be hundreds at 19)
|
||||
/// and represents it with `int16_t`. This max value may be changed once we could
|
||||
/// figure out an accurate limit.
|
||||
#[derive(HashStable)]
|
||||
#[encodable]
|
||||
#[orderable]
|
||||
#[max = 0xFFFF]
|
||||
#[debug_format = "ConditionId({})"]
|
||||
pub struct ConditionId {}
|
||||
}
|
||||
|
||||
impl ConditionId {
|
||||
pub const NONE: Self = Self::from_u32(0);
|
||||
}
|
||||
|
||||
/// Enum that can hold a constant zero value, the ID of an physical coverage
|
||||
/// counter, or the ID of a coverage-counter expression.
|
||||
///
|
||||
@ -106,6 +125,22 @@ pub enum CoverageKind {
|
||||
/// mappings. Intermediate expressions with no direct mappings are
|
||||
/// retained/zeroed based on whether they are transitively used.)
|
||||
ExpressionUsed { id: ExpressionId },
|
||||
|
||||
/// Marks the point in MIR control flow represented by a evaluated condition.
|
||||
///
|
||||
/// This is eventually lowered to `llvm.instrprof.mcdc.condbitmap.update` in LLVM IR.
|
||||
///
|
||||
/// If this statement does not survive MIR optimizations, the condition would never be
|
||||
/// taken as evaluated.
|
||||
CondBitmapUpdate { id: ConditionId, value: bool },
|
||||
|
||||
/// Marks the point in MIR control flow represented by a evaluated decision.
|
||||
///
|
||||
/// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR.
|
||||
///
|
||||
/// If this statement does not survive MIR optimizations, the decision would never be
|
||||
/// taken as evaluated.
|
||||
TestVectorBitmapUpdate { bitmap_idx: u32 },
|
||||
}
|
||||
|
||||
impl Debug for CoverageKind {
|
||||
@ -116,6 +151,12 @@ impl Debug for CoverageKind {
|
||||
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
|
||||
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
|
||||
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
|
||||
CondBitmapUpdate { id, value } => {
|
||||
write!(fmt, "CondBitmapUpdate({:?}, {:?})", id.index(), value)
|
||||
}
|
||||
TestVectorBitmapUpdate { bitmap_idx } => {
|
||||
write!(fmt, "TestVectorUpdate({:?})", bitmap_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,16 +213,23 @@ pub enum MappingKind {
|
||||
Code(CovTerm),
|
||||
/// Associates a branch region with separate counters for true and false.
|
||||
Branch { true_term: CovTerm, false_term: CovTerm },
|
||||
/// Associates a branch region with separate counters for true and false.
|
||||
MCDCBranch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo },
|
||||
/// Associates a decision region with a bitmap and number of conditions.
|
||||
MCDCDecision(DecisionInfo),
|
||||
}
|
||||
|
||||
impl MappingKind {
|
||||
/// Iterator over all coverage terms in this mapping kind.
|
||||
pub fn terms(&self) -> impl Iterator<Item = CovTerm> {
|
||||
let one = |a| std::iter::once(a).chain(None);
|
||||
let two = |a, b| std::iter::once(a).chain(Some(b));
|
||||
let zero = || None.into_iter().chain(None);
|
||||
let one = |a| Some(a).into_iter().chain(None);
|
||||
let two = |a, b| Some(a).into_iter().chain(Some(b));
|
||||
match *self {
|
||||
Self::Code(term) => one(term),
|
||||
Self::Branch { true_term, false_term } => two(true_term, false_term),
|
||||
Self::MCDCBranch { true_term, false_term, .. } => two(true_term, false_term),
|
||||
Self::MCDCDecision(_) => zero(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,6 +241,12 @@ impl MappingKind {
|
||||
Self::Branch { true_term, false_term } => {
|
||||
Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) }
|
||||
}
|
||||
Self::MCDCBranch { true_term, false_term, mcdc_params } => Self::MCDCBranch {
|
||||
true_term: map_fn(true_term),
|
||||
false_term: map_fn(false_term),
|
||||
mcdc_params,
|
||||
},
|
||||
Self::MCDCDecision(param) => Self::MCDCDecision(param),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -212,7 +266,7 @@ pub struct Mapping {
|
||||
pub struct FunctionCoverageInfo {
|
||||
pub function_source_hash: u64,
|
||||
pub num_counters: usize,
|
||||
|
||||
pub mcdc_bitmap_bytes: u32,
|
||||
pub expressions: IndexVec<ExpressionId, Expression>,
|
||||
pub mappings: Vec<Mapping>,
|
||||
}
|
||||
@ -226,6 +280,8 @@ pub struct BranchInfo {
|
||||
/// data structures without having to scan the entire body first.
|
||||
pub num_block_markers: usize,
|
||||
pub branch_spans: Vec<BranchSpan>,
|
||||
pub mcdc_branch_spans: Vec<MCDCBranchSpan>,
|
||||
pub mcdc_decision_spans: Vec<MCDCDecisionSpan>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -235,3 +291,47 @@ pub struct BranchSpan {
|
||||
pub true_marker: BlockMarkerId,
|
||||
pub false_marker: BlockMarkerId,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct ConditionInfo {
|
||||
pub condition_id: ConditionId,
|
||||
pub true_next_id: ConditionId,
|
||||
pub false_next_id: ConditionId,
|
||||
}
|
||||
|
||||
impl Default for ConditionInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
condition_id: ConditionId::NONE,
|
||||
true_next_id: ConditionId::NONE,
|
||||
false_next_id: ConditionId::NONE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct MCDCBranchSpan {
|
||||
pub span: Span,
|
||||
/// If `None`, this actually represents a normal branch span inserted for
|
||||
/// code that was too complex for MC/DC.
|
||||
pub condition_info: Option<ConditionInfo>,
|
||||
pub true_marker: BlockMarkerId,
|
||||
pub false_marker: BlockMarkerId,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct DecisionInfo {
|
||||
pub bitmap_idx: u32,
|
||||
pub conditions_num: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct MCDCDecisionSpan {
|
||||
pub span: Span,
|
||||
pub conditions_num: usize,
|
||||
pub end_markers: Vec<BlockMarkerId>,
|
||||
}
|
||||
|
@ -65,7 +65,9 @@ impl<'tcx> MonoItem<'tcx> {
|
||||
match instance.def {
|
||||
// "Normal" functions size estimate: the number of
|
||||
// statements, plus one for the terminator.
|
||||
InstanceDef::Item(..) | InstanceDef::DropGlue(..) => {
|
||||
InstanceDef::Item(..)
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..) => {
|
||||
let mir = tcx.instance_mir(instance.def);
|
||||
mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum()
|
||||
}
|
||||
@ -406,7 +408,8 @@ impl<'tcx> CodegenUnit<'tcx> {
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..) => None,
|
||||
}
|
||||
}
|
||||
MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),
|
||||
|
@ -187,6 +187,17 @@ fn dump_path<'tcx>(
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => {
|
||||
// Unfortunately, pretty-printed typed are not very filename-friendly.
|
||||
// We dome some filtering.
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
@ -475,7 +486,8 @@ fn write_coverage_branch_info(
|
||||
branch_info: &coverage::BranchInfo,
|
||||
w: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
let coverage::BranchInfo { branch_spans, .. } = branch_info;
|
||||
let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } =
|
||||
branch_info;
|
||||
|
||||
for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
|
||||
writeln!(
|
||||
@ -483,7 +495,26 @@ fn write_coverage_branch_info(
|
||||
"{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
|
||||
)?;
|
||||
}
|
||||
if !branch_spans.is_empty() {
|
||||
|
||||
for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
|
||||
mcdc_branch_spans
|
||||
{
|
||||
writeln!(
|
||||
w,
|
||||
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
|
||||
condition_info.map(|info| info.condition_id)
|
||||
)?;
|
||||
}
|
||||
|
||||
for coverage::MCDCDecisionSpan { span, conditions_num, end_markers } in mcdc_decision_spans {
|
||||
writeln!(
|
||||
w,
|
||||
"{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?} }} => {span:?}"
|
||||
)?;
|
||||
}
|
||||
|
||||
if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty()
|
||||
{
|
||||
writeln!(w)?;
|
||||
}
|
||||
|
||||
@ -1074,6 +1105,15 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
||||
|
||||
struct_fmt.finish()
|
||||
}),
|
||||
|
||||
AggregateKind::RawPtr(pointee_ty, mutability) => {
|
||||
let kind_str = match mutability {
|
||||
Mutability::Mut => "mut",
|
||||
Mutability::Not => "const",
|
||||
};
|
||||
with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?;
|
||||
fmt_tuple(fmt, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,4 +361,8 @@ pub struct CoverageIdsInfo {
|
||||
/// InstrumentCoverage MIR pass, if the highest-numbered counter increments
|
||||
/// were removed by MIR optimizations.
|
||||
pub max_counter_id: mir::coverage::CounterId,
|
||||
|
||||
/// Coverage codegen for mcdc needs to know the size of the global bitmap so that it can
|
||||
/// set the `bytemap-bytes` argument of the `llvm.instrprof.mcdc.tvbitmap.update` intrinsic.
|
||||
pub mcdc_bitmap_bytes: u32,
|
||||
}
|
||||
|
@ -1351,6 +1351,21 @@ pub enum AggregateKind<'tcx> {
|
||||
Closure(DefId, GenericArgsRef<'tcx>),
|
||||
Coroutine(DefId, GenericArgsRef<'tcx>),
|
||||
CoroutineClosure(DefId, GenericArgsRef<'tcx>),
|
||||
|
||||
/// Construct a raw pointer from the data pointer and metadata.
|
||||
///
|
||||
/// The `Ty` here is the type of the *pointee*, not the pointer itself.
|
||||
/// The `Mutability` indicates whether this produces a `*const` or `*mut`.
|
||||
///
|
||||
/// The [`Rvalue::Aggregate`] operands for thus must be
|
||||
///
|
||||
/// 0. A raw pointer of matching mutability with any [`core::ptr::Thin`] pointee
|
||||
/// 1. A value of the appropriate [`core::ptr::Pointee::Metadata`] type
|
||||
///
|
||||
/// *Both* operands must always be included, even the unit value if this is
|
||||
/// creating a thin pointer. If you're just converting between thin pointers,
|
||||
/// you may want an [`Rvalue::Cast`] with [`CastKind::PtrToPtr`] instead.
|
||||
RawPtr(Ty<'tcx>, Mutability),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
|
@ -206,6 +206,7 @@ impl<'tcx> Rvalue<'tcx> {
|
||||
AggregateKind::CoroutineClosure(did, args) => {
|
||||
Ty::new_coroutine_closure(tcx, did, args)
|
||||
}
|
||||
AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability),
|
||||
},
|
||||
Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
|
||||
Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
|
||||
|
@ -350,12 +350,14 @@ macro_rules! make_mir_visitor {
|
||||
receiver_by_ref: _,
|
||||
} |
|
||||
ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id } |
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, None) |
|
||||
ty::InstanceDef::DropGlue(_def_id, None) => {}
|
||||
|
||||
ty::InstanceDef::FnPtrShim(_def_id, ty) |
|
||||
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
|
||||
ty::InstanceDef::CloneShim(_def_id, ty) |
|
||||
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => {
|
||||
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) |
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, Some(ty)) => {
|
||||
// FIXME(eddyb) use a better `TyContext` here.
|
||||
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
|
||||
}
|
||||
@ -751,6 +753,9 @@ macro_rules! make_mir_visitor {
|
||||
) => {
|
||||
self.visit_args(coroutine_closure_args, location);
|
||||
}
|
||||
AggregateKind::RawPtr(ty, _) => {
|
||||
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
|
||||
}
|
||||
}
|
||||
|
||||
for operand in operands {
|
||||
|
@ -1344,6 +1344,14 @@ rustc_queries! {
|
||||
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `Unpin`", env.value }
|
||||
}
|
||||
/// Query backing `Ty::has_surface_async_drop`.
|
||||
query has_surface_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` has `AsyncDrop` implementation", env.value }
|
||||
}
|
||||
/// Query backing `Ty::has_surface_drop`.
|
||||
query has_surface_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` has `Drop` implementation", env.value }
|
||||
}
|
||||
/// Query backing `Ty::needs_drop`.
|
||||
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` needs drop", env.value }
|
||||
|
@ -1006,15 +1006,18 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
||||
|
||||
// This code is hot when compiling matches with many ranges. So we
|
||||
// special-case extraction of evaluated scalars for speed, for types where
|
||||
// unsigned int comparisons are appropriate. E.g. `unicode-normalization` has
|
||||
// we can do scalar comparisons. E.g. `unicode-normalization` has
|
||||
// many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
|
||||
// in this way.
|
||||
(Finite(a), Finite(b)) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => {
|
||||
(Finite(a), Finite(b)) if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) => {
|
||||
if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) {
|
||||
let sz = ty.primitive_size(tcx);
|
||||
let a = a.assert_uint(sz);
|
||||
let b = b.assert_uint(sz);
|
||||
return Some(a.cmp(&b));
|
||||
let cmp = match ty.kind() {
|
||||
ty::Uint(_) | ty::Char => a.assert_uint(sz).cmp(&b.assert_uint(sz)),
|
||||
ty::Int(_) => a.assert_int(sz).cmp(&b.assert_int(sz)),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
return Some(cmp);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
@ -263,7 +263,7 @@ impl<'tcx> Generics {
|
||||
/// Returns the `GenericParamDef` associated with this `EarlyParamRegion`.
|
||||
pub fn region_param(
|
||||
&'tcx self,
|
||||
param: &ty::EarlyParamRegion,
|
||||
param: ty::EarlyParamRegion,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> &'tcx GenericParamDef {
|
||||
let param = self.param_at(param.index as usize, tcx);
|
||||
@ -274,7 +274,7 @@ impl<'tcx> Generics {
|
||||
}
|
||||
|
||||
/// Returns the `GenericParamDef` associated with this `ParamTy`.
|
||||
pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef {
|
||||
pub fn type_param(&'tcx self, param: ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef {
|
||||
let param = self.param_at(param.index as usize, tcx);
|
||||
match param.kind {
|
||||
GenericParamDefKind::Type { .. } => param,
|
||||
@ -286,7 +286,7 @@ impl<'tcx> Generics {
|
||||
/// `Generics`.
|
||||
pub fn opt_type_param(
|
||||
&'tcx self,
|
||||
param: &ParamTy,
|
||||
param: ParamTy,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Option<&'tcx GenericParamDef> {
|
||||
let param = self.opt_param_at(param.index as usize, tcx)?;
|
||||
@ -297,7 +297,7 @@ impl<'tcx> Generics {
|
||||
}
|
||||
|
||||
/// Returns the `GenericParamDef` associated with this `ParamConst`.
|
||||
pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef {
|
||||
pub fn const_param(&'tcx self, param: ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef {
|
||||
let param = self.param_at(param.index as usize, tcx);
|
||||
match param.kind {
|
||||
GenericParamDefKind::Const { .. } => param,
|
||||
|
@ -168,6 +168,12 @@ pub enum InstanceDef<'tcx> {
|
||||
///
|
||||
/// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`.
|
||||
FnPtrAddrShim(DefId, Ty<'tcx>),
|
||||
|
||||
/// `core::future::async_drop::async_drop_in_place::<'_, T>`.
|
||||
///
|
||||
/// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty`
|
||||
/// is the type `T`.
|
||||
AsyncDropGlueCtorShim(DefId, Option<Ty<'tcx>>),
|
||||
}
|
||||
|
||||
impl<'tcx> Instance<'tcx> {
|
||||
@ -210,7 +216,9 @@ impl<'tcx> Instance<'tcx> {
|
||||
InstanceDef::Item(def) => tcx
|
||||
.upstream_monomorphizations_for(def)
|
||||
.and_then(|monos| monos.get(&self.args).cloned()),
|
||||
InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args),
|
||||
InstanceDef::DropGlue(_, Some(_)) | InstanceDef::AsyncDropGlueCtorShim(_, _) => {
|
||||
tcx.upstream_drop_glue_for(self.args)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -235,7 +243,8 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
| ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id }
|
||||
| InstanceDef::DropGlue(def_id, _)
|
||||
| InstanceDef::CloneShim(def_id, _)
|
||||
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
|
||||
| InstanceDef::FnPtrAddrShim(def_id, _)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(def_id, _) => def_id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,9 +252,9 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
|
||||
match self {
|
||||
ty::InstanceDef::Item(def) => Some(def),
|
||||
ty::InstanceDef::DropGlue(def_id, Some(_)) | InstanceDef::ThreadLocalShim(def_id) => {
|
||||
Some(def_id)
|
||||
}
|
||||
ty::InstanceDef::DropGlue(def_id, Some(_))
|
||||
| InstanceDef::AsyncDropGlueCtorShim(def_id, _)
|
||||
| InstanceDef::ThreadLocalShim(def_id) => Some(def_id),
|
||||
InstanceDef::VTableShim(..)
|
||||
| InstanceDef::ReifyShim(..)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
@ -279,6 +288,7 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
let def_id = match *self {
|
||||
ty::InstanceDef::Item(def) => def,
|
||||
ty::InstanceDef::DropGlue(_, Some(_)) => return false,
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => return false,
|
||||
ty::InstanceDef::ThreadLocalShim(_) => return false,
|
||||
_ => return true,
|
||||
};
|
||||
@ -347,11 +357,13 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::DropGlue(_, Some(_)) => false,
|
||||
| InstanceDef::DropGlue(_, Some(_))
|
||||
| InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => false,
|
||||
InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..)
|
||||
| InstanceDef::Item(_)
|
||||
| InstanceDef::Intrinsic(..)
|
||||
| InstanceDef::ReifyShim(..)
|
||||
@ -396,6 +408,8 @@ fn fmt_instance(
|
||||
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
|
||||
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"),
|
||||
InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"),
|
||||
InstanceDef::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"),
|
||||
InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -638,6 +652,12 @@ impl<'tcx> Instance<'tcx> {
|
||||
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
|
||||
}
|
||||
|
||||
pub fn resolve_async_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> {
|
||||
let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, None);
|
||||
let args = tcx.mk_args(&[ty.into()]);
|
||||
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
pub fn fn_once_adapter_instance(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
@ -1797,7 +1797,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..) => self.mir_shims(instance),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1276,7 +1276,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
||||
|
||||
fn pretty_print_inherent_projection(
|
||||
&mut self,
|
||||
alias_ty: &ty::AliasTy<'tcx>,
|
||||
alias_ty: ty::AliasTy<'tcx>,
|
||||
) -> Result<(), PrintError> {
|
||||
let def_key = self.tcx().def_key(alias_ty.def_id);
|
||||
self.path_generic_args(
|
||||
@ -3204,7 +3204,7 @@ define_print_and_forward_display! {
|
||||
|
||||
ty::AliasTy<'tcx> {
|
||||
if let DefKind::Impl { of_trait: false } = cx.tcx().def_kind(cx.tcx().parent(self.def_id)) {
|
||||
p!(pretty_print_inherent_projection(self))
|
||||
p!(pretty_print_inherent_projection(*self))
|
||||
} else {
|
||||
// If we're printing verbosely, or don't want to invoke queries
|
||||
// (`is_impl_trait_in_trait`), then fall back to printing the def path.
|
||||
|
@ -427,6 +427,7 @@ TrivialTypeTraversalImpls! {
|
||||
crate::mir::coverage::BlockMarkerId,
|
||||
crate::mir::coverage::CounterId,
|
||||
crate::mir::coverage::ExpressionId,
|
||||
crate::mir::coverage::ConditionId,
|
||||
crate::mir::Local,
|
||||
crate::mir::Promoted,
|
||||
crate::traits::Reveal,
|
||||
|
@ -24,6 +24,7 @@ use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
|
||||
use rustc_target::spec::abi::{self, Abi};
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
use std::ops::{ControlFlow, Deref, Range};
|
||||
use ty::util::IntTypeExt;
|
||||
|
||||
@ -1379,7 +1380,7 @@ impl<'tcx> ParamTy {
|
||||
Ty::new_param(tcx, self.index, self.name)
|
||||
}
|
||||
|
||||
pub fn span_from_generics(&self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span {
|
||||
pub fn span_from_generics(self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span {
|
||||
let generics = tcx.generics_of(item_with_generics);
|
||||
let type_param = generics.type_param(self, tcx);
|
||||
tcx.def_span(type_param.def_id)
|
||||
@ -2316,6 +2317,133 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type of the async destructor of this type.
|
||||
pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> {
|
||||
if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) {
|
||||
return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop)
|
||||
.instantiate_identity();
|
||||
}
|
||||
match *self.kind() {
|
||||
ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => {
|
||||
let assoc_items = tcx
|
||||
.associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None));
|
||||
Ty::new_projection(tcx, assoc_items[0], [self])
|
||||
}
|
||||
|
||||
ty::Array(elem_ty, _) | ty::Slice(elem_ty) => {
|
||||
let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice)
|
||||
.instantiate(tcx, &[elem_ty.into()]);
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
|
||||
.instantiate(tcx, &[dtor.into()])
|
||||
}
|
||||
|
||||
ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self
|
||||
.adt_async_destructor_ty(
|
||||
tcx,
|
||||
adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))),
|
||||
param_env,
|
||||
),
|
||||
ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env),
|
||||
ty::Closure(_, args) => self.adt_async_destructor_ty(
|
||||
tcx,
|
||||
iter::once(args.as_closure().upvar_tys()),
|
||||
param_env,
|
||||
),
|
||||
ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty(
|
||||
tcx,
|
||||
iter::once(args.as_coroutine_closure().upvar_tys()),
|
||||
param_env,
|
||||
),
|
||||
|
||||
ty::Adt(adt_def, _) => {
|
||||
assert!(adt_def.is_union());
|
||||
|
||||
let surface_drop = self.surface_async_dropper_ty(tcx, param_env).unwrap();
|
||||
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
|
||||
.instantiate(tcx, &[surface_drop.into()])
|
||||
}
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
|
||||
bug!("`async_destructor_ty` applied to unexpected type: {self:?}")
|
||||
}
|
||||
|
||||
_ => bug!("`async_destructor_ty` is not yet implemented for type: {self:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn adt_async_destructor_ty<I>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
variants: I,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
) -> Ty<'tcx>
|
||||
where
|
||||
I: Iterator + ExactSizeIterator,
|
||||
I::Item: IntoIterator<Item = Ty<'tcx>>,
|
||||
{
|
||||
debug_assert!(!self.is_async_destructor_noop(tcx, param_env));
|
||||
|
||||
let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer);
|
||||
let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain);
|
||||
|
||||
let noop =
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity();
|
||||
let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither);
|
||||
|
||||
let variants_dtor = variants
|
||||
.into_iter()
|
||||
.map(|variant| {
|
||||
variant
|
||||
.into_iter()
|
||||
.map(|ty| defer.instantiate(tcx, &[ty.into()]))
|
||||
.reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()]))
|
||||
.unwrap_or(noop)
|
||||
})
|
||||
.reduce(|other, matched| {
|
||||
either.instantiate(tcx, &[other.into(), matched.into(), self.into()])
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx, param_env) {
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain)
|
||||
.instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()])
|
||||
} else {
|
||||
variants_dtor
|
||||
};
|
||||
|
||||
Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse)
|
||||
.instantiate(tcx, &[dtor.into()])
|
||||
}
|
||||
|
||||
fn surface_async_dropper_ty(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if self.has_surface_async_drop(tcx, param_env) {
|
||||
Some(LangItem::SurfaceAsyncDropInPlace)
|
||||
} else if self.has_surface_drop(tcx, param_env) {
|
||||
Some(LangItem::AsyncDropSurfaceDropInPlace)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.map(|dropper| {
|
||||
Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()])
|
||||
})
|
||||
}
|
||||
|
||||
fn async_destructor_combinator(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
lang_item: LangItem,
|
||||
) -> ty::EarlyBinder<Ty<'tcx>> {
|
||||
tcx.fn_sig(tcx.require_lang_item(lang_item, None))
|
||||
.map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap())
|
||||
}
|
||||
|
||||
/// Returns the type of metadata for (potentially fat) pointers to this type,
|
||||
/// or the struct tail if the metadata type cannot be determined.
|
||||
pub fn ptr_metadata_ty_or_tail(
|
||||
|
@ -432,19 +432,19 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
.filter(|&(_, k)| {
|
||||
match k.unpack() {
|
||||
GenericArgKind::Lifetime(region) => match region.kind() {
|
||||
ty::ReEarlyParam(ref ebr) => {
|
||||
ty::ReEarlyParam(ebr) => {
|
||||
!impl_generics.region_param(ebr, self).pure_wrt_drop
|
||||
}
|
||||
// Error: not a region param
|
||||
_ => false,
|
||||
},
|
||||
GenericArgKind::Type(ty) => match ty.kind() {
|
||||
ty::Param(ref pt) => !impl_generics.type_param(pt, self).pure_wrt_drop,
|
||||
GenericArgKind::Type(ty) => match *ty.kind() {
|
||||
ty::Param(pt) => !impl_generics.type_param(pt, self).pure_wrt_drop,
|
||||
// Error: not a type param
|
||||
_ => false,
|
||||
},
|
||||
GenericArgKind::Const(ct) => match ct.kind() {
|
||||
ty::ConstKind::Param(ref pc) => {
|
||||
ty::ConstKind::Param(pc) => {
|
||||
!impl_generics.const_param(pc, self).pure_wrt_drop
|
||||
}
|
||||
// Error: not a const param
|
||||
@ -1303,6 +1303,98 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implements the `AsyncDrop`
|
||||
/// trait.
|
||||
pub fn has_surface_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.could_have_surface_async_drop() && tcx.has_surface_async_drop_raw(param_env.and(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type has `AsyncDrop`
|
||||
/// implementation.
|
||||
///
|
||||
/// Returning `false` means the type is known to not have `AsyncDrop`
|
||||
/// implementation. Returning `true` means nothing -- could be
|
||||
/// `AsyncDrop`, might not be.
|
||||
fn could_have_surface_async_drop(self) -> bool {
|
||||
!self.is_async_destructor_trivially_noop()
|
||||
&& !matches!(
|
||||
self.kind(),
|
||||
ty::Tuple(_)
|
||||
| ty::Slice(_)
|
||||
| ty::Array(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implements the `Drop`
|
||||
/// trait.
|
||||
pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
|
||||
self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type has `Drop` implementation.
|
||||
///
|
||||
/// Returning `false` means the type is known to not have `Drop`
|
||||
/// implementation. Returning `true` means nothing -- could be
|
||||
/// `Drop`, might not be.
|
||||
fn could_have_surface_drop(self) -> bool {
|
||||
!self.is_async_destructor_trivially_noop()
|
||||
&& !matches!(
|
||||
self.kind(),
|
||||
ty::Tuple(_)
|
||||
| ty::Slice(_)
|
||||
| ty::Array(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implement has noop async destructor.
|
||||
//
|
||||
// FIXME: implement optimization to make ADTs, which do not need drop,
|
||||
// to skip fields or to have noop async destructor.
|
||||
pub fn is_async_destructor_noop(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> bool {
|
||||
self.is_async_destructor_trivially_noop()
|
||||
|| if let ty::Adt(adt_def, _) = self.kind() {
|
||||
(adt_def.is_union() || adt_def.is_payloadfree())
|
||||
&& !self.has_surface_async_drop(tcx, param_env)
|
||||
&& !self.has_surface_drop(tcx, param_env)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type has noop async destructor.
|
||||
///
|
||||
/// Returning `true` means the type is known to have noop async destructor
|
||||
/// implementation. Returning `true` means nothing -- could be
|
||||
/// `Drop`, might not be.
|
||||
fn is_async_destructor_trivially_noop(self) -> bool {
|
||||
match self.kind() {
|
||||
ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Str
|
||||
| ty::Never
|
||||
| ty::Ref(..)
|
||||
| ty::RawPtr(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_) => true,
|
||||
ty::Tuple(tys) => tys.is_empty(),
|
||||
ty::Adt(adt_def, _) => adt_def.is_manually_drop(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely
|
||||
/// non-copy and *might* have a destructor attached; if it returns
|
||||
/// `false`, then `ty` definitely has no destructor (i.e., no drop glue).
|
||||
|
@ -97,6 +97,8 @@ mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
|
||||
.note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
|
||||
.label = dereference of raw pointer
|
||||
|
||||
mir_build_exceeds_mcdc_condition_num_limit = Conditions number of the decision ({$conditions_num}) exceeds limit ({$max_conditions_num}). MCDC analysis will not count this expression.
|
||||
|
||||
mir_build_extern_static_requires_unsafe =
|
||||
use of extern static is unsafe and requires unsafe block
|
||||
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
|
||||
|
@ -1,14 +1,20 @@
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
|
||||
use rustc_middle::mir::{self, BasicBlock, UnOp};
|
||||
use rustc_middle::thir::{ExprId, ExprKind, Thir};
|
||||
use rustc_middle::mir::coverage::{
|
||||
BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageKind, MCDCBranchSpan,
|
||||
MCDCDecisionSpan,
|
||||
};
|
||||
use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
|
||||
use rustc_middle::thir::{ExprId, ExprKind, LogicalOp, Thir};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::build::Builder;
|
||||
use crate::build::{Builder, CFG};
|
||||
use crate::errors::MCDCExceedsConditionNumLimit;
|
||||
|
||||
pub(crate) struct BranchInfoBuilder {
|
||||
/// Maps condition expressions to their enclosing `!`, for better instrumentation.
|
||||
@ -16,6 +22,10 @@ pub(crate) struct BranchInfoBuilder {
|
||||
|
||||
num_block_markers: usize,
|
||||
branch_spans: Vec<BranchSpan>,
|
||||
|
||||
mcdc_branch_spans: Vec<MCDCBranchSpan>,
|
||||
mcdc_decision_spans: Vec<MCDCDecisionSpan>,
|
||||
mcdc_state: Option<MCDCState>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -33,7 +43,14 @@ impl BranchInfoBuilder {
|
||||
/// is enabled and `def_id` represents a function that is eligible for coverage.
|
||||
pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Self> {
|
||||
if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) {
|
||||
Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] })
|
||||
Some(Self {
|
||||
nots: FxHashMap::default(),
|
||||
num_block_markers: 0,
|
||||
branch_spans: vec![],
|
||||
mcdc_branch_spans: vec![],
|
||||
mcdc_decision_spans: vec![],
|
||||
mcdc_state: MCDCState::new_if_enabled(tcx),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -79,21 +96,242 @@ impl BranchInfoBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_mcdc_condition_info(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'_>,
|
||||
true_marker: BlockMarkerId,
|
||||
false_marker: BlockMarkerId,
|
||||
) -> Option<ConditionInfo> {
|
||||
let mcdc_state = self.mcdc_state.as_mut()?;
|
||||
let (mut condition_info, decision_result) =
|
||||
mcdc_state.take_condition(true_marker, false_marker);
|
||||
if let Some(decision) = decision_result {
|
||||
match decision.conditions_num {
|
||||
0 => {
|
||||
unreachable!("Decision with no condition is not expected");
|
||||
}
|
||||
1..=MAX_CONDITIONS_NUM_IN_DECISION => {
|
||||
self.mcdc_decision_spans.push(decision);
|
||||
}
|
||||
_ => {
|
||||
// Do not generate mcdc mappings and statements for decisions with too many conditions.
|
||||
let rebase_idx = self.mcdc_branch_spans.len() - decision.conditions_num + 1;
|
||||
for branch in &mut self.mcdc_branch_spans[rebase_idx..] {
|
||||
branch.condition_info = None;
|
||||
}
|
||||
|
||||
// ConditionInfo of this branch shall also be reset.
|
||||
condition_info = None;
|
||||
|
||||
tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit {
|
||||
span: decision.span,
|
||||
conditions_num: decision.conditions_num,
|
||||
max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
condition_info
|
||||
}
|
||||
|
||||
fn add_two_way_branch<'tcx>(
|
||||
&mut self,
|
||||
cfg: &mut CFG<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
true_block: BasicBlock,
|
||||
false_block: BasicBlock,
|
||||
) {
|
||||
let true_marker = self.inject_block_marker(cfg, source_info, true_block);
|
||||
let false_marker = self.inject_block_marker(cfg, source_info, false_block);
|
||||
|
||||
self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker });
|
||||
}
|
||||
|
||||
fn next_block_marker_id(&mut self) -> BlockMarkerId {
|
||||
let id = BlockMarkerId::from_usize(self.num_block_markers);
|
||||
self.num_block_markers += 1;
|
||||
id
|
||||
}
|
||||
|
||||
fn inject_block_marker(
|
||||
&mut self,
|
||||
cfg: &mut CFG<'_>,
|
||||
source_info: SourceInfo,
|
||||
block: BasicBlock,
|
||||
) -> BlockMarkerId {
|
||||
let id = self.next_block_marker_id();
|
||||
|
||||
let marker_statement = mir::Statement {
|
||||
source_info,
|
||||
kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
|
||||
};
|
||||
cfg.push(block, marker_statement);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
|
||||
let Self { nots: _, num_block_markers, branch_spans } = self;
|
||||
let Self {
|
||||
nots: _,
|
||||
num_block_markers,
|
||||
branch_spans,
|
||||
mcdc_branch_spans,
|
||||
mcdc_decision_spans,
|
||||
mcdc_state: _,
|
||||
} = self;
|
||||
|
||||
if num_block_markers == 0 {
|
||||
assert!(branch_spans.is_empty());
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans }))
|
||||
Some(Box::new(mir::coverage::BranchInfo {
|
||||
num_block_markers,
|
||||
branch_spans,
|
||||
mcdc_branch_spans,
|
||||
mcdc_decision_spans,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen,
|
||||
/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge.
|
||||
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
|
||||
const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
|
||||
|
||||
struct MCDCState {
|
||||
/// To construct condition evaluation tree.
|
||||
decision_stack: VecDeque<ConditionInfo>,
|
||||
processing_decision: Option<MCDCDecisionSpan>,
|
||||
}
|
||||
|
||||
impl MCDCState {
|
||||
fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
|
||||
tcx.sess
|
||||
.instrument_coverage_mcdc()
|
||||
.then(|| Self { decision_stack: VecDeque::new(), processing_decision: None })
|
||||
}
|
||||
|
||||
// At first we assign ConditionIds for each sub expression.
|
||||
// If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS.
|
||||
//
|
||||
// Example: "x = (A && B) || (C && D) || (D && F)"
|
||||
//
|
||||
// Visit Depth1:
|
||||
// (A && B) || (C && D) || (D && F)
|
||||
// ^-------LHS--------^ ^-RHS--^
|
||||
// ID=1 ID=2
|
||||
//
|
||||
// Visit LHS-Depth2:
|
||||
// (A && B) || (C && D)
|
||||
// ^-LHS--^ ^-RHS--^
|
||||
// ID=1 ID=3
|
||||
//
|
||||
// Visit LHS-Depth3:
|
||||
// (A && B)
|
||||
// LHS RHS
|
||||
// ID=1 ID=4
|
||||
//
|
||||
// Visit RHS-Depth3:
|
||||
// (C && D)
|
||||
// LHS RHS
|
||||
// ID=3 ID=5
|
||||
//
|
||||
// Visit RHS-Depth2: (D && F)
|
||||
// LHS RHS
|
||||
// ID=2 ID=6
|
||||
//
|
||||
// Visit Depth1:
|
||||
// (A && B) || (C && D) || (D && F)
|
||||
// ID=1 ID=4 ID=3 ID=5 ID=2 ID=6
|
||||
//
|
||||
// A node ID of '0' always means MC/DC isn't being tracked.
|
||||
//
|
||||
// If a "next" node ID is '0', it means it's the end of the test vector.
|
||||
//
|
||||
// As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited.
|
||||
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
|
||||
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
|
||||
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
|
||||
let decision = match self.processing_decision.as_mut() {
|
||||
Some(decision) => {
|
||||
decision.span = decision.span.to(span);
|
||||
decision
|
||||
}
|
||||
None => self.processing_decision.insert(MCDCDecisionSpan {
|
||||
span,
|
||||
conditions_num: 0,
|
||||
end_markers: vec![],
|
||||
}),
|
||||
};
|
||||
|
||||
let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
|
||||
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
|
||||
decision.conditions_num += 1;
|
||||
ConditionId::from(decision.conditions_num)
|
||||
} else {
|
||||
parent_condition.condition_id
|
||||
};
|
||||
|
||||
decision.conditions_num += 1;
|
||||
let rhs_condition_id = ConditionId::from(decision.conditions_num);
|
||||
|
||||
let (lhs, rhs) = match op {
|
||||
LogicalOp::And => {
|
||||
let lhs = ConditionInfo {
|
||||
condition_id: lhs_id,
|
||||
true_next_id: rhs_condition_id,
|
||||
false_next_id: parent_condition.false_next_id,
|
||||
};
|
||||
let rhs = ConditionInfo {
|
||||
condition_id: rhs_condition_id,
|
||||
true_next_id: parent_condition.true_next_id,
|
||||
false_next_id: parent_condition.false_next_id,
|
||||
};
|
||||
(lhs, rhs)
|
||||
}
|
||||
LogicalOp::Or => {
|
||||
let lhs = ConditionInfo {
|
||||
condition_id: lhs_id,
|
||||
true_next_id: parent_condition.true_next_id,
|
||||
false_next_id: rhs_condition_id,
|
||||
};
|
||||
let rhs = ConditionInfo {
|
||||
condition_id: rhs_condition_id,
|
||||
true_next_id: parent_condition.true_next_id,
|
||||
false_next_id: parent_condition.false_next_id,
|
||||
};
|
||||
(lhs, rhs)
|
||||
}
|
||||
};
|
||||
// We visit expressions tree in pre-order, so place the left-hand side on the top.
|
||||
self.decision_stack.push_back(rhs);
|
||||
self.decision_stack.push_back(lhs);
|
||||
}
|
||||
|
||||
fn take_condition(
|
||||
&mut self,
|
||||
true_marker: BlockMarkerId,
|
||||
false_marker: BlockMarkerId,
|
||||
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
|
||||
let Some(condition_info) = self.decision_stack.pop_back() else {
|
||||
return (None, None);
|
||||
};
|
||||
let Some(decision) = self.processing_decision.as_mut() else {
|
||||
bug!("Processing decision should have been created before any conditions are taken");
|
||||
};
|
||||
if condition_info.true_next_id == ConditionId::NONE {
|
||||
decision.end_markers.push(true_marker);
|
||||
}
|
||||
if condition_info.false_next_id == ConditionId::NONE {
|
||||
decision.end_markers.push(false_marker);
|
||||
}
|
||||
|
||||
if self.decision_stack.is_empty() {
|
||||
(Some(condition_info), self.processing_decision.take())
|
||||
} else {
|
||||
(Some(condition_info), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +345,7 @@ impl Builder<'_, '_> {
|
||||
mut else_block: BasicBlock,
|
||||
) {
|
||||
// Bail out if branch coverage is not enabled for this function.
|
||||
let Some(branch_info) = self.coverage_branch_info.as_ref() else { return };
|
||||
let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
|
||||
|
||||
// If this condition expression is nested within one or more `!` expressions,
|
||||
// replace it with the enclosing `!` collected by `visit_unary_not`.
|
||||
@ -117,30 +355,34 @@ impl Builder<'_, '_> {
|
||||
std::mem::swap(&mut then_block, &mut else_block);
|
||||
}
|
||||
}
|
||||
let source_info = self.source_info(self.thir[expr_id].span);
|
||||
|
||||
// Now that we have `source_info`, we can upgrade to a &mut reference.
|
||||
let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut");
|
||||
let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
|
||||
|
||||
let mut inject_branch_marker = |block: BasicBlock| {
|
||||
let id = branch_info.next_block_marker_id();
|
||||
// Separate path for handling branches when MC/DC is enabled.
|
||||
if branch_info.mcdc_state.is_some() {
|
||||
let mut inject_block_marker =
|
||||
|block| branch_info.inject_block_marker(&mut self.cfg, source_info, block);
|
||||
let true_marker = inject_block_marker(then_block);
|
||||
let false_marker = inject_block_marker(else_block);
|
||||
let condition_info =
|
||||
branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker);
|
||||
branch_info.mcdc_branch_spans.push(MCDCBranchSpan {
|
||||
span: source_info.span,
|
||||
condition_info,
|
||||
true_marker,
|
||||
false_marker,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let marker_statement = mir::Statement {
|
||||
source_info,
|
||||
kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
|
||||
};
|
||||
self.cfg.push(block, marker_statement);
|
||||
branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
|
||||
}
|
||||
|
||||
id
|
||||
};
|
||||
|
||||
let true_marker = inject_branch_marker(then_block);
|
||||
let false_marker = inject_branch_marker(else_block);
|
||||
|
||||
branch_info.branch_spans.push(BranchSpan {
|
||||
span: source_info.span,
|
||||
true_marker,
|
||||
false_marker,
|
||||
});
|
||||
pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) {
|
||||
if let Some(branch_info) = self.coverage_branch_info.as_mut()
|
||||
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
|
||||
{
|
||||
mcdc_state.record_conditions(logical_op, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,11 +77,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
|
||||
this.visit_coverage_branch_operation(LogicalOp::And, expr_span);
|
||||
let lhs_then_block = unpack!(this.then_else_break_inner(block, lhs, args));
|
||||
let rhs_then_block = unpack!(this.then_else_break_inner(lhs_then_block, rhs, args));
|
||||
rhs_then_block.unit()
|
||||
}
|
||||
ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => {
|
||||
this.visit_coverage_branch_operation(LogicalOp::Or, expr_span);
|
||||
let local_scope = this.local_scope();
|
||||
let (lhs_success_block, failure_block) =
|
||||
this.in_if_then_scope(local_scope, expr_span, |this| {
|
||||
|
@ -818,6 +818,15 @@ pub struct NontrivialStructuralMatch<'tcx> {
|
||||
pub non_sm_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_exceeds_mcdc_condition_num_limit)]
|
||||
pub(crate) struct MCDCExceedsConditionNumLimit {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub conditions_num: usize,
|
||||
pub max_conditions_num: usize,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_pattern_not_covered, code = E0005)]
|
||||
pub(crate) struct PatternNotCovered<'s, 'tcx> {
|
||||
|
@ -9,7 +9,7 @@ mod tests;
|
||||
|
||||
use self::counters::{CounterIncrementSite, CoverageCounters};
|
||||
use self::graph::{BasicCoverageBlock, CoverageGraph};
|
||||
use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
|
||||
use self::spans::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans};
|
||||
|
||||
use crate::MirPass;
|
||||
|
||||
@ -100,9 +100,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
|
||||
&coverage_counters,
|
||||
);
|
||||
|
||||
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
|
||||
|
||||
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
||||
function_source_hash: hir_info.function_source_hash,
|
||||
num_counters: coverage_counters.num_counters(),
|
||||
mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
|
||||
expressions: coverage_counters.into_expressions(),
|
||||
mappings,
|
||||
}));
|
||||
@ -136,20 +139,47 @@ fn create_mappings<'tcx>(
|
||||
.as_term()
|
||||
};
|
||||
|
||||
coverage_spans
|
||||
.all_bcb_mappings()
|
||||
.filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| {
|
||||
let kind = match bcb_mapping_kind {
|
||||
let mut mappings = Vec::new();
|
||||
|
||||
mappings.extend(coverage_spans.mappings.iter().filter_map(
|
||||
|BcbMapping { kind: bcb_mapping_kind, span }| {
|
||||
let kind = match *bcb_mapping_kind {
|
||||
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
|
||||
BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch {
|
||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => {
|
||||
MappingKind::Branch {
|
||||
true_term: term_for_bcb(true_bcb),
|
||||
false_term: term_for_bcb(false_bcb),
|
||||
}
|
||||
}
|
||||
BcbMappingKind::MCDCBranch {
|
||||
true_bcb,
|
||||
false_bcb,
|
||||
condition_info: Some(mcdc_params),
|
||||
} => MappingKind::MCDCBranch {
|
||||
true_term: term_for_bcb(true_bcb),
|
||||
false_term: term_for_bcb(false_bcb),
|
||||
mcdc_params,
|
||||
},
|
||||
BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => {
|
||||
MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num })
|
||||
}
|
||||
};
|
||||
let code_region = make_code_region(source_map, file_name, *span, body_span)?;
|
||||
Some(Mapping { kind, code_region })
|
||||
},
|
||||
));
|
||||
|
||||
mappings.extend(coverage_spans.branch_pairs.iter().filter_map(
|
||||
|&BcbBranchPair { span, true_bcb, false_bcb }| {
|
||||
let true_term = term_for_bcb(true_bcb);
|
||||
let false_term = term_for_bcb(false_bcb);
|
||||
let kind = MappingKind::Branch { true_term, false_term };
|
||||
let code_region = make_code_region(source_map, file_name, span, body_span)?;
|
||||
Some(Mapping { kind, code_region })
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
));
|
||||
|
||||
mappings
|
||||
}
|
||||
|
||||
/// For each BCB node or BCB edge that has an associated coverage counter,
|
||||
@ -204,6 +234,55 @@ fn inject_coverage_statements<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// For each conditions inject statements to update condition bitmap after it has been evaluated.
|
||||
/// For each decision inject statements to update test vector bitmap after it has been evaluated.
|
||||
fn inject_mcdc_statements<'tcx>(
|
||||
mir_body: &mut mir::Body<'tcx>,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
coverage_spans: &CoverageSpans,
|
||||
) {
|
||||
if coverage_spans.test_vector_bitmap_bytes() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject test vector update first because `inject_statement` always insert new statement at head.
|
||||
for (end_bcbs, bitmap_idx) in
|
||||
coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind {
|
||||
BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => {
|
||||
Some((end_bcbs, *bitmap_idx))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
for end in end_bcbs {
|
||||
let end_bb = basic_coverage_blocks[*end].leader_bb();
|
||||
inject_statement(mir_body, CoverageKind::TestVectorBitmapUpdate { bitmap_idx }, end_bb);
|
||||
}
|
||||
}
|
||||
|
||||
for (true_bcb, false_bcb, condition_id) in
|
||||
coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind {
|
||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => {
|
||||
Some((true_bcb, false_bcb, condition_info?.condition_id))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
let true_bb = basic_coverage_blocks[true_bcb].leader_bb();
|
||||
inject_statement(
|
||||
mir_body,
|
||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: true },
|
||||
true_bb,
|
||||
);
|
||||
let false_bb = basic_coverage_blocks[false_bcb].leader_bb();
|
||||
inject_statement(
|
||||
mir_body,
|
||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: false },
|
||||
false_bb,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given two basic blocks that have a control-flow edge between them, creates
|
||||
/// and returns a new block that sits between those blocks.
|
||||
fn inject_edge_counter_basic_block(
|
||||
|
@ -61,7 +61,17 @@ fn coverage_ids_info<'tcx>(
|
||||
.max()
|
||||
.unwrap_or(CounterId::ZERO);
|
||||
|
||||
CoverageIdsInfo { max_counter_id }
|
||||
let mcdc_bitmap_bytes = mir_body
|
||||
.coverage_branch_info
|
||||
.as_deref()
|
||||
.map(|info| {
|
||||
info.mcdc_decision_spans
|
||||
.iter()
|
||||
.fold(0, |acc, decision| acc + (1_u32 << decision.conditions_num).div_ceil(8))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
CoverageIdsInfo { max_counter_id, mcdc_bitmap_bytes }
|
||||
}
|
||||
|
||||
fn all_coverage_in_mir_body<'a, 'tcx>(
|
||||
|
@ -1,7 +1,9 @@
|
||||
use rustc_data_structures::graph::DirectedGraph;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::coverage::ConditionInfo;
|
||||
use rustc_span::{BytePos, Span};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
|
||||
use crate::coverage::spans::from_mir::SpanFromMir;
|
||||
@ -9,12 +11,24 @@ use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
mod from_mir;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) enum BcbMappingKind {
|
||||
/// Associates an ordinary executable code span with its corresponding BCB.
|
||||
Code(BasicCoverageBlock),
|
||||
/// Associates a branch span with BCBs for its true and false arms.
|
||||
Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
|
||||
|
||||
// Ordinary branch mappings are stored separately, so they don't have a
|
||||
// variant in this enum.
|
||||
//
|
||||
/// Associates a mcdc branch span with condition info besides fields for normal branch.
|
||||
MCDCBranch {
|
||||
true_bcb: BasicCoverageBlock,
|
||||
false_bcb: BasicCoverageBlock,
|
||||
/// If `None`, this actually represents a normal branch mapping inserted
|
||||
/// for code that was too complex for MC/DC.
|
||||
condition_info: Option<ConditionInfo>,
|
||||
},
|
||||
/// Associates a mcdc decision with its join BCB.
|
||||
MCDCDecision { end_bcbs: BTreeSet<BasicCoverageBlock>, bitmap_idx: u32, conditions_num: u16 },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -23,9 +37,21 @@ pub(super) struct BcbMapping {
|
||||
pub(super) span: Span,
|
||||
}
|
||||
|
||||
/// This is separate from [`BcbMappingKind`] to help prepare for larger changes
|
||||
/// that will be needed for improved branch coverage in the future.
|
||||
/// (See <https://github.com/rust-lang/rust/pull/124217>.)
|
||||
#[derive(Debug)]
|
||||
pub(super) struct BcbBranchPair {
|
||||
pub(super) span: Span,
|
||||
pub(super) true_bcb: BasicCoverageBlock,
|
||||
pub(super) false_bcb: BasicCoverageBlock,
|
||||
}
|
||||
|
||||
pub(super) struct CoverageSpans {
|
||||
bcb_has_mappings: BitSet<BasicCoverageBlock>,
|
||||
mappings: Vec<BcbMapping>,
|
||||
pub(super) mappings: Vec<BcbMapping>,
|
||||
pub(super) branch_pairs: Vec<BcbBranchPair>,
|
||||
test_vector_bitmap_bytes: u32,
|
||||
}
|
||||
|
||||
impl CoverageSpans {
|
||||
@ -33,8 +59,8 @@ impl CoverageSpans {
|
||||
self.bcb_has_mappings.contains(bcb)
|
||||
}
|
||||
|
||||
pub(super) fn all_bcb_mappings(&self) -> impl Iterator<Item = &BcbMapping> {
|
||||
self.mappings.iter()
|
||||
pub(super) fn test_vector_bitmap_bytes(&self) -> u32 {
|
||||
self.test_vector_bitmap_bytes
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +74,7 @@ pub(super) fn generate_coverage_spans(
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Option<CoverageSpans> {
|
||||
let mut mappings = vec![];
|
||||
let mut branch_pairs = vec![];
|
||||
|
||||
if hir_info.is_async_fn {
|
||||
// An async function desugars into a function that returns a future,
|
||||
@ -69,14 +96,20 @@ pub(super) fn generate_coverage_spans(
|
||||
BcbMapping { kind: BcbMappingKind::Code(bcb), span }
|
||||
}));
|
||||
|
||||
mappings.extend(from_mir::extract_branch_mappings(
|
||||
branch_pairs.extend(from_mir::extract_branch_pairs(
|
||||
mir_body,
|
||||
hir_info,
|
||||
basic_coverage_blocks,
|
||||
));
|
||||
|
||||
mappings.extend(from_mir::extract_mcdc_mappings(
|
||||
mir_body,
|
||||
hir_info.body_span,
|
||||
basic_coverage_blocks,
|
||||
));
|
||||
}
|
||||
|
||||
if mappings.is_empty() {
|
||||
if mappings.is_empty() && branch_pairs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -85,17 +118,29 @@ pub(super) fn generate_coverage_spans(
|
||||
let mut insert = |bcb| {
|
||||
bcb_has_mappings.insert(bcb);
|
||||
};
|
||||
for &BcbMapping { kind, span: _ } in &mappings {
|
||||
match kind {
|
||||
let mut test_vector_bitmap_bytes = 0;
|
||||
for BcbMapping { kind, span: _ } in &mappings {
|
||||
match *kind {
|
||||
BcbMappingKind::Code(bcb) => insert(bcb),
|
||||
BcbMappingKind::Branch { true_bcb, false_bcb } => {
|
||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => {
|
||||
insert(true_bcb);
|
||||
insert(false_bcb);
|
||||
}
|
||||
BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => {
|
||||
// `bcb_has_mappings` is used for inject coverage counters
|
||||
// but they are not needed for decision BCBs.
|
||||
// While the length of test vector bitmap should be calculated here.
|
||||
test_vector_bitmap_bytes = test_vector_bitmap_bytes
|
||||
.max(bitmap_idx + (1_u32 << conditions_num as u32).div_ceil(8));
|
||||
}
|
||||
}
|
||||
}
|
||||
for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs {
|
||||
insert(true_bcb);
|
||||
insert(false_bcb);
|
||||
}
|
||||
|
||||
Some(CoverageSpans { bcb_has_mappings, mappings })
|
||||
Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes })
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,7 +1,9 @@
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
|
||||
use rustc_middle::mir::coverage::{
|
||||
BlockMarkerId, BranchSpan, CoverageKind, MCDCBranchSpan, MCDCDecisionSpan,
|
||||
};
|
||||
use rustc_middle::mir::{
|
||||
self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
|
||||
TerminatorKind,
|
||||
@ -11,7 +13,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||
use crate::coverage::graph::{
|
||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||
};
|
||||
use crate::coverage::spans::{BcbMapping, BcbMappingKind};
|
||||
use crate::coverage::spans::{BcbBranchPair, BcbMapping, BcbMappingKind};
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
/// Traverses the MIR body to produce an initial collection of coverage-relevant
|
||||
@ -227,7 +229,10 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
|
||||
|
||||
// These coverage statements should not exist prior to coverage instrumentation.
|
||||
StatementKind::Coverage(
|
||||
CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. },
|
||||
CoverageKind::CounterIncrement { .. }
|
||||
| CoverageKind::ExpressionUsed { .. }
|
||||
| CoverageKind::CondBitmapUpdate { .. }
|
||||
| CoverageKind::TestVectorBitmapUpdate { .. },
|
||||
) => bug!(
|
||||
"Unexpected coverage statement found during coverage instrumentation: {statement:?}"
|
||||
),
|
||||
@ -361,15 +366,10 @@ impl SpanFromMir {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn extract_branch_mappings(
|
||||
fn resolve_block_markers(
|
||||
branch_info: &mir::coverage::BranchInfo,
|
||||
mir_body: &mir::Body<'_>,
|
||||
body_span: Span,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Vec<BcbMapping> {
|
||||
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
|
||||
let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
|
||||
None,
|
||||
branch_info.num_block_markers,
|
||||
@ -384,6 +384,24 @@ pub(super) fn extract_branch_mappings(
|
||||
}
|
||||
}
|
||||
|
||||
block_markers
|
||||
}
|
||||
|
||||
// FIXME: There is currently a lot of redundancy between
|
||||
// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so
|
||||
// that they can each be modified without interfering with the other, but in
|
||||
// the long term we should try to bring them together again when branch coverage
|
||||
// and MC/DC coverage support are more mature.
|
||||
|
||||
pub(super) fn extract_branch_pairs(
|
||||
mir_body: &mir::Body<'_>,
|
||||
hir_info: &ExtractedHirInfo,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Vec<BcbBranchPair> {
|
||||
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] };
|
||||
|
||||
let block_markers = resolve_block_markers(branch_info, mir_body);
|
||||
|
||||
branch_info
|
||||
.branch_spans
|
||||
.iter()
|
||||
@ -393,7 +411,8 @@ pub(super) fn extract_branch_mappings(
|
||||
if !raw_span.ctxt().outer_expn_data().is_root() {
|
||||
return None;
|
||||
}
|
||||
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
|
||||
let (span, _) =
|
||||
unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?;
|
||||
|
||||
let bcb_from_marker =
|
||||
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
|
||||
@ -401,7 +420,75 @@ pub(super) fn extract_branch_mappings(
|
||||
let true_bcb = bcb_from_marker(true_marker)?;
|
||||
let false_bcb = bcb_from_marker(false_marker)?;
|
||||
|
||||
Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span })
|
||||
Some(BcbBranchPair { span, true_bcb, false_bcb })
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(super) fn extract_mcdc_mappings(
|
||||
mir_body: &mir::Body<'_>,
|
||||
body_span: Span,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Vec<BcbMapping> {
|
||||
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let block_markers = resolve_block_markers(branch_info, mir_body);
|
||||
|
||||
let bcb_from_marker =
|
||||
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
|
||||
|
||||
let check_branch_bcb =
|
||||
|raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| {
|
||||
// For now, ignore any branch span that was introduced by
|
||||
// expansion. This makes things like assert macros less noisy.
|
||||
if !raw_span.ctxt().outer_expn_data().is_root() {
|
||||
return None;
|
||||
}
|
||||
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
|
||||
|
||||
let true_bcb = bcb_from_marker(true_marker)?;
|
||||
let false_bcb = bcb_from_marker(false_marker)?;
|
||||
Some((span, true_bcb, false_bcb))
|
||||
};
|
||||
|
||||
let mcdc_branch_filter_map =
|
||||
|&MCDCBranchSpan { span: raw_span, true_marker, false_marker, condition_info }| {
|
||||
check_branch_bcb(raw_span, true_marker, false_marker).map(
|
||||
|(span, true_bcb, false_bcb)| BcbMapping {
|
||||
kind: BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info },
|
||||
span,
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
let mut next_bitmap_idx = 0;
|
||||
|
||||
let decision_filter_map = |decision: &MCDCDecisionSpan| {
|
||||
let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
|
||||
|
||||
let end_bcbs = decision
|
||||
.end_markers
|
||||
.iter()
|
||||
.map(|&marker| bcb_from_marker(marker))
|
||||
.collect::<Option<_>>()?;
|
||||
|
||||
let bitmap_idx = next_bitmap_idx;
|
||||
next_bitmap_idx += (1_u32 << decision.conditions_num).div_ceil(8);
|
||||
|
||||
Some(BcbMapping {
|
||||
kind: BcbMappingKind::MCDCDecision {
|
||||
end_bcbs,
|
||||
bitmap_idx,
|
||||
conditions_num: decision.conditions_num as u16,
|
||||
},
|
||||
span,
|
||||
})
|
||||
};
|
||||
|
||||
std::iter::empty()
|
||||
.chain(branch_info.mcdc_branch_spans.iter().filter_map(mcdc_branch_filter_map))
|
||||
.chain(branch_info.mcdc_decision_spans.iter().filter_map(decision_filter_map))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
@ -885,6 +885,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||
AggregateKind::Adt(did, ..) => tcx.def_kind(did) != DefKind::Enum,
|
||||
// Coroutines are never ZST, as they at least contain the implicit states.
|
||||
AggregateKind::Coroutine(..) => false,
|
||||
AggregateKind::RawPtr(..) => bug!("MIR for RawPtr aggregate must have 2 fields"),
|
||||
};
|
||||
|
||||
if is_zst {
|
||||
@ -910,6 +911,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||
}
|
||||
// Do not track unions.
|
||||
AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
|
||||
// FIXME: Do the extra work to GVN `from_raw_parts`
|
||||
AggregateKind::RawPtr(..) => return None,
|
||||
};
|
||||
|
||||
let fields: Option<Vec<_>> = fields
|
||||
|
@ -332,7 +332,8 @@ impl<'tcx> Inliner<'tcx> {
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..) => return Ok(()),
|
||||
}
|
||||
|
||||
if self.tcx.is_constructor(callee_def_id) {
|
||||
@ -1083,7 +1084,8 @@ fn try_instance_mir<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
instance: InstanceDef<'tcx>,
|
||||
) -> Result<&'tcx Body<'tcx>, &'static str> {
|
||||
if let ty::InstanceDef::DropGlue(_, Some(ty)) = instance
|
||||
if let ty::InstanceDef::DropGlue(_, Some(ty))
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) = instance
|
||||
&& let ty::Adt(def, args) = ty.kind()
|
||||
{
|
||||
let fields = def.all_fields();
|
||||
|
@ -94,8 +94,10 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
|
||||
| InstanceDef::CloneShim(..) => {}
|
||||
|
||||
// This shim does not call any other functions, thus there can be no recursion.
|
||||
InstanceDef::FnPtrAddrShim(..) => continue,
|
||||
InstanceDef::DropGlue(..) => {
|
||||
InstanceDef::FnPtrAddrShim(..) => {
|
||||
continue;
|
||||
}
|
||||
InstanceDef::DropGlue(..) | InstanceDef::AsyncDropGlueCtorShim(..) => {
|
||||
// FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
|
||||
// have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
|
||||
// needs some more analysis.
|
||||
|
@ -36,6 +36,7 @@ impl<'tcx> MirPass<'tcx> for InstSimplify {
|
||||
ctx.simplify_bool_cmp(&statement.source_info, rvalue);
|
||||
ctx.simplify_ref_deref(&statement.source_info, rvalue);
|
||||
ctx.simplify_len(&statement.source_info, rvalue);
|
||||
ctx.simplify_ptr_aggregate(&statement.source_info, rvalue);
|
||||
ctx.simplify_cast(rvalue);
|
||||
}
|
||||
_ => {}
|
||||
@ -58,8 +59,17 @@ struct InstSimplifyContext<'tcx, 'a> {
|
||||
|
||||
impl<'tcx> InstSimplifyContext<'tcx, '_> {
|
||||
fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
|
||||
self.should_simplify_custom(source_info, "Rvalue", rvalue)
|
||||
}
|
||||
|
||||
fn should_simplify_custom(
|
||||
&self,
|
||||
source_info: &SourceInfo,
|
||||
label: &str,
|
||||
value: impl std::fmt::Debug,
|
||||
) -> bool {
|
||||
self.tcx.consider_optimizing(|| {
|
||||
format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}")
|
||||
format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}")
|
||||
})
|
||||
}
|
||||
|
||||
@ -111,7 +121,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
|
||||
if a.const_.ty().is_bool() { a.const_.try_to_bool() } else { None }
|
||||
}
|
||||
|
||||
/// Transform "&(*a)" ==> "a".
|
||||
/// Transform `&(*a)` ==> `a`.
|
||||
fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
|
||||
if let Rvalue::Ref(_, _, place) = rvalue {
|
||||
if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
|
||||
@ -131,7 +141,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform "Len([_; N])" ==> "N".
|
||||
/// Transform `Len([_; N])` ==> `N`.
|
||||
fn simplify_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
|
||||
if let Rvalue::Len(ref place) = *rvalue {
|
||||
let place_ty = place.ty(self.local_decls, self.tcx).ty;
|
||||
@ -147,6 +157,30 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`.
|
||||
fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
|
||||
if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue
|
||||
{
|
||||
let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx);
|
||||
if meta_ty.is_unit() {
|
||||
// The mutable borrows we're holding prevent printing `rvalue` here
|
||||
if !self.should_simplify_custom(
|
||||
source_info,
|
||||
"Aggregate::RawPtr",
|
||||
(&pointee_ty, *mutability, &fields),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut fields = std::mem::take(fields);
|
||||
let _meta = fields.pop().unwrap();
|
||||
let data = fields.pop().unwrap();
|
||||
let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability);
|
||||
*rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
|
||||
if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue {
|
||||
let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
|
||||
|
@ -603,6 +603,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
AggregateKind::Adt(_, variant, _, _, _) => variant,
|
||||
AggregateKind::Array(_)
|
||||
| AggregateKind::Tuple
|
||||
| AggregateKind::RawPtr(_, _)
|
||||
| AggregateKind::Closure(_, _)
|
||||
| AggregateKind::Coroutine(_, _)
|
||||
| AggregateKind::CoroutineClosure(_, _) => VariantIdx::ZERO,
|
||||
|
@ -287,6 +287,34 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
|
||||
terminator.kind = TerminatorKind::Unreachable;
|
||||
}
|
||||
}
|
||||
sym::aggregate_raw_ptr => {
|
||||
let Ok([data, meta]) = <[_; 2]>::try_from(std::mem::take(args)) else {
|
||||
span_bug!(
|
||||
terminator.source_info.span,
|
||||
"Wrong number of arguments for aggregate_raw_ptr intrinsic",
|
||||
);
|
||||
};
|
||||
let target = target.unwrap();
|
||||
let pointer_ty = generic_args.type_at(0);
|
||||
let kind = if let ty::RawPtr(pointee_ty, mutability) = pointer_ty.kind() {
|
||||
AggregateKind::RawPtr(*pointee_ty, *mutability)
|
||||
} else {
|
||||
span_bug!(
|
||||
terminator.source_info.span,
|
||||
"Return type of aggregate_raw_ptr intrinsic must be a raw pointer",
|
||||
);
|
||||
};
|
||||
let fields = [data.node, meta.node];
|
||||
block.statements.push(Statement {
|
||||
source_info: terminator.source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
*destination,
|
||||
Rvalue::Aggregate(Box::new(kind), fields.into()),
|
||||
))),
|
||||
});
|
||||
|
||||
terminator.kind = TerminatorKind::Goto { target };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
||||
should_cleanup = true;
|
||||
continue;
|
||||
}
|
||||
if SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() {
|
||||
// unsound: https://github.com/rust-lang/rust/issues/124150
|
||||
if tcx.sess.opts.unstable_opts.unsound_mir_opts
|
||||
&& SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some()
|
||||
{
|
||||
should_cleanup = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ use crate::{
|
||||
use rustc_middle::mir::patch::MirPatch;
|
||||
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
|
||||
|
||||
mod async_destructor_ctor;
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
providers.mir_shims = make_shim;
|
||||
}
|
||||
@ -127,6 +129,9 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||
ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance),
|
||||
ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
|
||||
ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
|
||||
ty::InstanceDef::AsyncDropGlueCtorShim(def_id, ty) => {
|
||||
async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty)
|
||||
}
|
||||
ty::InstanceDef::Virtual(..) => {
|
||||
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
|
||||
}
|
||||
|
618
compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
Normal file
618
compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
Normal file
@ -0,0 +1,618 @@
|
||||
use std::iter;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_const_eval::interpret;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::mir::{
|
||||
BasicBlock, BasicBlockData, Body, CallSource, CastKind, Const, ConstOperand, ConstValue, Local,
|
||||
LocalDecl, MirSource, Operand, Place, PlaceElem, Rvalue, SourceInfo, Statement, StatementKind,
|
||||
Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE,
|
||||
};
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::util::Discr;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::source_map::respan;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use super::{local_decls_for_sig, new_body};
|
||||
|
||||
pub fn build_async_destructor_ctor_shim<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
) -> Body<'tcx> {
|
||||
debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
|
||||
|
||||
AsyncDestructorCtorShimBuilder::new(tcx, def_id, ty).build()
|
||||
}
|
||||
|
||||
/// Builder for async_drop_in_place shim. Functions as a stack machine
|
||||
/// to build up an expression using combinators. Stack contains pairs
|
||||
/// of locals and types. Combinator is a not yet instantiated pair of a
|
||||
/// function and a type, is considered to be an operator which consumes
|
||||
/// operands from the stack by instantiating its function and its type
|
||||
/// with operand types and moving locals into the function call. Top
|
||||
/// pair is considered to be the last operand.
|
||||
// FIXME: add mir-opt tests
|
||||
struct AsyncDestructorCtorShimBuilder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
self_ty: Option<Ty<'tcx>>,
|
||||
span: Span,
|
||||
source_info: SourceInfo,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
stack: Vec<Operand<'tcx>>,
|
||||
last_bb: BasicBlock,
|
||||
top_cleanup_bb: Option<BasicBlock>,
|
||||
|
||||
locals: IndexVec<Local, LocalDecl<'tcx>>,
|
||||
bbs: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum SurfaceDropKind {
|
||||
Async,
|
||||
Sync,
|
||||
}
|
||||
|
||||
impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
|
||||
const SELF_PTR: Local = Local::from_u32(1);
|
||||
const INPUT_COUNT: usize = 1;
|
||||
const MAX_STACK_LEN: usize = 2;
|
||||
|
||||
fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Option<Ty<'tcx>>) -> Self {
|
||||
let args = if let Some(ty) = self_ty {
|
||||
tcx.mk_args(&[ty.into()])
|
||||
} else {
|
||||
ty::GenericArgs::identity_for_item(tcx, def_id)
|
||||
};
|
||||
let sig = tcx.fn_sig(def_id).instantiate(tcx, args);
|
||||
let sig = tcx.instantiate_bound_regions_with_erased(sig);
|
||||
let span = tcx.def_span(def_id);
|
||||
|
||||
let source_info = SourceInfo::outermost(span);
|
||||
|
||||
debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT);
|
||||
let locals = local_decls_for_sig(&sig, span);
|
||||
|
||||
// Usual case: noop() + unwind resume + return
|
||||
let mut bbs = IndexVec::with_capacity(3);
|
||||
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
||||
AsyncDestructorCtorShimBuilder {
|
||||
tcx,
|
||||
def_id,
|
||||
self_ty,
|
||||
span,
|
||||
source_info,
|
||||
param_env,
|
||||
|
||||
stack: Vec::with_capacity(Self::MAX_STACK_LEN),
|
||||
last_bb: bbs.push(BasicBlockData::new(None)),
|
||||
top_cleanup_bb: match tcx.sess.panic_strategy() {
|
||||
PanicStrategy::Unwind => {
|
||||
// Don't drop input arg because it's just a pointer
|
||||
Some(bbs.push(BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::UnwindResume,
|
||||
}),
|
||||
is_cleanup: true,
|
||||
}))
|
||||
}
|
||||
PanicStrategy::Abort => None,
|
||||
},
|
||||
|
||||
locals,
|
||||
bbs,
|
||||
}
|
||||
}
|
||||
|
||||
fn build(self) -> Body<'tcx> {
|
||||
let (tcx, def_id, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else {
|
||||
return self.build_zst_output();
|
||||
};
|
||||
|
||||
let surface_drop_kind = || {
|
||||
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
||||
if self_ty.has_surface_async_drop(tcx, param_env) {
|
||||
Some(SurfaceDropKind::Async)
|
||||
} else if self_ty.has_surface_drop(tcx, param_env) {
|
||||
Some(SurfaceDropKind::Sync)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
match self_ty.kind() {
|
||||
ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty),
|
||||
ty::Slice(elem_ty) => self.build_slice(false, *elem_ty),
|
||||
|
||||
ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()),
|
||||
ty::Adt(adt_def, args) if adt_def.is_struct() => {
|
||||
let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args));
|
||||
self.build_chain(surface_drop_kind(), field_tys)
|
||||
}
|
||||
ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()),
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter())
|
||||
}
|
||||
|
||||
ty::Adt(adt_def, args) if adt_def.is_enum() => {
|
||||
self.build_enum(*adt_def, *args, surface_drop_kind())
|
||||
}
|
||||
|
||||
ty::Adt(adt_def, _) => {
|
||||
assert!(adt_def.is_union());
|
||||
match surface_drop_kind().unwrap() {
|
||||
SurfaceDropKind::Async => self.build_fused_async_surface(),
|
||||
SurfaceDropKind::Sync => self.build_fused_sync_surface(),
|
||||
}
|
||||
}
|
||||
|
||||
ty::Bound(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_))
|
||||
| ty::Param(_)
|
||||
| ty::Alias(..) => {
|
||||
bug!("Building async destructor for unexpected type: {self_ty:?}")
|
||||
}
|
||||
|
||||
_ => {
|
||||
bug!(
|
||||
"Building async destructor constructor shim is not yet implemented for type: {self_ty:?}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_enum(
|
||||
mut self,
|
||||
adt_def: ty::AdtDef<'tcx>,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
surface_drop: Option<SurfaceDropKind>,
|
||||
) -> Body<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let surface = match surface_drop {
|
||||
None => None,
|
||||
Some(kind) => {
|
||||
self.put_self();
|
||||
Some(match kind {
|
||||
SurfaceDropKind::Async => self.combine_async_surface(),
|
||||
SurfaceDropKind::Sync => self.combine_sync_surface(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let mut other = None;
|
||||
for (variant_idx, discr) in adt_def.discriminants(tcx) {
|
||||
let variant = adt_def.variant(variant_idx);
|
||||
|
||||
let mut chain = None;
|
||||
for (field_idx, field) in variant.fields.iter_enumerated() {
|
||||
let field_ty = field.ty(tcx, args);
|
||||
self.put_variant_field(variant.name, variant_idx, field_idx, field_ty);
|
||||
let defer = self.combine_defer(field_ty);
|
||||
chain = Some(match chain {
|
||||
None => defer,
|
||||
Some(chain) => self.combine_chain(chain, defer),
|
||||
})
|
||||
}
|
||||
let variant_dtor = chain.unwrap_or_else(|| self.put_noop());
|
||||
|
||||
other = Some(match other {
|
||||
None => variant_dtor,
|
||||
Some(other) => {
|
||||
self.put_self();
|
||||
self.put_discr(discr);
|
||||
self.combine_either(other, variant_dtor)
|
||||
}
|
||||
});
|
||||
}
|
||||
let variants_dtor = other.unwrap_or_else(|| self.put_noop());
|
||||
|
||||
let dtor = match surface {
|
||||
None => variants_dtor,
|
||||
Some(surface) => self.combine_chain(surface, variants_dtor),
|
||||
};
|
||||
self.combine_fuse(dtor);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_chain<I>(mut self, surface_drop: Option<SurfaceDropKind>, elem_tys: I) -> Body<'tcx>
|
||||
where
|
||||
I: Iterator<Item = Ty<'tcx>> + ExactSizeIterator,
|
||||
{
|
||||
let surface = match surface_drop {
|
||||
None => None,
|
||||
Some(kind) => {
|
||||
self.put_self();
|
||||
Some(match kind {
|
||||
SurfaceDropKind::Async => self.combine_async_surface(),
|
||||
SurfaceDropKind::Sync => self.combine_sync_surface(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let mut chain = None;
|
||||
for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) {
|
||||
self.put_field(field_idx, field_ty);
|
||||
let defer = self.combine_defer(field_ty);
|
||||
chain = Some(match chain {
|
||||
None => defer,
|
||||
Some(chain) => self.combine_chain(chain, defer),
|
||||
})
|
||||
}
|
||||
let chain = chain.unwrap_or_else(|| self.put_noop());
|
||||
|
||||
let dtor = match surface {
|
||||
None => chain,
|
||||
Some(surface) => self.combine_chain(surface, chain),
|
||||
};
|
||||
self.combine_fuse(dtor);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_zst_output(mut self) -> Body<'tcx> {
|
||||
self.put_zst_output();
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_fused_async_surface(mut self) -> Body<'tcx> {
|
||||
self.put_self();
|
||||
let surface = self.combine_async_surface();
|
||||
self.combine_fuse(surface);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_fused_sync_surface(mut self) -> Body<'tcx> {
|
||||
self.put_self();
|
||||
let surface = self.combine_sync_surface();
|
||||
self.combine_fuse(surface);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> {
|
||||
if is_array {
|
||||
self.put_array_as_slice(elem_ty)
|
||||
} else {
|
||||
self.put_self()
|
||||
}
|
||||
let dtor = self.combine_slice(elem_ty);
|
||||
self.combine_fuse(dtor);
|
||||
self.return_()
|
||||
}
|
||||
|
||||
fn put_zst_output(&mut self) {
|
||||
let return_ty = self.locals[RETURN_PLACE].ty;
|
||||
self.put_operand(Operand::Constant(Box::new(ConstOperand {
|
||||
span: self.span,
|
||||
user_ty: None,
|
||||
const_: Const::zero_sized(return_ty),
|
||||
})));
|
||||
}
|
||||
|
||||
/// Puts `to_drop: *mut Self` on top of the stack.
|
||||
fn put_self(&mut self) {
|
||||
self.put_operand(Operand::Copy(Self::SELF_PTR.into()))
|
||||
}
|
||||
|
||||
/// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]`
|
||||
/// on top of the stack.
|
||||
fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) {
|
||||
let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty));
|
||||
self.put_temp_rvalue(Rvalue::Cast(
|
||||
CastKind::PointerCoercion(PointerCoercion::Unsize),
|
||||
Operand::Copy(Self::SELF_PTR.into()),
|
||||
slice_ptr_ty,
|
||||
))
|
||||
}
|
||||
|
||||
/// If given Self is a struct puts `to_drop: *mut FieldTy` on top
|
||||
/// of the stack.
|
||||
fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) {
|
||||
let place = Place {
|
||||
local: Self::SELF_PTR,
|
||||
projection: self
|
||||
.tcx
|
||||
.mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]),
|
||||
};
|
||||
self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place))
|
||||
}
|
||||
|
||||
/// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
|
||||
/// the stack.
|
||||
fn put_variant_field(
|
||||
&mut self,
|
||||
variant_sym: Symbol,
|
||||
variant: VariantIdx,
|
||||
field: FieldIdx,
|
||||
field_ty: Ty<'tcx>,
|
||||
) {
|
||||
let place = Place {
|
||||
local: Self::SELF_PTR,
|
||||
projection: self.tcx.mk_place_elems(&[
|
||||
PlaceElem::Deref,
|
||||
PlaceElem::Downcast(Some(variant_sym), variant),
|
||||
PlaceElem::Field(field, field_ty),
|
||||
]),
|
||||
};
|
||||
self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place))
|
||||
}
|
||||
|
||||
/// If given Self is an enum puts `to_drop: *mut FieldTy` on top of
|
||||
/// the stack.
|
||||
fn put_discr(&mut self, discr: Discr<'tcx>) {
|
||||
let (size, _) = discr.ty.int_size_and_signed(self.tcx);
|
||||
self.put_operand(Operand::const_from_scalar(
|
||||
self.tcx,
|
||||
discr.ty,
|
||||
interpret::Scalar::from_uint(discr.val, size),
|
||||
self.span,
|
||||
));
|
||||
}
|
||||
|
||||
/// Puts `x: RvalueType` on top of the stack.
|
||||
fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) {
|
||||
let last_bb = &mut self.bbs[self.last_bb];
|
||||
debug_assert!(last_bb.terminator.is_none());
|
||||
let source_info = self.source_info;
|
||||
|
||||
let local_ty = rvalue.ty(&self.locals, self.tcx);
|
||||
// We need to create a new local to be able to "consume" it with
|
||||
// a combinator
|
||||
let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info));
|
||||
last_bb.statements.extend_from_slice(&[
|
||||
Statement { source_info, kind: StatementKind::StorageLive(local) },
|
||||
Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((local.into(), rvalue))),
|
||||
},
|
||||
]);
|
||||
|
||||
self.put_operand(Operand::Move(local.into()));
|
||||
}
|
||||
|
||||
/// Puts operand on top of the stack.
|
||||
fn put_operand(&mut self, operand: Operand<'tcx>) {
|
||||
if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
|
||||
let source_info = self.source_info;
|
||||
match &operand {
|
||||
Operand::Copy(_) | Operand::Constant(_) => {
|
||||
*top_cleanup_bb = self.bbs.push(BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::Goto { target: *top_cleanup_bb },
|
||||
}),
|
||||
is_cleanup: true,
|
||||
});
|
||||
}
|
||||
Operand::Move(place) => {
|
||||
let local = place.as_local().unwrap();
|
||||
*top_cleanup_bb = self.bbs.push(BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
terminator: Some(Terminator {
|
||||
source_info,
|
||||
kind: if self.locals[local].ty.needs_drop(self.tcx, self.param_env) {
|
||||
TerminatorKind::Drop {
|
||||
place: local.into(),
|
||||
target: *top_cleanup_bb,
|
||||
unwind: UnwindAction::Terminate(
|
||||
UnwindTerminateReason::InCleanup,
|
||||
),
|
||||
replace: false,
|
||||
}
|
||||
} else {
|
||||
TerminatorKind::Goto { target: *top_cleanup_bb }
|
||||
},
|
||||
}),
|
||||
is_cleanup: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
self.stack.push(operand);
|
||||
}
|
||||
|
||||
/// Puts `noop: async_drop::Noop` on top of the stack
|
||||
fn put_noop(&mut self) -> Ty<'tcx> {
|
||||
self.apply_combinator(0, LangItem::AsyncDropNoop, &[])
|
||||
}
|
||||
|
||||
fn combine_async_surface(&mut self) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.unwrap().into()])
|
||||
}
|
||||
|
||||
fn combine_sync_surface(&mut self) -> Ty<'tcx> {
|
||||
self.apply_combinator(
|
||||
1,
|
||||
LangItem::AsyncDropSurfaceDropInPlace,
|
||||
&[self.self_ty.unwrap().into()],
|
||||
)
|
||||
}
|
||||
|
||||
fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()])
|
||||
}
|
||||
|
||||
fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()])
|
||||
}
|
||||
|
||||
fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> {
|
||||
self.apply_combinator(
|
||||
4,
|
||||
LangItem::AsyncDropEither,
|
||||
&[other.into(), matched.into(), self.self_ty.unwrap().into()],
|
||||
)
|
||||
}
|
||||
|
||||
fn return_(mut self) -> Body<'tcx> {
|
||||
let last_bb = &mut self.bbs[self.last_bb];
|
||||
debug_assert!(last_bb.terminator.is_none());
|
||||
let source_info = self.source_info;
|
||||
|
||||
let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}",
|
||||
self.stack.len(),
|
||||
)
|
||||
};
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(ty) = self.self_ty {
|
||||
debug_assert_eq!(
|
||||
output.ty(&self.locals, self.tcx),
|
||||
ty.async_destructor_ty(self.tcx, self.param_env),
|
||||
"output async destructor types did not match for type: {ty:?}",
|
||||
);
|
||||
}
|
||||
|
||||
let dead_storage = match &output {
|
||||
Operand::Move(place) => Some(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::StorageDead(place.as_local().unwrap()),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
last_bb.statements.extend(
|
||||
iter::once(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((RETURN_PLACE.into(), Rvalue::Use(output)))),
|
||||
})
|
||||
.chain(dead_storage),
|
||||
);
|
||||
|
||||
last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return });
|
||||
|
||||
let source = MirSource::from_instance(ty::InstanceDef::AsyncDropGlueCtorShim(
|
||||
self.def_id,
|
||||
self.self_ty,
|
||||
));
|
||||
new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span)
|
||||
}
|
||||
|
||||
fn apply_combinator(
|
||||
&mut self,
|
||||
arity: usize,
|
||||
function: LangItem,
|
||||
args: &[ty::GenericArg<'tcx>],
|
||||
) -> Ty<'tcx> {
|
||||
let function = self.tcx.require_lang_item(function, Some(self.span));
|
||||
let operands_split = self
|
||||
.stack
|
||||
.len()
|
||||
.checked_sub(arity)
|
||||
.expect("async destructor ctor shim combinator tried to consume too many items");
|
||||
let operands = &self.stack[operands_split..];
|
||||
|
||||
let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied());
|
||||
let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap();
|
||||
#[cfg(debug_assertions)]
|
||||
operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| {
|
||||
let operand_ty = operand.ty(&self.locals, self.tcx);
|
||||
if operand_ty == *expected_ty {
|
||||
return;
|
||||
}
|
||||
|
||||
// If projection of Discriminant then compare with `Ty::discriminant_ty`
|
||||
if let ty::Alias(ty::AliasKind::Projection, ty::AliasTy { args, def_id, .. }) =
|
||||
expected_ty.kind()
|
||||
&& Some(*def_id) == self.tcx.lang_items().discriminant_type()
|
||||
&& args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_bug!(
|
||||
self.span,
|
||||
"Operand type and combinator argument type are not equal.
|
||||
operand_ty: {:?}
|
||||
argument_ty: {:?}
|
||||
",
|
||||
operand_ty,
|
||||
expected_ty
|
||||
);
|
||||
});
|
||||
|
||||
let target = self.bbs.push(BasicBlockData {
|
||||
statements: operands
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|o| {
|
||||
if let Operand::Move(Place { local, projection }) = o {
|
||||
assert!(projection.is_empty());
|
||||
Some(Statement {
|
||||
source_info: self.source_info,
|
||||
kind: StatementKind::StorageDead(*local),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
terminator: None,
|
||||
is_cleanup: false,
|
||||
});
|
||||
|
||||
let dest_ty = func_sig.output();
|
||||
let dest =
|
||||
self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable());
|
||||
|
||||
let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb {
|
||||
for _ in 0..arity {
|
||||
*top_cleanup_bb =
|
||||
self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap();
|
||||
}
|
||||
UnwindAction::Cleanup(*top_cleanup_bb)
|
||||
} else {
|
||||
UnwindAction::Unreachable
|
||||
};
|
||||
|
||||
let last_bb = &mut self.bbs[self.last_bb];
|
||||
debug_assert!(last_bb.terminator.is_none());
|
||||
last_bb.statements.push(Statement {
|
||||
source_info: self.source_info,
|
||||
kind: StatementKind::StorageLive(dest),
|
||||
});
|
||||
last_bb.terminator = Some(Terminator {
|
||||
source_info: self.source_info,
|
||||
kind: TerminatorKind::Call {
|
||||
func: Operand::Constant(Box::new(ConstOperand {
|
||||
span: self.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(ConstValue::ZeroSized, func_ty),
|
||||
})),
|
||||
destination: dest.into(),
|
||||
target: Some(target),
|
||||
unwind,
|
||||
call_source: CallSource::Misc,
|
||||
fn_span: self.span,
|
||||
args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(),
|
||||
},
|
||||
});
|
||||
|
||||
self.put_operand(Operand::Move(dest.into()));
|
||||
self.last_bb = target;
|
||||
|
||||
dest_ty
|
||||
}
|
||||
}
|
@ -966,13 +966,14 @@ fn visit_instance_use<'tcx>(
|
||||
ty::InstanceDef::ThreadLocalShim(..) => {
|
||||
bug!("{:?} being reified", instance);
|
||||
}
|
||||
ty::InstanceDef::DropGlue(_, None) => {
|
||||
ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) => {
|
||||
// Don't need to emit noop drop glue if we are calling directly.
|
||||
if !is_direct_call {
|
||||
output.push(create_fn_mono_item(tcx, instance, source));
|
||||
}
|
||||
}
|
||||
ty::InstanceDef::DropGlue(_, Some(_))
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_))
|
||||
| ty::InstanceDef::VTableShim(..)
|
||||
| ty::InstanceDef::ReifyShim(..)
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
|
@ -625,7 +625,8 @@ fn characteristic_def_id_of_mono_item<'tcx>(
|
||||
| ty::InstanceDef::Virtual(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..) => return None,
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::AsyncDropGlueCtorShim(..) => return None,
|
||||
};
|
||||
|
||||
// If this is a method, we want to put it into the same module as
|
||||
@ -769,7 +770,9 @@ fn mono_item_visibility<'tcx>(
|
||||
};
|
||||
|
||||
let def_id = match instance.def {
|
||||
InstanceDef::Item(def_id) | InstanceDef::DropGlue(def_id, Some(_)) => def_id,
|
||||
InstanceDef::Item(def_id)
|
||||
| InstanceDef::DropGlue(def_id, Some(_))
|
||||
| InstanceDef::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id,
|
||||
|
||||
// We match the visibility of statics here
|
||||
InstanceDef::ThreadLocalShim(def_id) => {
|
||||
@ -786,6 +789,7 @@ fn mono_item_visibility<'tcx>(
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::AsyncDropGlueCtorShim(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
|
||||
};
|
||||
|
@ -8,7 +8,6 @@
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(try_blocks)]
|
||||
|
@ -3,7 +3,6 @@
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(rustc::potential_query_instability, unused_parens)]
|
||||
|
@ -459,7 +459,8 @@ impl<D: Deps> DepGraph<D> {
|
||||
}
|
||||
TaskDepsRef::Ignore => return,
|
||||
TaskDepsRef::Forbid => {
|
||||
panic!("Illegal read of: {dep_node_index:?}")
|
||||
// Reading is forbidden in this context. ICE with a useful error message.
|
||||
panic_on_forbidden_read(data, dep_node_index)
|
||||
}
|
||||
};
|
||||
let task_deps = &mut *task_deps;
|
||||
@ -1366,3 +1367,45 @@ pub(crate) fn print_markframe_trace<D: Deps>(graph: &DepGraph<D>, frame: Option<
|
||||
|
||||
eprintln!("end of try_mark_green dep node stack");
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn panic_on_forbidden_read<D: Deps>(data: &DepGraphData<D>, dep_node_index: DepNodeIndex) -> ! {
|
||||
// We have to do an expensive reverse-lookup of the DepNode that
|
||||
// corresponds to `dep_node_index`, but that's OK since we are about
|
||||
// to ICE anyway.
|
||||
let mut dep_node = None;
|
||||
|
||||
// First try to find the dep node among those that already existed in the
|
||||
// previous session
|
||||
for (prev_index, index) in data.current.prev_index_to_index.lock().iter_enumerated() {
|
||||
if index == &Some(dep_node_index) {
|
||||
dep_node = Some(data.previous.index_to_node(prev_index));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if dep_node.is_none() {
|
||||
// Try to find it among the new nodes
|
||||
for shard in data.current.new_node_to_index.lock_shards() {
|
||||
if let Some((node, _)) = shard.iter().find(|(_, index)| **index == dep_node_index) {
|
||||
dep_node = Some(*node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dep_node = dep_node.map_or_else(
|
||||
|| format!("with index {:?}", dep_node_index),
|
||||
|dep_node| format!("`{:?}`", dep_node),
|
||||
);
|
||||
|
||||
panic!(
|
||||
"Error: trying to record dependency on DepNode {dep_node} in a \
|
||||
context that does not allow it (e.g. during query deserialization). \
|
||||
The most common case of recording a dependency on a DepNode `foo` is \
|
||||
when the correspondng query `foo` is invoked. Invoking queries is not \
|
||||
allowed as part of loading something from the incremental on-disk cache. \
|
||||
See <https://github.com/rust-lang/rust/pull/91919>."
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![feature(assert_matches)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(hash_raw_entry)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(let_chains)]
|
||||
|
@ -11,7 +11,6 @@
|
||||
#![cfg_attr(bootstrap, feature(associated_type_bounds))]
|
||||
#![feature(const_option)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(inline_const)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(never_type)]
|
||||
|
@ -148,6 +148,8 @@ pub enum InstrumentCoverage {
|
||||
pub struct CoverageOptions {
|
||||
/// Add branch coverage instrumentation.
|
||||
pub branch: bool,
|
||||
/// Add mcdc coverage instrumentation.
|
||||
pub mcdc: bool,
|
||||
}
|
||||
|
||||
/// Settings for `-Z instrument-xray` flag.
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(option_get_or_insert_default)]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user