diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index fb984912b1936..b913518c191db 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -403,6 +403,18 @@ pub(crate) struct InvalidTarget { #[derive(Subdiagnostic)] pub(crate) enum InvalidTargetHelp { + #[multipart_suggestion( + "did you mean to use `#[export_name]`?", + applicability = "maybe-incorrect" + )] + UseExportName { + #[suggestion_part(code = "unsafe(")] + unsafe_open: Option, + #[suggestion_part(code = "export_name")] + name: Span, + #[suggestion_part(code = ")")] + unsafe_close: Option, + }, #[help("use `#[rustc_align(...)]` instead")] UseRustcAlign, #[help("use `#[rustc_align_static(...)]` instead")] diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 461820626f2ad..6b0d1b094ca0f 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_ast::AttrStyle; +use rustc_ast::{AttrStyle, Safety}; use rustc_errors::{DiagArgValue, MultiSpan, StashKey}; use rustc_feature::Features; use rustc_hir::attrs::AttributeKind; @@ -187,6 +187,15 @@ impl<'sess> AttributeParser<'sess> { cx: &AcceptContext<'_, '_>, ) -> Option { match &*cx.attr_path.segments { + [sym::link_name] if cx.target == Target::Static => { + let needs_unsafe_wrapper = matches!(cx.attr_safety, Safety::Default); + + Some(InvalidTargetHelp::UseExportName { + unsafe_open: needs_unsafe_wrapper.then(|| cx.inner_span.shrink_to_lo()), + name: cx.attr_path.span, + unsafe_close: needs_unsafe_wrapper.then(|| cx.inner_span.shrink_to_hi()), + }) + } [sym::repr] if attribute_args == "(align(...))" => match cx.target { Target::Fn | Target::Method(..) if cx.features().fn_align() => { Some(InvalidTargetHelp::UseRustcAlign) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4f778ed461514..d8cd9fdc61e44 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -217,7 +217,7 @@ impl LocalsStateAtExit { HasStorageDead(DenseBitSet::new_empty(body.local_decls.len())); has_storage_dead.visit_body(body); let mut has_storage_dead_or_moved = has_storage_dead.0; - for move_out in &move_data.moves { + for move_out in &move_data.move_outs { has_storage_dead_or_moved.insert(move_data.base_local(move_out.path)); } LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index e3e36f9bbc715..8a9db246d7c15 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -129,7 +129,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { } let is_partial_move = move_site_vec.iter().any(|move_site| { - let move_out = self.move_data.moves[(*move_site).moi]; + let move_out = self.move_data.move_outs[(*move_site).moi]; let moved_place = &self.move_data.move_paths[move_out.path].place; // `*(_1)` where `_1` is a `Box` is actually a move out. let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref] @@ -215,7 +215,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { let mut seen_spans = FxIndexSet::default(); for move_site in &move_site_vec { - let move_out = self.move_data.moves[(*move_site).moi]; + let move_out = self.move_data.move_outs[(*move_site).moi]; let moved_place = &self.move_data.move_paths[move_out.path].place; let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); @@ -281,7 +281,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { _ => true, }; - let mpi = self.move_data.moves[move_out_indices[0]].path; + let mpi = self.move_data.move_outs[move_out_indices[0]].path; let place = &self.move_data.move_paths[mpi].place; let ty = place.ty(self.body, self.infcx.tcx).ty; @@ -3855,9 +3855,9 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { // worry about the other case: that is, if there is a move of a.b.c, it is already // marked as a move of a.b and a as well, so we will generate the correct errors // there. - for moi in &self.move_data.loc_map[location] { + for moi in &self.move_data.move_out_loc_map[location] { debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); - let path = self.move_data.moves[*moi].path; + let path = self.move_data.move_outs[*moi].path; if mpis.contains(&path) { debug!( "report_use_of_moved_or_uninitialized: found {:?}", diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 31208d1bd5ad8..303607a47a528 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2373,8 +2373,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // of the union - we should error in that case. let tcx = this.infcx.tcx; if base.ty(this.body(), tcx).ty.is_union() - && this.move_data.path_map[mpi].iter().any(|moi| { - this.move_data.moves[*moi].source.is_predecessor_of(location, this.body) + && this.move_data.move_out_path_map[mpi].iter().any(|moi| { + this.move_data.move_outs[*moi].source.is_predecessor_of(location, this.body) }) { return; diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 05fd6e39476b2..0ae3ff3c04790 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -132,9 +132,9 @@ fn emit_move_facts( // moved_out_at // deinitialisation is assumed to always happen! - facts - .path_moved_at_base - .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); + facts.path_moved_at_base.extend( + move_data.move_outs.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))), + ); } /// Emit universal regions facts, and their relations. diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs index e743b0ce6e901..16a9962f1190a 100644 --- a/compiler/rustc_borrowck/src/used_muts.rs +++ b/compiler/rustc_borrowck/src/used_muts.rs @@ -93,8 +93,8 @@ impl<'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'_, '_, '_, 'tcx> { fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) { if place_context.is_place_assignment() && self.temporary_used_locals.contains(&local) { // Propagate the Local assigned at this Location as a used mutable local variable - for moi in &self.mbcx.move_data.loc_map[location] { - let mpi = &self.mbcx.move_data.moves[*moi].path; + for moi in &self.mbcx.move_data.move_out_loc_map[location] { + let mpi = &self.mbcx.move_data.move_outs[*moi].path; let path = &self.mbcx.move_data.move_paths[*mpi]; debug!( "assignment of {:?} to {:?}, adding {:?} to used mutable set", diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 9e11346955124..953b0f1b9d464 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -355,7 +355,7 @@ impl<'tcx> Drop for InferCtxt<'tcx> { // No need for the drop bomb when we're in `TypingMode::PostTypeckUntilBorrowck`, and the `InferCtxt` // doesn't consider regions. This is okay since after typeck, the only reason we care about opaques is - // in relation to regions. In some places *after* typeck that aren't borrowck, like in lints we use + // in relation to regions. In some places *after* typeck that aren't borrowck, we use // `TypingMode::PostTypeckUntilBorrowck` to prevent defining opaque types and we simply don't care about regions. match self.typing_mode_raw() { TypingMode::Coherence diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index e992f1ed385e7..62db3208d521b 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -645,7 +645,7 @@ impl<'tcx> LateContext<'tcx> { && self.tcx.use_typing_mode_post_typeck_until_borrowck() { let def_id = self.tcx.hir_enclosing_body_owner(body_id.hir_id); - TypingMode::borrowck(self.tcx, def_id) + TypingMode::post_borrowck_analysis(self.tcx, def_id) } else { TypingMode::non_body_analysis() } diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index ed71ff6a463db..61578ee2892d5 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { } let def_id = opaque.def_id.to_def_id(); - let infcx = &cx.tcx.infer_ctxt().ignoring_regions().build(cx.typing_mode()); + let infcx = &cx.tcx.infer_ctxt().build(cx.typing_mode()); // For every projection predicate in the opaque type's explicit bounds, // check that the type that we're assigning actually satisfies the bounds // of the associated type. diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 91252ae6727ce..560d39403d4e0 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1432,17 +1432,26 @@ impl CrateMetadata { .attributes .get(self, id) .unwrap_or_else(|| { - // Structure and variant constructors don't have any attributes encoded for them, - // but we assume that someone passing a constructor ID actually wants to look at - // the attributes on the corresponding struct or variant. let def_key = self.def_key(id); - assert_eq!(def_key.disambiguated_data.data, DefPathData::Ctor); - let parent_id = def_key.parent.expect("no parent for a constructor"); - self.root - .tables - .attributes - .get(self, parent_id) - .expect("no encoded attributes for a structure or variant") + match def_key.disambiguated_data.data { + DefPathData::Ctor => { + // Structure and variant constructors don't have any attributes encoded for them, + // but we assume that someone passing a constructor ID actually wants to look at + // the attributes on the corresponding struct or variant. + assert_eq!(def_key.disambiguated_data.data, DefPathData::Ctor); + let parent_id = def_key.parent.expect("no parent for a constructor"); + self.root + .tables + .attributes + .get(self, parent_id) + .expect("no encoded attributes for a structure or variant") + } + DefPathData::SyntheticCoroutineBody => { + // SyntheticCoroutineBodies cannot have attributes + LazyArray::default() + } + _ => panic!("Definition key {def_key:?} of type `{:?}` did not have any attributes stored", def_key.disambiguated_data.data) + } }) .decode((self, tcx)) } diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index 1402a1a8b9136..b6fc1219a8503 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -114,7 +114,7 @@ pub fn drop_flag_effects_for_location<'tcx, F>( debug!("drop_flag_effects_for_location({:?})", loc); // first, move out of the RHS - for mi in &move_data.loc_map[loc] { + for mi in &move_data.move_out_loc_map[loc] { let path = mi.move_path_index(move_data); debug!("moving out of path {:?}", move_data.move_paths[path]); diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index b4cbe7d1da14c..4ca5ee56315a4 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -59,25 +59,21 @@ impl<'tcx> MaybePlacesSwitchIntData<'tcx> { { match enum_place.ty(body, tcx).ty.kind() { ty::Adt(enum_def, _) => { - // The value of each discriminant, in AdtDef order. - let discriminant_vals: SmallVec<[u128; 4]> = - enum_def.discriminants(tcx).map(|(_, discr)| discr.val).collect(); - let mut i = 0; - // For each value in the SwitchInt, find the VariantIdx for the variant // with that value. This works because `discriminant_vals` and // `targets.all_values()` are guaranteed to list variants in the same - // order. (If that ever changes we will get out-of-bounds panics here.) + // AdtDef order. (If that ever changes the `expect` will panic.) + let mut discriminants = enum_def.discriminants(tcx); let variants = targets .all_values() .iter() .map(|value| { - loop { - if discriminant_vals[i] == value.get() { - return VariantIdx::new(i); - } - i += 1; - } + // On each call to this closure `find` only consumes part of + // the `discriminants` iterator. + discriminants + .find(|(_, discr)| discr.val == value.get()) + .expect("SwitchInt vals should match a variant") + .0 }) .collect(); @@ -232,7 +228,6 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> { move_data: &'a MoveData<'tcx>, mark_inactive_variants_as_uninit: bool, - include_inactive_in_otherwise: bool, skip_unreachable_unwind: DenseBitSet, } @@ -243,7 +238,6 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { body, move_data, mark_inactive_variants_as_uninit: false, - include_inactive_in_otherwise: false, skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()), } } @@ -258,13 +252,6 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { self } - /// Ensures definitely inactive variants are included in the set of uninitialized places for - /// blocks reached through an `otherwise` edge. - pub fn include_inactive_in_otherwise(mut self) -> Self { - self.include_inactive_in_otherwise = true; - self - } - pub fn skipping_unreachable_unwind( mut self, unreachable_unwind: DenseBitSet, @@ -589,10 +576,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { SwitchTargetIndex::Normal(target_idx) => { InactiveVariants::Active(data.variants[target_idx]) } - SwitchTargetIndex::Otherwise if self.include_inactive_in_otherwise => { - InactiveVariants::Inactives(data.variants.clone()) - } - _ => return, + SwitchTargetIndex::Otherwise => InactiveVariants::Inactives(data.variants.clone()), }; // Mark all move paths that correspond to variants other than this one as maybe @@ -658,10 +642,9 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - let (body, move_data) = (self.body, self.move_data()); - let term = body[location.block].terminator(); + let move_data = self.move_data(); let init_loc_map = &move_data.init_loc_map; - debug!(?term); + debug!(?terminator); debug!("initializes move_indexes {:?}", init_loc_map[location]); state.gen_all( init_loc_map[location] diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index c9198da72c750..74aaa19bf2373 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -23,7 +23,7 @@ struct MoveDataBuilder<'a, 'tcx, F> { impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, filter: F) -> Self { let mut move_paths = IndexVec::new(); - let mut path_map = IndexVec::new(); + let mut move_out_path_map = IndexVec::new(); let mut init_path_map = IndexVec::new(); let locals = body @@ -36,7 +36,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { if filter(l.ty) { Some(new_move_path( &mut move_paths, - &mut path_map, + &mut move_out_path_map, &mut init_path_map, None, Place::from(i), @@ -52,15 +52,15 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { loc: Location::START, tcx, data: MoveData { - moves: IndexVec::new(), - loc_map: LocationMap::new(body), + move_outs: IndexVec::new(), + move_out_loc_map: LocationMap::new(body), rev_lookup: MovePathLookup { locals, projections: Default::default(), un_derefer: Default::default(), }, move_paths, - path_map, + move_out_path_map, inits: IndexVec::new(), init_loc_map: LocationMap::new(body), init_path_map, @@ -72,7 +72,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { fn new_move_path<'tcx>( move_paths: &mut IndexVec>, - path_map: &mut IndexVec>, + move_out_path_map: &mut IndexVec>, init_path_map: &mut IndexVec>, parent: Option, place: Place<'tcx>, @@ -85,7 +85,7 @@ fn new_move_path<'tcx>( move_paths[move_path].next_sibling = next_sibling; } - let path_map_ent = path_map.push(smallvec![]); + let path_map_ent = move_out_path_map.push(smallvec![]); assert_eq!(path_map_ent, move_path); let init_path_map_ent = init_path_map.push(smallvec![]); @@ -279,7 +279,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { base = *data.rev_lookup.projections.entry((base, move_elem)).or_insert_with(|| { new_move_path( &mut data.move_paths, - &mut data.path_map, + &mut data.move_out_path_map, &mut data.init_path_map, Some(base), place_ref.project_deeper(&[elem], tcx), @@ -305,12 +305,12 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>, ) -> MovePathIndex { let MoveDataBuilder { - data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }, + data: MoveData { rev_lookup, move_paths, move_out_path_map, init_path_map, .. }, tcx, .. } = self; *rev_lookup.projections.entry((base, elem)).or_insert_with(move || { - new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx)) + new_move_path(move_paths, move_out_path_map, init_path_map, Some(base), mk_place(*tcx)) }) } @@ -323,7 +323,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { fn finalize(self) -> MoveData<'tcx> { debug!("{}", { debug!("moves for {:?}:", self.body.span); - for (j, mo) in self.data.moves.iter_enumerated() { + for (j, mo) in self.data.move_outs.iter_enumerated() { debug!(" {:?} = {:?}", j, mo); } debug!("move paths for {:?}:", self.body.span); @@ -553,13 +553,13 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { } fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) { - let move_out = self.data.moves.push(MoveOut { path, source: self.loc }); + let move_out = self.data.move_outs.push(MoveOut { path, source: self.loc }); debug!( "gather_move({:?}, {:?}): adding move {:?} of {:?}", self.loc, place, move_out, path ); - self.data.path_map[path].push(move_out); - self.data.loc_map[self.loc].push(move_out); + self.data.move_out_path_map[path].push(move_out); + self.data.move_out_loc_map[self.loc].push(move_out); } fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 7f8872b3e3493..f53137948f99c 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -14,6 +14,7 @@ use smallvec::SmallVec; use crate::un_derefer::UnDerefer; rustc_index::newtype_index! { + /// Index identifying a `MovePath`. #[orderable] #[debug_format = "mp{}"] pub struct MovePathIndex {} @@ -26,26 +27,31 @@ impl polonius_engine::Atom for MovePathIndex { } rustc_index::newtype_index! { + /// Index identifying a `MoveOut`. #[orderable] #[debug_format = "mo{}"] pub struct MoveOutIndex {} } rustc_index::newtype_index! { + /// Index identifying an `Init`. #[debug_format = "in{}"] pub struct InitIndex {} } impl MoveOutIndex { pub fn move_path_index(self, move_data: &MoveData<'_>) -> MovePathIndex { - move_data.moves[self].path + move_data.move_outs[self].path } } -/// `MovePath` is a canonicalized representation of a path that is -/// moved or assigned to. +/// `MovePath` is a canonicalized representation of a place that is of +/// interest to dataflow analysis, as identified by `gather_moves`. This +/// is primarily places that are moved or inited (assigned). Each +/// `MovePath` is assigned a `MovePathIndex` by which it can be referred +/// to. /// -/// It follows a tree structure. +/// `MovePath` follows a tree structure. /// /// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;` /// move *out* of the place `x.m`. @@ -54,6 +60,8 @@ impl MoveOutIndex { /// one of them will link to the other via the `next_sibling` field, /// and the other will have no entry in its `next_sibling` field), and /// they both have the MovePath representing `x` as their parent. +/// (All tree roots are locals). This structure allows easy traversal +/// between related paths `x` and `x.m` and `x.n`. #[derive(Clone)] pub struct MovePath<'tcx> { pub next_sibling: Option, @@ -166,20 +174,28 @@ where #[derive(Debug)] pub struct MoveData<'tcx> { + /// All the gathered `MovePath`s. pub move_paths: IndexVec>, - pub moves: IndexVec, - /// Each Location `l` is mapped to the MoveOut's that are effects - /// of executing the code at `l`. (There can be multiple MoveOut's - /// for a given `l` because each MoveOut is associated with one - /// particular path being moved.) - pub loc_map: LocationMap>, - pub path_map: IndexVec>, + + /// All the `MoveOut`s. + pub move_outs: IndexVec, + /// Map from locations to `MoveOut`s. `SmallVec` because each location might cause more than + /// one `MoveOut`. Used during analysis and diagnostics. + pub move_out_loc_map: LocationMap>, + /// Map from `MovePath`s (places) to `MoveOuts`. `SmallVec` because each `MovePath` may be + /// moved-out of more than once. Used mostly for diagnostics. + pub move_out_path_map: IndexVec>, + + /// Map from places/locals to `MovePath`s. pub rev_lookup: MovePathLookup<'tcx>, + + /// All the `Init`s. pub inits: IndexVec, - /// Each Location `l` is mapped to the Inits that are effects - /// of executing the code at `l`. Only very rarely (e.g. inline asm) - /// is there more than one Init at any `l`. + /// Map from locations to `Init`s. `SmallVec` because each location might cause more than one + /// `Init`, though more than one is very rare (e.g. inline asm). pub init_loc_map: LocationMap>, + /// Map from `MovePath`s (places) to `Init`s. `SmallVec` because each `MovePath` (place) might + /// be inited more than once. pub init_path_map: IndexVec>, } @@ -223,7 +239,7 @@ where } /// `MoveOut` represents a point in a program that moves out of some -/// L-value; i.e., "creates" uninitialized memory. +/// L-value; i.e., "creates" uninitialized memory. The dual of `Init`. /// /// With respect to dataflow analysis: /// - Generated by moves and declaration of uninitialized variables. @@ -242,7 +258,7 @@ impl fmt::Debug for MoveOut { } } -/// `Init` represents a point in a program that initializes some L-value; +/// `Init` represents a point in a program that initializes some L-value. The dual of `MoveOut`. #[derive(Copy, Clone)] pub struct Init { /// path being initialized @@ -254,7 +270,7 @@ pub struct Init { } /// Initializations can be from an argument or from a statement. Arguments -/// do not have locations, in those cases the `Local` is kept.. +/// do not have locations, in those cases the `Local` is kept. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum InitLocation { Argument(Local), @@ -287,7 +303,7 @@ impl Init { } } -/// Tables mapping from a place to its MovePathIndex. +/// Tables mapping from a place to its `MovePathIndex`. #[derive(Debug)] pub struct MovePathLookup<'tcx> { locals: IndexVec>, @@ -307,7 +323,13 @@ mod builder; #[derive(Copy, Clone, Debug)] pub enum LookupResult { + /// This exact thing has a move path. E.g. we looked up `x` or `x.m` and it has been moved. Exact(MovePathIndex), + + /// - If the field is `None`, neither the exact thing nor any ancestor of it has a move path. + /// E.g. we looked up `x.m` and neither it nor `x` have a move path. + /// - If the field is `Some`, the exact thing has no move path, but an ancestor does. E.g. we + /// looked up `x.m` which has no move path but `x` has one. Not possible for locals. Parent(Option), } @@ -317,10 +339,12 @@ impl<'tcx> MovePathLookup<'tcx> { // unknown place, but will rather return the nearest available // parent. pub fn find(&self, place: PlaceRef<'tcx>) -> LookupResult { + // Look first in the locals (roots). let Some(mut result) = self.find_local(place.local) else { return LookupResult::Parent(None); }; + // Look for a projection through the found local. for (_, elem) in self.un_derefer.iter_projections(place) { let subpath = match MoveSubPath::of(elem.kind()) { MoveSubPathResult::One(kind) => self.projections.get(&(result, kind)), @@ -338,6 +362,7 @@ impl<'tcx> MovePathLookup<'tcx> { LookupResult::Exact(result) } + /// For locals, which are roots. #[inline] pub fn find_local(&self, local: Local) -> Option { self.locals[local] diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 01235b1824218..1fc3f55f8e145 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -68,7 +68,6 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { let dead_unwinds = compute_dead_unwinds(body, &mut inits); let uninits = MaybeUninitializedPlaces::new(tcx, body, &env.move_data) - .include_inactive_in_otherwise() .mark_inactive_variants_as_uninit() .skipping_unreachable_unwind(dead_unwinds) .iterate_to_fixpoint(tcx, body, Some("elaborate_drops")) diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index f17e59c12e57e..a9d5ec958889f 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -3,7 +3,7 @@ use rustc_abi::{ BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, }; -use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; +use crate::callconv::{ArgAbi, ArgAttribute, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; fn extend_integer_width_mips(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips @@ -27,8 +27,15 @@ where { match ret.layout.field(cx, i).backend_repr { BackendRepr::Scalar(scalar) => match scalar.primitive() { - Primitive::Float(Float::F32) => Some(Reg::f32()), - Primitive::Float(Float::F64) => Some(Reg::f64()), + Primitive::Float(float) => { + match float { + // C does not have the f16 type + Float::F16 => None, + Float::F32 => Some(Reg::f32()), + Float::F64 => Some(Reg::f64()), + Float::F128 => Some(Reg::f128()), + } + } _ => None, }, _ => None, @@ -55,14 +62,17 @@ where if let FieldsShape::Arbitrary { .. } = ret.layout.fields { if ret.layout.fields.count() == 1 { if let Some(reg) = float_reg(cx, ret, 0) { - ret.cast_to(reg); + // The inreg attribute forces LLVM to return a struct containing a f128 in + // $f0 and $f1 rather than $f0 and $f2, see: + // https://github.com/llvm/llvm-project/blob/a81db64570f94c2ca8ac0f598c0b5bba1a7ae59e/llvm/lib/Target/Mips/MipsCallingConv.td#L48-L51 + ret.cast_to_with_attrs(reg, ArgAttribute::InReg.into()); return; } } else if ret.layout.fields.count() == 2 && let Some(reg0) = float_reg(cx, ret, 0) && let Some(reg1) = float_reg(cx, ret, 1) { - ret.cast_to(CastTarget::pair(reg0, reg1)); + ret.cast_to_with_attrs(CastTarget::pair(reg0, reg1), ArgAttribute::InReg.into()); return; } } diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 59dfe6bd8d9b7..478af470637d7 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1575,10 +1575,10 @@ pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) #[cfg_attr(panic = "immediate-abort", inline)] #[cold] #[track_caller] -const fn from_ascii_radix_panic(radix: u32) -> ! { +const fn from_ascii_bytes_radix_panic(radix: u32) -> ! { const_panic!( - "from_ascii_radix: radix must lie in the range `[2, 36]`", - "from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}", + "from_ascii_bytes_radix: radix must lie in the range `[2, 36]`", + "from_ascii_bytes_radix: radix must lie in the range `[2, 36]` - found {radix}", radix: u32 = radix, ) } @@ -1676,7 +1676,7 @@ macro_rules! from_str_int_impl { #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] #[inline] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { - <$int_ty>::from_ascii_radix(src.as_bytes(), radix) + <$int_ty>::from_ascii_bytes_radix_impl(src.as_bytes(), radix) } /// Parses an integer from an ASCII-byte slice with decimal digits. @@ -1700,18 +1700,22 @@ macro_rules! from_str_int_impl { /// ``` /// #![feature(int_from_ascii)] /// - #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")] + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_bytes(b\"+10\"), Ok(10));")] /// ``` /// Trailing space returns error: /// ``` /// # #![feature(int_from_ascii)] /// # - #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")] + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_bytes(b\"1 \").is_err());")] /// ``` #[unstable(feature = "int_from_ascii", issue = "134821")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[inline] - pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> { - <$int_ty>::from_ascii_radix(src, 10) + pub const fn from_ascii_bytes(src: T) -> Result<$int_ty, ParseIntError> + where + T: [const] AsRef<[u8]> + [const] crate::marker::Destruct + { + <$int_ty>::from_ascii_bytes_radix(src.as_ref(), 10) } /// Parses an integer from an ASCII-byte slice with digits in a given base. @@ -1744,22 +1748,31 @@ macro_rules! from_str_int_impl { /// ``` /// #![feature(int_from_ascii)] /// - #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")] + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_bytes_radix(b\"A\", 16), Ok(10));")] /// ``` /// Trailing space returns error: /// ``` /// # #![feature(int_from_ascii)] /// # - #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")] + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_bytes_radix(b\"1 \", 10).is_err());")] /// ``` #[unstable(feature = "int_from_ascii", issue = "134821")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[inline] - pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> { + pub const fn from_ascii_bytes_radix(src: T, radix: u32) -> Result<$int_ty, ParseIntError> + where + T: [const] AsRef<[u8]> + [const] crate::marker::Destruct + { + <$int_ty>::from_ascii_bytes_radix_impl(src.as_ref(), radix) + } + + #[inline] + pub(super) const fn from_ascii_bytes_radix_impl(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; if 2 > radix || radix > 36 { - from_ascii_radix_panic(radix); + from_ascii_bytes_radix_panic(radix); } if src.is_empty() { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 0ce2c044a2dfd..e747ac591a3e2 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1262,7 +1262,7 @@ macro_rules! nonzero_integer { /// # /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii(b\"+10\"), Ok(NonZero::new(10)?));")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii_bytes(b\"+10\"), Ok(NonZero::new(10)?));")] /// # Some(()) /// # } /// ``` @@ -1274,12 +1274,16 @@ macro_rules! nonzero_integer { /// /// # use std::num::NonZero; /// # - #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii(b\"1 \").is_err());")] + #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii_bytes(b\"1 \").is_err());")] /// ``` #[unstable(feature = "int_from_ascii", issue = "134821")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[inline] - pub const fn from_ascii(src: &[u8]) -> Result { - Self::from_ascii_radix(src, 10) + pub const fn from_ascii_bytes(src: T) -> Result + where + T: [const] AsRef<[u8]> + [const] crate::marker::Destruct + { + Self::from_ascii_bytes_radix_impl(src.as_ref(), 10) } /// Parses a non-zero integer from an ASCII-byte slice with digits in a given base. @@ -1317,7 +1321,7 @@ macro_rules! nonzero_integer { /// # /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { - #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"A\", 16), Ok(NonZero::new(10)?));")] + #[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_ascii_bytes_radix(b\"A\", 16), Ok(NonZero::new(10)?));")] /// # Some(()) /// # } /// ``` @@ -1329,12 +1333,21 @@ macro_rules! nonzero_integer { /// /// # use std::num::NonZero; /// # - #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii_radix(b\"1 \", 10).is_err());")] + #[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_ascii_bytes_radix(b\"1 \", 10).is_err());")] /// ``` #[unstable(feature = "int_from_ascii", issue = "134821")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + #[inline] + pub const fn from_ascii_bytes_radix(src: T, radix: u32) -> Result + where + T: [const] AsRef<[u8]> + [const] crate::marker::Destruct + { + Self::from_ascii_bytes_radix_impl(src.as_ref(), radix) + } + #[inline] - pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result { - let n = match <$Int>::from_ascii_radix(src, radix) { + const fn from_ascii_bytes_radix_impl(src: &[u8], radix: u32) -> Result { + let n = match <$Int>::from_ascii_bytes_radix_impl(src, radix) { Ok(n) => n, Err(err) => return Err(err), }; @@ -1394,7 +1407,7 @@ macro_rules! nonzero_integer { #[rustc_const_stable(feature = "nonzero_from_str_radix", since = "1.98.0")] #[inline] pub const fn from_str_radix(src: &str, radix: u32) -> Result { - Self::from_ascii_radix(src.as_bytes(), radix) + Self::from_ascii_bytes_radix_impl(src.as_bytes(), radix) } } diff --git a/library/coretests/tests/nonzero.rs b/library/coretests/tests/nonzero.rs index 55d479efb4b40..5efc09fd2d98e 100644 --- a/library/coretests/tests/nonzero.rs +++ b/library/coretests/tests/nonzero.rs @@ -125,57 +125,60 @@ fn test_from_signed_nonzero() { } #[test] -fn test_from_ascii_radix() { - assert_eq!(NonZero::::from_ascii_radix(b"123", 10), Ok(NonZero::new(123).unwrap())); - assert_eq!(NonZero::::from_ascii_radix(b"1001", 2), Ok(NonZero::new(9).unwrap())); - assert_eq!(NonZero::::from_ascii_radix(b"123", 8), Ok(NonZero::new(83).unwrap())); - assert_eq!(NonZero::::from_ascii_radix(b"123", 16), Ok(NonZero::new(291).unwrap())); - assert_eq!(NonZero::::from_ascii_radix(b"ffff", 16), Ok(NonZero::new(65535).unwrap())); - assert_eq!(NonZero::::from_ascii_radix(b"z", 36), Ok(NonZero::new(35).unwrap())); +fn test_from_ascii_bytes_radix() { + assert_eq!(NonZero::::from_ascii_bytes_radix(b"123", 10), Ok(NonZero::new(123).unwrap())); + assert_eq!(NonZero::::from_ascii_bytes_radix(b"1001", 2), Ok(NonZero::new(9).unwrap())); + assert_eq!(NonZero::::from_ascii_bytes_radix(b"123", 8), Ok(NonZero::new(83).unwrap())); + assert_eq!(NonZero::::from_ascii_bytes_radix(b"123", 16), Ok(NonZero::new(291).unwrap())); assert_eq!( - NonZero::::from_ascii_radix(b"0", 10).err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes_radix(b"ffff", 16), + Ok(NonZero::new(65535).unwrap()) + ); + assert_eq!(NonZero::::from_ascii_bytes_radix(b"z", 36), Ok(NonZero::new(35).unwrap())); + assert_eq!( + NonZero::::from_ascii_bytes_radix(b"0", 10).err().map(|e| e.kind().clone()), Some(IntErrorKind::Zero) ); assert_eq!( - NonZero::::from_ascii_radix(b"-1", 10).err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes_radix(b"-1", 10).err().map(|e| e.kind().clone()), Some(IntErrorKind::InvalidDigit) ); assert_eq!( - NonZero::::from_ascii_radix(b"-129", 10).err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes_radix(b"-129", 10).err().map(|e| e.kind().clone()), Some(IntErrorKind::NegOverflow) ); assert_eq!( - NonZero::::from_ascii_radix(b"257", 10).err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes_radix(b"257", 10).err().map(|e| e.kind().clone()), Some(IntErrorKind::PosOverflow) ); assert_eq!( - NonZero::::from_ascii_radix(b"Z", 10).err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes_radix(b"Z", 10).err().map(|e| e.kind().clone()), Some(IntErrorKind::InvalidDigit) ); assert_eq!( - NonZero::::from_ascii_radix(b"_", 2).err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes_radix(b"_", 2).err().map(|e| e.kind().clone()), Some(IntErrorKind::InvalidDigit) ); } #[test] -fn test_from_ascii() { - assert_eq!(NonZero::::from_ascii(b"123"), Ok(NonZero::new(123).unwrap())); +fn test_from_ascii_bytes() { + assert_eq!(NonZero::::from_ascii_bytes(b"123"), Ok(NonZero::new(123).unwrap())); assert_eq!( - NonZero::::from_ascii(b"0").err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes(b"0").err().map(|e| e.kind().clone()), Some(IntErrorKind::Zero) ); assert_eq!( - NonZero::::from_ascii(b"-1").err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes(b"-1").err().map(|e| e.kind().clone()), Some(IntErrorKind::InvalidDigit) ); assert_eq!( - NonZero::::from_ascii(b"-129").err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes(b"-129").err().map(|e| e.kind().clone()), Some(IntErrorKind::NegOverflow) ); assert_eq!( - NonZero::::from_ascii(b"257").err().map(|e| e.kind().clone()), + NonZero::::from_ascii_bytes(b"257").err().map(|e| e.kind().clone()), Some(IntErrorKind::PosOverflow) ); } diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 90666fc0b1ad2..ac65a3b59c66a 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -118,7 +118,7 @@ fn from_str_issue7588() { #[test] #[should_panic = "radix must lie in the range `[2, 36]`"] -fn from_ascii_radix_panic() { +fn from_ascii_bytes_radix_panic() { let radix = 1; let _parsed = u64::from_str_radix("12345ABCD", radix); } diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 75a75ab6bc5fd..1f9b86e1eb75e 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -76,7 +76,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // fast path with a single syscall for systems with poll() #[cfg(not(any( - miri, // no `poll` target_os = "emscripten", target_os = "fuchsia", target_os = "vxworks", diff --git a/library/std/src/sys/platform_version/darwin/mod.rs b/library/std/src/sys/platform_version/darwin/mod.rs index 06b97fcdef4f2..4756716452c88 100644 --- a/library/std/src/sys/platform_version/darwin/mod.rs +++ b/library/std/src/sys/platform_version/darwin/mod.rs @@ -313,17 +313,17 @@ unsafe fn string_version_key( /// Parse an OS version from a bytestring like b"10.1" or b"14.3.7". fn parse_os_version(version: &[u8]) -> Result { if let Some((major, minor)) = version.split_once(|&b| b == b'.') { - let major = u16::from_ascii(major)?; + let major = u16::from_ascii_bytes(major)?; if let Some((minor, patch)) = minor.split_once(|&b| b == b'.') { - let minor = u8::from_ascii(minor)?; - let patch = u8::from_ascii(patch)?; + let minor = u8::from_ascii_bytes(minor)?; + let patch = u8::from_ascii_bytes(patch)?; Ok(pack_os_version(major, minor, patch)) } else { - let minor = u8::from_ascii(minor)?; + let minor = u8::from_ascii_bytes(minor)?; Ok(pack_os_version(major, minor, 0)) } } else { - let major = u16::from_ascii(version)?; + let major = u16::from_ascii_bytes(version)?; Ok(pack_os_version(major, 0, 0)) } } diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index bb36fb687b4af..92fb586d39dcf 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -1,7 +1,7 @@ use super::thread::Thread; use crate::cell::Cell; use crate::iter; -use crate::sync::Arc; +use crate::sync::{Arc, UniqueArc}; crate::thread_local! { /// A thread local linked list of spawn hooks. @@ -44,10 +44,12 @@ struct SpawnHook { /// In other words, adding a hook has no effect on already running threads (other than the current /// thread) and the threads they might spawn in the future. /// -/// Hooks can only be added, not removed. -/// /// The hooks will run in reverse order, starting with the most recently added. /// +/// Hooks can only be added, not removed. +/// Note that this does *not* mean that they are guaranteed to run. See [`Builder::no_hooks`]. +/// You cannot rely on a hook running for soundness. +/// /// # Usage /// /// ``` @@ -88,6 +90,8 @@ struct SpawnHook { /// assert_eq!(X.get(), 123); /// }).join().unwrap(); /// ``` +/// +/// [`Builder::no_hooks`]: crate::thread::Builder::no_hooks #[unstable(feature = "thread_spawn_hook", issue = "132951")] pub fn add_spawn_hook(hook: F) where @@ -95,12 +99,14 @@ where G: 'static + Send + FnOnce(), { SPAWN_HOOKS.with(|h| { - let mut hooks = h.take(); - let next = hooks.first.take(); - hooks.first = Some(Arc::new(SpawnHook { + // Perform all fallible operations before taking the current hooks (see #159923) + let mut new_first = UniqueArc::new(SpawnHook { hook: Box::new(move |thread| Box::new(hook(thread))), - next, - })); + next: None, + }); + let mut hooks = h.take(); + new_first.next = hooks.first.take(); + hooks.first = Some(UniqueArc::into_arc(new_first)); h.set(hooks); }); } diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.current.stderr similarity index 88% rename from src/tools/clippy/tests/ui/future_not_send.stderr rename to src/tools/clippy/tests/ui/future_not_send.current.stderr index 8b8af1ebaed39..471452d9a416c 100644 --- a/src/tools/clippy/tests/ui/future_not_send.stderr +++ b/src/tools/clippy/tests/ui/future_not_send.current.stderr @@ -1,11 +1,11 @@ error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:8:62 + --> tests/ui/future_not_send.rs:11:62 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { | ^^^^ future returned by `private_future` is not `Send` | note: future is not `Send` as this value is used across an await - --> tests/ui/future_not_send.rs:11:20 + --> tests/ui/future_not_send.rs:14:20 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { | -- has type `std::rc::Rc<[u8]>` which is not `Send` @@ -14,7 +14,7 @@ LL | async { true }.await | ^^^^^ await occurs here, with `rc` maybe used later = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> tests/ui/future_not_send.rs:8:39 + --> tests/ui/future_not_send.rs:11:39 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { | ^^^^ has type `&std::cell::Cell` which is not `Send`, because `std::cell::Cell` is not `Sync` @@ -23,13 +23,13 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:14:41 + --> tests/ui/future_not_send.rs:17:41 | LL | pub async fn public_future(rc: Rc<[u8]>) { | ^ future returned by `public_future` is not `Send` | note: future is not `Send` as this value is used across an await - --> tests/ui/future_not_send.rs:17:20 + --> tests/ui/future_not_send.rs:20:20 | LL | pub async fn public_future(rc: Rc<[u8]>) { | -- has type `std::rc::Rc<[u8]>` which is not `Send` @@ -39,45 +39,45 @@ LL | async { true }.await; = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:24:63 + --> tests/ui/future_not_send.rs:27:63 | LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { | ^^^^ future returned by `private_future2` is not `Send` | note: captured value is not `Send` - --> tests/ui/future_not_send.rs:24:26 + --> tests/ui/future_not_send.rs:27:26 | LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> tests/ui/future_not_send.rs:24:40 + --> tests/ui/future_not_send.rs:27:40 | LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { | ^^^^ has type `&std::cell::Cell` which is not `Send`, because `std::cell::Cell` is not `Sync` = note: `std::cell::Cell` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:30:42 + --> tests/ui/future_not_send.rs:33:42 | LL | pub async fn public_future2(rc: Rc<[u8]>) {} | ^ future returned by `public_future2` is not `Send` | note: captured value is not `Send` - --> tests/ui/future_not_send.rs:30:29 + --> tests/ui/future_not_send.rs:33:29 | LL | pub async fn public_future2(rc: Rc<[u8]>) {} | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:42:39 + --> tests/ui/future_not_send.rs:45:39 | LL | async fn private_future(&self) -> usize { | ^^^^^ future returned by `private_future` is not `Send` | note: future is not `Send` as this value is used across an await - --> tests/ui/future_not_send.rs:45:24 + --> tests/ui/future_not_send.rs:48:24 | LL | async fn private_future(&self) -> usize { | ----- has type `&Dummy` which is not `Send` @@ -87,26 +87,26 @@ LL | async { true }.await; = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:49:38 + --> tests/ui/future_not_send.rs:52:38 | LL | pub async fn public_future(&self) { | ^ future returned by `public_future` is not `Send` | note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> tests/ui/future_not_send.rs:49:32 + --> tests/ui/future_not_send.rs:52:32 | LL | pub async fn public_future(&self) { | ^^^^^ has type `&Dummy` which is not `Send`, because `Dummy` is not `Sync` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:61:37 + --> tests/ui/future_not_send.rs:64:37 | LL | async fn generic_future(t: T) -> T | ^ future returned by `generic_future` is not `Send` | note: future is not `Send` as this value is used across an await - --> tests/ui/future_not_send.rs:67:20 + --> tests/ui/future_not_send.rs:70:20 | LL | let rt = &t; | -- has type `&T` which is not `Send` @@ -115,13 +115,13 @@ LL | async { true }.await; = note: `T` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> tests/ui/future_not_send.rs:83:51 + --> tests/ui/future_not_send.rs:86:51 | LL | async fn generic_future_always_unsend(_: Rc) { | ^ future returned by `generic_future_always_unsend` is not `Send` | note: future is not `Send` as this value is used across an await - --> tests/ui/future_not_send.rs:86:20 + --> tests/ui/future_not_send.rs:89:20 | LL | async fn generic_future_always_unsend(_: Rc) { | - has type `std::rc::Rc` which is not `Send` diff --git a/src/tools/clippy/tests/ui/future_not_send.next.stderr b/src/tools/clippy/tests/ui/future_not_send.next.stderr new file mode 100644 index 0000000000000..e5cacd6c8b3bf --- /dev/null +++ b/src/tools/clippy/tests/ui/future_not_send.next.stderr @@ -0,0 +1,75 @@ +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:11:62 + | +LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^^^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + = note: `-D clippy::future-not-send` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` + +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:17:41 + | +LL | pub async fn public_future(rc: Rc<[u8]>) { + | ^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:27:63 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^^^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:33:42 + | +LL | pub async fn public_future2(rc: Rc<[u8]>) {} + | ^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:45:39 + | +LL | async fn private_future(&self) -> usize { + | ^^^^^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:52:38 + | +LL | pub async fn public_future(&self) { + | ^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:64:37 + | +LL | async fn generic_future(t: T) -> T + | ^ future returned by `generic_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> tests/ui/future_not_send.rs:70:20 + | +LL | let rt = &t; + | -- has type `&T` which is not `Send` +LL | async { true }.await; + | ^^^^^ await occurs here, with `rt` maybe used later + = note: `T` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> tests/ui/future_not_send.rs:86:51 + | +LL | async fn generic_future_always_unsend(_: Rc) { + | ^ + | + = note: `std::rc::Rc` doesn't implement `std::marker::Send` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/future_not_send.rs b/src/tools/clippy/tests/ui/future_not_send.rs index 662ecb9c955de..41d6fc448ffce 100644 --- a/src/tools/clippy/tests/ui/future_not_send.rs +++ b/src/tools/clippy/tests/ui/future_not_send.rs @@ -1,3 +1,6 @@ +//@revisions: current next +//@[next] compile-flags: -Znext-solver=globally + #![warn(clippy::future_not_send)] use std::cell::Cell; diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index a5304dc3ef781..6726b85e03403 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -227,7 +227,19 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { } } -impl FileDescription for io::Stdin { +#[derive(Debug)] +struct Stdin { + stdin: io::Stdin, + watched: ReadinessWatched, +} + +impl Stdin { + fn new() -> Self { + Self { stdin: io::stdin(), watched: ReadinessWatched::default() } + } +} + +impl FileDescription for Stdin { fn name(&self) -> &'static str { "stdin" } @@ -245,17 +257,42 @@ impl FileDescription for io::Stdin { helpers::isolation_abort_error("`read` from stdin")?; } - let mut stdin = &*self; - let result = ecx.read_from_host(|buf| stdin.read(buf), len, ptr)?; + // FIXME: this can block on the host, halting the entire interpreter. + let result = ecx.read_from_host(|buf| (&mut &self.stdin).read(buf), len, ptr)?; finish.call(ecx, result) } fn is_tty(&self, communicate_allowed: bool) -> bool { - communicate_allowed && self.is_terminal() + communicate_allowed && self.stdin.is_terminal() + } + + fn readiness_watched(&self) -> Option<&ReadinessWatched> { + Some(&self.watched) } + + fn readiness(&self) -> Readiness { + // Stdin is readable (we never return EWOULDBLOCK above) and also writable (since that never + // blocks either). This matches what we see on Linux. + let mut readiness = Readiness::EMPTY; + readiness.readable = true; + readiness.writable = true; + readiness + } +} + +#[derive(Debug)] +struct Stdout { + stdout: io::Stdout, + watched: ReadinessWatched, } -impl FileDescription for io::Stdout { +impl Stdout { + fn new() -> Self { + Self { stdout: io::stdout(), watched: ReadinessWatched::default() } + } +} + +impl FileDescription for Stdout { fn name(&self) -> &'static str { "stdout" } @@ -269,7 +306,7 @@ impl FileDescription for io::Stdout { finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // We allow writing to stdout even with isolation enabled. - let result = ecx.write_to_host(&*self, len, ptr)?; + let result = ecx.write_to_host(&self.stdout, len, ptr)?; // Stdout is buffered, flush to make sure it appears on the // screen. This is the write() syscall of the interpreted // program, we want it to correspond to a write() syscall on @@ -281,11 +318,34 @@ impl FileDescription for io::Stdout { } fn is_tty(&self, communicate_allowed: bool) -> bool { - communicate_allowed && self.is_terminal() + communicate_allowed && self.stdout.is_terminal() + } + + fn readiness_watched(&self) -> Option<&ReadinessWatched> { + Some(&self.watched) } + + fn readiness(&self) -> Readiness { + // stdout can always be written (we never return EWOULDBLOCK there) and never be read. + let mut readiness = Readiness::EMPTY; + readiness.writable = true; + readiness + } +} + +#[derive(Debug)] +struct Stderr { + stderr: io::Stderr, + watched: ReadinessWatched, } -impl FileDescription for io::Stderr { +impl Stderr { + fn new() -> Self { + Self { stderr: io::stderr(), watched: ReadinessWatched::default() } + } +} + +impl FileDescription for Stderr { fn name(&self) -> &'static str { "stderr" } @@ -299,13 +359,65 @@ impl FileDescription for io::Stderr { finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // We allow writing to stderr even with isolation enabled. - let result = ecx.write_to_host(&*self, len, ptr)?; + let result = ecx.write_to_host(&self.stderr, len, ptr)?; // No need to flush, stderr is not buffered. finish.call(ecx, result) } fn is_tty(&self, communicate_allowed: bool) -> bool { - communicate_allowed && self.is_terminal() + communicate_allowed && self.stderr.is_terminal() + } + + fn readiness_watched(&self) -> Option<&ReadinessWatched> { + Some(&self.watched) + } + + fn readiness(&self) -> Readiness { + // stderr can always be written (we never return EWOULDBLOCK there) and never be read. + let mut readiness = Readiness::EMPTY; + readiness.writable = true; + readiness + } +} + +/// Like /dev/null +#[derive(Debug)] +pub struct NullOutput { + watched: ReadinessWatched, +} + +impl NullOutput { + fn new() -> Self { + Self { watched: ReadinessWatched::default() } + } +} + +impl FileDescription for NullOutput { + fn name(&self) -> &'static str { + "null output" + } + + fn write<'tcx>( + self: FileDescriptionRef, + _communicate_allowed: bool, + _ptr: Pointer, + len: usize, + ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + // We just don't write anything, but report to the user that we did. + finish.call(ecx, Ok(len)) + } + + fn readiness_watched(&self) -> Option<&ReadinessWatched> { + Some(&self.watched) + } + + fn readiness(&self) -> Readiness { + // null output can always be written (we never return EWOULDBLOCK there) and never be read. + let mut readiness = Readiness::EMPTY; + readiness.writable = true; + readiness } } @@ -416,28 +528,6 @@ impl FileDescription for DirHandle { } } -/// Like /dev/null -#[derive(Debug)] -pub struct NullOutput; - -impl FileDescription for NullOutput { - fn name(&self) -> &'static str { - "stderr and stdout" - } - - fn write<'tcx>( - self: FileDescriptionRef, - _communicate_allowed: bool, - _ptr: Pointer, - len: usize, - ecx: &mut MiriInterpCx<'tcx>, - finish: DynMachineCallback<'tcx, Result>, - ) -> InterpResult<'tcx> { - // We just don't write anything, but report to the user that we did. - finish.call(ecx, Ok(len)) - } -} - /// Internal type of a file-descriptor - this is what [`FdTable`] expects pub type FdNum = i32; @@ -461,13 +551,13 @@ impl FdTable { } pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable { let mut fds = FdTable::new(); - fds.insert_new(io::stdin()); + fds.insert_new(Stdin::new()); if mute_stdout_stderr { - assert_eq!(fds.insert_new(NullOutput), 1); - assert_eq!(fds.insert_new(NullOutput), 2); + assert_eq!(fds.insert_new(NullOutput::new()), 1); + assert_eq!(fds.insert_new(NullOutput::new()), 2); } else { - assert_eq!(fds.insert_new(io::stdout()), 1); - assert_eq!(fds.insert_new(io::stderr()), 2); + assert_eq!(fds.insert_new(Stdout::new()), 1); + assert_eq!(fds.insert_new(Stderr::new()), 2); } fds } diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index 4691588eefb40..c59b729e5eeca 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -32,6 +32,13 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cross-crate-items" +version = "0.1.0" +dependencies = [ + "futures", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -290,6 +297,7 @@ dependencies = [ name = "miri-test-deps" version = "0.1.0" dependencies = [ + "cross-crate-items", "futures", "getrandom 0.1.16", "getrandom 0.2.17", diff --git a/src/tools/miri/tests/deps/Cargo.toml b/src/tools/miri/tests/deps/Cargo.toml index bbbcc316f31c2..a377141f98aaf 100644 --- a/src/tools/miri/tests/deps/Cargo.toml +++ b/src/tools/miri/tests/deps/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] # all dependencies (and their transitive ones) listed here can be used in `tests/*-dep`. +cross-crate-items = { path = "cross-crate-items" } # Used to test things that need multiple crates to check libc = "0.2" num_cpus = "1.10.1" @@ -35,3 +36,4 @@ windows-sys = { version = "0.61", features = [ # Make sure we are not part of the rustc workspace. [workspace] +members = ["cross-crate-items"] diff --git a/src/tools/miri/tests/deps/cross-crate-items/Cargo.toml b/src/tools/miri/tests/deps/cross-crate-items/Cargo.toml new file mode 100644 index 0000000000000..1f9fc93cd5237 --- /dev/null +++ b/src/tools/miri/tests/deps/cross-crate-items/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cross-crate-items" +version = "0.1.0" +edition = "2021" + +[dependencies] +futures = { version = "0.3.0", default-features = false, features = ["alloc", "async-await"] } diff --git a/src/tools/miri/tests/deps/cross-crate-items/src/lib.rs b/src/tools/miri/tests/deps/cross-crate-items/src/lib.rs new file mode 100644 index 0000000000000..8f462d5b515f1 --- /dev/null +++ b/src/tools/miri/tests/deps/cross-crate-items/src/lib.rs @@ -0,0 +1,5 @@ +/// Async closure in an external crate — required because decoder.rs is only called for +/// defs loaded from .rmeta files, not local crate defs. +pub fn returns_async_closure() -> impl futures::Sink<(), Error = std::io::Error> { + futures::sink::unfold((), async |(), ()| Ok::<_, std::io::Error>(())) +} diff --git a/src/tools/miri/tests/pass-dep/cross_crate_async_closure_ice.rs b/src/tools/miri/tests/pass-dep/cross_crate_async_closure_ice.rs new file mode 100644 index 0000000000000..d09ff7c77c9a4 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/cross_crate_async_closure_ice.rs @@ -0,0 +1,26 @@ +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +// This is a regression test for a Miri ICE when encountering a `SyntheticCoroutineBody` from an external crate (see +// `cross_crate_items` for details). Calling the async closure triggered the ICE: https://github.com/rust-lang/rust/issues/156905 + +use std::future::Future; +use std::task::{Context, Poll, Waker}; + +use futures::SinkExt as _; + +// Taken from tests/pass/async-fn.rs. +fn run_fut(fut: impl Future) -> T { + let mut context = Context::from_waker(Waker::noop()); + let mut pinned = Box::pin(fut); + loop { + match pinned.as_mut().poll(&mut context) { + Poll::Pending => continue, + Poll::Ready(v) => return v, + } + } +} + +fn main() { + let mut sink = Box::pin(cross_crate_items::returns_async_closure()); + run_fut(sink.send(())).unwrap(); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 09d3c6b857204..3c11d0f38e17a 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -21,6 +21,7 @@ use libc_utils::{errno_check, errno_result}; fn main() { test_dup(); test_dup_stdout_stderr(); + test_fcntl_getfd(); test_canonicalize_too_long(); test_rename(); test_ftruncate::(libc::ftruncate); @@ -310,6 +311,13 @@ fn test_dup() { } } +fn test_fcntl_getfd() { + // This should succeed for FDs that exist and fail for those that do not. + let _success = errno_result(unsafe { libc::fcntl(0, libc::F_GETFD) }).unwrap(); + let err = errno_result(unsafe { libc::fcntl(1337, libc::F_GETFD) }).unwrap_err(); + assert_eq!(err.raw_os_error().unwrap(), libc::EBADF); +} + fn test_canonicalize_too_long() { // Make sure we get an error for long paths. let too_long = "x/".repeat(libc::PATH_MAX.try_into().unwrap()); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-poll-std-handles.rs b/src/tools/miri/tests/pass-dep/libc/libc-poll-std-handles.rs new file mode 100644 index 0000000000000..bcff187859546 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-poll-std-handles.rs @@ -0,0 +1,23 @@ +//! Ensure we can poll the std handles, both the normal ones and the "null" ones. +//@ignore-target: windows # no libc +//@revisions: normal null +//@[null]compile-flags: -Zmiri-mute-stdout-stderr +//@run-native + +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + +fn main() { + let pfds: &mut [_] = &mut [ + libc::pollfd { fd: 0, events: libc::POLLOUT | libc::POLLIN, revents: 0 }, + libc::pollfd { fd: 1, events: libc::POLLOUT | libc::POLLIN, revents: 0 }, + libc::pollfd { fd: 2, events: libc::POLLOUT | libc::POLLIN, revents: 0 }, + ]; + let num = errno_result(unsafe { libc::poll(pfds.as_mut_ptr(), 3, 0) }).unwrap(); + assert_eq!(num, 3); + + assert_eq!(pfds[0].revents, libc::POLLIN | libc::POLLOUT); + assert_eq!(pfds[1].revents, libc::POLLOUT); + assert_eq!(pfds[2].revents, libc::POLLOUT); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index 3a4ece46c9b61..85d6302a0175d 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -908,7 +908,7 @@ fn test_sockopt_rcvtimeo() { /// the operation is finished, even when the socket file _descriptor_ gets /// closed in the mean time. fn test_unblock_after_socket_close() { - // MacOS behaves different (`read` errors with EBADFD when the file description is closed) + // MacOS behaves different (`read` errors with EBADF when the file description is closed) // so we skip the test when we are run on a native macOS target. if cfg!(not(miri)) && cfg!(target_os = "macos") { return; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index 461a209fbe10b..916aeed5232d7 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -185,7 +185,7 @@ fn test_blocking_write() { /// the operation is finished, even when the socket file _descriptor_ gets /// closed in the mean time. fn test_unblock_after_socket_close() { - // MacOS behaves different (`read` errors with EBADFD when the file description is closed) + // MacOS behaves different (`read` errors with EBADF when the file description is closed) // so we skip the test when we are run on a native macOS target. if cfg!(not(miri)) && cfg!(target_os = "macos") { return; diff --git a/tests/assembly-llvm/mips-struct-float.rs b/tests/assembly-llvm/mips-struct-float.rs new file mode 100644 index 0000000000000..92f10267e01d2 --- /dev/null +++ b/tests/assembly-llvm/mips-struct-float.rs @@ -0,0 +1,151 @@ +// Tests that MIPS targets return structs that are up to 128 bits large, only +// consist of 1 or 2 floating point fields and have a first field with offset 0 +// in FPRs rather than GPRs. +// +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 +// +//@ revisions: LE +//@[LE] compile-flags: --target=mips64el-unknown-linux-gnuabi64 +//@[LE] needs-llvm-components: mips +//@ revisions: BE +//@[BE] compile-flags: --target=mips64-unknown-linux-gnuabi64 +//@[BE] needs-llvm-components: mips + +#![crate_type = "lib"] +#![feature(no_core, f128)] +#![no_core] + +extern crate minicore; + +// 128-bit large struct with one floating point field, should be returned in +// $f0 and $f1 to match the de-facto GCC ABI. +#[repr(C)] +struct SingleF128 { + x: f128, +} + +// 64-bit large struct with one floating point field, should be returned in $f0. +#[repr(C)] +struct SingleF64 { + x: f64, +} + +// 32-bit large struct with one floating point field, should be returned in $f0. +#[repr(C)] +struct SingleF32 { + x: f32, +} + +// 128-bit large struct with two floating point fields, should be returned in +// $f0 and $f2. +#[repr(C)] +struct TwoF64 { + x: f64, + y: f64, +} + +// 64-bit large struct with two floating point fields, should be returned in +// $f0 and $f2. +#[repr(C)] +struct TwoF32 { + x: f32, + y: f32, +} + +// 96-bit large struct with two floating point fields, should be returned in +// $f0 and $f2. +#[repr(C)] +struct F32AndF64 { + x: f32, + y: f64, +} + +// 96-bit large struct with two floating-point fields, should be returned in +// $f0 and $f2. +#[repr(C)] +struct F64AndF32 { + x: f64, + y: f32, +} + +// CHECK-LABEL: single_f128 +// CHECK: dmtc1 $4, $f0 +// CHECK-NEXT: jr $ra +// CHECK-NEXT: dmtc1 $5, $f1 +#[unsafe(no_mangle)] +pub extern "C" fn single_f128(a: SingleF128) -> SingleF128 { + a +} + +// CHECK-LABEL: single_f64 +// CHECK: jr $ra +// CHECK-NEXT: mov.d $f0, $f12 +#[unsafe(no_mangle)] +pub extern "C" fn single_f64(a: SingleF64) -> SingleF64 { + a +} + +// CHECK-LABEL: single_f32 +// BE: dsrl $1, $4, 32 +// LE: sll $1, $4, 0 +// BE-NEXT: sll $1, $1, 0 +// CHECK-NEXT: jr $ra +// CHECK-NEXT: mtc1 $1, $f0 +#[unsafe(no_mangle)] +pub extern "C" fn single_f32(a: SingleF32) -> SingleF32 { + a +} + +// CHECK-LABEL: two_f64 +// CHECK: mov.d $f2, $f13 +// CHECK-NEXT: jr $ra +// CHECK-NEXT: mov.d $f0, $f12 +#[unsafe(no_mangle)] +pub extern "C" fn two_f64(a: TwoF64) -> TwoF64 { + a +} + +// CHECK-LABEL: two_f32 +// CHECK: sll $1, $4, 0 +// LE-NEXT: mtc1 $1, $f0 +// BE-NEXT: mtc1 $1, $f2 +// CHECK-NEXT: dsrl $1, $4, 32 +// CHECK-NEXT: sll $1, $1, 0 +// CHECK-NEXT: jr $ra +// LE-NEXT: mtc1 $1, $f2 +// BE-NEXT: mtc1 $1, $f0 +#[unsafe(no_mangle)] +pub extern "C" fn two_f32(a: TwoF32) -> TwoF32 { + a +} + +// We deviate from Clang in the order of instructions in the next two functions +// but that's okay + +// CHECK-LABEL: f32_and_f64 +// BE: dsrl $1, $4, 32 +// LE: mov.d $f2, $f13 +// BE-NEXT: mov.d $f2, $f13 +// LE-NEXT: sll $1, $4, 0 +// BE-NEXT: sll $1, $1, 0 +// CHECK-NEXT: jr $ra +// CHECK-NEXT: mtc1 $1, $f0 +#[unsafe(no_mangle)] +pub extern "C" fn f32_and_f64(a: F32AndF64) -> F32AndF64 { + a +} + +// CHECK-LABEL: f64_and_f32 +// BE: dsrl $1, $5, 32 +// LE: mov.d $f0, $f12 +// BE-NEXT: mov.d $f0, $f12 +// LE-NEXT: sll $1, $5, 0 +// BE-NEXT: sll $1, $1, 0 +// CHECK-NEXT: jr $ra +// CHECK-NEXT: mtc1 $1, $f2 +#[unsafe(no_mangle)] +pub extern "C" fn f64_and_f32(a: F64AndF32) -> F64AndF32 { + a +} diff --git a/tests/ui/attributes/link-name-on-static.rs b/tests/ui/attributes/link-name-on-static.rs new file mode 100644 index 0000000000000..85082d3a43fb4 --- /dev/null +++ b/tests/ui/attributes/link-name-on-static.rs @@ -0,0 +1,17 @@ +#[link_name = "VALUE"] +//~^ WARN the `link_name` attribute cannot be used on statics +//~| WARN this was previously accepted by the compiler but is being phased out +static VALUE_DEFINITION: u8 = 0; + +#[unsafe(link_name = "UNSAFE_VALUE")] +//~^ ERROR `link_name` is not an unsafe attribute +//~| WARN the `link_name` attribute cannot be used on statics +//~| WARN this was previously accepted by the compiler but is being phased out +static UNSAFE_VALUE_DEFINITION: u8 = 0; + +unsafe extern "C" { + #[link_name = "VALUE"] + static VALUE_DECLARATION: u8; +} + +fn main() {} diff --git a/tests/ui/attributes/link-name-on-static.stderr b/tests/ui/attributes/link-name-on-static.stderr new file mode 100644 index 0000000000000..2d768acd51bfb --- /dev/null +++ b/tests/ui/attributes/link-name-on-static.stderr @@ -0,0 +1,39 @@ +error: `link_name` is not an unsafe attribute + --> $DIR/link-name-on-static.rs:6:3 + | +LL | #[unsafe(link_name = "UNSAFE_VALUE")] + | ^^^^^^ this is not an unsafe attribute + | + = note: extraneous unsafe is not allowed in attributes + +warning: the `link_name` attribute cannot be used on statics + --> $DIR/link-name-on-static.rs:1:3 + | +LL | #[link_name = "VALUE"] + | ^^^^^^^^^ + | + = help: the `link_name` attribute can be applied to foreign functions and foreign statics + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: requested on the command line with `-W unused-attributes` +help: did you mean to use `#[export_name]`? + | +LL - #[link_name = "VALUE"] +LL + #[unsafe(export_name = "VALUE")] + | + +warning: the `link_name` attribute cannot be used on statics + --> $DIR/link-name-on-static.rs:6:10 + | +LL | #[unsafe(link_name = "UNSAFE_VALUE")] + | ^^^^^^^^^ + | + = help: the `link_name` attribute can be applied to foreign functions and foreign statics + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +help: did you mean to use `#[export_name]`? + | +LL - #[unsafe(link_name = "UNSAFE_VALUE")] +LL + #[unsafe(export_name = "UNSAFE_VALUE")] + | + +error: aborting due to 1 previous error; 2 warnings emitted + diff --git a/tests/ui/consts/const-eval/parse_ints.stderr b/tests/ui/consts/const-eval/parse_ints.stderr index 7f42e20cf8e83..b8027fd951d5a 100644 --- a/tests/ui/consts/const-eval/parse_ints.stderr +++ b/tests/ui/consts/const-eval/parse_ints.stderr @@ -1,4 +1,4 @@ -error[E0080]: evaluation panicked: from_ascii_radix: radix must lie in the range `[2, 36]` +error[E0080]: evaluation panicked: from_ascii_bytes_radix: radix must lie in the range `[2, 36]` --> $DIR/parse_ints.rs:7:24 | LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; @@ -9,14 +9,14 @@ note: inside `core::num::::from_str_radix` ::: $SRC_DIR/core/src/num/mod.rs:LL:COL | = note: in this macro invocation -note: inside `core::num::::from_ascii_radix` +note: inside `core::num::::from_ascii_bytes_radix_impl` --> $SRC_DIR/core/src/num/mod.rs:LL:COL ::: $SRC_DIR/core/src/num/mod.rs:LL:COL | = note: in this macro invocation = note: this error originates in the macro `from_str_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0080]: evaluation panicked: from_ascii_radix: radix must lie in the range `[2, 36]` +error[E0080]: evaluation panicked: from_ascii_bytes_radix: radix must lie in the range `[2, 36]` --> $DIR/parse_ints.rs:8:25 | LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; @@ -27,7 +27,7 @@ note: inside `core::num::::from_str_radix` ::: $SRC_DIR/core/src/num/mod.rs:LL:COL | = note: in this macro invocation -note: inside `core::num::::from_ascii_radix` +note: inside `core::num::::from_ascii_bytes_radix_impl` --> $SRC_DIR/core/src/num/mod.rs:LL:COL ::: $SRC_DIR/core/src/num/mod.rs:LL:COL | diff --git a/tests/ui/std/add-spawn-hook-reentrancy-159923.rs b/tests/ui/std/add-spawn-hook-reentrancy-159923.rs new file mode 100644 index 0000000000000..a3714bfda9501 --- /dev/null +++ b/tests/ui/std/add-spawn-hook-reentrancy-159923.rs @@ -0,0 +1,61 @@ +// https://github.com/rust-lang/rust/issues/159923 +//@ edition:2024 +//@ run-pass +//@ needs-threads + +#![feature(alloc_error_hook, thread_spawn_hook)] + +use std::{ + alloc::{GlobalAlloc, Layout, System, set_alloc_error_hook}, + sync::atomic::{AtomicBool, AtomicU32, Ordering}, + thread, +}; + +struct FailingGlobalAlloc; +unsafe impl GlobalAlloc for FailingGlobalAlloc { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if FAIL_NEXT_ALLOC.swap(false, Ordering::Relaxed) { + return std::ptr::null_mut(); + } + unsafe { System.alloc(layout) } + } + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe { System.dealloc(ptr, layout) } + } +} +#[global_allocator] +static ALLOC: FailingGlobalAlloc = FailingGlobalAlloc; + +static FAIL_NEXT_ALLOC: AtomicBool = AtomicBool::new(false); + +fn spawn_and_join() { + thread::scope(|scope| { + scope.spawn(|| {}); + }); +} + +fn main() { + static COUNT: AtomicU32 = AtomicU32::new(0); + + thread::add_spawn_hook(|_| || _ = COUNT.fetch_add(1, Ordering::Relaxed)); + thread::add_spawn_hook(|_| || _ = COUNT.fetch_add(1, Ordering::Relaxed)); + + spawn_and_join(); + spawn_and_join(); + assert_eq!(COUNT.swap(0, Ordering::Relaxed), 4); + + set_alloc_error_hook(|_| { + spawn_and_join(); + assert_eq!(COUNT.swap(0, Ordering::Relaxed), 2); + panic!() + }); + + FAIL_NEXT_ALLOC.store(true, Ordering::Relaxed); + std::panic::catch_unwind(|| { + std::thread::add_spawn_hook(|_| || {}); + }) + .unwrap_err(); + + spawn_and_join(); + assert_eq!(COUNT.swap(0, Ordering::Relaxed), 2); +}