diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 4859380daa662..0d42ccc1a73c9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1146,19 +1146,51 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (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 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - // 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( diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs new file mode 100644 index 0000000000000..ffc6e54f3cb06 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs @@ -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>>, + in_edges: IndexVec>>, +} + +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] + } +} diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 5134b7b7ca8f1..e99dcd1ef15cb 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -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 rustc_span::Span; 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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // 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> = None; for (node_vid, value) in var_data.values.iter_enumerated() { match *value { @@ -662,56 +659,18 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // 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); - } - } - } - } - - 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. + 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); } } } - - graph } fn collect_error_for_expanding_node( &self, - graph: &RegionGraph<'tcx>, + edges: &IndexedConstraintEdges<'_, 'tcx>, dup_vec: &mut IndexSlice>, node_idx: RegionVid, errors: &mut Vec>, @@ -719,9 +678,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // 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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// 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>>, ) -> (Vec>, FxHashSet, bool) { struct WalkState<'tcx> { @@ -850,7 +809,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, '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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, '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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, '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!( diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 08e40116b47c0..f1b80ec13a307 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -300,7 +300,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; diag.help( "try lowering `-Z threads` or checking the operating system's resource limits", ); - diag.emit(); + diag.emit() }) }) }) diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 1b3e01f3a3809..ad94c23f229c4 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -121,17 +121,29 @@ impl<'tcx> Visitor<'tcx> for DeduceParamAttrs { // `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); } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 1502e5ef5496f..1124f5830b6e6 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -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) } diff --git a/compiler/rustc_public/src/crate_def.rs b/compiler/rustc_public/src/crate_def.rs index e534004af4d3c..7773abdcb4d28 100644 --- a/compiler/rustc_public/src/crate_def.rs +++ b/compiler/rustc_public/src/crate_def.rs @@ -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 @@ pub trait CrateDefType: CrateDef { } } -/// 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 @@ macro_rules! crate_def_with_ty { impl CrateDefType for $name {} }; } - -macro_rules! impl_crate_def_items { - ( $name:ident $(;)? ) => { - impl CrateDefItems for $name {} - }; -} diff --git a/compiler/rustc_public/src/lib.rs b/compiler/rustc_public/src/lib.rs index e38265e5f0f56..e1e02e282bf2e 100644 --- a/compiler/rustc_public/src/lib.rs +++ b/compiler/rustc_public/src/lib.rs @@ -30,7 +30,7 @@ pub mod rustc_internal; 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(value: &T) -> Opaque { } macro_rules! bridge_impl { - ($name: ident, $ty: ty) => { - impl rustc_public_bridge::bridge::$name for $ty { - fn new(def: crate::DefId) -> Self { - Self(def) + ( $( $name:ident, $ty:ty ),* $(,)? ) => { + $( + impl rustc_public_bridge::bridge::$name 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 for crate::ty::Prov { fn new(aid: crate::mir::alloc::AllocId) -> Self { diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 5d8e833ac63b1..4800bf1e84e9c 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -7,11 +7,11 @@ use super::abi::ReprOptions; 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 @@ crate_def! { 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 @@ crate_def! { 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! { diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index f4c1df145af35..22155e961426f 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -5,7 +5,7 @@ use rustc_data_structures::hash_table::{Entry, HashTable}; 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`]. diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b88227264537f..d62a6fad02b01 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2504,7 +2504,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn names_to_string(names: impl Iterator) -> 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("::"); } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b278a6179fe7f..0677ca0fddbaa 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -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() } } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index eb6f8f9757215..d8521e79006e3 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1488,12 +1488,13 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { /// /// [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 { const_assert!( size_of::() == 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::() ); // SAFETY: because the caller must guarantee that it's inhabited and zero-sized, diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 5372c44cedf75..e89d5e60df62a 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -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)] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 1f0ced5d0fd0d..98b21781e0915 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -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 /// diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index e79391a107aac..2ca19f082693b 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -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 hasn’t 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 diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 2070a24f60c81..ce70478207a85 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -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 }) } diff --git a/tests/assembly-llvm/tail-call-indirect.rs b/tests/assembly-llvm/tail-call-indirect.rs new file mode 100644 index 0000000000000..2bc1743a9bafd --- /dev/null +++ b/tests/assembly-llvm/tail-call-indirect.rs @@ -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); +} diff --git a/tests/codegen-llvm/tail-call-u128.rs b/tests/codegen-llvm/tail-call-u128.rs new file mode 100644 index 0000000000000..25dfc47a2bfec --- /dev/null +++ b/tests/codegen-llvm/tail-call-u128.rs @@ -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; +} diff --git a/tests/crashes/144293-indirect-ops-llvm.rs b/tests/crashes/144293-indirect-ops-llvm.rs deleted file mode 100644 index 490a0116d7d21..0000000000000 --- a/tests/crashes/144293-indirect-ops-llvm.rs +++ /dev/null @@ -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 - ); -} diff --git a/tests/ui/explicit-tail-calls/indirect.rs b/tests/ui/explicit-tail-calls/indirect.rs new file mode 100644 index 0000000000000..537976b7f6f43 --- /dev/null +++ b/tests/ui/explicit-tail-calls/indirect.rs @@ -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(a: T, b: T) -> (T, T) { + #[inline(never)] + fn helper(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))); +} diff --git a/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.rs b/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.rs index 432e3c0b77efc..e9ff6306e16c2 100644 --- a/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.rs +++ b/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.rs @@ -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 diff --git a/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.stderr b/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.stderr index d74814dd876c2..e0e2c3a5fd822 100644 --- a/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.stderr +++ b/tests/ui/resolve/dot-notation-type-namespace-suggest-path-sep.stderr @@ -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 + ::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 diff --git a/tests/ui/resolve/issue-100365.rs b/tests/ui/resolve/issue-100365.rs index 1d8835036065b..2ecaf93601013 100644 --- a/tests/ui/resolve/issue-100365.rs +++ b/tests/ui/resolve/issue-100365.rs @@ -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` }; } diff --git a/tests/ui/resolve/issue-100365.stderr b/tests/ui/resolve/issue-100365.stderr index 7a880c6f31acd..7dea31713df2f 100644 --- a/tests/ui/resolve/issue-100365.stderr +++ b/tests/ui/resolve/issue-100365.stderr @@ -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 diff --git a/tests/ui/resolve/resolve-bad-visibility.rs b/tests/ui/resolve/resolve-bad-visibility.rs index 55e381e8be1a1..bfc5e90c31e71 100644 --- a/tests/ui/resolve/resolve-bad-visibility.rs +++ b/tests/ui/resolve/resolve-bad-visibility.rs @@ -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 diff --git a/tests/ui/resolve/resolve-bad-visibility.stderr b/tests/ui/resolve/resolve-bad-visibility.stderr index 4530757c3de55..d9fd4400b8e0e 100644 --- a/tests/ui/resolve/resolve-bad-visibility.stderr +++ b/tests/ui/resolve/resolve-bad-visibility.stderr @@ -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; diff --git a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs index 0828927a3976e..b773e419b16dd 100644 --- a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs +++ b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs @@ -6,6 +6,6 @@ use crate; //~ ERROR imports need to be explicitly named 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 } diff --git a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr index 13eb4e25ed6bb..fcd18a847d910 100644 --- a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr +++ b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr @@ -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;