Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c783871
coverage: Hoist expansion tree creation out of span refinement
Zalathar Nov 9, 2025
075f9c4
coverage: Eagerly ensure that span refinement has an expansion tree node
Zalathar Nov 11, 2025
696690b
coverage: Associate hole spans with expansion tree nodes
Zalathar Nov 11, 2025
06e2e7a
CStr docs: Fix CStr vs &CStr confusion
btj Nov 11, 2025
a56fc9c
Some tweaks
btj Nov 11, 2025
65b5d76
Implement `Read::read_array`
joshtriplett Nov 12, 2025
a0fe930
Refactor `Box::take`
edwloef Nov 12, 2025
c7e50d0
Remove unused LLVMModuleRef argument
QnnOkabayashi Nov 12, 2025
258a446
Simplify `Resolver::resolve_macro_path`.
nnethercote Nov 11, 2025
8ece939
Remove `trace` argument from `resolve_macro_or_delegation_path`.
nnethercote Nov 11, 2025
b728064
Add a helpful comment to `DeriveResolution::exts`.
nnethercote Nov 12, 2025
199f308
Guard against incorrect `read_buf_exact` implementations
joshtriplett Nov 13, 2025
a40c3e5
Disable rustdoc-test-builder test partially for SGX target.
Nov 13, 2025
776405c
add missing s390x target feature to std detect test
folkertdev Nov 13, 2025
5d70446
Emit error when using path-segment keyword as cfg pred
mu001999 Oct 16, 2025
abaccae
waffle: stop watching codegen ssa
WaffleLapkin Nov 13, 2025
78beefe
error when ABI does not support guaranteed tail calls
folkertdev Nov 12, 2025
101ef2b
Correctly link to associated trait items in reexports
GuillaumeGomez Nov 5, 2025
044245c
Add regression test for #148008
GuillaumeGomez Nov 5, 2025
ad1789a
Expose fmt::Arguments::from_str as unstable.
m-ou-se Nov 13, 2025
ddebb62
add assembly test for infinite recursion with `become`
folkertdev Nov 13, 2025
3b3e3bc
Rollup merge of #146978 - mu001999-contrib:fix/path-kw-as-cfg-pred, r…
Zalathar Nov 13, 2025
c39f35a
Rollup merge of #148543 - GuillaumeGomez:fix-import_trait_associated_…
Zalathar Nov 13, 2025
4e7a1da
Rollup merge of #148808 - nnethercote:resolve-cleanups, r=chenyukang,…
Zalathar Nov 13, 2025
2b003d3
Rollup merge of #148812 - Zalathar:expansions, r=JonathanBrouwer
Zalathar Nov 13, 2025
96f2179
Rollup merge of #148826 - btj:cstr-docs, r=joboet
Zalathar Nov 13, 2025
dab1ae9
Rollup merge of #148850 - joshtriplett:read-array, r=joboet
Zalathar Nov 13, 2025
ffcbeab
Rollup merge of #148867 - edwloef:refactor-box-take, r=joboet
Zalathar Nov 13, 2025
299c4fd
Rollup merge of #148870 - QnnOkabayashi:remove-unused-value, r=wesley…
Zalathar Nov 13, 2025
2b87ba5
Rollup merge of #148878 - folkertdev:tail-call-unsupported-abi, r=Waf…
Zalathar Nov 13, 2025
315d554
Rollup merge of #148901 - sardok:disable_rustdoc_test_builder_sgx, r=…
Zalathar Nov 13, 2025
4306047
Rollup merge of #148902 - folkertdev:detect-s390x-target-feature, r=t…
Zalathar Nov 13, 2025
15b62f0
Rollup merge of #148904 - WaffleLapkin:uncodegenssasitself, r=WaffleL…
Zalathar Nov 13, 2025
d4c2129
Rollup merge of #148906 - m-ou-se:fmt-args-from-str, r=dtolnay
Zalathar Nov 13, 2025
5e5ba85
Rollup merge of #148907 - folkertdev:tail-call-infinite-recursion, r=…
Zalathar Nov 13, 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
45 changes: 45 additions & 0 deletions compiler/rustc_abi/src/extern_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,51 @@ impl ExternAbi {
_ => CVariadicStatus::NotSupported,
}
}

