Auto merge of #116671 - matthiaskrgr:rollup-b41rw92, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - #116593 (Add unstable book page for the no-jump-tables codegen option)
 - #116625 (`rustc_hir_pretty` cleanups)
 - #116642 (Handle several `#[diagnostic::on_unimplemented]` attributes correctly)
 - #116654 (coverage: Clarify loop-edge detection and graph traversal)
 - #116669 (Fix mips platform support entries.)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-10-12 16:39:36 +00:00
commit 672fad9b86
12 changed files with 333 additions and 307 deletions

View File

@ -52,8 +52,6 @@ pub struct NoAnn;
impl PpAnn for NoAnn {}
pub const NO_ANN: &dyn PpAnn = &NoAnn;
/// Identical to the `PpAnn` implementation for `hir::Crate`,
/// except it avoids creating a dependency on the whole crate.
impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> {
fn nested(&self, state: &mut State<'_>, nested: Nested) {
match nested {
@ -75,7 +73,11 @@ pub struct State<'a> {
}
impl<'a> State<'a> {
pub fn print_node(&mut self, node: Node<'_>) {
fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] {
(self.attrs)(id)
}
fn print_node(&mut self, node: Node<'_>) {
match node {
Node::Param(a) => self.print_param(a),
Node::Item(a) => self.print_item(a),
@ -144,7 +146,7 @@ impl<'a> PrintState<'a> for State<'a> {
}
}
pub const INDENT_UNIT: isize = 4;
const INDENT_UNIT: isize = 4;
/// Requires you to pass an input filename and reader so that
/// it can scan the input text for comments to copy forward.
@ -156,7 +158,12 @@ pub fn print_crate<'a>(
attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute],
ann: &'a dyn PpAnn,
) -> String {
let mut s = State::new_from_input(sm, filename, input, attrs, ann);
let mut s = State {
s: pp::Printer::new(),
comments: Some(Comments::new(sm, filename, input)),
attrs,
ann,
};
// When printing the AST, we sometimes need to inject `#[no_std]` here.
// Since you can't compile the HIR, it's not necessary.
@ -166,28 +173,7 @@ pub fn print_crate<'a>(
s.s.eof()
}
impl<'a> State<'a> {
pub fn new_from_input(
sm: &'a SourceMap,
filename: FileName,
input: String,
attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute],
ann: &'a dyn PpAnn,
) -> State<'a> {
State {
s: pp::Printer::new(),
comments: Some(Comments::new(sm, filename, input)),
attrs,
ann,
}
}
fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] {
(self.attrs)(id)
}
}
pub fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
where
F: FnOnce(&mut State<'_>),
{
@ -196,52 +182,20 @@ where
printer.s.eof()
}
pub fn generic_params_to_string(generic_params: &[GenericParam<'_>]) -> String {
to_string(NO_ANN, |s| s.print_generic_params(generic_params))
}
pub fn bounds_to_string<'b>(bounds: impl IntoIterator<Item = &'b hir::GenericBound<'b>>) -> String {
to_string(NO_ANN, |s| s.print_bounds("", bounds))
}
pub fn ty_to_string(ty: &hir::Ty<'_>) -> String {
to_string(NO_ANN, |s| s.print_type(ty))
}
pub fn path_segment_to_string(segment: &hir::PathSegment<'_>) -> String {
to_string(NO_ANN, |s| s.print_path_segment(segment))
}
pub fn path_to_string(segment: &hir::Path<'_>) -> String {
to_string(NO_ANN, |s| s.print_path(segment, false))
}
pub fn qpath_to_string(segment: &hir::QPath<'_>) -> String {
to_string(NO_ANN, |s| s.print_qpath(segment, false))
}
pub fn fn_to_string(
decl: &hir::FnDecl<'_>,
header: hir::FnHeader,
name: Option<Symbol>,
generics: &hir::Generics<'_>,
arg_names: &[Ident],
body_id: Option<hir::BodyId>,
) -> String {
to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, arg_names, body_id))
}
pub fn enum_def_to_string(
enum_definition: &hir::EnumDef<'_>,
generics: &hir::Generics<'_>,
name: Symbol,
span: rustc_span::Span,
) -> String {
to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span))
pub fn pat_to_string(pat: &hir::Pat<'_>) -> String {
to_string(NO_ANN, |s| s.print_pat(pat))
}
impl<'a> State<'a> {
pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
self.maybe_print_comment(span.hi());
self.break_offset_if_not_bol(1, -INDENT_UNIT);
self.word("}");
@ -250,11 +204,11 @@ impl<'a> State<'a> {
}
}
pub fn bclose(&mut self, span: rustc_span::Span) {
fn bclose(&mut self, span: rustc_span::Span) {
self.bclose_maybe_open(span, true)
}
pub fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
where
F: FnMut(&mut State<'_>, &T),
G: FnMut(&T) -> rustc_span::Span,
@ -275,25 +229,25 @@ impl<'a> State<'a> {
self.end();
}
pub fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) {
fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) {
self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span);
}
pub fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
self.print_inner_attributes(attrs);
for &item_id in _mod.item_ids {
self.ann.nested(self, Nested::Item(item_id));
}
}
pub fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) {
fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) {
if !lifetime.is_elided() {
self.print_lifetime(lifetime);
self.nbsp();
}
}
pub fn print_type(&mut self, ty: &hir::Ty<'_>) {
fn print_type(&mut self, ty: &hir::Ty<'_>) {
self.maybe_print_comment(ty.span.lo());
self.ibox(0);
match ty.kind {
@ -371,7 +325,7 @@ impl<'a> State<'a> {
self.end()
}
pub fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
self.hardbreak_if_not_bol();
self.maybe_print_comment(item.span.lo());
self.print_outer_attributes(self.attrs(item.hir_id()));
@ -478,8 +432,7 @@ impl<'a> State<'a> {
self.end(); // end the outer ibox
}
/// Pretty-print an item
pub fn print_item(&mut self, item: &hir::Item<'_>) {
fn print_item(&mut self, item: &hir::Item<'_>) {
self.hardbreak_if_not_bol();
self.maybe_print_comment(item.span.lo());
let attrs = self.attrs(item.hir_id());
@ -704,7 +657,7 @@ impl<'a> State<'a> {
self.ann.post(self, AnnNode::Item(item))
}
pub fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) {
fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) {
self.print_path(t.path, false);
}
@ -721,7 +674,7 @@ impl<'a> State<'a> {
self.print_trait_ref(&t.trait_ref);
}
pub fn print_enum_def(
fn print_enum_def(
&mut self,
enum_definition: &hir::EnumDef<'_>,
generics: &hir::Generics<'_>,
@ -736,7 +689,7 @@ impl<'a> State<'a> {
self.print_variants(enum_definition.variants, span);
}
pub fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) {
fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) {
self.bopen();
for v in variants {
self.space_if_not_bol();
@ -751,14 +704,14 @@ impl<'a> State<'a> {
self.bclose(span)
}
pub fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
match defaultness {
hir::Defaultness::Default { .. } => self.word_nbsp("default"),
hir::Defaultness::Final => (),
}
}
pub fn print_struct(
fn print_struct(
&mut self,
struct_def: &hir::VariantData<'_>,
generics: &hir::Generics<'_>,
@ -807,7 +760,7 @@ impl<'a> State<'a> {
}
}
pub fn print_variant(&mut self, v: &hir::Variant<'_>) {
fn print_variant(&mut self, v: &hir::Variant<'_>) {
self.head("");
let generics = hir::Generics::empty();
self.print_struct(&v.data, generics, v.ident.name, v.span, false);
@ -817,7 +770,8 @@ impl<'a> State<'a> {
self.print_anon_const(d);
}
}
pub fn print_method_sig(
fn print_method_sig(
&mut self,
ident: Ident,
m: &hir::FnSig<'_>,
@ -828,7 +782,7 @@ impl<'a> State<'a> {
self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_names, body_id);
}
pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
self.hardbreak_if_not_bol();
self.maybe_print_comment(ti.span.lo());
@ -856,7 +810,7 @@ impl<'a> State<'a> {
self.ann.post(self, AnnNode::SubItem(ti.hir_id()))
}
pub fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) {
fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) {
self.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
self.hardbreak_if_not_bol();
self.maybe_print_comment(ii.span.lo());
@ -881,7 +835,7 @@ impl<'a> State<'a> {
self.ann.post(self, AnnNode::SubItem(ii.hir_id()))
}
pub fn print_local(
fn print_local(
&mut self,
init: Option<&hir::Expr<'_>>,
els: Option<&hir::Block<'_>>,
@ -914,7 +868,7 @@ impl<'a> State<'a> {
self.end()
}
pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
self.maybe_print_comment(st.span.lo());
match st.kind {
hir::StmtKind::Local(loc) => {
@ -937,19 +891,19 @@ impl<'a> State<'a> {
self.maybe_print_trailing_comment(st.span, None)
}
pub fn print_block(&mut self, blk: &hir::Block<'_>) {
fn print_block(&mut self, blk: &hir::Block<'_>) {
self.print_block_with_attrs(blk, &[])
}
pub fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) {
fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) {
self.print_block_maybe_unclosed(blk, &[], false)
}
pub fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) {
fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) {
self.print_block_maybe_unclosed(blk, attrs, true)
}
pub fn print_block_maybe_unclosed(
fn print_block_maybe_unclosed(
&mut self,
blk: &hir::Block<'_>,
attrs: &[ast::Attribute],
@ -1005,7 +959,7 @@ impl<'a> State<'a> {
}
}
pub fn print_if(
fn print_if(
&mut self,
test: &hir::Expr<'_>,
blk: &hir::Expr<'_>,
@ -1018,14 +972,14 @@ impl<'a> State<'a> {
self.print_else(elseopt)
}
pub fn print_array_length(&mut self, len: &hir::ArrayLen) {
fn print_array_length(&mut self, len: &hir::ArrayLen) {
match len {
hir::ArrayLen::Infer(_, _) => self.word("_"),
hir::ArrayLen::Body(ct) => self.print_anon_const(ct),
}
}
pub fn print_anon_const(&mut self, constant: &hir::AnonConst) {
fn print_anon_const(&mut self, constant: &hir::AnonConst) {
self.ann.nested(self, Nested::Body(constant.body))
}
@ -1041,7 +995,7 @@ impl<'a> State<'a> {
/// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
/// `if cond { ... }`.
pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
}
@ -1360,7 +1314,7 @@ impl<'a> State<'a> {
self.pclose();
}
pub fn print_expr(&mut self, expr: &hir::Expr<'_>) {
fn print_expr(&mut self, expr: &hir::Expr<'_>) {
self.maybe_print_comment(expr.span.lo());
self.print_outer_attributes(self.attrs(expr.hir_id));
self.ibox(INDENT_UNIT);
@ -1593,7 +1547,7 @@ impl<'a> State<'a> {
self.end()
}
pub fn print_local_decl(&mut self, loc: &hir::Local<'_>) {
fn print_local_decl(&mut self, loc: &hir::Local<'_>) {
self.print_pat(loc.pat);
if let Some(ty) = loc.ty {
self.word_space(":");
@ -1601,11 +1555,11 @@ impl<'a> State<'a> {
}
}
pub fn print_name(&mut self, name: Symbol) {
fn print_name(&mut self, name: Symbol) {
self.print_ident(Ident::with_dummy_span(name))
}
pub fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
self.maybe_print_comment(path.span.lo());
for (i, segment) in path.segments.iter().enumerate() {
@ -1619,14 +1573,14 @@ impl<'a> State<'a> {
}
}
pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
if segment.ident.name != kw::PathRoot {
self.print_ident(segment.ident);
self.print_generic_args(segment.args(), false);
}
}
pub fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) {
fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) {
match *qpath {
hir::QPath::Resolved(None, path) => self.print_path(path, colons_before_params),
hir::QPath::Resolved(Some(qself), path) => {
@ -1743,7 +1697,7 @@ impl<'a> State<'a> {
}
}
pub fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
self.print_ident(binding.ident);
self.print_generic_args(binding.gen_args, false);
self.space();
@ -1761,7 +1715,7 @@ impl<'a> State<'a> {
}
}
pub fn print_pat(&mut self, pat: &hir::Pat<'_>) {
fn print_pat(&mut self, pat: &hir::Pat<'_>) {
self.maybe_print_comment(pat.span.lo());
self.ann.pre(self, AnnNode::Pat(pat));
// Pat isn't normalized, but the beauty of it
@ -1905,7 +1859,7 @@ impl<'a> State<'a> {
self.ann.post(self, AnnNode::Pat(pat))
}
pub fn print_patfield(&mut self, field: &hir::PatField<'_>) {
fn print_patfield(&mut self, field: &hir::PatField<'_>) {
if self.attrs(field.hir_id).is_empty() {
self.space();
}
@ -1919,12 +1873,12 @@ impl<'a> State<'a> {
self.end();
}
pub fn print_param(&mut self, arg: &hir::Param<'_>) {
fn print_param(&mut self, arg: &hir::Param<'_>) {
self.print_outer_attributes(self.attrs(arg.hir_id));
self.print_pat(arg.pat);
}
pub fn print_arm(&mut self, arm: &hir::Arm<'_>) {
fn print_arm(&mut self, arm: &hir::Arm<'_>) {
// I have no idea why this check is necessary, but here it
// is :(
if self.attrs(arm.hir_id).is_empty() {
@ -1976,7 +1930,7 @@ impl<'a> State<'a> {
self.end() // close enclosing cbox
}
pub fn print_fn(
fn print_fn(
&mut self,
decl: &hir::FnDecl<'_>,
header: hir::FnHeader,
@ -2056,14 +2010,14 @@ impl<'a> State<'a> {
}
}
pub fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
match capture_clause {
hir::CaptureBy::Value => self.word_space("move"),
hir::CaptureBy::Ref => {}
}
}
pub fn print_closure_binder(
fn print_closure_binder(
&mut self,
binder: hir::ClosureBinder,
generic_params: &[GenericParam<'_>],
@ -2083,7 +2037,8 @@ impl<'a> State<'a> {
match binder {
hir::ClosureBinder::Default => {}
// we need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional restrictions
// We need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional
// restrictions.
hir::ClosureBinder::For { .. } if generic_params.is_empty() => self.word("for<>"),
hir::ClosureBinder::For { .. } => {
self.word("for");
@ -2099,7 +2054,7 @@ impl<'a> State<'a> {
}
}
pub fn print_bounds<'b>(
fn print_bounds<'b>(
&mut self,
prefix: &'static str,
bounds: impl IntoIterator<Item = &'b hir::GenericBound<'b>>,
@ -2137,7 +2092,7 @@ impl<'a> State<'a> {
}
}
pub fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
if !generic_params.is_empty() {
self.word("<");
@ -2147,7 +2102,7 @@ impl<'a> State<'a> {
}
}
pub fn print_generic_param(&mut self, param: &GenericParam<'_>) {
fn print_generic_param(&mut self, param: &GenericParam<'_>) {
if let GenericParamKind::Const { .. } = param.kind {
self.word_space("const");
}
@ -2175,11 +2130,11 @@ impl<'a> State<'a> {
}
}
pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) {
fn print_lifetime(&mut self, lifetime: &hir::Lifetime) {
self.print_ident(lifetime.ident)
}
pub fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
if generics.predicates.is_empty() {
return;
}
@ -2236,7 +2191,7 @@ impl<'a> State<'a> {
}
}
pub fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) {
fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) {
match mutbl {
hir::Mutability::Mut => self.word_nbsp("mut"),
hir::Mutability::Not => {
@ -2247,12 +2202,12 @@ impl<'a> State<'a> {
}
}
pub fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) {
fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) {
self.print_mutability(mt.mutbl, print_const);
self.print_type(mt.ty);
}
pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
if let hir::FnRetTy::DefaultReturn(..) = decl.output {
return;
}
@ -2271,7 +2226,7 @@ impl<'a> State<'a> {
}
}
pub fn print_ty_fn(
fn print_ty_fn(
&mut self,
abi: Abi,
unsafety: hir::Unsafety,
@ -2299,7 +2254,7 @@ impl<'a> State<'a> {
self.end();
}
pub fn print_fn_header_info(&mut self, header: hir::FnHeader) {
fn print_fn_header_info(&mut self, header: hir::FnHeader) {
self.print_constness(header.constness);
match header.asyncness {
@ -2317,21 +2272,21 @@ impl<'a> State<'a> {
self.word("fn")
}
pub fn print_constness(&mut self, s: hir::Constness) {
fn print_constness(&mut self, s: hir::Constness) {
match s {
hir::Constness::NotConst => {}
hir::Constness::Const => self.word_nbsp("const"),
}
}
pub fn print_unsafety(&mut self, s: hir::Unsafety) {
fn print_unsafety(&mut self, s: hir::Unsafety) {
match s {
hir::Unsafety::Normal => {}
hir::Unsafety::Unsafe => self.word_nbsp("unsafe"),
}
}
pub fn print_is_auto(&mut self, s: hir::IsAuto) {
fn print_is_auto(&mut self, s: hir::IsAuto) {
match s {
hir::IsAuto::Yes => self.word_nbsp("auto"),
hir::IsAuto::No => {}

View File

@ -1504,9 +1504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand);
if has_shorthand_field_name {
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
s.print_qpath(qpath, false)
});
let path = rustc_hir_pretty::qpath_to_string(qpath);
let mut err = struct_span_err!(
self.tcx.sess,
pat.span,
@ -1688,9 +1686,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
s.print_qpath(qpath, false)
});
let path = rustc_hir_pretty::qpath_to_string(qpath);
let mut err = struct_span_err!(
self.tcx.sess,
pat.span,
@ -1740,9 +1736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
f
}
}
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
s.print_pat(field.pat)
}),
Err(_) => rustc_hir_pretty::pat_to_string(field.pat),
}
})
.collect::<Vec<String>>()

View File

@ -245,13 +245,13 @@ impl<'a> MakeBcbCounters<'a> {
// the loop. The `traversal` state includes a `context_stack`, providing a way to know if
// the current BCB is in one or more nested loops or not.
let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks);
while let Some(bcb) = traversal.next(self.basic_coverage_blocks) {
while let Some(bcb) = traversal.next() {
if bcb_has_coverage_spans(bcb) {
debug!("{:?} has at least one coverage span. Get or make its counter", bcb);
let branching_counter_operand = self.get_or_make_counter_operand(bcb)?;
if self.bcb_needs_branch_counters(bcb) {
self.make_branch_counters(&mut traversal, bcb, branching_counter_operand)?;
self.make_branch_counters(&traversal, bcb, branching_counter_operand)?;
}
} else {
debug!(
@ -274,7 +274,7 @@ impl<'a> MakeBcbCounters<'a> {
fn make_branch_counters(
&mut self,
traversal: &mut TraverseCoverageGraphWithLoops,
traversal: &TraverseCoverageGraphWithLoops<'_>,
branching_bcb: BasicCoverageBlock,
branching_counter_operand: Operand,
) -> Result<(), Error> {
@ -507,21 +507,14 @@ impl<'a> MakeBcbCounters<'a> {
/// found, select any branch.
fn choose_preferred_expression_branch(
&self,
traversal: &TraverseCoverageGraphWithLoops,
traversal: &TraverseCoverageGraphWithLoops<'_>,
branches: &[BcbBranch],
) -> BcbBranch {
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches);
if let Some(reloop_branch_without_counter) =
some_reloop_branch.filter(branch_needs_a_counter)
{
debug!(
"Selecting reloop_branch={:?} that still needs a counter, to get the \
`Expression`",
reloop_branch_without_counter
);
reloop_branch_without_counter
let good_reloop_branch = self.find_good_reloop_branch(traversal, &branches);
if let Some(reloop_branch) = good_reloop_branch {
assert!(self.branch_has_no_counter(&reloop_branch));
debug!("Selecting reloop branch {reloop_branch:?} to get an expression");
reloop_branch
} else {
let &branch_without_counter =
branches.iter().find(|&branch| self.branch_has_no_counter(branch)).expect(
@ -538,75 +531,52 @@ impl<'a> MakeBcbCounters<'a> {
}
}
/// At most, one of the branches (or its edge, from the branching_bcb, if the branch has
/// multiple incoming edges) can have a counter computed by expression.
///
/// If at least one of the branches leads outside of a loop (`found_loop_exit` is
/// true), and at least one other branch does not exit the loop (the first of which
/// is captured in `some_reloop_branch`), it's likely any reloop branch will be
/// executed far more often than loop exit branch, making the reloop branch a better
/// candidate for an expression.
fn find_some_reloop_branch(
/// Tries to find a branch that leads back to the top of a loop, and that
/// doesn't already have a counter. Such branches are good candidates to
/// be given an expression (instead of a physical counter), because they
/// will tend to be executed more times than a loop-exit branch.
fn find_good_reloop_branch(
&self,
traversal: &TraverseCoverageGraphWithLoops,
traversal: &TraverseCoverageGraphWithLoops<'_>,
branches: &[BcbBranch],
) -> Option<BcbBranch> {
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
// Consider each loop on the current traversal context stack, top-down.
for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
let mut all_branches_exit_this_loop = true;
let mut some_reloop_branch: Option<BcbBranch> = None;
for context in traversal.context_stack.iter().rev() {
if let Some((backedge_from_bcbs, _)) = &context.loop_backedges {
let mut found_loop_exit = false;
for &branch in branches.iter() {
if backedge_from_bcbs.iter().any(|&backedge_from_bcb| {
self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
}) {
if let Some(reloop_branch) = some_reloop_branch {
if self.branch_has_no_counter(&reloop_branch) {
// we already found a candidate reloop_branch that still
// needs a counter
continue;
}
}
// The path from branch leads back to the top of the loop. Set this
// branch as the `reloop_branch`. If this branch already has a
// counter, and we find another reloop branch that doesn't have a
// counter yet, that branch will be selected as the `reloop_branch`
// instead.
some_reloop_branch = Some(branch);
} else {
// The path from branch leads outside this loop
found_loop_exit = true;
}
if found_loop_exit
&& some_reloop_branch.filter(branch_needs_a_counter).is_some()
{
// Found both a branch that exits the loop and a branch that returns
// to the top of the loop (`reloop_branch`), and the `reloop_branch`
// doesn't already have a counter.
break;
// Try to find a branch that doesn't exit this loop and doesn't
// already have a counter.
for &branch in branches {
// A branch is a reloop branch if it dominates any BCB that has
// an edge back to the loop header. (Other branches are exits.)
let is_reloop_branch = reloop_bcbs.iter().any(|&reloop_bcb| {
self.basic_coverage_blocks.dominates(branch.target_bcb, reloop_bcb)
});
if is_reloop_branch {
all_branches_exit_this_loop = false;
if self.branch_has_no_counter(&branch) {
// We found a good branch to be given an expression.
return Some(branch);
}
// Keep looking for another reloop branch without a counter.
} else {
// This branch exits the loop.
}
if !found_loop_exit {
debug!(
"No branches exit the loop, so any branch without an existing \
counter can have the `Expression`."
);
break;
}
if some_reloop_branch.is_some() {
debug!(
"Found a branch that exits the loop and a branch the loops back to \
the top of the loop (`reloop_branch`). The `reloop_branch` will \
get the `Expression`, as long as it still needs a counter."
);
break;
}
// else all branches exited this loop context, so run the same checks with
// the outer loop(s)
}
if !all_branches_exit_this_loop {
// We found one or more reloop branches, but all of them already
// have counters. Let the caller choose one of the exit branches.
debug!("All reloop branches had counters; skip checking the other loops");
return None;
}
// All of the branches exit this loop, so keep looking for a good
// reloop branch for one of the outer loops.
}
some_reloop_branch
None
}
#[inline]
@ -652,9 +622,4 @@ impl<'a> MakeBcbCounters<'a> {
fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool {
self.bcb_predecessors(bcb).len() <= 1
}
#[inline]
fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
self.basic_coverage_blocks.dominates(dom, node)
}
}

View File

@ -6,6 +6,7 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::{self, BasicBlock, TerminatorKind};
use std::cmp::Ordering;
use std::collections::VecDeque;
use std::ops::{Index, IndexMut};
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
@ -385,57 +386,72 @@ fn bcb_filtered_successors<'a, 'tcx>(
/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
#[derive(Debug)]
pub(super) struct TraversalContext {
/// From one or more backedges returning to a loop header.
pub loop_backedges: Option<(Vec<BasicCoverageBlock>, BasicCoverageBlock)>,
/// BCB with one or more incoming loop backedges, indicating which loop
/// this context is for.
///
/// If `None`, this is the non-loop context for the function as a whole.
loop_header: Option<BasicCoverageBlock>,
/// worklist, to be traversed, of CoverageGraph in the loop with the given loop
/// backedges, such that the loop is the inner inner-most loop containing these
/// CoverageGraph
pub worklist: Vec<BasicCoverageBlock>,
/// Worklist of BCBs to be processed in this context.
worklist: VecDeque<BasicCoverageBlock>,
}
pub(super) struct TraverseCoverageGraphWithLoops {
pub backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
pub context_stack: Vec<TraversalContext>,
pub(super) struct TraverseCoverageGraphWithLoops<'a> {
basic_coverage_blocks: &'a CoverageGraph,
backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
context_stack: Vec<TraversalContext>,
visited: BitSet<BasicCoverageBlock>,
}
impl TraverseCoverageGraphWithLoops {
pub fn new(basic_coverage_blocks: &CoverageGraph) -> Self {
let start_bcb = basic_coverage_blocks.start_node();
impl<'a> TraverseCoverageGraphWithLoops<'a> {
pub(super) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self {
let backedges = find_loop_backedges(basic_coverage_blocks);
let context_stack =
vec![TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }];
let worklist = VecDeque::from([basic_coverage_blocks.start_node()]);
let context_stack = vec![TraversalContext { loop_header: None, worklist }];
// `context_stack` starts with a `TraversalContext` for the main function context (beginning
// with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
// of the stack as loops are entered, and popped off of the stack when a loop's worklist is
// exhausted.
let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes());
Self { backedges, context_stack, visited }
Self { basic_coverage_blocks, backedges, context_stack, visited }
}
pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option<BasicCoverageBlock> {
/// For each loop on the loop context stack (top-down), yields a list of BCBs
/// within that loop that have an outgoing edge back to the loop header.
pub(super) fn reloop_bcbs_per_loop(&self) -> impl Iterator<Item = &[BasicCoverageBlock]> {
self.context_stack
.iter()
.rev()
.filter_map(|context| context.loop_header)
.map(|header_bcb| self.backedges[header_bcb].as_slice())
}
pub(super) fn next(&mut self) -> Option<BasicCoverageBlock> {
debug!(
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
self.context_stack.iter().rev().collect::<Vec<_>>()
);
while let Some(context) = self.context_stack.last_mut() {
if let Some(next_bcb) = context.worklist.pop() {
if !self.visited.insert(next_bcb) {
debug!("Already visited: {:?}", next_bcb);
if let Some(bcb) = context.worklist.pop_front() {
if !self.visited.insert(bcb) {
debug!("Already visited: {bcb:?}");
continue;
}
debug!("Visiting {:?}", next_bcb);
if self.backedges[next_bcb].len() > 0 {
debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb);
debug!("Visiting {bcb:?}");
if self.backedges[bcb].len() > 0 {
debug!("{bcb:?} is a loop header! Start a new TraversalContext...");
self.context_stack.push(TraversalContext {
loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)),
worklist: Vec::new(),
loop_header: Some(bcb),
worklist: VecDeque::new(),
});
}
self.extend_worklist(basic_coverage_blocks, next_bcb);
return Some(next_bcb);
self.add_successors_to_worklists(bcb);
return Some(bcb);
} else {
// Strip contexts with empty worklists from the top of the stack
self.context_stack.pop();
@ -445,13 +461,10 @@ impl TraverseCoverageGraphWithLoops {
None
}
pub fn extend_worklist(
&mut self,
basic_coverage_blocks: &CoverageGraph,
bcb: BasicCoverageBlock,
) {
let successors = &basic_coverage_blocks.successors[bcb];
pub fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) {
let successors = &self.basic_coverage_blocks.successors[bcb];
debug!("{:?} has {} successors:", bcb, successors.len());
for &successor in successors {
if successor == bcb {
debug!(
@ -460,56 +473,44 @@ impl TraverseCoverageGraphWithLoops {
bcb
);
// Don't re-add this successor to the worklist. We are already processing it.
// FIXME: This claims to skip just the self-successor, but it actually skips
// all other successors as well. Does that matter?
break;
}
for context in self.context_stack.iter_mut().rev() {
// Add successors of the current BCB to the appropriate context. Successors that
// stay within a loop are added to the BCBs context worklist. Successors that
// exit the loop (they are not dominated by the loop header) must be reachable
// from other BCBs outside the loop, and they will be added to a different
// worklist.
//
// Branching blocks (with more than one successor) must be processed before
// blocks with only one successor, to prevent unnecessarily complicating
// `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
// branching block would have given an `Expression` (or vice versa).
let (some_successor_to_add, some_loop_header) =
if let Some((_, loop_header)) = context.loop_backedges {
if basic_coverage_blocks.dominates(loop_header, successor) {
(Some(successor), Some(loop_header))
} else {
(None, None)
}
} else {
(Some(successor), None)
};
if let Some(successor_to_add) = some_successor_to_add {
if basic_coverage_blocks.successors[successor_to_add].len() > 1 {
debug!(
"{:?} successor is branching. Prioritize it at the beginning of \
the {}",
successor_to_add,
if let Some(loop_header) = some_loop_header {
format!("worklist for the loop headed by {loop_header:?}")
} else {
String::from("non-loop worklist")
},
);
context.worklist.insert(0, successor_to_add);
} else {
debug!(
"{:?} successor is non-branching. Defer it to the end of the {}",
successor_to_add,
if let Some(loop_header) = some_loop_header {
format!("worklist for the loop headed by {loop_header:?}")
} else {
String::from("non-loop worklist")
},
);
context.worklist.push(successor_to_add);
// Add successors of the current BCB to the appropriate context. Successors that
// stay within a loop are added to the BCBs context worklist. Successors that
// exit the loop (they are not dominated by the loop header) must be reachable
// from other BCBs outside the loop, and they will be added to a different
// worklist.
//
// Branching blocks (with more than one successor) must be processed before
// blocks with only one successor, to prevent unnecessarily complicating
// `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
// branching block would have given an `Expression` (or vice versa).
let context = self
.context_stack
.iter_mut()
.rev()
.find(|context| match context.loop_header {
Some(loop_header) => {
self.basic_coverage_blocks.dominates(loop_header, successor)
}
break;
}
None => true,
})
.unwrap_or_else(|| bug!("should always fall back to the root non-loop context"));
debug!("adding to worklist for {:?}", context.loop_header);
// FIXME: The code below had debug messages claiming to add items to a
// particular end of the worklist, but was confused about which end was
// which. The existing behaviour has been preserved for now, but it's
// unclear what the intended behaviour was.
if self.basic_coverage_blocks.successors[successor].len() > 1 {
context.worklist.push_back(successor);
} else {
context.worklist.push_front(successor);
}
}
}

View File

@ -628,7 +628,7 @@ fn test_traverse_coverage_with_loops() {
let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
let mut traversed_in_order = Vec::new();
let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks);
while let Some(bcb) = traversal.next(&basic_coverage_blocks) {
while let Some(bcb) = traversal.next() {
traversed_in_order.push(bcb);
}

View File

@ -1,6 +1,6 @@
use super::{ObligationCauseCode, PredicateObligation};
use crate::infer::error_reporting::TypeErrCtxt;
use rustc_ast::{MetaItem, NestedMetaItem};
use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{struct_span_err, ErrorGuaranteed};
@ -474,18 +474,40 @@ impl<'tcx> OnUnimplementedDirective {
}
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
let mut is_diagnostic_namespace_variant = false;
let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| {
if tcx.features().diagnostic_namespace {
is_diagnostic_namespace_variant = true;
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next()
} else {
None
}
}) else {
return Ok(None);
};
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
return Self::parse_attribute(attr, false, tcx, item_def_id);
} else if tcx.features().diagnostic_namespace {
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
.filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
.try_fold(None, |aggr: Option<Self>, directive| {
let directive = directive?;
if let Some(aggr) = aggr {
let mut subcommands = aggr.subcommands;
subcommands.extend(directive.subcommands);
Ok(Some(Self {
condition: aggr.condition.or(directive.condition),
subcommands,
message: aggr.message.or(directive.message),
label: aggr.label.or(directive.label),
note: aggr.note.or(directive.note),
parent_label: aggr.parent_label.or(directive.parent_label),
append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
}))
} else {
Ok(Some(directive))
}
})
} else {
Ok(None)
}
}
fn parse_attribute(
attr: &Attribute,
is_diagnostic_namespace_variant: bool,
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
) -> Result<Option<Self>, ErrorGuaranteed> {
let result = if let Some(items) = attr.meta_item_list() {
Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
} else if let Some(value) = attr.value_str() {

View File

@ -286,10 +286,6 @@ target | std | host | notes
`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl libc
`mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP)
[`mipsel-sony-psx`](platform-support/mipsel-sony-psx.md) | * | | MIPS (LE) Sony PlayStation 1 (PSX)
`mips-unknown-linux-gnu` | MIPS Linux (kernel 4.4, glibc 2.23)
`mips64-unknown-linux-gnuabi64` | MIPS64 Linux, n64 ABI (kernel 4.4, glibc 2.23)
`mips64el-unknown-linux-gnuabi64` | MIPS64 (LE) Linux, n64 ABI (kernel 4.4, glibc 2.23)
`mipsel-unknown-linux-gnu` | MIPS (LE) Linux (kernel 4.4, glibc 2.23)
`mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc
`mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat
[`mipsisa32r6-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Big Endian

View File

@ -0,0 +1,19 @@
# `no-jump-tables`
The tracking issue for this feature is [#116592](https://github.com/rust-lang/rust/issues/116592)
---
This option enables the `-fno-jump-tables` flag for LLVM, which makes the
codegen backend avoid generating jump tables when lowering switches.
This option adds the LLVM `no-jump-tables=true` attribute to every function.
The option can be used to help provide protection against
jump-oriented-programming (JOP) attacks, such as with the linux kernel's [IBT].
```sh
RUSTFLAGS="-Zno-jump-tables" cargo +nightly build -Z build-std
```
[IBT]: https://www.phoronix.com/news/Linux-IBT-By-Default-Tip

View File

@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
for arm in arms {
if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
let path_str = rustc_hir_pretty::qpath_to_string(path);
if path_str == "Err" {
let mut matching_wild = inner.iter().any(is_wild);
let mut ident_bind_name = kw::Underscore;

View File

@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
cx,
arguments.iter().collect(),
cx.typeck_results().expr_ty(fn_expr),
&rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
&rustc_hir_pretty::qpath_to_string(path),
"function",
);
}

View File

@ -0,0 +1,22 @@
#![feature(diagnostic_namespace)]
#[diagnostic::on_unimplemented(
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
if(Self = ()),
message = "not used yet",
label = "not used yet",
note = "not used yet"
)]
#[diagnostic::on_unimplemented(message = "fallback!!")]
#[diagnostic::on_unimplemented(label = "fallback label")]
#[diagnostic::on_unimplemented(note = "fallback note")]
#[diagnostic::on_unimplemented(message = "fallback2!!")]
trait Foo {}
fn takes_foo(_: impl Foo) {}
fn main() {
takes_foo(());
//~^ERROR fallback!!
}

View File

@ -0,0 +1,52 @@
warning: malformed `on_unimplemented` attribute
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
|
LL | / #[diagnostic::on_unimplemented(
LL | |
LL | |
LL | | if(Self = ()),
... |
LL | | note = "not used yet"
LL | | )]
| |__^
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
warning: malformed `on_unimplemented` attribute
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
|
LL | / #[diagnostic::on_unimplemented(
LL | |
LL | |
LL | | if(Self = ()),
... |
LL | | note = "not used yet"
LL | | )]
| |__^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: fallback!!
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15
|
LL | takes_foo(());
| --------- ^^ fallback label
| |
| required by a bound introduced by this call
|
= help: the trait `Foo` is not implemented for `()`
= note: fallback note
help: this trait has no implementations, consider adding one
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1
|
LL | trait Foo {}
| ^^^^^^^^^
note: required by a bound in `takes_foo`
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22
|
LL | fn takes_foo(_: impl Foo) {}
| ^^^ required by this bound in `takes_foo`
error: aborting due to previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0277`.