Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ac159dd
Add help message suggesting explicit reference cast for From/TryFrom
Muhtasim-Rasheed Feb 1, 2026
dc24aae
Modernize diagnostic for indeterminate trait object lifetime bounds
fmease Jan 29, 2026
03dcd99
Format heterogeneous try blocks
ia0 Feb 7, 2026
d5c6866
improve tests
ia0 Feb 7, 2026
90fd768
Make it clearer that `check_pat_lit` only handles literal patterns
Zalathar Jan 4, 2026
91d47a5
Don't import `TyCtxt` from `crate::ty::context`
Zalathar Feb 8, 2026
4234d2d
Move `impl Interner for TyCtxt` to its own submodule
Zalathar Feb 8, 2026
b7bfb76
Indicate that `bidirectional_lang_item_map!` declares functions
Zalathar Feb 8, 2026
4dc82e9
Move the needs-drop check for `arena_cache` queries out of macro code
Zalathar Feb 5, 2026
20f65fc
Uplift `Predicate::allow_normalization` to `rustc_type_ir`
ShoyuVanilla Feb 9, 2026
d215efd
Port #![test_runner] to the attribute parser
Ozzy1423 Feb 10, 2026
37d354a
Rollup merge of #151954 - Muhtasim-Rasheed:issue-109829-help-message,…
JonathanBrouwer Feb 10, 2026
1efb99b
Rollup merge of #152148 - Zalathar:tcx-interner, r=petrochenkov
JonathanBrouwer Feb 10, 2026
fb4af55
Rollup merge of #152226 - fmease:modernize-indeterminate-object-lifet…
JonathanBrouwer Feb 10, 2026
f24a465
Rollup merge of #152417 - Zalathar:arena-alloc, r=nnethercote
JonathanBrouwer Feb 10, 2026
10d1fd3
Rollup merge of #150688 - Zalathar:check-pat-lit, r=petrochenkov
JonathanBrouwer Feb 10, 2026
ee7ad93
Rollup merge of #152293 - ia0:try_blocks_heterogeneous, r=ytmimi
JonathanBrouwer Feb 10, 2026
0f7d646
Rollup merge of #152396 - ShoyuVanilla:uplift-allow-normalize, r=lcnr
JonathanBrouwer Feb 10, 2026
a58e015
Rollup merge of #152425 - Ozzy1423:test-runner, r=JonathanBrouwer
JonathanBrouwer Feb 10, 2026
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: 29 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,32 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcOutlivesParser {
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOutlives;
}

pub(crate) struct TestRunnerParser;

impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
const PATH: &[Symbol] = &[sym::test_runner];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(List: &["path"]);

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return None;
};

let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};

let Some(meta) = single.meta_item() else {
cx.unexpected_literal(single.span());
return None;
};