/// Returns whether the ABI supports guaranteed tail calls.
#[cfg(feature = "nightly")]
pub fn supports_guaranteed_tail_call(self) -> bool {
match self {
Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {
// See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers
// before returning, and hence cannot guarantee a tail call.
false
}
Self::AvrInterrupt
| Self::AvrNonBlockingInterrupt
| Self::Msp430Interrupt
| Self::RiscvInterruptM
| Self::RiscvInterruptS
| Self::X86Interrupt => {
// See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.
false
}
Self::GpuKernel | Self::PtxKernel => {
// See https://godbolt.org/z/jq5TE5jK1.
false
}
Self::Custom => {
// This ABI does not support calls at all (except via assembly).
false
}
Self::C { .. }
| Self::System { .. }
| Self::Rust
| Self::RustCall
| Self::RustCold
| Self::RustInvalid
| Self::Unadjusted
| Self::EfiApi
| Self::Aapcs { .. }
| Self::Cdecl { .. }
| Self::Stdcall { .. }
| Self::Fastcall { .. }
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. } => true,
}
}
}

pub fn all_names() -> Vec<&'static str> {
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ pub fn parse_cfg_entry<S: Stage>(
}
},
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = meta.path().word_sym() else {
let Some(name) = meta.path().word_sym().filter(|s| !s.is_path_segment_keyword())
else {
return Err(cx.expected_identifier(meta.path().span()));
};
parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)?
Expand Down Expand Up @@ -158,7 +159,7 @@ fn parse_cfg_entry_target<S: Stage>(
};

// Then, parse it as a name-value item
let Some(name) = sub_item.path().word_sym() else {
let Some(name) = sub_item.path().word_sym().filter(|s| !s.is_path_segment_keyword()) else {
return Err(cx.expected_identifier(sub_item.path().span()));
};
let name = Symbol::intern(&format!("target_{name}"));
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/cfg_old.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ pub fn eval_condition(
}
}
}
MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
MetaItemKind::Word | MetaItemKind::NameValue(..)
if cfg.path.segments.len() != 1
|| cfg.path.segments[0].ident.is_path_segment_keyword() =>
{
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
true
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ pub(crate) unsafe fn llvm_optimize(
// Here we map the old arguments to the new arguments, with an offset of 1 to make sure
// that we don't use the newly added `%dyn_ptr`.
unsafe {
llvm::LLVMRustOffloadMapper(cx.llmod(), old_fn, new_fn);
llvm::LLVMRustOffloadMapper(old_fn, new_fn);
}

llvm::set_linkage(new_fn, llvm::get_linkage(old_fn));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2025,7 +2025,7 @@ unsafe extern "C" {
) -> &Attribute;

// Operations on functions
pub(crate) fn LLVMRustOffloadMapper<'a>(M: &'a Module, Fn: &'a Value, Fn: &'a Value);
pub(crate) fn LLVMRustOffloadMapper<'a>(Fn: &'a Value, Fn: &'a Value);
pub(crate) fn LLVMRustGetOrInsertFunction<'a>(
M: &'a Module,
Name: *const c_char,
Expand Down
22 changes: 8 additions & 14 deletions compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,30 +532,24 @@ impl Emitter for HumanEmitter {
}
}

