Skip to content
Closed
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2e57845
actually use a doc comment
lcnr Jun 16, 2025
8710c1f
update comment
lcnr Jun 17, 2025
75da5c4
reevaluate: reset `encountered_overflow`
lcnr Jun 17, 2025
ecd65f8
`evaluate_goal`: accept different inputs
lcnr Jun 18, 2025
bd2dd25
Generate symbols.o for proc-macros too
bjorn3 Jun 17, 2025
a0badba
Pass -Cpanic=abort for the panic_abort crate
bjorn3 Apr 24, 2025
5babf24
integer docs: remove extraneous text
tshepang Jun 20, 2025
e51c37c
Add AttributeExt::doc_resolution_scope
dtolnay Jun 20, 2025
86f40ac
Remove style() from AttributeExt trait
dtolnay Jun 20, 2025
715e02f
Add regression test for issue 142649
dtolnay Jun 19, 2025
6729b66
All HIR attributes are outer
dtolnay Jun 20, 2025
d86d3f3
Port `#[rustc_pub_transparent]` to the new attribute system
GrigorenkoPV Jun 14, 2025
eb86d09
remove asm_goto feature annotation, for it is now stabilized
tshepang Jun 21, 2025
6854f7d
Use `sym::asterisk` to avoid a `Symbol::intern` call.
nnethercote Jun 17, 2025
4736142
Remove some dead code.
nnethercote Jun 17, 2025
9a59774
Remove some code.
nnethercote Jun 17, 2025
88388f4
Use `FromClean` more.
nnethercote Jun 17, 2025
2878e1c
Rename some methods.
nnethercote Jun 19, 2025
6ff229b
Notify `jieyouxu` on tidy changes
jieyouxu Jun 22, 2025
9499207
Rollup merge of #140254 - bjorn3:rustc_panic_abort_abort, r=petrochenkov
jhpratt Jun 22, 2025
962df64
Rollup merge of #142600 - GrigorenkoPV:attributes/rustc_pub_transpare…
jhpratt Jun 22, 2025
4ce8c2a
Rollup merge of #142617 - lcnr:search_graph-3, r=compiler-errors
jhpratt Jun 22, 2025
1b27161
Rollup merge of #142641 - bjorn3:proc_macro_symbols_o, r=jieyouxu
jhpratt Jun 22, 2025
18cf60b
Rollup merge of #142747 - nnethercote:json-conversion-cleanups, r=aDo…
jhpratt Jun 22, 2025
17d8ac8
Rollup merge of #142776 - dtolnay:hirattrstyle2, r=jdonszelmann
jhpratt Jun 22, 2025
641e666
Rollup merge of #142800 - tshepang:extraneous, r=Mark-Simulacrum
jhpratt Jun 22, 2025
932d004
Rollup merge of #142850 - tshepang:patch-1, r=jieyouxu
jhpratt Jun 22, 2025
e4e8842
Rollup merge of #142860 - jieyouxu:notify-tidy, r=jieyouxu
jhpratt Jun 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -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)>),