Some(AttributeKind::TestRunner(meta.path().0.clone()))
}
}
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ attribute_parsers!(
Single<SanitizeParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,
Single<TestRunnerParser>,
Single<TransparencyParser>,
Single<TypeLengthLimitParser>,
Single<WindowsSubsystemParser>,
Expand Down
14 changes: 0 additions & 14 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,20 +1002,6 @@ pub(crate) struct AsmUnsupportedClobberAbi {
pub(crate) macro_name: &'static str,
}

#[derive(Diagnostic)]
#[diag("`test_runner` argument must be a path")]
pub(crate) struct TestRunnerInvalid {
#[primary_span]
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag("`#![test_runner(..)]` accepts exactly 1 argument")]
pub(crate) struct TestRunnerNargs {
#[primary_span]
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag("expected token: `,`")]
pub(crate) struct ExpectedCommaInList {
Expand Down
31 changes: 14 additions & 17 deletions compiler/rustc_builtin_macros/src/test_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use rustc_ast::entry::EntryPointType;
use rustc_ast::mut_visit::*;
use rustc_ast::visit::Visitor;
use rustc_ast::{ModKind, attr};
use rustc_errors::DiagCtxtHandle;
use rustc_attr_parsing::AttributeParser;
use rustc_expand::base::{ExtCtxt, ResolverExpand};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind;
use rustc_session::Session;
use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS;
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
Expand Down Expand Up @@ -60,7 +61,7 @@ pub fn inject(

// Do this here so that the test_runner crate attribute gets marked as used
// even in non-test builds
let test_runner = get_test_runner(dcx, krate);
let test_runner = get_test_runner(sess, features, krate);

if sess.is_test_crate() {
let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) {
Expand Down Expand Up @@ -386,20 +387,16 @@ fn get_test_name(i: &ast::Item) -> Option<Symbol> {
attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker)
}

fn get_test_runner(dcx: DiagCtxtHandle<'_>, krate: &ast::Crate) -> Option<ast::Path> {
let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
let meta_list = test_attr.meta_item_list()?;
let span = test_attr.span;
match &*meta_list {
[single] => match single.meta_item() {
Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
_ => {
dcx.emit_err(errors::TestRunnerInvalid { span });
}
},
_ => {
dcx.emit_err(errors::TestRunnerNargs { span });
}
fn get_test_runner(sess: &Session, features: &Features, krate: &ast::Crate) -> Option<ast::Path> {
match AttributeParser::parse_limited(
sess,
&krate.attrs,
sym::test_runner,
krate.spans.inner_span,
krate.id,
Some(features),
) {
Some(rustc_hir::Attribute::Parsed(AttributeKind::TestRunner(path))) => Some(path),
_ => None,
}
None
}
5 changes: 4 additions & 1 deletion compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub use ReprAttr::*;
use rustc_abi::Align;
pub use rustc_ast::attr::data_structures::*;
use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrStyle, ast};
use rustc_ast::{AttrStyle, Path, ast};
use rustc_data_structures::fx::FxIndexMap;
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
Expand Down Expand Up @@ -1292,6 +1292,9 @@ pub enum AttributeKind {
/// `#[unsafe(force_target_feature(enable = "...")]`.
TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool },

/// Represents `#![test_runner(path)]`
TestRunner(Path),

/// Represents `#[thread_local]`
ThreadLocal,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ impl AttributeKind {
ShouldPanic { .. } => No,
Stability { .. } => Yes,
TargetFeature { .. } => No,
TestRunner(..) => Yes,
ThreadLocal => No,
TrackCaller(..) => Yes,
TypeLengthLimit { .. } => No,
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_hir/src/attrs/pretty_printing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::ops::Deref;
use std::path::PathBuf;

use rustc_abi::Align;
use rustc_ast::ast::{Path, join_path_idents};
use rustc_ast::attr::data_structures::CfgEntry;
use rustc_ast::attr::version::RustcVersion;
use rustc_ast::token::{CommentKind, DocFragmentKind};
Expand Down Expand Up @@ -106,6 +107,16 @@ impl PrintAttribute for PathBuf {
p.word(self.display().to_string());
}
}
impl PrintAttribute for Path {
fn should_render(&self) -> bool {
true
}

fn print_attribute(&self, p: &mut Printer) {
p.word(join_path_idents(self.segments.iter().map(|seg| seg.ident)));
}
}

macro_rules! print_skip {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {
Expand Down
32 changes: 19 additions & 13 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ use rustc_abi::{ExternAbi, Size};
use rustc_ast::Recovered;
use rustc_data_structures::assert_matches;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey, struct_span_code_err,
};
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
Expand Down Expand Up @@ -318,16 +316,24 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
}

fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> {
if let RegionInferReason::ObjectLifetimeDefault = reason {
let e = struct_span_code_err!(
self.dcx(),
span,
E0228,
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound"
)
.emit();
ty::Region::new_error(self.tcx(), e)
if let RegionInferReason::ObjectLifetimeDefault(sugg_sp) = reason {
// FIXME: Account for trailing plus `dyn Trait+`, the need of parens in
// `*const dyn Trait` and `Fn() -> *const dyn Trait`.
let guar = self
.dcx()
.struct_span_err(
span,
"cannot deduce the lifetime bound for this trait object type from context",
)
.with_code(E0228)
.with_span_suggestion_verbose(
sugg_sp,
"please supply an explicit bound",
" + /* 'a */",
Applicability::HasPlaceholders,
)
.emit();
ty::Region::new_error(self.tcx(), guar)
} else {
// This indicates an illegal lifetime in a non-assoc-trait position
ty::Region::new_error_with_message(self.tcx(), span, "unelided lifetime in signature")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Parent lifetime must have failed to resolve. Don't emit a redundant error.
RegionInferReason::ExplicitObjectLifetime
} else {
RegionInferReason::ObjectLifetimeDefault
RegionInferReason::ObjectLifetimeDefault(span.shrink_to_hi())
}
} else {
RegionInferReason::ExplicitObjectLifetime
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub enum RegionInferReason<'a> {
/// Lifetime on a trait object that is spelled explicitly, e.g. `+ 'a` or `+ '_`.
ExplicitObjectLifetime,
/// A trait object's lifetime when it is elided, e.g. `dyn Any`.
ObjectLifetimeDefault,
ObjectLifetimeDefault(Span),
/// Generic lifetime parameter
Param(&'a ty::GenericParamDef),
RegionPredicate,
Expand Down
24 changes: 12 additions & 12 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};

use rustc_abi::FieldIdx;
use rustc_ast as ast;
use rustc_data_structures::assert_matches;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::codes::*;
use rustc_errors::{
Expand All @@ -24,7 +25,6 @@ use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
Expand Down Expand Up @@ -611,7 +611,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.write_ty(*hir_id, ty);
ty
}
PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, &pat_info.top_info),
PatKind::Expr(expr @ PatExpr { kind: PatExprKind::Lit { lit, .. }, .. }) => {
self.check_pat_lit(pat.span, expr, &lit.node, expected, &pat_info.top_info)
}
PatKind::Range(lhs, rhs, _) => {
self.check_pat_range(pat.span, lhs, rhs, expected, &pat_info.top_info)
}
Expand Down Expand Up @@ -938,31 +940,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_pat_lit(
&self,
span: Span,
lt: &hir::PatExpr<'tcx>,
expr: &hir::PatExpr<'tcx>,
lit_kind: &ast::LitKind,
expected: Ty<'tcx>,
ti: &TopInfo<'tcx>,
) -> Ty<'tcx> {
assert_matches!(expr.kind, hir::PatExprKind::Lit { .. });

// We've already computed the type above (when checking for a non-ref pat),
// so avoid computing it again.
let ty = self.node_ty(lt.hir_id);
let ty = self.node_ty(expr.hir_id);

// Byte string patterns behave the same way as array patterns
// They can denote both statically and dynamically-sized byte arrays.
// Additionally, when `deref_patterns` is enabled, byte string literal patterns may have
// types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`.
let mut pat_ty = ty;
if let hir::PatExprKind::Lit {
lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, ..
} = lt.kind
{
if matches!(lit_kind, ast::LitKind::ByteStr(..)) {
let tcx = self.tcx;
let expected = self.structurally_resolve_type(span, expected);
match *expected.kind() {
// Allow `b"...": &[u8]`
ty::Ref(_, inner_ty, _)
if self.try_structurally_resolve_type(span, inner_ty).is_slice() =>
{
trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
trace!(?expr.hir_id.local_id, "polymorphic byte string lit");
pat_ty = Ty::new_imm_ref(
tcx,
tcx.lifetimes.re_static,
Expand All @@ -988,9 +990,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow
// string literal patterns to have type `str`. This is accounted for when lowering to MIR.
if self.tcx.features().deref_patterns()
&& let hir::PatExprKind::Lit {
lit: Spanned { node: ast::LitKind::Str(..), .. }, ..
} = lt.kind
&& matches!(lit_kind, ast::LitKind::Str(..))
&& self.try_structurally_resolve_type(span, expected).is_str()
{
pat_ty = self.tcx.types.str_;
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
#![allow(unused_parens)]

use std::ffi::OsStr;
use std::mem;
use std::path::PathBuf;
use std::sync::Arc;

Expand Down
33 changes: 22 additions & 11 deletions compiler/rustc_middle/src/query/arena_cached.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
use std::mem;

use rustc_arena::TypedArena;

use crate::ty::TyCtxt;

/// Helper trait that allows `arena_cache` queries to return `Option<&T>`
/// instead of `&Option<T>`, and avoid allocating `None` in the arena.
///
Expand All @@ -11,10 +17,11 @@ pub trait ArenaCached<'tcx>: Sized {
/// Type that is stored in the arena.
type Allocated: 'tcx;

/// Takes a provided value, and allocates it in the arena (if appropriate)
/// with the help of the given `arena_alloc` closure.
/// Takes a provided value, and allocates it in an appropriate arena,
/// unless the particular value doesn't need allocation (e.g. `None`).
fn alloc_in_arena(
arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated,
tcx: TyCtxt<'tcx>,
typed_arena: &'tcx TypedArena<Self::Allocated>,
value: Self::Provided,
) -> Self;
}
Expand All @@ -23,12 +30,9 @@ impl<'tcx, T> ArenaCached<'tcx> for &'tcx T {
type Provided = T;
type Allocated = T;

fn alloc_in_arena(
arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated,
value: Self::Provided,
) -> Self {
fn alloc_in_arena(tcx: TyCtxt<'tcx>, typed_arena: &'tcx TypedArena<T>, value: T) -> Self {
// Just allocate in the arena normally.
arena_alloc(value)
do_alloc(tcx, typed_arena, value)
}
}

Expand All @@ -38,10 +42,17 @@ impl<'tcx, T> ArenaCached<'tcx> for Option<&'tcx T> {
type Allocated = T;

fn alloc_in_arena(
arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated,
value: Self::Provided,
tcx: TyCtxt<'tcx>,
typed_arena: &'tcx TypedArena<T>,
value: Option<T>,
) -> Self {
// Don't store None in the arena, and wrap the allocated reference in Some.
value.map(arena_alloc)
try { do_alloc(tcx, typed_arena, value?) }
}
}

/// Allocates a value in either its dedicated arena, or in the common dropless
/// arena, depending on whether it needs to be dropped.
fn do_alloc<'tcx, T>(tcx: TyCtxt<'tcx>, typed_arena: &'tcx TypedArena<T>, value: T) -> &'tcx T {
if mem::needs_drop::<T>() { typed_arena.alloc(value) } else { tcx.arena.dropless.alloc(value) }
}
Loading
Loading