Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
675ba38
feat: add `from_fn_ptr` to `Waker` and `LocalWaker`
Ddystopia Aug 31, 2025
8537abb
Stabilize `fmt::{from_fn, FromFn}` under feature `fmt_from_fn`
coolreader18 Aug 27, 2025
f8d7b41
Reword docs slightly
coolreader18 Aug 27, 2025
859ae05
Remove some feature(debug_closure_helpers) attributes
coolreader18 Aug 27, 2025
93c8bf1
Remove F: Fn bound from FromFn struct
coolreader18 Sep 19, 2025
7cc7480
Make named asm_labels lint not trigger on hexagon register spans
androm3da Jun 26, 2025
471f2ba
library: std: sys: net: uefi: tcp: Implement write_vectored
Ayush1325 Sep 7, 2025
484424b
Add `#[rustc_pass_indirectly_in_non_rustic_abis]`
beetrees Jul 4, 2025
d300134
assert that `#[rustc_pass_indirectly_in_non_rustic_abis]` is respected
folkertdev Sep 24, 2025
27a34ec
additional tests for `pass_indirectly_in_non_rustic_abis`
folkertdev Sep 24, 2025
1bc898e
test for aarch64-unknown-uefi
Jamesbarford Nov 3, 2025
4959d18
Rename -Zno-jump-tables to -Zjump-tables=<bool>
pmur Aug 28, 2025
bb9d800
Stabilize -Zjump-tables=<bool> into -Cjump-table=<bool>
pmur Aug 28, 2025
a78ba93
Improve documentation of -Cjump-tables
pmur Sep 11, 2025
b24b7bd
Update books
rustbot Nov 3, 2025
97e69d1
tidy: Fix false positives with absolute repo paths in `pal.rs` `check()`
Enselic Nov 3, 2025
fd11125
CI: rfl: move to temporary commit to support `-Cjump-tables=n`
ojeda Nov 1, 2025
4ab1fc5
Provide more context on `Fn` closure modifying binding
estebank Nov 17, 2024
75835fb
Repoint Waker::from_fn_ptr from feature request issue to tracking issue
dtolnay Nov 4, 2025
cb8a5cc
Rollup merge of #133149 - estebank:niko-rustnation, r=wesleywiser
Zalathar Nov 4, 2025
6b0a5c0
Rollup merge of #143037 - androm3da:bcain/hexagon_regspan_label, r=Am…
Zalathar Nov 4, 2025
0d23fc3
Rollup merge of #144529 - beetrees:pass-indirectly-attr, r=bjorn3
Zalathar Nov 4, 2025
96db472
Rollup merge of #145915 - coolreader18:stabilize-fmt_from_fn, r=dtolnay
Zalathar Nov 4, 2025
327018c
Rollup merge of #145974 - pmur:murp/stabilize-zno-jump-tables, r=wesl…
Zalathar Nov 4, 2025
a21b45c
Rollup merge of #146057 - Ddystopia:waker-fn, r=dtolnay
Zalathar Nov 4, 2025
4551083
Rollup merge of #146301 - Ayush1325:uefi-box, r=joboet
Zalathar Nov 4, 2025
eeafaff
Rollup merge of #148437 - Jamesbarford:test/issue-98254, r=jieyouxu
Zalathar Nov 4, 2025
5144cf8
Rollup merge of #148448 - rustbot:docs-update, r=ehuss
Zalathar Nov 4, 2025
161ba70
Rollup merge of #148451 - Enselic:tidy-fix, r=clubby789
Zalathar Nov 4, 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
26 changes: 26 additions & 0 deletions compiler/rustc_abi/src/layout/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool;
}

impl<'a, Ty> TyAndLayout<'a, Ty> {
Expand Down Expand Up @@ -269,6 +271,30 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
Ty::is_transparent(self)
}

/// If this method returns `true`, then this type should always have a `PassMode` of
/// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a
/// non-Rustic ABI (this is true for structs annotated with the
/// `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute).
///
/// This is used to replicate some of the behaviour of C array-to-pointer decay; however unlike
/// C any changes the caller makes to the passed value will not be reflected in the callee, so
/// the attribute is only useful for types where observing the value in the caller after the
/// function call isn't allowed (a.k.a. `va_list`).
///
/// This function handles transparent types automatically.
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
while self.is_transparent()
&& let Some((_, field)) = self.non_1zst_field(cx)
{
self = field;
}

Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
}

