diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index bb18599155781..53fa0a2fb9293 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/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index d1e2f2437ae2f..e0e5252ef1ed1 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -5,14 +5,11 @@ use rustc_infer::infer::canonical::{QueryRegionConstraint, QueryRegionConstraint use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; -use rustc_infer::traits::query::type_op::Normalize; -use rustc_middle::bug; use rustc_middle::ty::{ - self, GenericArgKind, RegionExt, RegionUtilitiesExt, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, elaborate, fold_regions, + self, GenericArgKind, RegionExt, RegionUtilitiesExt, TyCtxt, TypeFoldable, TypeVisitableExt, + elaborate, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::traits::query::type_op::TypeOpOutput; use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; @@ -137,83 +134,49 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // Extract out various useful fields we'll need below. let ConstraintConversion { - infcx, + infcx: _, universal_regions, region_bound_pairs, known_type_outlives_obligations, .. } = *self; - let mut outlives_predicates = vec![(predicate, constraint_category)]; - for iteration in 0.. { - if outlives_predicates.is_empty() { - break; - } + let pred = predicate; + // Constraint is implied by a coroutine's well-formedness. + if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions + && higher_ranked_assumptions.contains(&pred) + { + return; + } - if !tcx.recursion_limit().value_within_limit(iteration) { - // This may actually be reachable. If so, we should convert - // this to a proper error/consider whether we should detect - // this somewhere else. - bug!( - "unexpected overflowed when processing region obligations: {outlives_predicates:#?}" - ); + let ty::OutlivesPredicate(k1, r2) = pred; + match k1.kind() { + GenericArgKind::Lifetime(r1) => { + let r1_vid = self.to_region_vid(r1); + let r2_vid = self.to_region_vid(r2); + self.add_outlives(r1_vid, r2_vid, constraint_category); } - let mut next_outlives_predicates = vec![]; - for (pred, constraint_category) in outlives_predicates { - // Constraint is implied by a coroutine's well-formedness. - if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions - && higher_ranked_assumptions.contains(&pred) - { - continue; - } - - let ty::OutlivesPredicate(k1, r2) = pred; - match k1.kind() { - GenericArgKind::Lifetime(r1) => { - let r1_vid = self.to_region_vid(r1); - let r2_vid = self.to_region_vid(r2); - self.add_outlives(r1_vid, r2_vid, constraint_category); - } - - GenericArgKind::Type(mut t1) => { - // Scraped constraints may have had inference vars. - t1 = self.infcx.resolve_vars_if_possible(t1); - - // Normalize the type we receive from a `TypeOutlives` obligation - // in the new trait solver. - if infcx.next_trait_solver() { - t1 = self.normalize_and_add_type_outlives_constraints( - ty::Unnormalized::new_wip(t1), - &mut next_outlives_predicates, - ); - } + GenericArgKind::Type(mut t1) => { + // Scraped constraints may have had inference vars. + t1 = self.infcx.resolve_vars_if_possible(t1); - let implicit_region_bound = - ty::Region::new_var(tcx, universal_regions.implicit_region_bound()); - // we don't actually use this for anything, but - // the `TypeOutlives` code needs an origin. - let origin = SubregionOrigin::RelateParamBound(self.span, t1, None); - TypeOutlives::new( - &mut *self, - tcx, - region_bound_pairs, - Some(implicit_region_bound), - known_type_outlives_obligations, - ) - .type_must_outlive( - origin, - t1, - r2, - constraint_category, - ); - } - - GenericArgKind::Const(_) => unreachable!(), - } + let implicit_region_bound = + ty::Region::new_var(tcx, universal_regions.implicit_region_bound()); + // we don't actually use this for anything, but + // the `TypeOutlives` code needs an origin. + let origin = SubregionOrigin::RelateParamBound(self.span, t1, None); + TypeOutlives::new( + &mut *self, + tcx, + region_bound_pairs, + Some(implicit_region_bound), + known_type_outlives_obligations, + ) + .type_must_outlive(origin, t1, r2, constraint_category); } - outlives_predicates = next_outlives_predicates; + GenericArgKind::Const(_) => unreachable!(), } } @@ -279,32 +242,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { debug!("add_type_test(type_test={:?})", type_test); self.constraints.type_tests.push(type_test); } - - // FIXME(trait-refactor-initiative#260): This function should be - // removed. - fn normalize_and_add_type_outlives_constraints( - &self, - ty: ty::Unnormalized<'tcx, Ty<'tcx>>, - next_outlives_predicates: &mut Vec<( - ty::ArgOutlivesPredicate<'tcx>, - ConstraintCategory<'tcx>, - )>, - ) -> Ty<'tcx> { - match self.infcx.fully_perform(Normalize { value: ty }, self.span) { - Ok(TypeOpOutput { output: ty, constraints, .. }) => { - // FIXME(higher_ranked_auto): What should we do with the assumptions here? - if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { - next_outlives_predicates.extend(constraints.iter().flat_map( - |QueryRegionConstraint { constraint, category, .. }| { - constraint.iter_outlives().map(|outlives| (outlives, *category)) - }, - )); - } - ty - } - Err(_) => ty.skip_norm_wip(), - } - } } impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'tcx> { 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_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 2fb89939181e6..34ddd9427cdde 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -107,40 +107,42 @@ impl CfgEval<'_> { // our attribute target will correctly configure the tokens as well. let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None); parser.capture_cfg = true; - let res: PResult<'_, Annotatable> = try { - match annotatable { - Annotatable::Item(_) => { - let item = - parser.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?.unwrap(); - Annotatable::Item(self.flat_map_item(item).pop().unwrap()) - } + let res: PResult<'_, Option> = try { + match &annotatable { + Annotatable::Item(_) => parser + .parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)? + .and_then(|item| self.flat_map_item(item).pop().map(Annotatable::Item)), Annotatable::AssocItem(_, ctxt) => { - let item = parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(); - Annotatable::AssocItem( - self.flat_map_assoc_item(item, ctxt).pop().unwrap(), - ctxt, - ) + parser.parse_trait_item(ForceCollect::Yes)?.flatten().and_then(|item| { + self.flat_map_assoc_item(item, *ctxt) + .pop() + .map(|item| Annotatable::AssocItem(item, *ctxt)) + }) } Annotatable::ForeignItem(_) => { - let item = parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(); - Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) + parser.parse_foreign_item(ForceCollect::Yes)?.flatten().and_then(|item| { + self.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem) + }) } Annotatable::Stmt(_) => { let stmt = parser.parse_stmt_without_recovery(false, ForceCollect::Yes, false)?; - Annotatable::Stmt(Box::new(self.flat_map_stmt(stmt).pop().unwrap())) + self.flat_map_stmt(stmt).pop().map(|stmt| Annotatable::Stmt(Box::new(stmt))) } Annotatable::Expr(_) => { let mut expr = parser.parse_expr_force_collect()?; self.visit_expr(&mut expr); - Annotatable::Expr(expr) + Some(Annotatable::Expr(expr)) } _ => unreachable!(), } }; match res { - Ok(ann) => ann, + Ok(Some(ann)) => ann, + // Parser recovery may emit errors without reconstructing an annotatable. + // Keep the original node so cfg-eval stays best-effort instead of ICEing. + Ok(None) => annotatable, Err(err) => { err.emit(); annotatable diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index b94a8cd5e6d63..9a8008214ed04 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -282,7 +282,7 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( // reference the params from the ADT instead of from the impl which is bad UX. To resolve // this we "rename" the ADT's params to be the impl's params which should not affect behaviour. let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args); - let adt_env = ty::EarlyBinder::bind(tcx, tcx.param_env(adt_def_id)) + let adt_env = ty::EarlyBinder::bind_unchecked(tcx.param_env(adt_def_id)) .instantiate(tcx, adt_to_impl_args) .skip_norm_wip(); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f59345cd970eb..fd2944a122f03 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -31,7 +31,7 @@ use rustc_session::diagnostics::feature_err; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::regions::{ - InferCtxtRegionExt, OutlivesEnvironmentBuildExt, region_known_to_outlive, ty_known_to_outlive, + OutlivesEnvironmentBuildExt, region_known_to_outlive, ty_known_to_outlive, }; use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, type_allowed_to_implement_const_param_ty, diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index ac58a37cc8e81..0157703532268 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -83,7 +83,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( span_bug!(span, "Should not deduce placeholder outlives component"); } - Component::Alias(alias_ty) => { + Component::Alias(is_rigid, alias_ty) => { // This would either arise from something like: // // ``` @@ -102,7 +102,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( // // Here we want to add an explicit `where ::Item: 'a` // or `Opaque: 'a` depending on the alias kind. - let ty = alias_ty.to_ty(tcx, ty::IsRigid::No); + let ty = alias_ty.to_ty(tcx, is_rigid); required_predicates .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) .or_insert(span); 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_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index 15756f03f25f9..1ad0c5d192cf1 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -56,7 +56,7 @@ where // either `'static` or a unique outlives region, and if one is // found, we just need to prove that that region is still live. // If one is not found, then we continue to walk through the alias. - ty::Alias(_, alias_ty @ ty::AliasTy { kind, args, .. }) => { + ty::Alias(is_rigid, alias_ty @ ty::AliasTy { kind, args, .. }) => { let tcx = self.tcx; let param_env = self.param_env; let def_id = match kind { @@ -82,11 +82,7 @@ where &outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| { VerifyIfEq { ty, bound } }), - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - alias_ty.to_ty(tcx, ty::IsRigid::No), + alias_ty.to_ty(tcx, is_rigid), ) } }) diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 76db3830d3962..ce2650dff9f18 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -3,14 +3,14 @@ use std::iter; use rustc_data_structures::undo_log::UndoLogs; -use rustc_middle::traits::query::{NoSolution, OutlivesBound}; +use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; use rustc_span::Span; use tracing::instrument; use self::env::OutlivesEnvironment; use super::region_constraints::{RegionConstraintData, UndoLog}; -use super::{InferCtxt, RegionResolutionError, SubregionOrigin}; +use super::{InferCtxt, RegionResolutionError}; use crate::infer::free_regions::RegionRelations; use crate::infer::lexical_region_resolve; use crate::infer::region_constraints::ConstraintKind; @@ -38,25 +38,15 @@ impl<'tcx> InferCtxt<'tcx> { /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. /// - /// If you are in a crate that has access to `rustc_trait_selection`, - /// then it's probably better to use `resolve_regions`, - /// which knows how to normalize registered region obligations. + /// Don't call this directly unless you know what you're doing. + /// You probably want to use `resolve_regions` instead. #[must_use] - pub fn resolve_regions_with_normalize( + pub fn resolve_regions_with_outlives_env( &self, outlives_env: &OutlivesEnvironment<'tcx>, - deeply_normalize_ty: impl Fn( - ty::PolyTypeOutlivesPredicate<'tcx>, - SubregionOrigin<'tcx>, - ) -> Result, NoSolution>, span: Span, ) -> Vec> { - match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty, span) { - Ok(()) => {} - Err((clause, origin)) => { - return vec![RegionResolutionError::CannotNormalize(clause, origin)]; - } - }; + self.process_registered_region_obligations(outlives_env, span); let mut storage = { let mut inner = self.inner.borrow_mut(); diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index c8a94d26133ea..058aaa017cad4 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -63,11 +63,10 @@ use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{ self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, RegionExt, RegionVid, - Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt, + Ty, TyCtxt, TypeVisitableExt, eager_resolve_vars, }; use rustc_span::Span; use smallvec::smallvec; @@ -76,7 +75,6 @@ use tracing::{debug, instrument}; use super::env::OutlivesEnvironment; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; -use crate::infer::resolve::OpportunisticRegionResolver; use crate::infer::snapshot::undo_log::UndoLog; use crate::infer::{ self, GenericKind, InferCtxt, SubregionOrigin, TypeOutlivesConstraint, VerifyBound, @@ -288,17 +286,12 @@ impl<'tcx> InferCtxt<'tcx> { /// flow of the inferencer. The key point is that it is /// invoked after all type-inference variables have been bound -- /// right before lexical region resolution. - #[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))] + #[instrument(level = "debug", skip(self, outlives_env))] pub fn process_registered_region_obligations( &self, outlives_env: &OutlivesEnvironment<'tcx>, - mut deeply_normalize_ty: impl FnMut( - PolyTypeOutlivesPredicate<'tcx>, - SubregionOrigin<'tcx>, - ) - -> Result, NoSolution>, span: Span, - ) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { + ) { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); if self.tcx.assumptions_on_binders() { @@ -322,17 +315,10 @@ impl<'tcx> InferCtxt<'tcx> { } for TypeOutlivesConstraint { sup_type, sub_region, origin } in my_region_obligations { - let outlives = ty::Binder::dummy(ty::OutlivesPredicate(sup_type, sub_region)); - let ty::OutlivesPredicate(sup_type, sub_region) = - deeply_normalize_ty(outlives, origin.clone()) - .map_err(|NoSolution| (outlives, origin.clone()))? - .no_bound_vars() - .expect("started with no bound vars, should end with no bound vars"); // `TypeOutlives` is structural, so we should try to opportunistically resolve all // region vids before processing regions, so we have a better chance to match clauses // in our param-env. - let (sup_type, sub_region) = - (sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self)); + let (sup_type, sub_region) = eager_resolve_vars(self, (sup_type, sub_region)); if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions && outlives_env @@ -355,8 +341,6 @@ impl<'tcx> InferCtxt<'tcx> { outlives.type_must_outlive(origin, sup_type, sub_region, category); } } - - Ok(()) } } @@ -435,6 +419,11 @@ where category: ConstraintCategory<'tcx>, ) { assert!(!ty.has_escaping_bound_vars()); + debug_assert!(!ty.has_non_region_infer()); + debug_assert!( + !self.tcx.next_trait_solver_globally() || !ty.has_non_rigid_aliases(), + "{ty:?} has non-rigid aliases" + ); let mut components = smallvec![]; push_outlives_components(self.tcx, ty, &mut components); @@ -460,7 +449,10 @@ where Component::Placeholder(placeholder_ty) => { self.placeholder_ty_must_outlive(origin, region, *placeholder_ty); } - Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty), + Component::Alias(is_rigid, alias_ty) => { + debug_assert_eq!(*is_rigid, ty::IsRigid::yes_if_next_solver(self.tcx)); + self.alias_ty_must_outlive(origin, region, *alias_ty); + } Component::EscapingAlias(subcomponents) => { self.components_must_outlive(origin, subcomponents, region, category); } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index b84c6d0aed42d..a90f7d58847e3 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -44,13 +44,10 @@ pub fn extract_verify_if_eq<'tcx>( assert!(!verify_if_eq_b.has_escaping_bound_vars()); let mut m = MatchAgainstHigherRankedOutlives::new(tcx); let verify_if_eq = verify_if_eq_b.skip_binder(); - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - let (verify_ty, test_ty) = - ty::set_aliases_to_non_rigid(tcx, (verify_if_eq.ty, test_ty)).skip_norm_wip(); - m.relate(verify_ty, test_ty).ok()?; + debug_assert!( + !tcx.next_trait_solver_globally() || !(verify_if_eq.ty, test_ty).has_non_rigid_aliases() + ); + m.relate(verify_if_eq.ty, test_ty).ok()?; if let ty::RegionKind::ReBound(index_kind, br) = verify_if_eq.bound.kind() { assert!(matches!(index_kind, ty::BoundVarIndexKind::Bound(ty::INNERMOST))); @@ -86,12 +83,6 @@ pub(super) fn can_match_erased_ty<'tcx>( assert!(!outlives_predicate.has_escaping_bound_vars()); let erased_outlives_predicate = tcx.erase_and_anonymize_regions(outlives_predicate); let outlives_ty = erased_outlives_predicate.skip_binder().0; - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - let (outlives_ty, erased_ty) = - ty::set_aliases_to_non_rigid(tcx, (outlives_ty, erased_ty)).skip_normalization(); if outlives_ty == erased_ty { // pointless micro-optimization true diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 3c07a32471bdb..f1e927912ef51 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -96,14 +96,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { &self, alias_ty: ty::AliasTy<'tcx>, ) -> Vec> { - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. let erased_alias_ty = self.tcx.erase_and_anonymize_regions( - ty::set_aliases_to_non_rigid(self.tcx, alias_ty) - .skip_norm_wip() - .to_ty(self.tcx, ty::IsRigid::No), + alias_ty.to_ty(self.tcx, ty::IsRigid::yes_if_next_solver(self.tcx)), ); self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty) } @@ -168,7 +162,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { Component::Placeholder(placeholder_ty) => { self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty)) } - Component::Alias(alias_ty) => self.alias_bound(alias_ty), + // `type_must_outlive` already asserted that it's rigid in the next solver. + Component::Alias(_, alias_ty) => self.alias_bound(alias_ty), Component::EscapingAlias(ref components) => self.bound_from_components(components), Component::UnresolvedInferenceVariable(v) => { // Ignore this, we presume it will yield an error later, since @@ -245,20 +240,14 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // And therefore we can safely use structural equality for alias types. (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {} (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {} - // FIXME(#155345): We probably want to assert that the rhs is rigid. - (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.kind == a2.kind => {} + (GenericKind::Alias(a1), ty::Alias(is_rigid, a2)) if a1.kind == a2.kind => { + debug_assert_eq!(*is_rigid, ty::IsRigid::yes_if_next_solver(self.tcx)); + } _ => return None, } let p_ty = p.to_ty(tcx); - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - let erased_p_ty = self.tcx.erase_and_anonymize_regions( - ty::set_aliases_to_non_rigid(self.tcx, p_ty).skip_norm_wip(), - ); - let erased_ty = ty::set_aliases_to_non_rigid(self.tcx, erased_ty).skip_norm_wip(); + let erased_p_ty = self.tcx.erase_and_anonymize_regions(p_ty); (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) })); diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 32ed93564bd37..ef77c9b17bba8 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -190,6 +190,8 @@ pub struct Verify<'tcx> { pub enum GenericKind<'tcx> { Param(ty::ParamTy), Placeholder(ty::PlaceholderType<'tcx>), + // FIXME: we expect this alias to be rigid in the next solver. + // But we can't assert that in construction since this enum is public. Alias(ty::AliasTy<'tcx>), } @@ -807,11 +809,7 @@ impl<'tcx> GenericKind<'tcx> { match *self { GenericKind::Param(ref p) => p.to_ty(tcx), GenericKind::Placeholder(ref p) => Ty::new_placeholder(tcx, *p), - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - GenericKind::Alias(ref p) => p.to_ty(tcx, ty::IsRigid::No), + GenericKind::Alias(ref p) => p.to_ty(tcx, ty::IsRigid::yes_if_next_solver(tcx)), } } } 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_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index f325862102318..5d54065260888 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -19,12 +19,11 @@ use rustc_type_ir::relate::{ }; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, Region, - TypeFoldable, TypingMode, TypingModeEqWrapper, + TypeFoldable, TypingMode, TypingModeEqWrapper, eager_resolve_vars, }; use tracing::instrument; use crate::delegate::SolverDelegate; -use crate::resolve::eager_resolve_vars; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, ExternalRegionConstraints, Goal, NestedNormalizationGoals, QueryInput, Response, diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index 57bbf8772d322..6396c0e7f2b57 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -15,5 +15,4 @@ pub mod coherence; pub mod delegate; pub mod normalize; pub mod placeholder; -pub mod resolve; pub mod solve; diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 959e18071bb57..e2036f0ab0cc3 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -4,7 +4,7 @@ use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::inherent::*; use rustc_type_ir::{ self as ty, AliasTerm, Binder, FallibleTypeFolder, InferCtxtLike, Interner, TypeFoldable, - TypeSuperFoldable, TypeVisitableExt, UniverseIndex, + TypeSuperFoldable, TypeVisitableExt, UniverseIndex, eager_resolve_vars, }; use tracing::instrument; @@ -143,8 +143,8 @@ where if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { // find out missing typing env change. - let original = crate::resolve::eager_resolve_vars(infcx, original); - let normalized = crate::resolve::eager_resolve_vars(infcx, normalized); + let original = eager_resolve_vars(infcx, original); + let normalized = eager_resolve_vars(infcx, normalized); assert_eq!(original, normalized, "rigid alias is further normalized"); } Ok(normalized) @@ -196,8 +196,8 @@ where if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { // find out missing typing env change. - let original = crate::resolve::eager_resolve_vars(infcx, original); - let normalized = crate::resolve::eager_resolve_vars(infcx, normalized); + let original = eager_resolve_vars(infcx, original); + let normalized = eager_resolve_vars(infcx, normalized); assert_eq!(original, normalized, "rigid alias is further normalized"); } diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs deleted file mode 100644 index 3a927e2a92506..0000000000000 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ /dev/null @@ -1,107 +0,0 @@ -use rustc_type_ir::data_structures::DelayedMap; -use rustc_type_ir::inherent::*; -use rustc_type_ir::{ - self as ty, InferCtxtLike, Interner, Region, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, -}; - -/////////////////////////////////////////////////////////////////////////// -// EAGER RESOLUTION - -/// Resolves ty, region, and const vars to their inferred values or their root vars. -struct EagerResolver<'a, D, I = ::Interner> -where - D: InferCtxtLike, - I: Interner, -{ - delegate: &'a D, - /// We're able to use a cache here as the folder does not have any - /// mutable state. - cache: DelayedMap, -} - -pub fn eager_resolve_vars>( - infcx: &Infcx, - value: T, -) -> T { - if value.has_infer() { - let mut folder = EagerResolver::new(infcx); - value.fold_with(&mut folder) - } else { - value - } -} - -impl<'a, Infcx: InferCtxtLike> EagerResolver<'a, Infcx> { - fn new(delegate: &'a Infcx) -> Self { - EagerResolver { delegate, cache: Default::default() } - } -} - -impl, I: Interner> TypeFolder for EagerResolver<'_, Infcx> { - fn cx(&self) -> I { - self.delegate.cx() - } - - fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - match t.kind() { - ty::Infer(ty::TyVar(vid)) => { - let resolved = self.delegate.opportunistic_resolve_ty_var(vid); - if t != resolved && resolved.has_infer() { - resolved.fold_with(self) - } else { - resolved - } - } - ty::Infer(ty::IntVar(vid)) => self.delegate.opportunistic_resolve_int_var(vid), - ty::Infer(ty::FloatVar(vid)) => self.delegate.opportunistic_resolve_float_var(vid), - _ => { - if t.has_infer() { - if let Some(&ty) = self.cache.get(&t) { - return ty; - } - let res = t.super_fold_with(self); - assert!(self.cache.insert(t, res)); - res - } else { - t - } - } - } - } - - fn fold_region(&mut self, r: Region) -> Region { - match r.kind() { - ty::ReVar(vid) => self.delegate.opportunistic_resolve_lt_var(vid), - _ => r, - } - } - - fn fold_const(&mut self, c: I::Const) -> I::Const { - match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - let resolved = self.delegate.opportunistic_resolve_ct_var(vid); - if c != resolved && resolved.has_infer() { - resolved.fold_with(self) - } else { - resolved - } - } - _ => { - if c.has_infer() { - c.super_fold_with(self) - } else { - c - } - } - } - } - - fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { - if p.has_infer() { p.super_fold_with(self) } else { p } - } - - fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { - if c.has_infer() { c.super_fold_with(self) } else { c } - } -} diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 392d27e213388..2ec0b6fc9e2e8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -17,7 +17,7 @@ use rustc_type_ir::solve::{ use rustc_type_ir::{ self as ty, CanonicalVarValues, ClauseKind, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, PredicateKind, Region, TypeFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, + TypeVisitableExt, TypeVisitor, TypingMode, eager_resolve_vars, }; use tracing::{Level, debug, instrument, trace, warn}; @@ -30,7 +30,6 @@ use crate::coherence; use crate::delegate::SolverDelegate; use crate::normalize::{NormalizationFolder, NormalizationWasAmbiguous}; use crate::placeholder::BoundVarReplacer; -use crate::resolve::eager_resolve_vars; use crate::solve::eval_ctxt::fast_path::{ RerunStalled, compute_goal_fast_path, rerunning_stalled_goal_may_make_progress, }; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index 129a7b0f0de78..f31f292240a56 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -179,7 +179,8 @@ where Placeholder(p) => { RegionConstraint::PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r) } - Alias(alias) => self.destructure_alias_outlives(*alias, r), + // The alias is either rigid or ambiguous in which case we'll return with ambiguity. + Alias(_, alias) => self.destructure_alias_outlives(*alias, r), UnresolvedInferenceVariable(_) => RegionConstraint::Ambiguity, Param(_) => panic!("Params should have been canonicalized to placeholders"), EscapingAlias(components) => self.destructure_components(components, r), diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 195f08dfafd59..8a869df067301 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -23,7 +23,7 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner, Region}; +use rustc_type_ir::{self as ty, Interner, Region, TypeVisitableExt}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -90,16 +90,25 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let ty::OutlivesPredicate(ty, lt) = goal.predicate; + let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?; if self.cx().assumptions_on_binders() { - // FIXME(-Zassumptions-on-binders): we need to normalize `ty` let constraint = self.destructure_type_outlives(ty, lt); self.register_solver_region_constraint(constraint); } else { self.register_ty_outlives(ty, lt); } - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + // The normalized type can still contain non-rigid higher ranked aliases if their + // normalization ends up with ambiguity. Or we have non-rigid aliases inside rigid ones. + // Infer vars may be resolved to types/consts containing non-rigid aliases later. + // Thus we should stall this goal to avoid registering non-rigid type outlives into + // the outer infcx. + if ty.has_non_region_infer() || ty.has_non_rigid_aliases() { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } } #[instrument(level = "trace", skip(self))] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 94d3c7dccfc6c..34044e72ab92b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1380,12 +1380,22 @@ impl<'a> Parser<'a> { /// Parse an indexing expression `expr[...]`. fn parse_expr_index(&mut self, lo: Span, base: Box) -> PResult<'a, Box> { - let prev_span = self.prev_token.span; + let prev_token = self.prev_token; let open_delim_span = self.token.span; self.bump(); // `[` let index = self.parse_expr()?; - self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?; - self.expect(exp!(CloseBracket))?; + self.suggest_missing_semicolon_before_array(prev_token.span, open_delim_span)?; + self.expect(exp!(CloseBracket)).map_err(|mut e| { + if let TokenKind::Ident(_, _) = prev_token.kind { + e.span_suggestion_verbose( + prev_token.span.shrink_to_hi(), + "you might have meant to call a macro", + "!".to_string(), + Applicability::MaybeIncorrect, + ); + } + e + })?; Ok(self.mk_expr( lo.to(self.prev_token.span), self.mk_index(base, index, open_delim_span.to(self.prev_token.span)), 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/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 5ff2c904e94c5..c63b7773739d0 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -5,12 +5,9 @@ use rustc_infer::infer::{ InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt, TypeOutlivesConstraint, }; use rustc_macros::extension; -use rustc_middle::traits::ObligationCause; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode, Unnormalized, elaborate}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, elaborate}; +use rustc_span::DUMMY_SP; -use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; #[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] @@ -34,18 +31,8 @@ impl<'tcx> OutlivesEnvironment<'tcx> { let mut bounds = vec![]; for bound in param_env.caller_bounds() { - if let Some(mut type_outlives) = bound.as_type_outlives_clause() { - if infcx.next_trait_solver() { - match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>( - infcx.at(&ObligationCause::dummy(), param_env), - Unnormalized::new_wip(type_outlives), - ) { - Ok(new) => type_outlives = new, - Err(_) => { - infcx.dcx().delayed_bug(format!("could not normalize `{bound}`")); - } - } - } + if let Some(type_outlives) = bound.as_type_outlives_clause() { + debug_assert!(!infcx.next_trait_solver() || !type_outlives.has_non_rigid_aliases()); bounds.push(type_outlives); } } @@ -75,14 +62,12 @@ impl<'tcx> OutlivesEnvironment<'tcx> { #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { - /// Resolve regions, using the deep normalizer to normalize any type-outlives - /// obligations in the process. This is in `rustc_trait_selection` because - /// we need to normalize. - /// - /// Prefer this method over `resolve_regions_with_normalize`, unless you are - /// doing something specific for normalization. + /// Resolve regions lexically. /// /// This function assumes that all infer variables are already constrained. + /// + /// FIXME(#155345): this can probably be moved back to `rustc_infer` now that normalization is + /// no longer required. These two extension traits won't be needed then. fn resolve_regions( &self, body_def_id: LocalDefId, @@ -94,34 +79,6 @@ impl<'tcx> InferCtxt<'tcx> { self.tcx.def_span(body_def_id), ) } - - /// Don't call this directly unless you know what you're doing. - fn resolve_regions_with_outlives_env( - &self, - outlives_env: &OutlivesEnvironment<'tcx>, - span: Span, - ) -> Vec> { - self.resolve_regions_with_normalize( - &outlives_env, - |ty, origin| { - let ty = self.resolve_vars_if_possible(ty); - - if self.next_trait_solver() { - crate::solve::deeply_normalize( - self.at( - &ObligationCause::dummy_with_span(origin.span()), - outlives_env.param_env, - ), - Unnormalized::new_wip(ty), - ) - .map_err(|_: Vec>| NoSolution) - } else { - Ok(ty) - } - }, - span, - ) - } } /// Given a known `param_env` and a set of well formed types, can we prove that diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f5a30d2b26563..d3998955ef2c0 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -1,7 +1,7 @@ use std::collections::hash_map::Entry; use std::ops::Deref; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; @@ -16,7 +16,8 @@ use rustc_infer::traits::solve::{ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; use rustc_middle::ty::{ - self, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt, TypingMode, + self, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques}; use rustc_span::{DUMMY_SP, Span}; @@ -87,6 +88,33 @@ fn goal_stalled_on_args_or_nonempty_opaques<'tcx>( } } +struct CollectNonRegionInfer<'tcx> { + infers: Vec>, + visited: FxHashSet>, +} + +impl<'tcx> TypeVisitor> for CollectNonRegionInfer<'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if self.visited.contains(&ty) { + return; + } + + match ty.kind() { + ty::Infer(_) => self.infers.push(ty.into()), + _ => ty.super_visit_with(self), + } + + self.visited.insert(ty); + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) { + match ct.kind() { + ty::ConstKind::Infer(_) => self.infers.push(ct.into()), + _ => ct.super_visit_with(self), + } + } +} + impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> { type Infcx = InferCtxt<'tcx>; type Interner = TyCtxt<'tcx>; @@ -189,6 +217,21 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< return Outcome::NoFastPath; } + let ty = self.resolve_vars_if_possible(outlives.0); + let mut infer_collector = CollectNonRegionInfer { + infers: Default::default(), + visited: Default::default(), + }; + ty.visit_with(&mut infer_collector); + let infers = infer_collector.infers; + if !infers.is_empty() { + return goal_stalled_on_args(infers); + } + + if ty.has_non_rigid_aliases() { + return Outcome::NoFastPath; + } + self.0.register_type_outlives_constraint( outlives.0, outlives.1, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 7bf41d343ecf4..37cbdfb66f505 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -14,10 +14,9 @@ use std::assert_matches; use rustc_infer::infer::InferCtxt; use rustc_macros::extension; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; -use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; +use rustc_middle::ty::{TyCtxt, VisitorResult, eager_resolve_vars, try_visit}; use rustc_middle::{bug, ty}; use rustc_next_trait_solver::canonical::instantiate_canonical_state; -use rustc_next_trait_solver::resolve::eager_resolve_vars; use rustc_next_trait_solver::solve::{MaybeCause, MaybeInfo, SolverDelegateEvalExt as _, inspect}; use rustc_span::Span; use tracing::instrument; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 691290f75495b..c959b6856ca70 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -173,8 +173,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []); - let _ = - infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty), DUMMY_SP); + let _ = infcx.process_registered_region_obligations(&outlives_env, DUMMY_SP); let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index a98f8b9a9af88..c9de97ea82c11 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -61,6 +61,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( "compute_implied_outlives_bounds assumes region obligations are empty before starting" ); + let tcx = ocx.infcx.tcx; + // FIXME: This doesn't seem right. All call sites already normalize `ty`: // - `Ty`s from the `DefiningTy` in Borrowck: we have to normalize in the caller // in order to get implied bounds involving any unconstrained region vars @@ -141,8 +143,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( r_b, ))) => { let mut components = smallvec![]; - push_outlives_components(ocx.infcx.tcx, ty_a, &mut components); - outlives_bounds.extend(implied_bounds_from_components(r_b, components)) + push_outlives_components(tcx, ty_a, &mut components); + outlives_bounds.extend(implied_bounds_from_components(tcx, r_b, components)) } } } @@ -159,8 +161,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx.infcx.clone_registered_region_obligations() { let mut components = smallvec![]; - push_outlives_components(ocx.infcx.tcx, sup_type, &mut components); - outlives_bounds.extend(implied_bounds_from_components(sub_region, components)); + push_outlives_components(tcx, sup_type, &mut components); + outlives_bounds.extend(implied_bounds_from_components(tcx, sub_region, components)); } } @@ -197,6 +199,7 @@ impl<'tcx> TypeVisitor> for ContainsBevyParamSet<'tcx> { /// `T: 'a` to hold. We get to assume that the caller has validated /// those relationships. fn implied_bounds_from_components<'tcx>( + tcx: TyCtxt<'tcx>, sub_region: ty::Region<'tcx>, sup_components: SmallVec<[Component>; 4]>, ) -> Vec> { @@ -206,7 +209,11 @@ fn implied_bounds_from_components<'tcx>( match component { Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), - Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), + Component::Alias(is_rigid, p) => { + // We expect them to be already deeply normalized. + debug_assert_eq!(is_rigid, ty::IsRigid::yes_if_next_solver(tcx)); + Some(OutlivesBound::RegionSubAlias(sub_region, p)) + } Component::Placeholder(_p) => { // FIXME(non_lifetime_binders): Placeholders don't currently // imply anything for outlives, though they could easily. diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index a3ba407b4d7c7..c11532c798a42 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -354,6 +354,14 @@ impl> EarlyBinder { } } +impl EarlyBinder { + /// Use `bind/bind_iter/bind_no_rigid_aliases` instead. + /// Don't use this unless you know what you're doing. + pub fn bind_unchecked(value: T) -> EarlyBinder { + EarlyBinder { value, _tcx: PhantomData } + } +} + impl EarlyBinder { pub fn as_ref(&self) -> EarlyBinder { EarlyBinder { value: &self.value, _tcx: PhantomData } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index a22f558fe5b54..03d5fc1ff8330 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -274,11 +274,11 @@ fn elaborate_component_to_clause( Component::UnresolvedInferenceVariable(_) => None, - Component::Alias(alias_ty) => { + Component::Alias(is_rigid, alias_ty) => { // We might end up here if we have `Foo<::Assoc>: 'a`. // With this, we can deduce that `::Assoc: 'a`. Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - alias_ty.to_ty(cx, ty::IsRigid::No), + alias_ty.to_ty(cx, is_rigid), outlives_region, ))) } @@ -415,9 +415,9 @@ pub fn elaborate_outlives_assumptions( collected.insert(ty::OutlivesPredicate(ty.into(), r2)); } - Component::Alias(alias_ty) => { + Component::Alias(is_rigid, alias_ty) => { collected.insert(ty::OutlivesPredicate( - alias_ty.to_ty(cx, ty::IsRigid::No).into(), + alias_ty.to_ty(cx, is_rigid).into(), r2, )); } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 8aa9773267342..d8c11542be4a3 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -5,12 +5,15 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash_NoContext}; -use crate::fold::TypeFoldable; +use crate::data_structures::DelayedMap; use crate::inherent::*; use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; use crate::solve::VisibleForLeakCheck; -use crate::{self as ty, Interner, Region, TyVid}; +use crate::{ + self as ty, Interner, Region, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, +}; mod private { pub trait Sealed {} @@ -601,3 +604,101 @@ where TypingMode::Codegen => true, } } + +/// Resolves ty, region, and const vars to their inferred values or their root vars. +pub fn eager_resolve_vars>( + infcx: &Infcx, + value: T, +) -> T { + if value.has_infer() { + let mut folder = EagerResolver::new(infcx); + value.fold_with(&mut folder) + } else { + value + } +} + +struct EagerResolver<'a, D, I = ::Interner> +where + D: InferCtxtLike, + I: Interner, +{ + delegate: &'a D, + /// We're able to use a cache here as the folder does not have any + /// mutable state. + cache: DelayedMap, +} + +impl<'a, Infcx: InferCtxtLike> EagerResolver<'a, Infcx> { + fn new(delegate: &'a Infcx) -> Self { + EagerResolver { delegate, cache: Default::default() } + } +} + +impl, I: Interner> TypeFolder for EagerResolver<'_, Infcx> { + fn cx(&self) -> I { + self.delegate.cx() + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + match t.kind() { + ty::Infer(ty::TyVar(vid)) => { + let resolved = self.delegate.opportunistic_resolve_ty_var(vid); + if t != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + ty::Infer(ty::IntVar(vid)) => self.delegate.opportunistic_resolve_int_var(vid), + ty::Infer(ty::FloatVar(vid)) => self.delegate.opportunistic_resolve_float_var(vid), + _ => { + if t.has_infer() { + if let Some(&ty) = self.cache.get(&t) { + return ty; + } + let res = t.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res + } else { + t + } + } + } + } + + fn fold_region(&mut self, r: Region) -> Region { + match r.kind() { + ty::ReVar(vid) => self.delegate.opportunistic_resolve_lt_var(vid), + _ => r, + } + } + + fn fold_const(&mut self, c: I::Const) -> I::Const { + match c.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + let resolved = self.delegate.opportunistic_resolve_ct_var(vid); + if c != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + _ => { + if c.has_infer() { + c.super_fold_with(self) + } else { + c + } + } + } + } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_infer() { p.super_fold_with(self) } else { p } + } + + fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { + if c.has_infer() { c.super_fold_with(self) } else { c } + } +} diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 1474e65a9ccbf..1494da29527c4 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -26,7 +26,10 @@ pub enum Component { // is not in a position to judge which is the best technique, so // we just product the projection as a component and leave it to // the consumer to decide (but see `EscapingProjection` below). - Alias(ty::AliasTy), + // + // We have to track rigidness because it's also used in param env + // elaboration where things are not normalized yet. + Alias(ty::IsRigid, ty::AliasTy), // In the case where a projection has escaping regions -- meaning // regions bound within the type itself -- we always use @@ -149,7 +152,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // trait-ref. Therefore, if we see any higher-ranked regions, // we simply fallback to the most restrictive rule, which // requires that `Pi: 'a` for all `i`. - ty::Alias(_, alias_ty) => { + ty::Alias(is_rigid, alias_ty) => { if !alias_ty.has_escaping_bound_vars() { // best case: no escaping regions, so push the // projection and skip the subtree (thus generating no @@ -157,7 +160,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // the rules OutlivesProjectionEnv, // OutlivesProjectionTraitDef, and // OutlivesProjectionComponents to regionck. - self.out.push(Component::Alias(alias_ty)); + self.out.push(Component::Alias(is_rigid, alias_ty)); } else { // fallback case: hard code // OutlivesProjectionComponents. Continue walking diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 34de563fe4f33..98192e1fb5b01 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -1258,7 +1258,7 @@ impl BinaryHeap { /// /// Ok(heap.pop()) /// } - /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # find_max_slow(&[1, 2, 3]).expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve_2", since = "1.63.0")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1294,7 +1294,7 @@ impl BinaryHeap { /// /// Ok(heap.pop()) /// } - /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # find_max_slow(&[1, 2, 3]).expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve_2", since = "1.63.0")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 9eac8bcde1d1c..940fce7377938 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1153,7 +1153,7 @@ impl VecDeque { /// /// Ok(output) /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # process_data(&[1, 2, 3]).expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1201,7 +1201,7 @@ impl VecDeque { /// /// Ok(output) /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # process_data(&[1, 2, 3]).expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -3871,7 +3871,7 @@ impl Index for VecDeque { #[inline] fn index(&self, index: usize) -> &T { - self.get(index).expect("Out of bounds access") + self.get(index).expect("out of bounds access") } } @@ -3879,7 +3879,7 @@ impl Index for VecDeque { impl IndexMut for VecDeque { #[inline] fn index_mut(&mut self, index: usize) -> &mut T { - self.get_mut(index).expect("Out of bounds access") + self.get_mut(index).expect("out of bounds access") } } 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/fs.rs b/library/std/src/fs.rs index 0622637e72738..1f16265094518 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2769,7 +2769,8 @@ pub fn remove_file>(path: P) -> io::Result<()> { /// directory, etc. /// /// This function will traverse symbolic links to query information about the -/// destination file. +/// destination file. To query metadata about the path itself without following +/// symbolic links, use [`symlink_metadata`]. /// /// # Platform-specific behavior /// @@ -2786,6 +2787,7 @@ pub fn remove_file>(path: P) -> io::Result<()> { /// /// * The user lacks permissions to perform `metadata` call on `path`. /// * `path` does not exist. +/// * `path` is a symbolic link, but the destination file cannot be resolved. /// /// # Examples /// @@ -2806,6 +2808,11 @@ pub fn metadata>(path: P) -> io::Result { /// Queries the metadata about a file without following symlinks. /// +/// This function will return the [`Metadata`] of the exact path without +/// traversing symbolic links to a resolved destination file. Using this function +/// on a path that is a file or directory (not a symbolic link) will behave the +/// same as [`metadata`]. +/// /// # Platform-specific behavior /// /// This function currently corresponds to the `lstat` function on Unix 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/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index 33e7f6a9a0fea..18b9782f40230 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -171,12 +171,9 @@ impl Process { } } - pub fn send_process_group_signal(&self, signal: i32) -> io::Result<()> { - // See note in `send_signal` regarding recycled PIDs. - if self.status.is_some() { - return Ok(()); - } - cvt(unsafe { libc::killpg(self.pid, signal) }).map(drop) + pub fn send_process_group_signal(&self, _signal: i32) -> io::Result<()> { + // VxWorks doesn't have process groups + unimplemented!() } pub fn wait(&mut self) -> io::Result { 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 5039054e04990..7c7112f3af6b7 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/crashes/148891.rs b/tests/crashes/148891.rs deleted file mode 100644 index 75534440d4902..0000000000000 --- a/tests/crashes/148891.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ known-bug: #148891 -macro_rules! values { - ($inner:ty) => { - #[derive(Debug)] - pub enum TokenKind { - #[cfg(test)] - STRING ([u8; $inner]), - } - }; -} - -values!(String); - -pub fn main() {} 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/cfg/cfg-eval-derive-invalid-reparse-no-ice.rs b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.rs new file mode 100644 index 0000000000000..1f0215afadc7b --- /dev/null +++ b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.rs @@ -0,0 +1,17 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148891. + +macro_rules! values { + ($inner:ty) => { + #[derive(Debug)] + pub enum TokenKind { + #[cfg(test)] + STRING([u8; $inner]), + //~^ ERROR expected expression, found `ty` metavariable + //~| ERROR macro expansion ignores `)` and any tokens following + } + }; +} + +values!(String); + +fn main() {} diff --git a/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.stderr b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.stderr new file mode 100644 index 0000000000000..0248b6636fb7a --- /dev/null +++ b/tests/ui/cfg/cfg-eval-derive-invalid-reparse-no-ice.stderr @@ -0,0 +1,28 @@ +error: expected expression, found `ty` metavariable + --> $DIR/cfg-eval-derive-invalid-reparse-no-ice.rs:8:25 + | +LL | pub enum TokenKind { + | --------- while parsing this enum +LL | #[cfg(test)] +LL | STRING([u8; $inner]), + | ^^^^^^ expected expression +... +LL | values!(String); + | --------------- in this macro invocation + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + = note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: macro expansion ignores `)` and any tokens following + --> $DIR/cfg-eval-derive-invalid-reparse-no-ice.rs:8:32 + | +LL | STRING([u8; $inner]), + | ^ +... +LL | values!(String); + | --------------- caused by the macro expansion here + | + = note: the usage of `values!` is likely invalid in item context + +error: aborting due to 2 previous errors + 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/parser/macro/do-not-suggest-semicolon-between-macro-without-exclamation-mark-and-array.stderr b/tests/ui/parser/macro/do-not-suggest-semicolon-between-macro-without-exclamation-mark-and-array.stderr index 2796312f4ad17..548c142732edf 100644 --- a/tests/ui/parser/macro/do-not-suggest-semicolon-between-macro-without-exclamation-mark-and-array.stderr +++ b/tests/ui/parser/macro/do-not-suggest-semicolon-between-macro-without-exclamation-mark-and-array.stderr @@ -3,6 +3,11 @@ error: expected one of `.`, `?`, `]`, or an operator, found `,` | LL | let _x = vec[1, 2, 3]; | ^ expected one of `.`, `?`, `]`, or an operator + | +help: you might have meant to call a macro + | +LL | let _x = vec![1, 2, 3]; + | + error: aborting due to 1 previous error 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); +} diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs index 5c13a871a7b8d..70c1d9713d0a9 100644 --- a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs @@ -86,4 +86,5 @@ fn foo() {} fn main() { foo::<&_>(); //~^ ERROR overflow evaluating the requirement `&_: Typed` + //~| ERROR: type annotations needed: cannot satisfy `_: '_` } diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr index d350eb0f7795c..133bfd7140c9c 100644 --- a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr @@ -1,3 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `_: '_` + --> $DIR/cycle-modulo-ambig-aliases.rs:87:11 + | +LL | foo::<&_>(); + | ^^ cannot satisfy `_: '_` + error[E0275]: overflow evaluating the requirement `&_: Typed` --> $DIR/cycle-modulo-ambig-aliases.rs:87:11 | @@ -10,6 +16,7 @@ note: required by a bound in `foo` LL | fn foo() {} | ^^^^^ required by this bound in `foo` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0275`. +Some errors have detailed explanations: E0275, E0284. +For more information about an error, try `rustc --explain E0275`.