diff options
| author | bors <bors@rust-lang.org> | 2025-06-22 09:49:14 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-06-22 09:49:14 +0000 |
| commit | a30f1783fe136d92545423dd30b12eb619973cdb (patch) | |
| tree | 61f87ea56fcedeae69b194a37dd41b06e1f5f866 /compiler | |
| parent | 9972ebfcc2b1ab322dc6611c1c997344078e05cd (diff) | |
| parent | 34dd5362be9fc460208d7b465d0911f25ab4035b (diff) | |
| download | rust-a30f1783fe136d92545423dd30b12eb619973cdb.tar.gz rust-a30f1783fe136d92545423dd30b12eb619973cdb.zip | |
Auto merge of #142864 - jhpratt:rollup-mf0yq8o, r=jhpratt
Rollup of 10 pull requests Successful merges: - rust-lang/rust#140254 (Pass -Cpanic=abort for the panic_abort crate) - rust-lang/rust#142600 (Port `#[rustc_pub_transparent]` to the new attribute system) - rust-lang/rust#142617 (improve search graph docs, reset `encountered_overflow` between reruns) - rust-lang/rust#142747 (rustdoc_json: conversion cleanups) - rust-lang/rust#142776 (All HIR attributes are outer) - rust-lang/rust#142800 (integer docs: remove extraneous text) - rust-lang/rust#142841 (Enable fmt-write-bloat for Windows) - rust-lang/rust#142845 (Enable textrel-on-minimal-lib for Windows) - rust-lang/rust#142850 (remove asm_goto feature annotation, for it is now stabilized) - rust-lang/rust#142860 (Notify me on tidy changes) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_ast/src/attr/mod.rs | 29 | ||||
| -rw-r--r-- | compiler/rustc_attr_data_structures/src/attributes.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/context.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_feature/src/builtin_attrs.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_hir/src/hir.rs | 31 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/check.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_hir_pretty/src/lib.rs | 94 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/expr.rs | 9 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/context.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/check_attr.rs | 71 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/rustdoc.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_type_ir/src/search_graph/global_cache.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_type_ir/src/search_graph/mod.rs | 168 | ||||
| -rw-r--r-- | compiler/rustc_type_ir/src/search_graph/stack.rs | 8 |
16 files changed, 275 insertions, 196 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 621e3042b62..44865c493b3 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -206,12 +206,24 @@ impl AttributeExt for Attribute { } } - fn style(&self) -> AttrStyle { - self.style + fn doc_resolution_scope(&self) -> Option<AttrStyle> { + match &self.kind { + AttrKind::DocComment(..) => Some(self.style), + AttrKind::Normal(normal) + if normal.item.path == sym::doc && normal.item.value_str().is_some() => + { + Some(self.style) + } + _ => None, + } } } impl Attribute { + pub fn style(&self) -> AttrStyle { + self.style + } + pub fn may_have_doc_links(&self) -> bool { self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str())) } @@ -806,7 +818,14 @@ pub trait AttributeExt: Debug { /// * `#[doc(...)]` returns `None`. fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>; - fn style(&self) -> AttrStyle; + /// Returns outer or inner if this is a doc attribute or a sugared doc + /// comment, otherwise None. + /// + /// This is used in the case of doc comments on modules, to decide whether + /// to resolve intra-doc links against the symbols in scope within the + /// commented module (for inner doc) vs within its parent module (for outer + /// doc). + fn doc_resolution_scope(&self) -> Option<AttrStyle>; } // FIXME(fn_delegation): use function delegation instead of manually forwarding @@ -881,8 +900,4 @@ impl Attribute { pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { AttributeExt::doc_str_and_comment_kind(self) } - - pub fn style(&self) -> AttrStyle { - AttributeExt::style(self) - } } diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 066e3e9eceb..f0f5cc4db07 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -240,6 +240,9 @@ pub enum AttributeKind { /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), + /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). + PubTransparent(Span), + /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr(ThinVec<(ReprAttr, Span)>), diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index d4c846de56e..4cfd9a82ce8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -19,3 +19,16 @@ impl<S: Stage> SingleAttributeParser<S> for AsPtrParser { Some(AttributeKind::AsPtr(cx.attr_span)) } } + +pub(crate) struct PubTransparentParser; +impl<S: Stage> SingleAttributeParser<S> for PubTransparentParser { + const PATH: &[Symbol] = &[sym::rustc_pub_transparent]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(Word); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> { + // FIXME: check that there's no args (this is currently checked elsewhere) + Some(AttributeKind::PubTransparent(cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1bcf500459d..b95ea598e72 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -19,7 +19,7 @@ use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser}; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::lint_helpers::AsPtrParser; +use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser}; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -113,6 +113,7 @@ attribute_parsers!( Single<InlineParser>, Single<MayDangleParser>, Single<OptimizeParser>, + Single<PubTransparentParser>, Single<RustcForceInlineParser>, Single<TransparencyParser>, // tidy-alphabetical-end diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5b1f1684d54..280b33f0723 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -710,7 +710,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_pub_transparent, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, + ErrorFollowing, EncodeCrossCrate::Yes, "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation", ), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 1a526d5bce0..679904c7cfe 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1346,12 +1346,13 @@ impl AttributeExt for Attribute { } } - #[inline] - fn style(&self) -> AttrStyle { - match &self { - Attribute::Unparsed(u) => u.style, - Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style, - _ => panic!(), + fn doc_resolution_scope(&self) -> Option<AttrStyle> { + match self { + Attribute::Parsed(AttributeKind::DocComment { style, .. }) => Some(*style), + Attribute::Unparsed(attr) if self.has_name(sym::doc) && self.value_str().is_some() => { + Some(attr.style) + } + _ => None, } } } @@ -1442,11 +1443,6 @@ impl Attribute { pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { AttributeExt::doc_str_and_comment_kind(self) } - - #[inline] - pub fn style(&self) -> AttrStyle { - AttributeExt::style(self) - } } /// Attributes owned by a HIR owner. @@ -2286,16 +2282,9 @@ pub struct Expr<'hir> { } impl Expr<'_> { - pub fn precedence( - &self, - for_each_attr: &dyn Fn(HirId, &mut dyn FnMut(&Attribute)), - ) -> ExprPrecedence { + pub fn precedence(&self, has_attr: &dyn Fn(HirId) -> bool) -> ExprPrecedence { let prefix_attrs_precedence = || -> ExprPrecedence { - let mut has_outer_attr = false; - for_each_attr(self.hir_id, &mut |attr: &Attribute| { - has_outer_attr |= matches!(attr.style(), AttrStyle::Outer) - }); - if has_outer_attr { ExprPrecedence::Prefix } else { ExprPrecedence::Unambiguous } + if has_attr(self.hir_id) { ExprPrecedence::Prefix } else { ExprPrecedence::Unambiguous } }; match &self.kind { @@ -2351,7 +2340,7 @@ impl Expr<'_> { | ExprKind::Use(..) | ExprKind::Err(_) => prefix_attrs_precedence(), - ExprKind::DropTemps(expr, ..) => expr.precedence(for_each_attr), + ExprKind::DropTemps(expr, ..) => expr.precedence(has_attr), } } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 32fec0604c0..752cc2eff97 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2,6 +2,7 @@ use std::cell::LazyCell; use std::ops::ControlFlow; use rustc_abi::FieldIdx; +use rustc_attr_data_structures::AttributeKind; use rustc_attr_data_structures::ReprAttr::ReprPacked; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; @@ -1384,7 +1385,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)), ty::Array(ty, _) => check_non_exhaustive(tcx, *ty), ty::Adt(def, args) => { - if !def.did().is_local() && !tcx.has_attr(def.did(), sym::rustc_pub_transparent) + if !def.did().is_local() + && !attrs::find_attr!( + tcx.get_all_attrs(def.did()), + AttributeKind::PubTransparent(_) + ) { let non_exhaustive = def.is_variant_list_non_exhaustive() || def diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 233bb5cd5b8..d3289e4cc6d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -10,7 +10,7 @@ use std::vec; use rustc_abi::ExternAbi; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; -use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs}; +use rustc_ast::{DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, BoxMarker, Breaks}; use rustc_ast_pretty::pprust::state::MacHeader; @@ -81,32 +81,24 @@ impl<'a> State<'a> { } fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { - let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&hir::Attribute)| { - self.attrs(id).iter().for_each(callback); - }; - expr.precedence(&for_each_attr) - } - - fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) { - self.print_either_attributes(attrs, ast::AttrStyle::Inner) - } - - fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) { - self.print_either_attributes(attrs, ast::AttrStyle::Outer) + let has_attr = |id: HirId| !self.attrs(id).is_empty(); + expr.precedence(&has_attr) } - fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) { + fn print_attrs(&mut self, attrs: &[hir::Attribute]) { if attrs.is_empty() { return; } for attr in attrs { - self.print_attribute_inline(attr, style); + self.print_attribute_as_style(attr, ast::AttrStyle::Outer); } self.hardbreak_if_not_bol(); } - fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) { + /// Print a single attribute as if it has style `style`, disregarding the + /// actual style of the attribute. + fn print_attribute_as_style(&mut self, attr: &hir::Attribute, style: ast::AttrStyle) { match &attr { hir::Attribute::Unparsed(unparsed) => { self.maybe_print_comment(unparsed.span.lo()); @@ -118,14 +110,17 @@ impl<'a> State<'a> { self.word("]"); self.hardbreak() } - hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => { + hir::Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string( - *kind, *style, *comment, + *kind, style, *comment, )); self.hardbreak() } hir::Attribute::Parsed(pa) => { - self.word("#[attr = "); + match style { + ast::AttrStyle::Inner => self.word("#![attr = "), + ast::AttrStyle::Outer => self.word("#[attr = "), + } pa.print_attribute(self); self.word("]"); self.hardbreak() @@ -281,10 +276,17 @@ pub fn print_crate<'a>( ann, }; + // Print all attributes, regardless of actual style, as inner attributes + // since this is the crate root with nothing above it to print outer + // attributes. + for attr in s.attrs(hir::CRATE_HIR_ID) { + s.print_attribute_as_style(attr, ast::AttrStyle::Inner); + } + // When printing the AST, we sometimes need to inject `#[no_std]` here. // Since you can't compile the HIR, it's not necessary. - s.print_mod(krate, (*attrs)(hir::CRATE_HIR_ID)); + s.print_mod(krate); s.print_remaining_comments(); s.s.eof() } @@ -299,7 +301,7 @@ where } pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String { - to_string(ann, |s| s.print_attribute_inline(attr, AttrStyle::Outer)) + to_string(ann, |s| s.print_attribute_as_style(attr, ast::AttrStyle::Outer)) } pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String { @@ -361,8 +363,7 @@ impl<'a> State<'a> { self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span); } - fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) { - self.print_attrs_as_inner(attrs); + fn print_mod(&mut self, _mod: &hir::Mod<'_>) { for &item_id in _mod.item_ids { self.ann.nested(self, Nested::Item(item_id)); } @@ -479,7 +480,7 @@ impl<'a> State<'a> { fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); - self.print_attrs_as_outer(self.attrs(item.hir_id())); + self.print_attrs(self.attrs(item.hir_id())); match item.kind { hir::ForeignItemKind::Fn(sig, arg_idents, generics) => { let (cb, ib) = self.head(""); @@ -565,7 +566,7 @@ impl<'a> State<'a> { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); let attrs = self.attrs(item.hir_id()); - self.print_attrs_as_outer(attrs); + self.print_attrs(attrs); self.ann.pre(self, AnnNode::Item(item)); match item.kind { hir::ItemKind::ExternCrate(orig_name, ident) => { @@ -647,14 +648,13 @@ impl<'a> State<'a> { self.print_ident(ident); self.nbsp(); self.bopen(ib); - self.print_mod(mod_, attrs); + self.print_mod(mod_); self.bclose(item.span, cb); } hir::ItemKind::ForeignMod { abi, items } => { let (cb, ib) = self.head("extern"); self.word_nbsp(abi.to_string()); self.bopen(ib); - self.print_attrs_as_inner(self.attrs(item.hir_id())); for item in items { self.ann.nested(self, Nested::ForeignItem(item.id)); } @@ -731,7 +731,6 @@ impl<'a> State<'a> { self.space(); self.bopen(ib); - self.print_attrs_as_inner(attrs); for impl_item in items { self.ann.nested(self, Nested::ImplItem(impl_item.id)); } @@ -822,7 +821,7 @@ impl<'a> State<'a> { for v in variants { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); - self.print_attrs_as_outer(self.attrs(v.hir_id)); + self.print_attrs(self.attrs(v.hir_id)); let ib = self.ibox(INDENT_UNIT); self.print_variant(v); self.word(","); @@ -857,7 +856,7 @@ impl<'a> State<'a> { self.popen(); self.commasep(Inconsistent, struct_def.fields(), |s, field| { s.maybe_print_comment(field.span.lo()); - s.print_attrs_as_outer(s.attrs(field.hir_id)); + s.print_attrs(s.attrs(field.hir_id)); s.print_type(field.ty); }); self.pclose(); @@ -878,7 +877,7 @@ impl<'a> State<'a> { for field in struct_def.fields() { self.hardbreak_if_not_bol(); self.maybe_print_comment(field.span.lo()); - self.print_attrs_as_outer(self.attrs(field.hir_id)); + self.print_attrs(self.attrs(field.hir_id)); self.print_ident(field.ident); self.word_nbsp(":"); self.print_type(field.ty); @@ -916,7 +915,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ti.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ti.span.lo()); - self.print_attrs_as_outer(self.attrs(ti.hir_id())); + self.print_attrs(self.attrs(ti.hir_id())); match ti.kind { hir::TraitItemKind::Const(ty, default) => { self.print_associated_const(ti.ident, ti.generics, ty, default); @@ -944,7 +943,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ii.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ii.span.lo()); - self.print_attrs_as_outer(self.attrs(ii.hir_id())); + self.print_attrs(self.attrs(ii.hir_id())); match ii.kind { hir::ImplItemKind::Const(ty, expr) => { @@ -1028,27 +1027,16 @@ impl<'a> State<'a> { } fn print_block(&mut self, blk: &hir::Block<'_>, cb: BoxMarker, ib: BoxMarker) { - self.print_block_with_attrs(blk, &[], cb, ib) + self.print_block_maybe_unclosed(blk, Some(cb), ib) } fn print_block_unclosed(&mut self, blk: &hir::Block<'_>, ib: BoxMarker) { - self.print_block_maybe_unclosed(blk, &[], None, ib) - } - - fn print_block_with_attrs( - &mut self, - blk: &hir::Block<'_>, - attrs: &[hir::Attribute], - cb: BoxMarker, - ib: BoxMarker, - ) { - self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib) + self.print_block_maybe_unclosed(blk, None, ib) } fn print_block_maybe_unclosed( &mut self, blk: &hir::Block<'_>, - attrs: &[hir::Attribute], cb: Option<BoxMarker>, ib: BoxMarker, ) { @@ -1060,8 +1048,6 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Block(blk)); self.bopen(ib); - self.print_attrs_as_inner(attrs); - for st in blk.stmts { self.print_stmt(st); } @@ -1251,7 +1237,7 @@ impl<'a> State<'a> { fn print_expr_field(&mut self, field: &hir::ExprField<'_>) { let cb = self.cbox(INDENT_UNIT); - self.print_attrs_as_outer(self.attrs(field.hir_id)); + self.print_attrs(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_space(":"); @@ -1451,7 +1437,7 @@ impl<'a> State<'a> { fn print_expr(&mut self, expr: &hir::Expr<'_>) { self.maybe_print_comment(expr.span.lo()); - self.print_attrs_as_outer(self.attrs(expr.hir_id)); + self.print_attrs(self.attrs(expr.hir_id)); let ib = self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { @@ -2076,7 +2062,7 @@ impl<'a> State<'a> { self.space(); } let cb = self.cbox(INDENT_UNIT); - self.print_attrs_as_outer(self.attrs(field.hir_id)); + self.print_attrs(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_nbsp(":"); @@ -2086,7 +2072,7 @@ impl<'a> State<'a> { } fn print_param(&mut self, arg: &hir::Param<'_>) { - self.print_attrs_as_outer(self.attrs(arg.hir_id)); + self.print_attrs(self.attrs(arg.hir_id)); self.print_pat(arg.pat); } @@ -2121,7 +2107,7 @@ impl<'a> State<'a> { let cb = self.cbox(INDENT_UNIT); self.ann.pre(self, AnnNode::Arm(arm)); let ib = self.ibox(0); - self.print_attrs_as_outer(self.attrs(arm.hir_id)); + self.print_attrs(self.attrs(arm.hir_id)); self.print_pat(arm.pat); self.space(); if let Some(ref g) = arm.guard { @@ -2409,7 +2395,7 @@ impl<'a> State<'a> { } fn print_where_predicate(&mut self, predicate: &hir::WherePredicate<'_>) { - self.print_attrs_as_outer(self.attrs(predicate.hir_id)); + self.print_attrs(self.attrs(predicate.hir_id)); match *predicate.kind { hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bound_generic_params, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 223cadc056f..bd3ca8317eb 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -18,7 +18,7 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{Attribute, ExprKind, HirId, QPath}; +use rustc_hir::{ExprKind, HirId, QPath}; use rustc_hir_analysis::NoVariantNamed; use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _}; use rustc_infer::infer; @@ -55,7 +55,7 @@ use crate::{ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { - let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&Attribute)| { + let has_attr = |id: HirId| -> bool { for attr in self.tcx.hir_attrs(id) { // For the purpose of rendering suggestions, disregard attributes // that originate from desugaring of any kind. For example, `x?` @@ -71,11 +71,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // let y: u32 = (x?).try_into().unwrap(); // + +++++++++++++++++++++ if attr.span().desugaring_kind().is_none() { - callback(attr); + return true; } } + false }; - expr.precedence(&for_each_attr) + expr.precedence(&has_attr) } /// Check an expr with an expectation type, and also demand that the expr's diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index b6bf45dfbcf..414f2a1846b 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -855,14 +855,15 @@ impl<'tcx> LateContext<'tcx> { /// rendering diagnostic. This is not the same as the precedence that would /// be used for pretty-printing HIR by rustc_hir_pretty. pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { - let for_each_attr = |id: hir::HirId, callback: &mut dyn FnMut(&hir::Attribute)| { + let has_attr = |id: hir::HirId| -> bool { for attr in self.tcx.hir_attrs(id) { if attr.span().desugaring_kind().is_none() { - callback(attr); + return true; } } + false }; - expr.precedence(&for_each_attr) + expr.precedence(&has_attr) } /// If the given expression is a local binding, find the initializer expression. diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 7ead0a6d6b7..00fd3ba8046 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -430,7 +430,7 @@ where canonical_input, step_kind_from_parent, &mut canonical_goal_evaluation, - |search_graph, canonical_goal_evaluation| { + |search_graph, cx, canonical_input, canonical_goal_evaluation| { EvalCtxt::enter_canonical( cx, search_graph, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d0630383477..d802bf4df19 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -116,6 +116,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { + let mut style = None; match attr { Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => { self.check_confusables(*first_span, target); @@ -149,6 +150,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */ } + + &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => { + self.check_rustc_pub_transparent(attr_span, span, attrs) + } Attribute::Parsed(AttributeKind::Cold(attr_span)) => { self.check_cold(hir_id, *attr_span, span, target) } @@ -163,10 +168,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => { self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target) } - &Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => { - self.check_may_dangle(hir_id, attr_span) + Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => { + self.check_may_dangle(hir_id, *attr_span) } - Attribute::Unparsed(_) => { + Attribute::Unparsed(attr_item) => { + style = Some(attr_item.style); match attr.path().as_slice() { [sym::diagnostic, sym::do_not_recommend, ..] => { self.check_do_not_recommend(attr.span(), hir_id, target, attr, item) @@ -189,6 +195,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } [sym::doc, ..] => self.check_doc_attrs( attr, + attr_item.style, hir_id, target, &mut specified_inline, @@ -288,7 +295,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_type_const(hir_id,attr, target); } [sym::linkage, ..] => self.check_linkage(attr, span, target), - [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs), [ // ok sym::allow @@ -350,14 +356,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { - match attr.style() { - ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + match style { + Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::OuterCrateLevelAttr, ), - ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), @@ -371,7 +377,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen); } - self.check_unused_attribute(hir_id, attr) + self.check_unused_attribute(hir_id, attr, style) } self.check_repr(attrs, span, target, item, hir_id); @@ -1194,7 +1200,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// the first `inline`/`no_inline` attribute. fn check_doc_inline( &self, - attr: &Attribute, + style: AttrStyle, meta: &MetaItemInner, hir_id: HirId, target: Target, @@ -1224,8 +1230,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocInlineOnlyUse { attr_span: meta.span(), - item_span: (attr.style() == AttrStyle::Outer) - .then(|| self.tcx.hir_span(hir_id)), + item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), }, ); } @@ -1234,7 +1239,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_doc_masked( &self, - attr: &Attribute, + style: AttrStyle, meta: &MetaItemInner, hir_id: HirId, target: Target, @@ -1246,8 +1251,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedOnlyExternCrate { attr_span: meta.span(), - item_span: (attr.style() == AttrStyle::Outer) - .then(|| self.tcx.hir_span(hir_id)), + item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), }, ); return; @@ -1260,8 +1264,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedNotExternCrateSelf { attr_span: meta.span(), - item_span: (attr.style() == AttrStyle::Outer) - .then(|| self.tcx.hir_span(hir_id)), + item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)), }, ); } @@ -1285,13 +1288,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_attr_crate_level( &self, attr: &Attribute, + style: AttrStyle, meta: &MetaItemInner, hir_id: HirId, ) -> bool { if hir_id != CRATE_HIR_ID { // insert a bang between `#` and `[...` let bang_span = attr.span().lo() + BytePos(1); - let sugg = (attr.style() == AttrStyle::Outer + let sugg = (style == AttrStyle::Outer && self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID) .then_some(errors::AttrCrateLevelOnlySugg { attr: attr.span().with_lo(bang_span).with_hi(bang_span), @@ -1308,7 +1312,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place. - fn check_test_attr(&self, attr: &Attribute, meta: &MetaItemInner, hir_id: HirId) { + fn check_test_attr( + &self, + attr: &Attribute, + style: AttrStyle, + meta: &MetaItemInner, + hir_id: HirId, + ) { if let Some(metas) = meta.meta_item_list() { for i_meta in metas { match (i_meta.name(), i_meta.meta_item()) { @@ -1316,7 +1326,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // Allowed everywhere like `#[doc]` } (Some(sym::no_crate_inject), _) => { - self.check_attr_crate_level(attr, meta, hir_id); + self.check_attr_crate_level(attr, style, meta, hir_id); } (_, Some(m)) => { self.tcx.emit_node_span_lint( @@ -1370,6 +1380,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_doc_attrs( &self, attr: &Attribute, + style: AttrStyle, hir_id: HirId, target: Target, specified_inline: &mut Option<(bool, Span)>, @@ -1404,7 +1415,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Some(sym::test) => { - self.check_test_attr(attr, meta, hir_id); + self.check_test_attr(attr, style, meta, hir_id); } Some( @@ -1415,25 +1426,25 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::html_root_url | sym::html_no_source, ) => { - self.check_attr_crate_level(attr, meta, hir_id); + self.check_attr_crate_level(attr, style, meta, hir_id); } Some(sym::cfg_hide) => { - if self.check_attr_crate_level(attr, meta, hir_id) { + if self.check_attr_crate_level(attr, style, meta, hir_id) { self.check_doc_cfg_hide(meta, hir_id); } } Some(sym::inline | sym::no_inline) => { - self.check_doc_inline(attr, meta, hir_id, target, specified_inline) + self.check_doc_inline(style, meta, hir_id, target, specified_inline) } - Some(sym::masked) => self.check_doc_masked(attr, meta, hir_id, target), + Some(sym::masked) => self.check_doc_masked(style, meta, hir_id, target), Some(sym::cfg | sym::hidden | sym::notable_trait) => {} Some(sym::rust_logo) => { - if self.check_attr_crate_level(attr, meta, hir_id) + if self.check_attr_crate_level(attr, style, meta, hir_id) && !self.tcx.features().rustdoc_internals() { feature_err( @@ -1472,7 +1483,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocTestUnknownInclude { path, value: value.to_string(), - inner: match attr.style() { + inner: match style { AttrStyle::Inner => "!", AttrStyle::Outer => "", }, @@ -2426,7 +2437,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { + fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) { // FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very // ugly now but can 100% be removed later. if let Attribute::Parsed(p) = attr { @@ -2479,14 +2490,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }) { if hir_id != CRATE_HIR_ID { - match attr.style() { - ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + match style { + Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::OuterCrateLevelAttr, ), - ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index fa839d2748d..931c6241bf2 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -356,7 +356,12 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen /// If there are no doc-comments, return true. /// FIXME(#78591): Support both inner and outer attributes on the same item. pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { - attrs.iter().find(|a| a.doc_str().is_some()).is_none_or(|a| a.style() == ast::AttrStyle::Inner) + for attr in attrs { + if let Some(attr_style) = attr.doc_resolution_scope() { + return attr_style == ast::AttrStyle::Inner; + } + } + true } /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`. diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index a2442660259..1b99cc820f1 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -2,6 +2,7 @@ use derive_where::derive_where; use super::{AvailableDepth, Cx, NestedGoals}; use crate::data_structures::HashMap; +use crate::search_graph::EvaluationResult; struct Success<X: Cx> { required_depth: usize, @@ -43,28 +44,26 @@ impl<X: Cx> GlobalCache<X> { &mut self, cx: X, input: X::Input, - - origin_result: X::Result, + evaluation_result: EvaluationResult<X>, dep_node: X::DepNodeIndex, - - required_depth: usize, - encountered_overflow: bool, - nested_goals: NestedGoals<X>, ) { - let result = cx.mk_tracked(origin_result, dep_node); + let EvaluationResult { encountered_overflow, required_depth, heads, nested_goals, result } = + evaluation_result; + debug_assert!(heads.is_empty()); + let result = cx.mk_tracked(result, dep_node); let entry = self.map.entry(input).or_default(); if encountered_overflow { let with_overflow = WithOverflow { nested_goals, result }; let prev = entry.with_overflow.insert(required_depth, with_overflow); if let Some(prev) = &prev { assert!(cx.evaluation_is_concurrent()); - assert_eq!(cx.get_tracked(&prev.result), origin_result); + assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } else { let prev = entry.success.replace(Success { required_depth, nested_goals, result }); if let Some(prev) = &prev { assert!(cx.evaluation_is_concurrent()); - assert_eq!(cx.get_tracked(&prev.result), origin_result); + assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index b59b4f92854..a857da2fcd5 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -1,16 +1,16 @@ -/// The search graph is responsible for caching and cycle detection in the trait -/// solver. Making sure that caching doesn't result in soundness bugs or unstable -/// query results is very challenging and makes this one of the most-involved -/// self-contained components of the compiler. -/// -/// We added fuzzing support to test its correctness. The fuzzers used to verify -/// the current implementation can be found in https://github.com/lcnr/search_graph_fuzz. -/// -/// This is just a quick overview of the general design, please check out the relevant -/// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for -/// more details. Caching is split between a global cache and the per-cycle `provisional_cache`. -/// The global cache has to be completely unobservable, while the per-cycle cache may impact -/// behavior as long as the resulting behavior is still correct. +//! The search graph is responsible for caching and cycle detection in the trait +//! solver. Making sure that caching doesn't result in soundness bugs or unstable +//! query results is very challenging and makes this one of the most-involved +//! self-contained components of the compiler. +//! +//! We added fuzzing support to test its correctness. The fuzzers used to verify +//! the current implementation can be found in <https://github.com/lcnr/search_graph_fuzz>. +//! +//! This is just a quick overview of the general design, please check out the relevant +//! [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for +//! more details. Caching is split between a global cache and the per-cycle `provisional_cache`. +//! The global cache has to be completely unobservable, while the per-cycle cache may impact +//! behavior as long as the resulting behavior is still correct. use std::cmp::Ordering; use std::collections::BTreeMap; use std::collections::hash_map::Entry; @@ -381,18 +381,16 @@ impl PathsToNested { /// The nested goals of each stack entry and the path from the /// stack entry to that nested goal. /// +/// They are used when checking whether reevaluating a global cache +/// would encounter a cycle or use a provisional cache entry given the +/// currentl search graph state. We need to disable the global cache +/// in this case as it could otherwise result in behaviorial differences. +/// Cycles can impact behavior. The cycle ABA may have different final +/// results from a the cycle BAB depending on the cycle root. +/// /// We only start tracking nested goals once we've either encountered /// overflow or a solver cycle. This is a performance optimization to /// avoid tracking nested goals on the happy path. -/// -/// We use nested goals for two reasons: -/// - when rebasing provisional cache entries -/// - when checking whether we have to ignore a global cache entry as reevaluating -/// it would encounter a cycle or use a provisional cache entry. -/// -/// We need to disable the global cache if using it would hide a cycle, as -/// cycles can impact behavior. The cycle ABA may have different final -/// results from a the cycle BAB depending on the cycle root. #[derive_where(Debug, Default, Clone; X: Cx)] struct NestedGoals<X: Cx> { nested_goals: HashMap<X::Input, PathsToNested>, @@ -450,6 +448,43 @@ struct ProvisionalCacheEntry<X: Cx> { result: X::Result, } +/// The final result of evaluating a goal. +/// +/// We reset `encountered_overflow` when reevaluating a goal, +/// but need to track whether we've hit the recursion limit at +/// all for correctness. +/// +/// We've previously simply returned the final `StackEntry` but this +/// made it easy to accidentally drop information from the previous +/// evaluation. +#[derive_where(Debug; X: Cx)] +struct EvaluationResult<X: Cx> { + encountered_overflow: bool, + required_depth: usize, + heads: CycleHeads, + nested_goals: NestedGoals<X>, + result: X::Result, +} + +impl<X: Cx> EvaluationResult<X> { + fn finalize( + final_entry: StackEntry<X>, + encountered_overflow: bool, + result: X::Result, + ) -> EvaluationResult<X> { + EvaluationResult { + encountered_overflow, + // Unlike `encountered_overflow`, we share `heads`, `required_depth`, + // and `nested_goals` between evaluations. + required_depth: final_entry.required_depth, + heads: final_entry.heads, + nested_goals: final_entry.nested_goals, + // We only care about the final result. + result, + } + } +} + pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> { root_depth: AvailableDepth, /// The stack of goals currently being computed. @@ -562,7 +597,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { input: X::Input, step_kind_from_parent: PathKind, inspect: &mut D::ProofTreeBuilder, - mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, + evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy, ) -> X::Result { let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::<D>(self.root_depth, &self.stack) @@ -616,12 +651,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { input, step_kind_from_parent, available_depth, + provisional_result: None, required_depth: 0, heads: Default::default(), encountered_overflow: false, has_been_used: None, nested_goals: Default::default(), - provisional_result: None, }); // This is for global caching, so we properly track query dependencies. @@ -630,35 +665,41 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // not tracked by the cache key and from outside of this anon task, it // must not be added to the global cache. Notably, this is the case for // trait solver cycles participants. - let ((final_entry, result), dep_node) = cx.with_cached_task(|| { - self.evaluate_goal_in_task(cx, input, inspect, &mut evaluate_goal) - }); + let (evaluation_result, dep_node) = + cx.with_cached_task(|| self.evaluate_goal_in_task(cx, input, inspect, evaluate_goal)); // We've finished computing the goal and have popped it from the stack, // lazily update its parent goal. Self::update_parent_goal( &mut self.stack, - final_entry.step_kind_from_parent, - final_entry.required_depth, - &final_entry.heads, - final_entry.encountered_overflow, - UpdateParentGoalCtxt::Ordinary(&final_entry.nested_goals), + step_kind_from_parent, + evaluation_result.required_depth, + &evaluation_result.heads, + evaluation_result.encountered_overflow, + UpdateParentGoalCtxt::Ordinary(&evaluation_result.nested_goals), ); + let result = evaluation_result.result; // We're now done with this goal. We only add the root of cycles to the global cache. // In case this goal is involved in a larger cycle add it to the provisional cache. - if final_entry.heads.is_empty() { + if evaluation_result.heads.is_empty() { if let Some((_scope, expected)) = validate_cache { // Do not try to move a goal into the cache again if we're testing // the global cache. - assert_eq!(result, expected, "input={input:?}"); + assert_eq!(evaluation_result.result, expected, "input={input:?}"); } else if D::inspect_is_noop(inspect) { - self.insert_global_cache(cx, final_entry, result, dep_node) + self.insert_global_cache(cx, input, evaluation_result, dep_node) } } else if D::ENABLE_PROVISIONAL_CACHE { debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}"); let entry = self.provisional_cache.entry(input).or_default(); - let StackEntry { heads, encountered_overflow, .. } = final_entry; + let EvaluationResult { + encountered_overflow, + required_depth: _, + heads, + nested_goals: _, + result, + } = evaluation_result; let path_from_head = Self::cycle_path_kind( &self.stack, step_kind_from_parent, @@ -1023,19 +1064,25 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { cx: X, input: X::Input, inspect: &mut D::ProofTreeBuilder, - mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, - ) -> (StackEntry<X>, X::Result) { + evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy, + ) -> EvaluationResult<X> { + // We reset `encountered_overflow` each time we rerun this goal + // but need to make sure we currently propagate it to the global + // cache even if only some of the evaluations actually reach the + // recursion limit. + let mut encountered_overflow = false; let mut i = 0; loop { - let result = evaluate_goal(self, inspect); + let result = evaluate_goal(self, cx, input, inspect); let stack_entry = self.stack.pop(); + encountered_overflow |= stack_entry.encountered_overflow; debug_assert_eq!(stack_entry.input, input); // If the current goal is not the root of a cycle, we are done. // // There are no provisional cache entries which depend on this goal. let Some(usage_kind) = stack_entry.has_been_used else { - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; // If it is a cycle head, we have to keep trying to prove it until @@ -1051,7 +1098,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // final result is equal to the initial response for that case. if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) { self.rebase_provisional_cache_entries(&stack_entry, |_, result| result); - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } // If computing this goal results in ambiguity with no constraints, @@ -1070,7 +1117,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { self.rebase_provisional_cache_entries(&stack_entry, |input, _| { D::propagate_ambiguity(cx, input, result) }); - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; // If we've reached the fixpoint step limit, we bail with overflow and taint all @@ -1082,7 +1129,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { self.rebase_provisional_cache_entries(&stack_entry, |input, _| { D::on_fixpoint_overflow(cx, input) }); - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } // Clear all provisional cache entries which depend on a previous provisional @@ -1091,9 +1138,22 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { debug!(?result, "fixpoint changed provisional results"); self.stack.push(StackEntry { - has_been_used: None, + input, + step_kind_from_parent: stack_entry.step_kind_from_parent, + available_depth: stack_entry.available_depth, provisional_result: Some(result), - ..stack_entry + // We can keep these goals from previous iterations as they are only + // ever read after finalizing this evaluation. + required_depth: stack_entry.required_depth, + heads: stack_entry.heads, + nested_goals: stack_entry.nested_goals, + // We reset these two fields when rerunning this goal. We could + // keep `encountered_overflow` as it's only used as a performance + // optimization. However, given that the proof tree will likely look + // similar to the previous iterations when reevaluating, it's better + // for caching if the reevaluation also starts out with `false`. + encountered_overflow: false, + has_been_used: None, }); } } @@ -1109,21 +1169,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { fn insert_global_cache( &mut self, cx: X, - final_entry: StackEntry<X>, - result: X::Result, + input: X::Input, + evaluation_result: EvaluationResult<X>, dep_node: X::DepNodeIndex, ) { - debug!(?final_entry, ?result, "insert global cache"); - cx.with_global_cache(|cache| { - cache.insert( - cx, - final_entry.input, - result, - dep_node, - final_entry.required_depth, - final_entry.encountered_overflow, - final_entry.nested_goals, - ) - }) + debug!(?evaluation_result, "insert global cache"); + cx.with_global_cache(|cache| cache.insert(cx, input, evaluation_result, dep_node)) } } diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs index 8bb247bf055..e0fd934df69 100644 --- a/compiler/rustc_type_ir/src/search_graph/stack.rs +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -26,6 +26,10 @@ pub(super) struct StackEntry<X: Cx> { /// The available depth of a given goal, immutable. pub available_depth: AvailableDepth, + /// Starts out as `None` and gets set when rerunning this + /// goal in case we encounter a cycle. + pub provisional_result: Option<X::Result>, + /// The maximum depth required while evaluating this goal. pub required_depth: usize, @@ -42,10 +46,6 @@ pub(super) struct StackEntry<X: Cx> { /// The nested goals of this goal, see the doc comment of the type. pub nested_goals: NestedGoals<X>, - - /// Starts out as `None` and gets set when rerunning this - /// goal in case we encounter a cycle. - pub provisional_result: Option<X::Result>, } #[derive_where(Default; X: Cx)] |
