Auto merge of #153183 - JonathanBrouwer:rollup-APFHc2s, r=JonathanBrouwer

Rollup of 12 pull requests

Successful merges:

 - rust-lang/rust#151143 (explicit tail calls: support indirect arguments)
 - rust-lang/rust#153012 (Stop using `LinkedGraph` in `lexical_region_resolve`)
 - rust-lang/rust#153175 (Clarify a confusing green-path function)
 - rust-lang/rust#153179 (Force a CI LLVM stamp bump)
 - rust-lang/rust#150828 (Improved security section in rustdoc for `current_exe`)
 - rust-lang/rust#152673 (rustc_public: rewrite `bridge_impl` to reduce boilerplate)
 - rust-lang/rust#152674 (rustc_public: remove the `CrateDefItems` trait)
 - rust-lang/rust#153073 (Fix mem::conjure_zst panic message to use any::type_name instead)
 - rust-lang/rust#153117 (Remove mutation from macro path URL construction)
 - rust-lang/rust#153128 (Recover feature lang_items for emscripten)
 - rust-lang/rust#153138 (Print path root when printing path)
 - rust-lang/rust#153159 (Work around a false `err.emit();` type error in rust-analyzer)
This commit is contained in:
bors
2026-02-27 22:04:20 +00:00
29 changed files with 533 additions and 276 deletions
+105 -19
View File
@@ -1146,19 +1146,51 @@ fn codegen_call_terminator(
(args, None)
};
// Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments.
//
// Normally an indirect argument with `on_stack: false` would be passed as a pointer into
// the caller's stack frame. For tail calls, that would be unsound, because the caller's
// stack frame is overwritten by the callee's stack frame.
//
// Therefore we store the argument for the callee in the corresponding caller's slot.
// Because guaranteed tail calls demand that the caller's signature matches the callee's,
// the corresponding slot has the correct type.
//
// To handle cases like the one below, the tail call arguments must first be copied to a
// temporary, and only then copied to the caller's argument slots.
//
// ```
// // A struct big enough that it is not passed via registers.
// pub struct Big([u64; 4]);
//
// fn swapper(a: Big, b: Big) -> (Big, Big) {
// become swapper_helper(b, a);
// }
// ```
let mut tail_call_temporaries = vec![];
if kind == CallKind::Tail {
tail_call_temporaries = vec![None; first_args.len()];
// Copy the arguments that use `PassMode::Indirect { on_stack: false , ..}`
// to temporary stack allocations. See the comment above.
for (i, arg) in first_args.iter().enumerate() {
if !matches!(fn_abi.args[i].mode, PassMode::Indirect { on_stack: false, .. }) {
continue;
}
let op = self.codegen_operand(bx, &arg.node);
let tmp = PlaceRef::alloca(bx, op.layout);
bx.lifetime_start(tmp.val.llval, tmp.layout.size);
op.store_with_annotation(bx, tmp);
tail_call_temporaries[i] = Some(tmp);
}
}
// When generating arguments we sometimes introduce temporary allocations with lifetime
// that extend for the duration of a call. Keep track of those allocations and their sizes
// to generate `lifetime_end` when the call returns.
let mut lifetime_ends_after_call: Vec<(Bx::Value, Size)> = Vec::new();
'make_args: for (i, arg) in first_args.iter().enumerate() {
if kind == CallKind::Tail && matches!(fn_abi.args[i].mode, PassMode::Indirect { .. }) {
// FIXME: https://github.com/rust-lang/rust/pull/144232#discussion_r2218543841
span_bug!(
fn_span,
"arguments using PassMode::Indirect are currently not supported for tail calls"
);
}
let mut op = self.codegen_operand(bx, &arg.node);
if let (0, Some(ty::InstanceKind::Virtual(_, idx))) = (i, instance.map(|i| i.def)) {
@@ -1209,18 +1241,72 @@ fn codegen_call_terminator(
}
}
// The callee needs to own the argument memory if we pass it
// by-ref, so make a local copy of non-immediate constants.
match (&arg.node, op.val) {
(&mir::Operand::Copy(_), Ref(PlaceValue { llextra: None, .. }))
| (&mir::Operand::Constant(_), Ref(PlaceValue { llextra: None, .. })) => {
let tmp = PlaceRef::alloca(bx, op.layout);
bx.lifetime_start(tmp.val.llval, tmp.layout.size);
op.store_with_annotation(bx, tmp);
op.val = Ref(tmp.val);
lifetime_ends_after_call.push((tmp.val.llval, tmp.layout.size));
match kind {
CallKind::Normal => {
// The callee needs to own the argument memory if we pass it
// by-ref, so make a local copy of non-immediate constants.
if let &mir::Operand::Copy(_) | &mir::Operand::Constant(_) = &arg.node
&& let Ref(PlaceValue { llextra: None, .. }) = op.val
{
let tmp = PlaceRef::alloca(bx, op.layout);
bx.lifetime_start(tmp.val.llval, tmp.layout.size);
op.store_with_annotation(bx, tmp);
op.val = Ref(tmp.val);
lifetime_ends_after_call.push((tmp.val.llval, tmp.layout.size));
}
}
CallKind::Tail => {
match fn_abi.args[i].mode {
PassMode::Indirect { on_stack: false, .. } => {
let Some(tmp) = tail_call_temporaries[i].take() else {
span_bug!(
fn_span,
"missing temporary for indirect tail call argument #{i}"
)
};
let local = self.mir.args_iter().nth(i).unwrap();
match &self.locals[local] {
LocalRef::Place(arg) => {
bx.typed_place_copy(arg.val, tmp.val, fn_abi.args[i].layout);
op.val = Ref(arg.val);
}
LocalRef::Operand(arg) => {
let Ref(place_value) = arg.val else {
bug!("only `Ref` should use `PassMode::Indirect`");
};
bx.typed_place_copy(
place_value,
tmp.val,
fn_abi.args[i].layout,
);
op.val = arg.val;
}
LocalRef::UnsizedPlace(_) => {
span_bug!(fn_span, "unsized types are not supported")
}
LocalRef::PendingOperand => {
span_bug!(fn_span, "argument local should not be pending")
}
};
bx.lifetime_end(tmp.val.llval, tmp.layout.size);
}
PassMode::Indirect { on_stack: true, .. } => {
// FIXME: some LLVM backends (notably x86) do not correctly pass byval
// arguments to tail calls (as of LLVM 21). See also:
//
// - https://github.com/rust-lang/rust/pull/144232#discussion_r2218543841
// - https://github.com/rust-lang/rust/issues/144855
span_bug!(
fn_span,
"arguments using PassMode::Indirect {{ on_stack: true, .. }} are currently not supported for tail calls"
)
}
_ => (),
}
}
_ => {}
}
self.codegen_argument(
@@ -0,0 +1,68 @@
use rustc_index::IndexVec;
use rustc_type_ir::RegionVid;
use crate::infer::SubregionOrigin;
use crate::infer::region_constraints::{Constraint, ConstraintKind, RegionConstraintData};
/// Selects either out-edges or in-edges for [`IndexedConstraintEdges::adjacent_edges`].
#[derive(Clone, Copy, Debug)]
pub(super) enum EdgeDirection {
Out,
In,
}
/// Type alias for the pairs stored in [`RegionConstraintData::constraints`],
/// which we are indexing.
type ConstraintPair<'tcx> = (Constraint<'tcx>, SubregionOrigin<'tcx>);
/// An index from region variables to their corresponding constraint edges,
/// used on some error paths.
pub(super) struct IndexedConstraintEdges<'data, 'tcx> {
out_edges: IndexVec<RegionVid, Vec<&'data ConstraintPair<'tcx>>>,
in_edges: IndexVec<RegionVid, Vec<&'data ConstraintPair<'tcx>>>,
}
impl<'data, 'tcx> IndexedConstraintEdges<'data, 'tcx> {
pub(super) fn build_index(num_vars: usize, data: &'data RegionConstraintData<'tcx>) -> Self {
let mut out_edges = IndexVec::from_fn_n(|_| vec![], num_vars);
let mut in_edges = IndexVec::from_fn_n(|_| vec![], num_vars);
for pair @ (c, _) in &data.constraints {
// Only push a var out-edge for `VarSub...` constraints.
match c.kind {
ConstraintKind::VarSubVar | ConstraintKind::VarSubReg => {
out_edges[c.sub.as_var()].push(pair)
}
ConstraintKind::RegSubVar | ConstraintKind::RegSubReg => {}
}
}
// Index in-edges in reverse order, to match what current tests expect.
// (It's unclear whether this is important or not.)
for pair @ (c, _) in data.constraints.iter().rev() {
// Only push a var in-edge for `...SubVar` constraints.
match c.kind {
ConstraintKind::VarSubVar | ConstraintKind::RegSubVar => {
in_edges[c.sup.as_var()].push(pair)
}
ConstraintKind::VarSubReg | ConstraintKind::RegSubReg => {}
}
}
IndexedConstraintEdges { out_edges, in_edges }
}
/// Returns either the out-edges or in-edges of the specified region var,
/// as selected by `dir`.
pub(super) fn adjacent_edges(
&self,
region_vid: RegionVid,
dir: EdgeDirection,
) -> &[&'data ConstraintPair<'tcx>] {
let edges = match dir {
EdgeDirection::Out => &self.out_edges,
EdgeDirection::In => &self.in_edges,
};
&edges[region_vid]
}
}
@@ -3,9 +3,6 @@
use std::fmt;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::linked_graph::{
Direction, INCOMING, LinkedGraph, NodeIndex, OUTGOING,
};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::unord::UnordSet;
use rustc_index::{IndexSlice, IndexVec};
@@ -18,11 +15,14 @@
use tracing::{debug, instrument};
use super::outlives::test_type_match;
use crate::infer::lexical_region_resolve::indexed_edges::{EdgeDirection, IndexedConstraintEdges};
use crate::infer::region_constraints::{
Constraint, ConstraintKind, GenericKind, RegionConstraintData, VarInfos, VerifyBound,
ConstraintKind, GenericKind, RegionConstraintData, VarInfos, VerifyBound,
};
use crate::infer::{RegionRelations, RegionVariableOrigin, SubregionOrigin};
mod indexed_edges;
/// This function performs lexical region resolution given a complete
/// set of constraints and variable origins. It performs a fixed-point
/// iteration to find region values which satisfy all constraints,
@@ -118,8 +118,6 @@ struct RegionAndOrigin<'tcx> {
origin: SubregionOrigin<'tcx>,
}
type RegionGraph<'tcx> = LinkedGraph<(), Constraint<'tcx>>;
struct LexicalResolver<'cx, 'tcx> {
region_rels: &'cx RegionRelations<'cx, 'tcx>,
var_infos: VarInfos<'tcx>,
@@ -626,9 +624,8 @@ fn collect_var_errors(
// overlapping locations.
let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars());
// Only construct the graph when necessary, because it's moderately
// expensive.
let mut graph = None;
// Only construct the edge index when necessary, because it's moderately expensive.
let mut edges: Option<IndexedConstraintEdges<'_, 'tcx>> = None;
for (node_vid, value) in var_data.values.iter_enumerated() {
match *value {
@@ -662,56 +659,18 @@ fn collect_var_errors(
// influence the constraints on this value for
// richer diagnostics in `static_impl_trait`.
let g = graph.get_or_insert_with(|| self.construct_graph());
self.collect_error_for_expanding_node(g, &mut dup_vec, node_vid, errors);
let e = edges.get_or_insert_with(|| {
IndexedConstraintEdges::build_index(self.num_vars(), &self.data)
});
self.collect_error_for_expanding_node(e, &mut dup_vec, node_vid, errors);
}
}
}
}
fn construct_graph(&self) -> RegionGraph<'tcx> {
let num_vars = self.num_vars();
let mut graph = LinkedGraph::new();
for _ in 0..num_vars {
graph.add_node(());
}
// Issue #30438: two distinct dummy nodes, one for incoming
// edges (dummy_source) and another for outgoing edges
// (dummy_sink). In `dummy -> a -> b -> dummy`, using one
// dummy node leads one to think (erroneously) there exists a
// path from `b` to `a`. Two dummy nodes sidesteps the issue.
let dummy_source = graph.add_node(());
let dummy_sink = graph.add_node(());
for (c, _) in &self.data.constraints {
match c.kind {
ConstraintKind::VarSubVar => {
let sub_vid = c.sub.as_var();
let sup_vid = c.sup.as_var();
graph.add_edge(NodeIndex(sub_vid.index()), NodeIndex(sup_vid.index()), *c);
}
ConstraintKind::RegSubVar => {
graph.add_edge(dummy_source, NodeIndex(c.sup.as_var().index()), *c);
}
ConstraintKind::VarSubReg => {
graph.add_edge(NodeIndex(c.sub.as_var().index()), dummy_sink, *c);
}
ConstraintKind::RegSubReg => {
// this would be an edge from `dummy_source` to
// `dummy_sink`; just ignore it.
}
}
}
graph
}
fn collect_error_for_expanding_node(
&self,
graph: &RegionGraph<'tcx>,
edges: &IndexedConstraintEdges<'_, 'tcx>,
dup_vec: &mut IndexSlice<RegionVid, Option<RegionVid>>,
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'tcx>>,
@@ -719,9 +678,9 @@ fn collect_error_for_expanding_node(
// Errors in expanding nodes result from a lower-bound that is
// not contained by an upper-bound.
let (mut lower_bounds, lower_vid_bounds, lower_dup) =
self.collect_bounding_regions(graph, node_idx, INCOMING, Some(dup_vec));
self.collect_bounding_regions(edges, node_idx, EdgeDirection::In, Some(dup_vec));
let (mut upper_bounds, _, upper_dup) =
self.collect_bounding_regions(graph, node_idx, OUTGOING, Some(dup_vec));
self.collect_bounding_regions(edges, node_idx, EdgeDirection::Out, Some(dup_vec));
if lower_dup || upper_dup {
return;
@@ -829,9 +788,9 @@ fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 {
/// those returned by a previous call for another region.
fn collect_bounding_regions(
&self,
graph: &RegionGraph<'tcx>,
edges: &IndexedConstraintEdges<'_, 'tcx>,
orig_node_idx: RegionVid,
dir: Direction,
dir: EdgeDirection,
mut dup_vec: Option<&mut IndexSlice<RegionVid, Option<RegionVid>>>,
) -> (Vec<RegionAndOrigin<'tcx>>, FxHashSet<RegionVid>, bool) {
struct WalkState<'tcx> {
@@ -850,7 +809,7 @@ struct WalkState<'tcx> {
// to start off the process, walk the source node in the
// direction specified
process_edges(&self.data, &mut state, graph, orig_node_idx, dir);
process_edges(&mut state, edges, orig_node_idx, dir);
while let Some(node_idx) = state.stack.pop() {
// check whether we've visited this node on some previous walk
@@ -867,30 +826,25 @@ struct WalkState<'tcx> {
);
}
process_edges(&self.data, &mut state, graph, node_idx, dir);
process_edges(&mut state, edges, node_idx, dir);
}
let WalkState { result, dup_found, set, .. } = state;
return (result, set, dup_found);
fn process_edges<'tcx>(
this: &RegionConstraintData<'tcx>,
state: &mut WalkState<'tcx>,
graph: &RegionGraph<'tcx>,
edges: &IndexedConstraintEdges<'_, 'tcx>,
source_vid: RegionVid,
dir: Direction,
dir: EdgeDirection,
) {
debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir);
let source_node_index = NodeIndex(source_vid.index());
for (_, edge) in graph.adjacent_edges(source_node_index, dir) {
let get_origin =
|| this.constraints.iter().find(|(c, _)| *c == edge.data).unwrap().1.clone();
match edge.data.kind {
for (c, origin) in edges.adjacent_edges(source_vid, dir) {
match c.kind {
ConstraintKind::VarSubVar => {
let from_vid = edge.data.sub.as_var();
let to_vid = edge.data.sup.as_var();
let from_vid = c.sub.as_var();
let to_vid = c.sup.as_var();
let opp_vid = if from_vid == source_vid { to_vid } else { from_vid };
if state.set.insert(opp_vid) {
state.stack.push(opp_vid);
@@ -898,13 +852,13 @@ fn process_edges<'tcx>(
}
ConstraintKind::RegSubVar => {
let origin = get_origin();
state.result.push(RegionAndOrigin { region: edge.data.sub, origin });
let origin = origin.clone();
state.result.push(RegionAndOrigin { region: c.sub, origin });
}
ConstraintKind::VarSubReg => {
let origin = get_origin();
state.result.push(RegionAndOrigin { region: edge.data.sup, origin });
let origin = origin.clone();
state.result.push(RegionAndOrigin { region: c.sup, origin });
}
ConstraintKind::RegSubReg => panic!(
+1 -1
View File
@@ -300,7 +300,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
diag.help(
"try lowering `-Z threads` or checking the operating system's resource limits",
);
diag.emit();
diag.emit()
})
})
})
@@ -121,17 +121,29 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
// `f` passes. Note that function arguments are the only situation in which this problem can
// arise: every other use of `move` in MIR doesn't actually write to the value it moves
// from.
if let TerminatorKind::Call { ref args, .. } = terminator.kind {
for arg in args {
if let Operand::Move(place) = arg.node
&& !place.is_indirect_first_projection()
&& let Some(i) = self.as_param(place.local)
{
self.usage[i] |= UsageSummary::MUTATE;
self.usage[i] |= UsageSummary::CAPTURE;
match terminator.kind {
TerminatorKind::Call { ref args, .. } => {
for arg in args {
if let Operand::Move(place) = arg.node
&& !place.is_indirect_first_projection()
&& let Some(i) = self.as_param(place.local)
{
self.usage[i] |= UsageSummary::MUTATE;
self.usage[i] |= UsageSummary::CAPTURE;
}
}
}
};
// Like a call, but more conservative because the backend may introduce writes to an
// argument if the argument is passed as `PassMode::Indirect { on_stack: false, ... }`.
TerminatorKind::TailCall { .. } => {
for usage in self.usage.iter_mut() {
*usage |= UsageSummary::MUTATE;
*usage |= UsageSummary::CAPTURE;
}
}
_ => {}
}
self.super_terminator(terminator, location);
}
+1 -1
View File
@@ -132,7 +132,7 @@ pub fn new_parser_from_file<'a>(
if let Some(sp) = sp {
err.span(sp);
}
err.emit();
err.emit()
});
new_parser_from_source_file(psess, source_file, strip_tokens)
}
+1 -15
View File
@@ -2,7 +2,7 @@
//! such as, a function, a trait, an enum, and any other definitions.
use crate::ty::{GenericArgs, Span, Ty, index_impl};
use crate::{AssocItems, Crate, Symbol, ThreadLocalIndex, with};
use crate::{Crate, Symbol, ThreadLocalIndex, with};
/// A unique identification number for each item accessible for the current compilation unit.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
@@ -111,14 +111,6 @@ fn ty_with_args(&self, args: &GenericArgs) -> Ty {
}
}
/// A trait for retrieving all items from a definition within a crate.
pub trait CrateDefItems: CrateDef {
/// Retrieve all associated items from a definition.
fn associated_items(&self) -> AssocItems {
with(|cx| cx.associated_items(self.def_id()))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute {
value: String,
@@ -174,9 +166,3 @@ fn def_id(&self) -> DefId {
impl CrateDefType for $name {}
};
}
macro_rules! impl_crate_def_items {
( $name:ident $(;)? ) => {
impl CrateDefItems for $name {}
};
}
+31 -26
View File
@@ -30,7 +30,7 @@
use serde::Serialize;
use crate::compiler_interface::with;
pub use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId};
pub use crate::crate_def::{CrateDef, CrateDefType, DefId};
pub use crate::error::*;
use crate::mir::mono::StaticDef;
use crate::mir::{Body, Mutability};
@@ -238,35 +238,40 @@ pub fn opaque<T: Debug>(value: &T) -> Opaque {
}
macro_rules! bridge_impl {
($name: ident, $ty: ty) => {
impl rustc_public_bridge::bridge::$name<compiler_interface::BridgeTys> for $ty {
fn new(def: crate::DefId) -> Self {
Self(def)
( $( $name:ident, $ty:ty ),* $(,)? ) => {
$(
impl rustc_public_bridge::bridge::$name<compiler_interface::BridgeTys> for $ty {
fn new(def: crate::DefId) -> Self {
Self(def)
}
}
}
)*
};
}
bridge_impl!(CrateItem, crate::CrateItem);
bridge_impl!(AdtDef, crate::ty::AdtDef);
bridge_impl!(ForeignModuleDef, crate::ty::ForeignModuleDef);
bridge_impl!(ForeignDef, crate::ty::ForeignDef);
bridge_impl!(FnDef, crate::ty::FnDef);
bridge_impl!(ClosureDef, crate::ty::ClosureDef);
bridge_impl!(CoroutineDef, crate::ty::CoroutineDef);
bridge_impl!(CoroutineClosureDef, crate::ty::CoroutineClosureDef);
bridge_impl!(AliasDef, crate::ty::AliasDef);
bridge_impl!(ParamDef, crate::ty::ParamDef);
bridge_impl!(BrNamedDef, crate::ty::BrNamedDef);
bridge_impl!(TraitDef, crate::ty::TraitDef);
bridge_impl!(GenericDef, crate::ty::GenericDef);
bridge_impl!(ConstDef, crate::ty::ConstDef);
bridge_impl!(ImplDef, crate::ty::ImplDef);
bridge_impl!(RegionDef, crate::ty::RegionDef);
bridge_impl!(CoroutineWitnessDef, crate::ty::CoroutineWitnessDef);
bridge_impl!(AssocDef, crate::ty::AssocDef);
bridge_impl!(OpaqueDef, crate::ty::OpaqueDef);
bridge_impl!(StaticDef, crate::mir::mono::StaticDef);
#[rustfmt::skip]
bridge_impl!(
CrateItem, crate::CrateItem,
AdtDef, crate::ty::AdtDef,
ForeignModuleDef, crate::ty::ForeignModuleDef,
ForeignDef, crate::ty::ForeignDef,
FnDef, crate::ty::FnDef,
ClosureDef, crate::ty::ClosureDef,
CoroutineDef, crate::ty::CoroutineDef,
CoroutineClosureDef, crate::ty::CoroutineClosureDef,
AliasDef, crate::ty::AliasDef,
ParamDef, crate::ty::ParamDef,
BrNamedDef, crate::ty::BrNamedDef,
TraitDef, crate::ty::TraitDef,
GenericDef, crate::ty::GenericDef,
ConstDef, crate::ty::ConstDef,
ImplDef, crate::ty::ImplDef,
RegionDef, crate::ty::RegionDef,
CoroutineWitnessDef, crate::ty::CoroutineWitnessDef,
AssocDef, crate::ty::AssocDef,
OpaqueDef, crate::ty::OpaqueDef,
StaticDef, crate::mir::mono::StaticDef
);
impl rustc_public_bridge::bridge::Prov<compiler_interface::BridgeTys> for crate::ty::Prov {
fn new(aid: crate::mir::alloc::AllocId) -> Self {
+10 -10
View File
@@ -7,11 +7,11 @@
use super::mir::{Body, Mutability, Safety};
use super::{DefId, Error, Symbol, with};
use crate::abi::{FnAbi, Layout};
use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType};
use crate::crate_def::{CrateDef, CrateDefType};
use crate::mir::alloc::{AllocId, read_target_int, read_target_uint};
use crate::mir::mono::StaticDef;
use crate::target::MachineInfo;
use crate::{Filename, IndexedVal, Opaque, ThreadLocalIndex};
use crate::{AssocItems, Filename, IndexedVal, Opaque, ThreadLocalIndex};
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Ty(usize, ThreadLocalIndex);
@@ -943,14 +943,14 @@ pub fn is_union(&self) -> bool {
pub TraitDef;
}
impl_crate_def_items! {
TraitDef;
}
impl TraitDef {
pub fn declaration(trait_def: &TraitDef) -> TraitDecl {
with(|cx| cx.trait_decl(trait_def))
}
pub fn associated_items(&self) -> AssocItems {
with(|cx| cx.associated_items(self.def_id()))
}
}
crate_def! {
@@ -969,15 +969,15 @@ pub fn declaration(trait_def: &TraitDef) -> TraitDecl {
pub ImplDef;
}
impl_crate_def_items! {
ImplDef;
}
impl ImplDef {
/// Retrieve information about this implementation.
pub fn trait_impl(&self) -> ImplTrait {
with(|cx| cx.trait_impl(self))
}
pub fn associated_items(&self) -> AssocItems {
with(|cx| cx.associated_items(self.def_id()))
}
}
crate_def! {
+33 -20
View File
@@ -5,7 +5,7 @@
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::{outline, sharded, sync};
use rustc_errors::{Diag, FatalError, StashKey};
use rustc_middle::dep_graph::{DepGraphData, DepNodeKey};
use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex};
use rustc_middle::query::plumbing::QueryVTable;
use rustc_middle::query::{
ActiveKeyStatus, CycleError, CycleErrorHandling, EnsureMode, QueryCache, QueryJob, QueryJobId,
@@ -455,8 +455,18 @@ fn execute_job_incr<'tcx, C: QueryCache>(
// The diagnostics for this query will be promoted to the current session during
// `try_mark_green()`, so we can ignore them here.
if let Some(ret) = start_query(tcx, job_id, false, || {
try_load_from_disk_and_cache_in_memory(query, dep_graph_data, tcx, &key, dep_node)
if let Some(ret) = start_query(tcx, job_id, false, || try {
let (prev_index, dep_node_index) = dep_graph_data.try_mark_green(tcx, dep_node)?;
let value = load_from_disk_or_invoke_provider_green(
tcx,
dep_graph_data,
query,
&key,
dep_node,
prev_index,
dep_node_index,
);
(value, dep_node_index)
}) {
return ret;
}
@@ -490,29 +500,32 @@ fn execute_job_incr<'tcx, C: QueryCache>(
(result, dep_node_index)
}
/// Given that the dep node for this query+key is green, obtain a value for it
/// by loading one from disk if possible, or by invoking its query provider if
/// necessary.
#[inline(always)]
fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>(
query: &'tcx QueryVTable<'tcx, C>,
dep_graph_data: &DepGraphData,
fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>(
tcx: TyCtxt<'tcx>,
dep_graph_data: &DepGraphData,
query: &'tcx QueryVTable<'tcx, C>,
key: &C::Key,
dep_node: &DepNode,
) -> Option<(C::Value, DepNodeIndex)> {
prev_index: SerializedDepNodeIndex,
dep_node_index: DepNodeIndex,
) -> C::Value {
// Note this function can be called concurrently from the same query
// We must ensure that this is handled correctly.
let (prev_dep_node_index, dep_node_index) = dep_graph_data.try_mark_green(tcx, dep_node)?;
debug_assert!(dep_graph_data.is_index_green(prev_dep_node_index));
debug_assert!(dep_graph_data.is_index_green(prev_index));
// First we try to load the result from the on-disk cache.
// Some things are never cached on disk.
if let Some(result) = query.try_load_from_disk(tcx, key, prev_dep_node_index, dep_node_index) {
if let Some(value) = query.try_load_from_disk(tcx, key, prev_index, dep_node_index) {
if std::intrinsics::unlikely(tcx.sess.opts.unstable_opts.query_dep_graph) {
dep_graph_data.mark_debug_loaded_from_disk(*dep_node)
}
let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_dep_node_index);
let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index);
// If `-Zincremental-verify-ich` is specified, re-hash results from
// the cache and make sure that they have the expected fingerprint.
//
@@ -527,14 +540,14 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>(
incremental_verify_ich(
tcx,
dep_graph_data,
&result,
prev_dep_node_index,
&value,
prev_index,
query.hash_result,
query.format_value,
);
}
return Some((result, dep_node_index));
return value;
}
// We always expect to find a cached result for things that
@@ -548,7 +561,7 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>(
// Sanity check for the logic in `ensure`: if the node is green and the result loadable,
// we should actually be able to load it.
debug_assert!(
!query.is_loadable_from_disk(tcx, key, prev_dep_node_index),
!query.is_loadable_from_disk(tcx, key, prev_index),
"missing on-disk cache entry for loadable {dep_node:?}"
);
@@ -558,7 +571,7 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>(
// The dep-graph for this computation is already in-place.
// Call the query provider.
let result = tcx.dep_graph.with_ignore(|| (query.invoke_provider_fn)(tcx, *key));
let value = tcx.dep_graph.with_ignore(|| (query.invoke_provider_fn)(tcx, *key));
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
@@ -574,13 +587,13 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>(
incremental_verify_ich(
tcx,
dep_graph_data,
&result,
prev_dep_node_index,
&value,
prev_index,
query.hash_result,
query.format_value,
);
Some((result, dep_node_index))
value
}
/// Return value struct for [`check_if_ensure_can_skip_execution`].
+1 -1
View File
@@ -2504,7 +2504,7 @@ fn resolve_main(&mut self) {
fn names_to_string(names: impl Iterator<Item = Symbol>) -> String {
let mut result = String::new();
for (i, name) in names.filter(|name| *name != kw::PathRoot).enumerate() {
for (i, name) in names.enumerate().filter(|(_, name)| *name != kw::PathRoot) {
if i > 0 {
result.push_str("::");
}
+1 -1
View File
@@ -1615,7 +1615,7 @@ pub fn build_target_config(
let mut err =
early_dcx.early_struct_fatal(format!("error loading target specification: {e}"));
err.help("run `rustc --print target-list` for a list of built-in targets");
err.emit();
err.emit()
}
}
}
+4 -3
View File
@@ -1488,12 +1488,13 @@ impl<T> SizedTypeProperties for T {}
///
/// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited
#[unstable(feature = "mem_conjure_zst", issue = "95383")]
#[rustc_const_unstable(feature = "mem_conjure_zst", issue = "95383")]
pub const unsafe fn conjure_zst<T>() -> T {
const_assert!(
size_of::<T>() == 0,
"mem::conjure_zst invoked on a nonzero-sized type",
"mem::conjure_zst invoked on type {t}, which is not zero-sized",
t: &str = stringify!(T)
"mem::conjure_zst invoked on a non-zero-sized type",
"mem::conjure_zst invoked on type {name}, which is not zero-sized",
name: &str = crate::any::type_name::<T>()
);
// SAFETY: because the caller must guarantee that it's inhabited and zero-sized,
+1
View File
@@ -14,6 +14,7 @@
#![no_std]
#![unstable(feature = "panic_unwind", issue = "32837")]
#![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]
#![cfg_attr(all(target_os = "emscripten", not(emscripten_wasm_eh)), lang_items)]
#![feature(cfg_emscripten_wasm_eh)]
#![feature(core_intrinsics)]
#![feature(panic_unwind)]
+12 -19
View File
@@ -712,28 +712,21 @@ pub fn temp_dir() -> PathBuf {
///
/// # Security
///
/// The output of this function should not be trusted for anything
/// that might have security implications. Basically, if users can run
/// the executable, they can change the output arbitrarily.
/// The output of this function must be treated with care to avoid security
/// vulnerabilities, particularly in processes that run with privileges higher
/// than the user, such as setuid or setgid programs.
///
/// As an example, you can easily introduce a race condition. It goes
/// like this:
/// For example, on some Unix platforms, the result is calculated by
/// searching `$PATH` for an executable matching `argv[0]`, but both the
/// environment and arguments can be be set arbitrarily by the user who
/// invokes the program.
///
/// 1. You get the path to the current executable using `current_exe()`, and
/// store it in a variable.
/// 2. Time passes. A malicious actor removes the current executable, and
/// replaces it with a malicious one.
/// 3. You then use the stored path to re-execute the current
/// executable.
/// On Linux, if `fs.secure_hardlinks` is not set, an attacker who can
/// create hardlinks to the executable may be able to cause this function
/// to return an attacker-controlled path, which they later replace with
/// a different program.
///
/// You expected to safely execute the current executable, but you're
/// instead executing something completely different. The code you
/// just executed runs with your privileges.
///
/// This sort of behavior has been known to [lead to privilege escalation] when
/// used incorrectly.
///
/// [lead to privilege escalation]: https://securityvulns.com/Wdocument183.html
/// This list of illustrative example attacks is not exhaustive.
///
/// # Examples
///
+1 -1
View File
@@ -1,4 +1,4 @@
Change this file to make users of the `download-ci-llvm` configuration download
a new version of LLVM from CI, even if the LLVM submodule hasnt changed.
Last change is for: https://github.com/rust-lang/rust/pull/147935
Last change is for: https://github.com/rust-lang/rust/pull/153179
+14 -19
View File
@@ -386,33 +386,32 @@ fn generate_macro_def_id_path(
} else {
ItemType::Macro
};
let mut path = clean::inline::get_item_path(tcx, def_id, item_type);
if path.len() < 2 {
// The minimum we can have is the crate name followed by the macro name. If shorter, then
// it means that `relative` was empty, which is an error.
debug!("macro path cannot be empty!");
let path = clean::inline::get_item_path(tcx, def_id, item_type);
// The minimum we can have is the crate name followed by the macro name. If shorter, then
// it means that `relative` was empty, which is an error.
let [module_path @ .., last] = path.as_slice() else {
debug!("macro path is empty!");
return Err(HrefError::NotInExternalCache);
};
if module_path.is_empty() {
debug!("macro path too short: missing crate prefix (got 1 element, need at least 2)");
return Err(HrefError::NotInExternalCache);
}
// FIXME: Try to use `iter().chain().once()` instead.
let mut prev = None;
if let Some(last) = path.pop() {
path.push(Symbol::intern(&format!("{}.{last}.html", item_type.as_str())));
prev = Some(last);
}
let url = match cache.extern_locations[&def_id.krate] {
ExternalLocation::Remote { ref url, is_absolute } => {
let mut prefix = remote_url_prefix(url, is_absolute, cx.current.len());
prefix.extend(path.iter().copied());
prefix.extend(module_path.iter().copied());
prefix.push_fmt(format_args!("{}.{last}.html", item_type.as_str()));
prefix.finish()
}
ExternalLocation::Local => {
// `root_path` always end with a `/`.
format!(
"{root_path}{path}",
"{root_path}{path}/{item_type}.{last}.html",
root_path = root_path.unwrap_or(""),
path = fmt::from_fn(|f| path.iter().joined("/", f))
path = fmt::from_fn(|f| module_path.iter().joined("/", f)),
item_type = item_type.as_str(),
)
}
ExternalLocation::Unknown => {
@@ -420,10 +419,6 @@ fn generate_macro_def_id_path(
return Err(HrefError::NotInExternalCache);
}
};
if let Some(prev) = prev {
path.pop();
path.push(prev);
}
Ok(HrefInfo { url, kind: item_type, rust_path: path })
}
+76
View File
@@ -0,0 +1,76 @@
//@ add-minicore
//@ min-llvm-version: 22
//@ assembly-output: emit-asm
//@ needs-llvm-components: x86
//@ compile-flags: --target=x86_64-unknown-linux-gnu
//@ compile-flags: -Copt-level=3 -C llvm-args=-x86-asm-syntax=intel
#![feature(no_core, explicit_tail_calls)]
#![expect(incomplete_features)]
#![no_core]
#![crate_type = "lib"]
// Test tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments.
//
// Normally an indirect argument with `on_stack: false` would be passed as a pointer to the
// caller's stack frame. For tail calls, that would be unsound, because the caller's stack
// frame is overwritten by the callee's stack frame.
//
// The solution is to write the argument into the caller's argument place (stored somewhere further
// up the stack), and forward that place.
extern crate minicore;
use minicore::*;
#[repr(C)]
struct S {
x: u64,
y: u64,
z: u64,
}
unsafe extern "C" {
safe fn force_usage(_: u64, _: u64, _: u64) -> u64;
}
// CHECK-LABEL: callee:
// CHECK-NEXT: .cfi_startproc
//
// CHECK-NEXT: mov rax, qword ptr [rdi]
// CHECK-NEXT: mov rsi, qword ptr [rdi + 8]
// CHECK-NEXT: mov rdx, qword ptr [rdi + 16]
// CHECK-NEXT: mov rdi, rax
//
// CHECK-NEXT: jmp qword ptr [rip + force_usage@GOTPCREL]
#[inline(never)]
#[unsafe(no_mangle)]
fn callee(s: S) -> u64 {
force_usage(s.x, s.y, s.z)
}
// CHECK-LABEL: caller1:
// CHECK-NEXT: .cfi_startproc
//
// Just forward the argument:
//
// CHECK-NEXT: jmp qword ptr [rip + callee@GOTPCREL]
#[unsafe(no_mangle)]
fn caller1(s: S) -> u64 {
become callee(s);
}
// CHECK-LABEL: caller2:
// CHECK-NEXT: .cfi_startproc
//
// Construct the S value directly into the argument slot:
//
// CHECK-NEXT: mov qword ptr [rdi], 1
// CHECK-NEXT: mov qword ptr [rdi + 8], 2
// CHECK-NEXT: mov qword ptr [rdi + 16], 3
//
// CHECK-NEXT: jmp qword ptr [rip + callee@GOTPCREL]
#[unsafe(no_mangle)]
fn caller2(_: S) -> u64 {
let s = S { x: 1, y: 2, z: 3 };
become callee(s);
}
+41
View File
@@ -0,0 +1,41 @@
//@ add-minicore
//@ revisions: win linux
//@ min-llvm-version: 22
//
//@ compile-flags: -Copt-level=3
//@[linux] compile-flags: --target x86_64-unknown-linux-gnu
//@[linux] needs-llvm-components: x86
//@[win] compile-flags: --target x86_64-pc-windows-msvc
//@[win] needs-llvm-components: x86
#![crate_type = "lib"]
#![feature(no_core, lang_items, explicit_tail_calls)]
#![allow(incomplete_features)]
#![no_core]
// Test passing of i128/u128, which is passed directly on x86, but indirectly on win64.
extern crate minicore;
use minicore::*;
// linux: define noundef i128 @foo(i128 noundef %a, i128 noundef %b)
// win: define <16 x i8> @foo(ptr {{.*}} %a, ptr {{.*}} %b)
#[unsafe(no_mangle)]
#[inline(never)]
extern "C" fn foo(a: u128, b: u128) -> u128 {
// linux: start:
// linux-NEXT: musttail call noundef i128 @bar(i128 noundef %b, i128 noundef %a)
//
//
// win: start:
// win-NEXT: %0 = load i128, ptr %b
// win-NEXT: %1 = load i128, ptr %a
// win-NEXT: store i128 %0, ptr %a
// win-NEXT: store i128 %1, ptr %b
// win-NEXT: musttail call <16 x i8> @bar(ptr {{.*}} %a, ptr {{.*}} %b)
become bar(b, a)
}
unsafe extern "C" {
safe fn bar(a: u128, b: u128) -> u128;
}
-42
View File
@@ -1,42 +0,0 @@
//@ known-bug: #144293
// Same as recursion-etc but eggs LLVM emission into giving indirect arguments.
#![expect(incomplete_features)]
#![feature(explicit_tail_calls)]
use std::hint::black_box;
struct U64Wrapper {
pub x: u64,
pub arbitrary: String,
}
fn count(curr: U64Wrapper, top: U64Wrapper) -> U64Wrapper {
if black_box(curr.x) >= top.x {
curr
} else {
become count(
U64Wrapper {
x: curr.x + 1,
arbitrary: curr.arbitrary,
},
top,
)
}
}
fn main() {
println!(
"{}",
count(
U64Wrapper {
x: 0,
arbitrary: "hello!".into()
},
black_box(U64Wrapper {
x: 1000000,
arbitrary: "goodbye!".into()
})
)
.x
);
}
+68
View File
@@ -0,0 +1,68 @@
//@ run-pass
//@ ignore-backends: gcc
//
//@ ignore-wasm
//@ ignore-riscv64
#![feature(explicit_tail_calls)]
#![expect(incomplete_features)]
// Test tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments.
//
// Normally an indirect argument with `on_stack: false` would be passed as a pointer to the
// caller's stack frame. For tail calls, that would be unsound, because the caller's stack
// frame is overwritten by the callee's stack frame.
//
// The solution is to write the argument into the caller's argument place (stored somewhere further
// up the stack), and forward that place.
// A struct big enough that it is not passed via registers, so that the rust calling convention uses
// `Indirect { on_stack: false, .. }`.
#[repr(C)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct Big([u64; 4]);
#[inline(never)]
fn update_in_caller(y: Big) -> u64 {
#[inline(never)]
fn helper(x: Big) -> u64 {
x.0.iter().sum()
}
let x = Big([y.0[0], 2, 3, 4]);
// `x` is actually stored in `y`'s space.
become helper(x)
}
#[inline(never)]
fn swapper<T>(a: T, b: T) -> (T, T) {
#[inline(never)]
fn helper<T>(a: T, b: T) -> (T, T) {
(a, b)
}
become helper(b, a)
}
#[inline(never)]
fn swapper_derived(a: Big, _: (u64, u64), b: Big, _: (u64, u64)) -> ((u64, u64), (u64, u64)) {
#[inline(never)]
fn helper(_: Big, x: (u64, u64), _: Big, y: (u64, u64)) -> ((u64, u64), (u64, u64)) {
(x, y)
}
// Read the values at various points in the swapping process, testing that they have the correct
// value at every point.
become helper(b, (a.0[0], b.0[0]), a, (a.0[0], b.0[0]));
}
fn main() {
assert_eq!(update_in_caller(Big::default()), 0 + 2 + 3 + 4);
assert_eq!(swapper(u8::MIN, u8::MAX), (u8::MAX, u8::MIN));
// i128 uses `PassMode::Indirect { on_stack: false, .. }` on x86_64 MSVC.
assert_eq!(swapper(i128::MIN, i128::MAX), (i128::MAX, i128::MIN));
assert_eq!(swapper(Big([1; 4]), Big([2; 4])), (Big([2; 4]), Big([1; 4])));
assert_eq!(swapper_derived(Big([1; 4]), (0, 0), Big([2; 4]), (0, 0)), ((1, 2), (1, 2)));
}
@@ -35,9 +35,9 @@ fn main() {
macro_rules! Type {
() => {
::std::cell::Cell
//~^ ERROR expected value, found struct `std::cell::Cell`
//~| ERROR expected value, found struct `std::cell::Cell`
//~| ERROR expected value, found struct `std::cell::Cell`
//~^ ERROR expected value, found struct `::std::cell::Cell`
//~| ERROR expected value, found struct `::std::cell::Cell`
//~| ERROR expected value, found struct `::std::cell::Cell`
};
(alias) => {
Alias
@@ -70,7 +70,7 @@ LL - let _ = foo.bar;
LL + let _ = foo::bar;
|
error[E0423]: expected value, found struct `std::cell::Cell`
error[E0423]: expected value, found struct `::std::cell::Cell`
--> $DIR/dot-notation-type-namespace-suggest-path-sep.rs:37:9
|
LL | ::std::cell::Cell
@@ -86,7 +86,7 @@ LL - Type!().get();
LL + <Type!()>::get();
|
error[E0423]: expected value, found struct `std::cell::Cell`
error[E0423]: expected value, found struct `::std::cell::Cell`
--> $DIR/dot-notation-type-namespace-suggest-path-sep.rs:37:9
|
LL | ::std::cell::Cell
@@ -166,7 +166,7 @@ LL - Vec.new
LL + Vec::new
|
error[E0423]: expected value, found struct `std::cell::Cell`
error[E0423]: expected value, found struct `::std::cell::Cell`
--> $DIR/dot-notation-type-namespace-suggest-path-sep.rs:37:9
|
LL | ::std::cell::Cell
+2 -2
View File
@@ -15,8 +15,8 @@ fn main() {
macro_rules! Trait {
() => {
::std::iter::Iterator
//~^ ERROR expected value, found trait `std::iter::Iterator`
//~| ERROR expected value, found trait `std::iter::Iterator`
//~^ ERROR expected value, found trait `::std::iter::Iterator`
//~| ERROR expected value, found trait `::std::iter::Iterator`
};
}
+2 -2
View File
@@ -34,7 +34,7 @@ LL - let _ = Into::<()>.into;
LL + let _ = Into::<()>::into;
|
error[E0423]: expected value, found trait `std::iter::Iterator`
error[E0423]: expected value, found trait `::std::iter::Iterator`
--> $DIR/issue-100365.rs:17:9
|
LL | ::std::iter::Iterator
@@ -45,7 +45,7 @@ LL | Trait!().map(std::convert::identity); // no `help` here!
|
= note: this error originates in the macro `Trait` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0423]: expected value, found trait `std::iter::Iterator`
error[E0423]: expected value, found trait `::std::iter::Iterator`
--> $DIR/issue-100365.rs:17:9
|
LL | ::std::iter::Iterator
+2 -2
View File
@@ -2,8 +2,8 @@
enum E {}
trait Tr {}
pub(in E) struct S; //~ ERROR expected module, found enum `E`
pub(in Tr) struct Z; //~ ERROR expected module, found trait `Tr`
pub(in E) struct S; //~ ERROR expected module, found enum `::E`
pub(in Tr) struct Z; //~ ERROR expected module, found trait `::Tr`
pub(in std::vec) struct F; //~ ERROR visibilities can only be restricted to ancestor modules
pub(in nonexistent) struct G; //~ ERROR cannot find
pub(in too_soon) struct H; //~ ERROR cannot find
@@ -1,10 +1,10 @@
error[E0577]: expected module, found enum `E`
error[E0577]: expected module, found enum `::E`
--> $DIR/resolve-bad-visibility.rs:5:8
|
LL | pub(in E) struct S;
| ^ not a module
error[E0577]: expected module, found trait `Tr`
error[E0577]: expected module, found trait `::Tr`
--> $DIR/resolve-bad-visibility.rs:6:8
|
LL | pub(in Tr) struct Z;
@@ -6,6 +6,6 @@
use *; //~ ERROR cannot glob-import all possible crates
fn main() {
let s = ::xcrate; //~ ERROR expected value, found crate `xcrate`
let s = ::xcrate; //~ ERROR expected value, found crate `::xcrate`
//~^ NOTE not a value
}
@@ -15,7 +15,7 @@ error: cannot glob-import all possible crates
LL | use *;
| ^
error[E0423]: expected value, found crate `xcrate`
error[E0423]: expected value, found crate `::xcrate`
--> $DIR/single-segment.rs:9:13
|
LL | let s = ::xcrate;