/// An emitter that does nothing when emitting a non-fatal diagnostic.
/// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent
/// failures of rustc, as witnessed e.g. in issue #89358.
pub struct FatalOnlyEmitter {
pub fatal_emitter: Box<dyn Emitter + DynSend>,
pub fatal_note: Option<String>,
/// An emitter that adds a note to each diagnostic.
pub struct EmitterWithNote {
pub emitter: Box<dyn Emitter + DynSend>,
pub note: String,
}

impl Emitter for FatalOnlyEmitter {
impl Emitter for EmitterWithNote {
fn source_map(&self) -> Option<&SourceMap> {
None
}

fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
if diag.level == Level::Fatal {
if let Some(fatal_note) = &self.fatal_note {
diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
}
self.fatal_emitter.emit_diagnostic(diag, registry);
}
diag.sub(Level::Note, self.note.clone(), MultiSpan::new());
self.emitter.emit_diagnostic(diag, registry);
}

fn translator(&self) -> &Translator {
self.fatal_emitter.translator()
self.emitter.translator()
}
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,9 @@ pub struct Indeterminate;
pub struct DeriveResolution {
pub path: ast::Path,
pub item: Annotatable,
// FIXME: currently this field is only used in `is_none`/`is_some` conditions. However, the
// `Arc<SyntaxExtension>` will be used if the FIXME in `MacroExpander::fully_expand_fragment`
// is completed.
pub exts: Option<Arc<SyntaxExtension>>,
pub is_const: bool,
}
Expand Down
70 changes: 44 additions & 26 deletions compiler/rustc_interface/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_middle::ty::CurrentGcx;
use rustc_middle::util::Providers;
use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str;
use rustc_parse::parser::Recovery;
use rustc_parse::parser::attr::AllowLeadingUnsafe;
use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::print_query_stack;
Expand Down Expand Up @@ -52,46 +53,56 @@ pub struct Compiler {
pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
cfgs.into_iter()
.map(|s| {
let psess = ParseSess::with_fatal_emitter(
let psess = ParseSess::emitter_with_note(
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
format!("this error occurred on the command line: `--cfg={s}`"),
format!("this occurred on the command line: `--cfg={s}`"),
);
let filename = FileName::cfg_spec_source_code(&s);

macro_rules! error {
($reason: expr) => {
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
dcx.fatal(format!(
concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
s
));
dcx.fatal(format!("invalid `--cfg` argument: `{s}` ({})", $reason));
};
}

match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing)
{
Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) {
Ok(meta_item) if parser.token == token::Eof => {
if meta_item.path.segments.len() != 1 {
error!("argument key must be an identifier");
}
match &meta_item.kind {
MetaItemKind::List(..) => {}
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
error!("argument value must be a string");
Ok(mut parser) => {
parser = parser.recovery(Recovery::Forbidden);
match parser.parse_meta_item(AllowLeadingUnsafe::No) {
Ok(meta_item)
if parser.token == token::Eof
&& parser.dcx().has_errors().is_none() =>
{
if meta_item.path.segments.len() != 1 {
error!("argument key must be an identifier");
}
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
let ident = meta_item.ident().expect("multi-segment cfg key");
return (ident.name, meta_item.value_str());
match &meta_item.kind {
MetaItemKind::List(..) => {}
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
error!("argument value must be a string");
}
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
let ident = meta_item.ident().expect("multi-segment cfg key");

if ident.is_path_segment_keyword() {
error!(
"malformed `cfg` input, expected a valid identifier"
);
}

return (ident.name, meta_item.value_str());
}
}
}
Ok(..) => {}
Err(err) => err.cancel(),
}
Ok(..) => {}
Err(err) => err.cancel(),
},
}
Err(errs) => errs.into_iter().for_each(|err| err.cancel()),
}
};

// If the user tried to use a key="value" flag, but is missing the quotes, provide
// a hint about how to resolve this.
Expand All @@ -116,9 +127,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };

for s in specs {
let psess = ParseSess::with_fatal_emitter(
let psess = ParseSess::emitter_with_note(
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
format!("this error occurred on the command line: `--check-cfg={s}`"),
format!("this occurred on the command line: `--check-cfg={s}`"),
);
let filename = FileName::cfg_spec_source_code(&s);

Expand Down Expand Up @@ -171,15 +182,17 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
let mut parser =
match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing)
{
Ok(parser) => parser,
Ok(parser) => parser.recovery(Recovery::Forbidden),
Err(errs) => {
errs.into_iter().for_each(|err| err.cancel());
expected_error();
}
};

let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::No) {
Ok(meta_item) if parser.token == token::Eof => meta_item,
Ok(meta_item) if parser.token == token::Eof && parser.dcx().has_errors().is_none() => {
meta_item
}
Ok(..) => expected_error(),
Err(err) => {
err.cancel();
Expand Down Expand Up @@ -209,6 +222,11 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
if values_specified {
error!("`cfg()` names cannot be after values");
}

if ident.is_path_segment_keyword() {
error!("malformed `cfg` input, expected a valid identifier");
}

names.push(ident);
} else if let Some(boolean) = arg.boolean_literal() {
if values_specified {
Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ extern "C" void LLVMRustPrintStatistics(RustStringRef OutBuf) {
llvm::PrintStatistics(OS);
}

extern "C" void LLVMRustOffloadMapper(LLVMModuleRef M, LLVMValueRef OldFn,
LLVMValueRef NewFn) {
llvm::Module *module = llvm::unwrap(M);
extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) {
llvm::Function *oldFn = llvm::unwrap<llvm::Function>(OldFn);
llvm::Function *newFn = llvm::unwrap<llvm::Function>(NewFn);

Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_mir_build/src/check_tail_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi);
}

if !callee_sig.abi.supports_guaranteed_tail_call() {
self.report_unsupported_abi(expr.span, callee_sig.abi);
}

// FIXME(explicit_tail_calls): this currently fails for cases where opaques are used.
// e.g.
// ```
Expand Down Expand Up @@ -358,6 +362,16 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
self.found_errors = Err(err);
}

fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) {
let err = self
.tcx
.dcx()
.struct_span_err(sp, "ABI does not support guaranteed tail calls")
.with_note(format!("`become` is not supported for `extern {callee_abi}` functions"))
.emit();
self.found_errors = Err(err);
}

fn report_signature_mismatch(
&mut self,
sp: Span,
Expand Down
33 changes: 30 additions & 3 deletions compiler/rustc_mir_transform/src/coverage/expansion.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
use rustc_middle::mir;
use rustc_middle::mir::coverage::BasicCoverageBlock;
use rustc_span::{ExpnId, ExpnKind, Span};

use crate::coverage::from_mir;
use crate::coverage::graph::CoverageGraph;
use crate::coverage::hir_info::ExtractedHirInfo;

#[derive(Clone, Copy, Debug)]
pub(crate) struct SpanWithBcb {
pub(crate) span: Span,
Expand Down Expand Up @@ -70,6 +75,10 @@ pub(crate) struct ExpnNode {
pub(crate) spans: Vec<SpanWithBcb>,
/// Expansions whose call-site is in this expansion.
pub(crate) child_expn_ids: FxIndexSet<ExpnId>,

/// Hole spans belonging to this expansion, to be carved out from the
/// code spans during span refinement.
pub(crate) hole_spans: Vec<Span>,
}

impl ExpnNode {
Expand All @@ -88,17 +97,27 @@ impl ExpnNode {

spans: vec![],
child_expn_ids: FxIndexSet::default(),

hole_spans: vec![],
}
}
}

/// Given a collection of span/BCB pairs from potentially-different syntax contexts,
/// Extracts raw span/BCB pairs from potentially-different syntax contexts, and
/// arranges them into an "expansion tree" based on their expansion call-sites.
pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> ExpnTree {
pub(crate) fn build_expn_tree(
mir_body: &mir::Body<'_>,
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
) -> ExpnTree {
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);

let mut nodes = FxIndexMap::default();
let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id);

for span_with_bcb in spans {
for from_mir::RawSpanFromMir { raw_span, bcb } in raw_spans {
let span_with_bcb = SpanWithBcb { span: raw_span, bcb };

// Create a node for this span's enclosing expansion, and add the span to it.
let expn_id = span_with_bcb.span.ctxt().outer_expn();
let node = nodes.entry(expn_id).or_insert_with_key(new_node);
Expand All @@ -123,5 +142,13 @@ pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> E
}
}

// Associate each hole span (extracted from HIR) with its corresponding
// expansion tree node.
for &hole_span in &hir_info.hole_spans {
let expn_id = hole_span.ctxt().outer_expn();
let Some(node) = nodes.get_mut(&expn_id) else { continue };
node.hole_spans.push(hole_span);
}

ExpnTree { nodes }
}
Loading
Loading