13 changes: 13 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
Original file line number Diff line number Diff line change
@@ -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))
}
}
3 changes: 2 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
@@ -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
9 changes: 7 additions & 2 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
@@ -1817,8 +1817,13 @@ pub(crate) fn linked_symbols(
crate_type: CrateType,
) -> Vec<(String, SymbolExportKind)> {
match crate_type {
CrateType::Executable | CrateType::Cdylib | CrateType::Dylib | CrateType::Sdylib => (),
CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => {
CrateType::Executable
| CrateType::ProcMacro
| CrateType::Cdylib
| CrateType::Dylib
| CrateType::Sdylib => (),
CrateType::Staticlib | CrateType::Rlib => {
// These are not linked, so no need to generate symbols.o for them.
return Vec::new();
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
@@ -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",
),

31 changes: 10 additions & 21 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}

7 changes: 6 additions & 1 deletion compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
@@ -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
94 changes: 40 additions & 54 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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,
9 changes: 5 additions & 4 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
@@ -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
7 changes: 4 additions & 3 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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,
71 changes: 41 additions & 30 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
@@ -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,15 +1312,21 @@ 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()) {
(Some(sym::attr), _) => {
// 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(),
7 changes: 6 additions & 1 deletion compiler/rustc_resolve/src/rustdoc.rs
Original file line number Diff line number Diff line change
@@ -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)]`.
17 changes: 8 additions & 9 deletions compiler/rustc_type_ir/src/search_graph/global_cache.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
168 changes: 109 additions & 59 deletions compiler/rustc_type_ir/src/search_graph/mod.rs
Original file line number Diff line number Diff line change
@@ -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))
}
}
8 changes: 4 additions & 4 deletions compiler/rustc_type_ir/src/search_graph/stack.rs
Original file line number Diff line number Diff line change
@@ -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)]
10 changes: 10 additions & 0 deletions library/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
cargo-features = ["profile-rustflags"]

[workspace]
resolver = "1"
members = [
@@ -44,6 +46,14 @@ object.debug = 0
rustc-demangle.debug = 0
rustc-demangle.opt-level = "s"

# panic_abort must always be compiled with panic=abort, even when the rest of the
# sysroot is panic=unwind.
[profile.dev.package.panic_abort]
rustflags = ["-Cpanic=abort"]

[profile.release.package.panic_abort]
rustflags = ["-Cpanic=abort"]

[patch.crates-io]
# See comments in `library/rustc-std-workspace-core/README.md` for what's going on
# here
208 changes: 0 additions & 208 deletions library/core/src/num/int_macros.rs

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
@@ -1399,7 +1399,6 @@ macro_rules! from_str_int_impl {
///
/// # Examples
///
/// Basic usage:
/// ```
/// use std::str::FromStr;
///
@@ -1445,7 +1444,6 @@ macro_rules! from_str_int_impl {
///
/// # Examples
///
/// Basic usage:
/// ```
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")]
/// ```
@@ -1478,7 +1476,6 @@ macro_rules! from_str_int_impl {
///
/// # Examples
///
/// Basic usage:
/// ```
/// #![feature(int_from_ascii)]
///
@@ -1523,7 +1520,6 @@ macro_rules! from_str_int_impl {
///
/// # Examples
///
/// Basic usage:
/// ```
/// #![feature(int_from_ascii)]
///
46 changes: 0 additions & 46 deletions library/core/src/num/saturating.rs
Original file line number Diff line number Diff line change
@@ -300,8 +300,6 @@ macro_rules! saturating_impl {

/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -490,8 +488,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -504,8 +500,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -518,8 +512,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -532,8 +524,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -556,8 +546,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -576,8 +564,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -603,8 +589,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -631,8 +615,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -654,8 +636,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -683,8 +663,6 @@ macro_rules! saturating_int_impl {
/// Please note that this example is shared between integer types.
/// Which explains why `i16` is used here.
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -712,8 +690,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -740,8 +716,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -768,8 +742,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -797,8 +769,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -823,8 +793,6 @@ macro_rules! saturating_int_impl {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -860,8 +828,6 @@ macro_rules! saturating_int_impl_signed {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -883,8 +849,6 @@ macro_rules! saturating_int_impl_signed {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -911,8 +875,6 @@ macro_rules! saturating_int_impl_signed {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -934,8 +896,6 @@ macro_rules! saturating_int_impl_signed {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -955,8 +915,6 @@ macro_rules! saturating_int_impl_signed {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -994,8 +952,6 @@ macro_rules! saturating_int_impl_unsigned {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
@@ -1016,8 +972,6 @@ macro_rules! saturating_int_impl_unsigned {
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use std::num::Saturating;
///
197 changes: 0 additions & 197 deletions library/core/src/num/uint_macros.rs

Large diffs are not rendered by default.

12 changes: 0 additions & 12 deletions src/bootstrap/src/bin/rustc.rs
Original file line number Diff line number Diff line change
@@ -151,18 +151,6 @@ fn main() {
cmd.arg("--sysroot").arg(&sysroot);
}

// If we're compiling specifically the `panic_abort` crate then we pass
// the `-C panic=abort` option. Note that we do not do this for any
// other crate intentionally as this is the only crate for now that we
// ship with panic=abort.
//
// This... is a bit of a hack how we detect this. Ideally this
// information should be encoded in the crate I guess? Would likely
// require an RFC amendment to RFC 1513, however.
if crate_name == Some("panic_abort") {
cmd.arg("-C").arg("panic=abort");
}

let crate_type = parse_value_from_args(&orig_args, "--crate-type");
// `-Ztls-model=initial-exec` must not be applied to proc-macros, see
// issue https://github.com/rust-lang/rust/issues/100530
14 changes: 0 additions & 14 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
@@ -2432,20 +2432,6 @@ pub(crate) enum ConstantKind {
Infer,
}

impl Constant {
pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String {
self.kind.expr(tcx)
}

pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option<String> {
self.kind.value(tcx)
}

pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool {
self.kind.is_literal(tcx)
}
}

impl ConstantKind {
pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String {
match *self {
344 changes: 169 additions & 175 deletions src/librustdoc/json/conversions.rs

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::attr::AttributeExt as _;
use rustc_ast::token::CommentKind;
use rustc_errors::Applicability;
use rustc_hir::{AttrStyle, Attribute};
@@ -43,13 +44,19 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &F
"looks like a footnote ref, but has no matching footnote",
|diag| {
if this_fragment.kind == DocFragmentKind::SugaredDoc {
let (doc_attr, (_, doc_attr_comment_kind)) = attrs
let (doc_attr, (_, doc_attr_comment_kind), attr_style) = attrs
.iter()
.filter(|attr| attr.span().overlaps(this_fragment.span))
.rev()
.find_map(|attr| Some((attr, attr.doc_str_and_comment_kind()?)))
.find_map(|attr| {
Some((
attr,
attr.doc_str_and_comment_kind()?,
attr.doc_resolution_scope()?,
))
})
.unwrap();
let (to_add, terminator) = match (doc_attr_comment_kind, doc_attr.style()) {
let (to_add, terminator) = match (doc_attr_comment_kind, attr_style) {
(CommentKind::Line, AttrStyle::Outer) => ("\n///\n/// ", ""),
(CommentKind::Line, AttrStyle::Inner) => ("\n//!\n//! ", ""),
(CommentKind::Block, AttrStyle::Outer) => ("\n/** ", " */"),
1 change: 0 additions & 1 deletion tests/codegen/asm/critical.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//@ only-x86_64
//@ compile-flags: -C no-prepopulate-passes
#![feature(asm_goto)]
#![feature(asm_goto_with_outputs)]
#![crate_type = "lib"]
use std::arch::asm;
4 changes: 4 additions & 0 deletions tests/run-make/used-proc-macro/dep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#![crate_type = "lib"]

#[used]
static VERY_IMPORTANT_SYMBOL: u32 = 12345;
3 changes: 3 additions & 0 deletions tests/run-make/used-proc-macro/proc_macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#![crate_type = "proc-macro"]

extern crate dep as _;
16 changes: 16 additions & 0 deletions tests/run-make/used-proc-macro/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Test that #[used] statics are included in the final dylib for proc-macros too.

//@ needs-target-std
//@ needs-crate-type: proc-macro

use run_make_support::{dynamic_lib_name, llvm_readobj, rustc};

fn main() {
rustc().input("dep.rs").run();
rustc().input("proc_macro.rs").run();
llvm_readobj()
.input(dynamic_lib_name("proc_macro"))
.arg("--all")
.run()
.assert_stdout_contains("VERY_IMPORTANT_SYMBOL");
}
8 changes: 8 additions & 0 deletions tests/ui/deprecation/deprecated-expr-precedence.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//@ check-fail
//@ compile-flags: --crate-type=lib

// Regression test for issue 142649
pub fn public() {
#[deprecated] 0
//~^ ERROR mismatched types
}
11 changes: 11 additions & 0 deletions tests/ui/deprecation/deprecated-expr-precedence.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0308]: mismatched types
--> $DIR/deprecated-expr-precedence.rs:6:19
|
LL | pub fn public() {
| - help: try adding a return type: `-> i32`
LL | #[deprecated] 0
| ^ expected `()`, found integer

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
5 changes: 5 additions & 0 deletions tests/ui/unpretty/deprecated-attr.rs
Original file line number Diff line number Diff line change
@@ -16,3 +16,8 @@ pub struct SinceAndNote;

#[deprecated(note = "here's why this is deprecated", since = "1.2.3")]
pub struct FlippedOrder;

pub fn f() {
// Attribute is ignored here (with a warning), but still preserved in HIR
#[deprecated] 0
}
9 changes: 9 additions & 0 deletions tests/ui/unpretty/deprecated-attr.stdout
Original file line number Diff line number Diff line change
@@ -24,3 +24,12 @@ struct SinceAndNote;
#[attr = Deprecation {deprecation: Deprecation {since: NonStandard("1.2.3"),
note: "here's why this is deprecated"}}]
struct FlippedOrder;

fn f() {

// Attribute is ignored here (with a warning), but still preserved in HIR
#[attr = Deprecation {deprecation:
Deprecation {since:
Unspecified}}]
0
}
4 changes: 1 addition & 3 deletions tests/ui/unpretty/diagnostic-attr.stdout
Original file line number Diff line number Diff line change
@@ -12,6 +12,4 @@ extern crate std;
trait ImportantTrait<A> { }

#[diagnostic::do_not_recommend]
impl <T> ImportantTrait<T> for T where T: Clone
{#![diagnostic::do_not_recommend]
}
impl <T> ImportantTrait<T> for T where T: Clone { }
2 changes: 1 addition & 1 deletion tests/ui/unpretty/exhaustive-asm.hir.stdout
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ mod expressions {

mod items {
/// ItemKind::GlobalAsm
mod item_global_asm {/// ItemKind::GlobalAsm
mod item_global_asm {
global_asm! (".globl my_asm_func");
}
}
69 changes: 28 additions & 41 deletions tests/ui/unpretty/exhaustive.hir.stdout
Original file line number Diff line number Diff line change
@@ -50,20 +50,14 @@ mod prelude {
}
}

//! inner single-line doc comment
/*!
/// inner single-line doc comment
/**
* inner multi-line doc comment
*/
#[doc = "inner doc attribute"]
#[allow(dead_code, unused_variables)]
#[no_std]
mod attributes {//! inner single-line doc comment
/*!
* inner multi-line doc comment
*/
#![doc = "inner doc attribute"]
#![allow(dead_code, unused_variables)]
#![no_std]
mod attributes {

/// outer single-line doc comment
/**
@@ -413,25 +407,25 @@ mod expressions {
}
mod items {
/// ItemKind::ExternCrate
mod item_extern_crate {/// ItemKind::ExternCrate
mod item_extern_crate {
extern crate core;
extern crate self as unpretty;
extern crate core as _;
}
/// ItemKind::Use
mod item_use {/// ItemKind::Use
mod item_use {
use ::{};
use crate::expressions;
use crate::items::item_use;
use core::*;
}
/// ItemKind::Static
mod item_static {/// ItemKind::Static
mod item_static {
static A: () = { };
static mut B: () = { };
}
/// ItemKind::Const
mod item_const {/// ItemKind::Const
mod item_const {
const A: () = { };
trait TraitItems {
const
@@ -445,7 +439,7 @@ mod items {
}
}
/// ItemKind::Fn
mod item_fn {/// ItemKind::Fn
mod item_fn {
const unsafe extern "C" fn f() { }
async unsafe extern "C" fn g()
->
@@ -460,21 +454,19 @@ mod items {
}
}
/// ItemKind::Mod
mod item_mod {/// ItemKind::Mod
}
mod item_mod { }
/// ItemKind::ForeignMod
mod item_foreign_mod {/// ItemKind::ForeignMod
mod item_foreign_mod {
extern "Rust" { }
extern "C" { }
}
/// ItemKind::GlobalAsm: see exhaustive-asm.rs
/// ItemKind::TyAlias
mod item_ty_alias {/// ItemKind::GlobalAsm: see exhaustive-asm.rs
/// ItemKind::TyAlias
mod item_ty_alias {
type Type<'a> where T: 'a = T;
}
/// ItemKind::Enum
mod item_enum {/// ItemKind::Enum
mod item_enum {
enum Void { }
enum Empty {
Unit,
@@ -490,7 +482,7 @@ mod items {
}
}
/// ItemKind::Struct
mod item_struct {/// ItemKind::Struct
mod item_struct {
struct Unit;
struct Tuple();
struct Newtype(Unit);
@@ -501,45 +493,40 @@ mod items {
}
}
/// ItemKind::Union
mod item_union {/// ItemKind::Union
mod item_union {
union Generic<'a, T> where T: 'a {
t: T,
}
}
/// ItemKind::Trait
mod item_trait {/// ItemKind::Trait
mod item_trait {
auto unsafe trait Send { }
trait Trait<'a>: Sized where Self: 'a { }
}
/// ItemKind::TraitAlias
mod item_trait_alias {/// ItemKind::TraitAlias
mod item_trait_alias {
trait Trait<T> = Sized where for<'a> T: 'a;
}
/// ItemKind::Impl
mod item_impl {/// ItemKind::Impl
mod item_impl {
impl () { }
impl <T> () { }
impl Default for () { }
impl const <T> Default for () { }
}
/// ItemKind::MacCall
mod item_mac_call {/// ItemKind::MacCall
}
mod item_mac_call { }
/// ItemKind::MacroDef
mod item_macro_def {/// ItemKind::MacroDef
mod item_macro_def {
macro_rules! mac { () => {...}; }
macro stringify { () => {} }
}
/// ItemKind::Delegation
/*! FIXME: todo */
mod item_delegation {/// ItemKind::Delegation
/*! FIXME: todo */
}
/** FIXME: todo */
mod item_delegation { }
/// ItemKind::DelegationMac
/*! FIXME: todo */
mod item_delegation_mac {/// ItemKind::DelegationMac
/*! FIXME: todo */
}
/** FIXME: todo */
mod item_delegation_mac { }
}
mod patterns {
/// PatKind::Missing
@@ -690,29 +677,29 @@ mod types {
/// TyKind::Paren
fn ty_paren() { let _: T; }
/// TyKind::Typeof
/*! unused for now */
/** unused for now */
fn ty_typeof() { }
/// TyKind::Infer
fn ty_infer() { let _: _; }
/// TyKind::ImplicitSelf
/*! there is no syntax for this */
/** there is no syntax for this */
fn ty_implicit_self() { }
/// TyKind::MacCall
#[expect(deprecated)]
fn ty_mac_call() { let _: T; let _: T; let _: T; }
/// TyKind::CVarArgs
/*! FIXME: todo */
/** FIXME: todo */
fn ty_c_var_args() { }
/// TyKind::Pat
fn ty_pat() { let _: u32 is 1..=RangeMax; }
}
mod visibilities {
/// VisibilityKind::Public
mod visibility_public {/// VisibilityKind::Public
mod visibility_public {
struct Pub;
}
/// VisibilityKind::Restricted
mod visibility_restricted {/// VisibilityKind::Restricted
mod visibility_restricted {
struct PubCrate;
struct PubSelf;
struct PubSuper;
4 changes: 4 additions & 0 deletions triagebot.toml
Original file line number Diff line number Diff line change
@@ -1023,6 +1023,10 @@ Otherwise, you can ignore this comment.
[mentions."src/tools/x"]
message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version."

[mentions."src/tools/tidy"]
message = "There are changes to the `tidy` tool."
cc = ["@jieyouxu"]

[mentions."src/tools/tidy/src/deps.rs"]
message = "The list of allowed third-party dependencies may have been modified! You must ensure that any new dependencies have compatible licenses before merging."
cc = ["@davidtwco", "@wesleywiser"]