/// Finds the one field that is not a 1-ZST.
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,17 @@ bitflags! {
const IS_C = 1 << 0;
const IS_SIMD = 1 << 1;
const IS_TRANSPARENT = 1 << 2;
// Internal only for now. If true, don't reorder fields.
// On its own it does not prevent ABI optimizations.
/// Internal only for now. If true, don't reorder fields.
/// On its own it does not prevent ABI optimizations.
const IS_LINEAR = 1 << 3;
// If true, the type's crate has opted into layout randomization.
// Other flags can still inhibit reordering and thus randomization.
// The seed stored in `ReprOptions.field_shuffle_seed`.
/// If true, the type's crate has opted into layout randomization.
/// Other flags can still inhibit reordering and thus randomization.
/// The seed stored in `ReprOptions.field_shuffle_seed`.
const RANDOMIZE_LAYOUT = 1 << 4;
// Any of these flags being set prevent field reordering optimisation.
/// If true, the type is always passed indirectly by non-Rustic ABIs.
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5;
/// Any of these flags being set prevent field reordering optimisation.
const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits()
| ReprFlags::IS_SIMD.bits()
| ReprFlags::IS_LINEAR.bits();
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,3 +676,12 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
}
}

pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
}
6 changes: 4 additions & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ use crate::attributes::allow_unstable::{
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser,
TargetFeatureParser, TrackCallerParser, UsedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
Expand Down Expand Up @@ -241,6 +242,7 @@ attribute_parsers!(
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
Single<WithoutArgs<RustcMainParser>>,
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<TrackCallerParser>>,
Expand Down
58 changes: 50 additions & 8 deletions compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
}
PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
PlaceRef { local, projection: [proj_base @ .., ProjectionElem::Deref] } => {
if local == ty::CAPTURE_STRUCT_LOCAL
&& proj_base.is_empty()
&& !self.upvars.is_empty()
{
Expand All @@ -165,10 +165,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
", as `Fn` closures cannot mutate their captured variables".to_string()
}
} else {
let source = self.borrowed_content_source(PlaceRef {
local: the_place_err.local,
projection: proj_base,
});
let source =
self.borrowed_content_source(PlaceRef { local, projection: proj_base });
let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
opt_source = Some(source);
if let Some(desc) = self.describe_place(access_place.as_ref()) {
Expand Down Expand Up @@ -532,6 +530,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
PlaceRef { local, projection: [ProjectionElem::Deref] }
if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
{
self.point_at_binding_outside_closure(&mut err, local, access_place);
self.expected_fn_found_fn_mut_call(&mut err, span, act);
}

Expand Down Expand Up @@ -950,6 +949,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}

/// When modifying a binding from inside of an `Fn` closure, point at the binding definition.
fn point_at_binding_outside_closure(
&self,
err: &mut Diag<'_>,
local: Local,
access_place: Place<'tcx>,
) {
let place = access_place.as_ref();
for (index, elem) in place.projection.into_iter().enumerate() {
if let ProjectionElem::Deref = elem {
if index == 0 {
if self.body.local_decls[local].is_ref_for_guard() {
continue;
}
if let LocalInfo::StaticRef { .. } = *self.body.local_decls[local].local_info()
{
continue;
}
}
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
local,
projection: place.projection.split_at(index + 1).0,
}) {
let var_index = field.index();
let upvar = self.upvars[var_index];
if let Some(hir_id) = upvar.info.capture_kind_expr_id {
let node = self.infcx.tcx.hir_node(hir_id);
if let hir::Node::Expr(expr) = node
&& let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
&& let hir::def::Res::Local(hir_id) = path.res
&& let hir::Node::Pat(pat) = self.infcx.tcx.hir_node(hir_id)
{
let name = upvar.to_string(self.infcx.tcx);
err.span_label(
pat.span,
format!("`{name}` declared here, outside the closure"),
);
break;
}
}
}
}
}
}
/// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) {
err.span_label(sp, format!("cannot {act}"));
Expand All @@ -962,6 +1005,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
let mut look_at_return = true;

err.span_label(closure_span, "in this closure");
// If the HIR node is a function or method call, get the DefId
// of the callee function or method, the span, and args of the call expr
let get_call_details = || {
Expand Down Expand Up @@ -1032,7 +1076,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if let Some(span) = arg {
err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
err.span_label(call_span, "expects `Fn` instead of `FnMut`");
err.span_label(closure_span, "in this closure");
look_at_return = false;
}
}
Expand All @@ -1059,7 +1102,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
sig.decl.output.span(),
"change this to return `FnMut` instead of `Fn`",
);
err.span_label(closure_span, "in this closure");
}
_ => {}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ fn instrument_function_attr<'ll>(
}

fn nojumptables_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
if !sess.opts.unstable_opts.no_jump_tables {
if sess.opts.cg.jump_tables {
return None;
}

Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-naked-attribute"),
WarnFollowing, EncodeCrossCrate::No
),
// See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
rustc_attr!(
rustc_pass_indirectly_in_non_rustic_abis, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::No,
"types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic abis."
),

