diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 8587b009f25aa..ab6ab391484f3 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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(ann: &dyn PpAnn, f: F) -> String +fn to_string(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>) -> 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, - generics: &hir::Generics<'_>, - arg_names: &[Ident], - body_id: Option, -) -> 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(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) + fn commasep_cmnt(&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(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) { + fn print_path(&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>, @@ -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 => {} diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 3f9c9b3381baa..072c7a9f36ad5 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -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::>() diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 78845af016276..77e3dee1fefa1 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -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 { - let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch); - - let mut some_reloop_branch: Option = 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; + // 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; + + // 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) - } } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 39027d21f066a..9a7adaada09fc 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -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)>, - - /// 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, + /// 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, + + /// Worklist of BCBs to be processed in this context. + worklist: VecDeque, } -pub(super) struct TraverseCoverageGraphWithLoops { - pub backedges: IndexVec>, - pub context_stack: Vec, +pub(super) struct TraverseCoverageGraphWithLoops<'a> { + basic_coverage_blocks: &'a CoverageGraph, + + backedges: IndexVec>, + context_stack: Vec, visited: BitSet, } -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 { + /// 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 { + 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 { debug!( "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", self.context_stack.iter().rev().collect::>() ); 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); } } } diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index ee7cb791c8180..487d22823644b 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -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); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index d9059e46a8c55..cc1f13a2f0f22 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -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, 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, 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, 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() { diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d25fa661c3f3a..1fb5e56db5d76 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -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 diff --git a/src/doc/unstable-book/src/compiler-flags/no-jump-tables.md b/src/doc/unstable-book/src/compiler-flags/no-jump-tables.md new file mode 100644 index 0000000000000..f096c20f4bd54 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/no-jump-tables.md @@ -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 diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index de911f7a02803..a2903e52ae08d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -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; diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index e53e146ec5dbe..01b850cdb1139 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -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", ); } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs new file mode 100644 index 0000000000000..353075863918c --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs @@ -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!! +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr new file mode 100644 index 0000000000000..6a83d8e39c656 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr @@ -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`.