// Limits:
ungated!(
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,

/// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
RustcPassIndirectlyInNonRusticAbis(Span),

/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
RustcSimdMonomorphizeLaneLimit(Limit),

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 @@ -91,6 +91,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcMain => No,
RustcObjectLifetimeDefault => No,
RustcPassIndirectlyInNonRusticAbis(..) => No,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
Sanitize { .. } => No,
ShouldPanic { .. } => No,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
#![feature(associated_type_defaults)]
#![feature(closure_track_caller)]
#![feature(debug_closure_helpers)]
#![feature(exhaustive_patterns)]
#![feature(never_type)]
#![feature(variant_count)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ This API is completely unstable and subject to change.
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(debug_closure_helpers))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(debug_closure_helpers)]
#![feature(gen_blocks)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ fn test_codegen_options_tracking_hash() {
tracked!(force_frame_pointers, FramePointer::Always);
tracked!(force_unwind_tables, Some(true));
tracked!(instrument_coverage, InstrumentCoverage::Yes);
tracked!(jump_tables, false);
tracked!(link_dead_code, Some(true));
tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
Expand Down Expand Up @@ -831,7 +832,6 @@ fn test_unstable_options_tracking_hash() {
tracked!(mutable_noalias, false);
tracked!(next_solver, NextSolverConfig { coherence: true, globally: true });
tracked!(no_generate_arange_section, true);
tracked!(no_jump_tables, true);
tracked!(no_link, true);
tracked!(no_profiler_runtime, true);
tracked!(no_trait_vptr, true);
Expand Down
73 changes: 73 additions & 0 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2875,6 +2875,71 @@ enum AsmLabelKind {
Binary,
}

/// Checks if a potential label is actually a Hexagon register span notation.
///
/// Hexagon assembly uses register span notation like `r1:0`, `V5:4.w`, `p1:0` etc.
/// These follow the pattern: `[letter][digit(s)]:[digit(s)][optional_suffix]`
///
/// Returns `true` if the string matches a valid Hexagon register span pattern.
pub fn is_hexagon_register_span(possible_label: &str) -> bool {
// Extract the full register span from the context
if let Some(colon_idx) = possible_label.find(':') {
let after_colon = &possible_label[colon_idx + 1..];
is_hexagon_register_span_impl(&possible_label[..colon_idx], after_colon)
} else {
false
}
}

/// Helper function for use within the lint when we have statement context.
fn is_hexagon_register_span_context(
possible_label: &str,
statement: &str,
colon_idx: usize,
) -> bool {
// Extract what comes after the colon in the statement
let after_colon_start = colon_idx + 1;
if after_colon_start >= statement.len() {
return false;
}

// Get the part after the colon, up to the next whitespace or special character
let after_colon_full = &statement[after_colon_start..];
let after_colon = after_colon_full
.chars()
.take_while(|&c| c.is_ascii_alphanumeric() || c == '.')
.collect::<String>();

is_hexagon_register_span_impl(possible_label, &after_colon)
}

/// Core implementation for checking hexagon register spans.
fn is_hexagon_register_span_impl(before_colon: &str, after_colon: &str) -> bool {
if before_colon.len() < 1 || after_colon.is_empty() {
return false;
}

let mut chars = before_colon.chars();
let start = chars.next().unwrap();

// Must start with a letter (r, V, p, etc.)
if !start.is_ascii_alphabetic() {
return false;
}

let rest = &before_colon[1..];

// Check if the part after the first letter is all digits and non-empty
if rest.is_empty() || !rest.chars().all(|c| c.is_ascii_digit()) {
return false;
}

// Check if after colon starts with digits (may have suffix like .w, .h)
let digits_after = after_colon.chars().take_while(|c| c.is_ascii_digit()).collect::<String>();

!digits_after.is_empty()
}

impl<'tcx> LateLintPass<'tcx> for AsmLabels {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if let hir::Expr {
Expand Down Expand Up @@ -2957,6 +3022,14 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
break 'label_loop;
}

// Check for Hexagon register span notation (e.g., "r1:0", "V5:4", "V3:2.w")
// This is valid Hexagon assembly syntax, not a label
if matches!(cx.tcx.sess.asm_arch, Some(InlineAsmArch::Hexagon))
&& is_hexagon_register_span_context(possible_label, statement, idx)
{
break 'label_loop;
}

for c in chars {
// Inside a template format arg, any character is permitted for the
// purposes of label detection because we assume that it can be
Expand Down
Loading
Loading