diff --git a/.mailmap b/.mailmap index 61d85c105549c..4f38001b2989f 100644 --- a/.mailmap +++ b/.mailmap @@ -743,3 +743,4 @@ Zack Corr Zack Slayton Zbigniew Siciarz Zbigniew Siciarz y21 <30553356+y21@users.noreply.github.com> +朝倉水希 diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index a2669ced50c30..b43694596d17c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -478,14 +478,9 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { let errci = ErrorConstraintInfo { fr, outlived_fr, category, span }; let mut diag = match (category, fr_is_local, outlived_fr_is_local) { - (ConstraintCategory::SolverRegionConstraint(span), _, _) => { - let mut d = self.dcx().struct_span_err( - span, - "unsatisfied lifetime constraint from -Zassumptions-on-binders :3", - ); - d.note("meoow :c"); - d - } + (ConstraintCategory::SolverRegionConstraint(span), _, _) => self + .dcx() + .struct_span_err(span, "higher-ranked lifetime bound could not be satisfied"), (ConstraintCategory::Return(kind), true, false) if self.regioncx.is_closure_fn_mut(fr) => { diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index f1db73fdd7654..f845d9137f759 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -153,7 +153,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { 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); + self.add_outlives(r1_vid, r2_vid, constraint_category, self.span); } GenericArgKind::Type(mut t1) => { @@ -221,6 +221,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { sup: ty::RegionVid, sub: ty::RegionVid, category: ConstraintCategory<'tcx>, + span: Span, ) { let category = match self.category { ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation => category, @@ -229,7 +230,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { self.constraints.outlives_constraints.push(OutlivesConstraint { locations: self.locations, category, - span: self.span, + span, sub, sup, variance_info: ty::VarianceDiagInfo::default(), @@ -246,14 +247,14 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'tcx> { fn push_sub_region_constraint( &mut self, - _origin: SubregionOrigin<'tcx>, + origin: SubregionOrigin<'tcx>, a: ty::Region<'tcx>, b: ty::Region<'tcx>, constraint_category: ConstraintCategory<'tcx>, ) { let b = self.to_region_vid(b); let a = self.to_region_vid(a); - self.add_outlives(b, a, constraint_category); + self.add_outlives(b, a, constraint_category, origin.span()); } fn push_verify( diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs index 9591da83708b7..9c39645ec677d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs @@ -1,4 +1,5 @@ use rustc_index::IndexVec; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{Body, Local, Location}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; @@ -97,9 +98,11 @@ impl LocalUseMap { return local_use_map; } - let mut locals_with_use_data: IndexVec = - IndexVec::from_elem(false, &body.local_decls); - live_locals.iter().for_each(|&local| locals_with_use_data[local] = true); + let mut locals_with_use_data: DenseBitSet = + DenseBitSet::new_empty(body.local_decls.len()); + live_locals.iter().for_each(|&local| { + locals_with_use_data.insert(local); + }); LocalUseMapBuild { local_use_map: &mut local_use_map, location_map, locals_with_use_data } .visit_body(body); @@ -134,12 +137,12 @@ struct LocalUseMapBuild<'me> { // obtained the same information from `live_locals` but we want to // avoid repeatedly calling `Vec::contains()` (see `LocalUseMap` for // the rationale on the time-memory trade-off we're favoring here). - locals_with_use_data: IndexVec, + locals_with_use_data: DenseBitSet, } impl Visitor<'_> for LocalUseMapBuild<'_> { fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { - if self.locals_with_use_data[local] + if self.locals_with_use_data.contains(local) && let Some(def_use) = def_use::categorize(context) { let first_appearance = match def_use { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c858eb200d791..6f89a64f95360 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -188,7 +188,6 @@ pub(crate) fn type_check<'tcx>( &mut converter, typeck.known_type_outlives_obligations, universal_region_relations.outlives.clone(), - infcx.tcx.def_span(infcx.root_def_id), ); } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index ed572b3774567..5964f5b858ac0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -434,8 +434,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { parse_atomic_ordering(fail_ordering), weak, ); - let val = bx.from_immediate(val); - let success = bx.from_immediate(success); let mut builder = OperandRefBuilder::new(result_layout); builder.insert_imm(FieldIdx::from_u32(0), val); diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index d77e43b9858af..f6e0208d98835 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -61,7 +61,6 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { // Fill all fields of the `TypeInfo` struct. for (idx, field) in ty_struct.fields.iter_enumerated() { let field_dest = self.project_field(dest, idx)?; - let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits(); match field.name { sym::kind => { let variant_index = match ty.kind() { @@ -115,33 +114,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { self.project_downcast_named(&field_dest, sym::Char)?; variant } - ty::Int(int_ty) => { - let (variant, variant_place) = + ty::Int(_) => { + let (variant, _variant_place) = self.project_downcast_named(&field_dest, sym::Int)?; - let place = self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_int_type_info( - place, - int_ty.bit_width().unwrap_or_else(/* isize */ ptr_bit_width), - true, - )?; variant } - ty::Uint(uint_ty) => { - let (variant, variant_place) = + ty::Uint(_) => { + let (variant, _variant_place) = self.project_downcast_named(&field_dest, sym::Int)?; - let place = self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_int_type_info( - place, - uint_ty.bit_width().unwrap_or_else(/* usize */ ptr_bit_width), - false, - )?; variant } - ty::Float(float_ty) => { - let (variant, variant_place) = + ty::Float(_) => { + let (variant, _variant_place) = self.project_downcast_named(&field_dest, sym::Float)?; - let place = self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_float_type_info(place, float_ty.bit_width())?; variant } ty::Str => { @@ -316,48 +301,6 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { interp_ok(()) } - fn write_int_type_info( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - bit_width: u64, - signed: bool, - ) -> InterpResult<'tcx> { - for (field_idx, field) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - match field.name { - sym::bits => self.write_scalar( - Scalar::from_u32(bit_width.try_into().expect("bit_width overflowed")), - &field_place, - )?, - sym::signed => self.write_scalar(Scalar::from_bool(signed), &field_place)?, - other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), - } - } - interp_ok(()) - } - - fn write_float_type_info( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - bit_width: u64, - ) -> InterpResult<'tcx> { - for (field_idx, field) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - match field.name { - sym::bits => self.write_scalar( - Scalar::from_u32(bit_width.try_into().expect("bit_width overflowed")), - &field_place, - )?, - other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), - } - } - interp_ok(()) - } - pub(crate) fn write_reference_type_info( &mut self, place: impl Writeable<'tcx, CtfeProvenance>, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 3402f56bef6dc..da269186f009b 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -13,6 +13,7 @@ use tracing::instrument; use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder}; use crate::check::wfcheck::check_static_item; +use crate::diagnostics::ParamInTyOfConstParam; use crate::hir_ty_lowering::HirTyLowerer; mod opaque; @@ -239,8 +240,19 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ } Node::GenericParam(param) => match ¶m.kind { - GenericParamKind::Type { default: Some(ty), .. } - | GenericParamKind::Const { ty, .. } => icx.lower_ty(ty), + GenericParamKind::Type { default: Some(ty), .. } => icx.lower_ty(ty), + GenericParamKind::Const { ty, .. } => { + let lowered_ty = icx.lower_ty(ty); + if !tcx.features().generic_const_parameter_types() && lowered_ty.has_param() { + let guar = tcx + .dcx() + .create_err(ParamInTyOfConstParam { span: ty.span, ty: lowered_ty }) + .emit(); + Ty::new_error(tcx, guar) + } else { + lowered_ty + } + } x => bug!("unexpected non-type Node::GenericParam: {:?}", x), }, diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 2547695132e7d..ef287b737851f 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -17,6 +17,13 @@ pub(crate) use precise_captures::*; pub(crate) mod remove_or_use_generic; +#[derive(Diagnostic)] +#[diag("complex const arguments must be placed inside of a `const` block")] +pub(crate) struct ComplexConstArg { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`")] pub(crate) struct AmbiguousAssocItem<'a> { @@ -2144,3 +2151,12 @@ pub(crate) struct OnlyStructsCanBeViewedAdt<'tcx> { pub article: &'static str, pub kind: &'static str, } + +#[derive(Diagnostic)] +#[diag("the type of const parameters must not depend on other generic parameters", code = E0770)] +pub(crate) struct ParamInTyOfConstParam<'tcx> { + #[primary_span] + #[label("the type `{$ty}` must not depend on other generic parameter")] + pub(crate) span: Span, + pub(crate) ty: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 4bdfc328a8522..cc7c19a103d87 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1,3 +1,5 @@ +// ignore-tidy-file-filelength + //! HIR ty lowering: Lowers type-system entities[^1] from the [HIR][hir] to //! the [`rustc_middle::ty`] representation. //! @@ -2523,6 +2525,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_value(tcx, valtree, ty) } + fn try_recover_misrepresented_function_call( + &self, + hir_self_ty: &'tcx hir::Ty<'tcx>, + span: Span, + ) -> Option { + // Only an enum can host a tuple-variant constructor (`>::Some(..)`). + // For any other self type, a type-relative call is an associated function, not a + // constructor, and must be wrapped in `const { ... }`. We catch that here, before + // lowering the self type, so a generic struct/union written without its args + // (`FieldName::len()`, from `tracing`'s macros) reports this clear error instead + // of a spurious E0107 "missing generics" (#157152), and a primitive or foreign + // type reports it instead of an opaque downstream resolution error. Enums, + // aliases, `Self` and type parameters are let through: each may resolve to an + // enum, so they must reach constructor lowering. + let self_ty_res = match hir_self_ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => path.res, + _ => Res::Err, + }; + matches!( + self_ty_res, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::ForeignTy, _) | Res::PrimTy(_) + ) + .then(|| self.dcx().emit_err(diagnostics::ComplexConstArg { span })) + } + fn lower_const_arg_tuple_call( &self, hir_id: HirId, @@ -2543,6 +2570,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_resolved_const_path(opt_self_ty, path, hir_id) } hir::QPath::TypeRelative(hir_self_ty, segment) => { + if let Some(e) = self.try_recover_misrepresented_function_call(hir_self_ty, span) { + return ty::Const::new_error(tcx, e); + } + let self_ty = self.lower_ty(hir_self_ty); match self.lower_type_relative_const_path( self_ty, @@ -2576,10 +2607,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { (tcx.adt_def(parent_did), fn_args, parent_did) } _ => { - let e = self.dcx().span_err( - span, - "complex const arguments must be placed inside of a `const` block", - ); + let e = self.dcx().emit_err(diagnostics::ComplexConstArg { span }); return Const::new_error(tcx, e); } }; diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index afc083aececaa..84ffccd6f2c80 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -64,12 +64,13 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn get_solver_region_constraint( &self, ) -> rustc_type_ir::region_constraint::RegionConstraint> { - self.inner.borrow().solver_region_constraint_storage.get_constraint() + self.inner.borrow().solver_region_constraint_storage.get_unspanned_constraint() } fn overwrite_solver_region_constraint( &self, constraint: rustc_type_ir::region_constraint::RegionConstraint>, + span: Span, ) { let mut inner = self.inner.borrow_mut(); use rustc_data_structures::undo_log::UndoLogs; @@ -77,7 +78,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { use crate::infer::UndoLog; let old_constraint = inner.solver_region_constraint_storage.get_constraint(); inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraint { old_constraint }); - inner.solver_region_constraint_storage.overwrite_solver_region_constraint(constraint); + inner.solver_region_constraint_storage.overwrite(constraint, span); } fn universe_of_ty(&self, vid: ty::TyVid) -> Option { @@ -331,6 +332,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn register_solver_region_constraint( &self, c: rustc_type_ir::region_constraint::RegionConstraint>, + span: Span, ) { let mut inner = self.inner.borrow_mut(); use rustc_data_structures::undo_log::UndoLogs; @@ -338,7 +340,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { use crate::infer::UndoLog; let previous_was_and = inner.solver_region_constraint_storage.is_and(); inner.undo_log.push(UndoLog::PushSolverRegionConstraint { previous_was_and }); - inner.solver_region_constraint_storage.push(c); + inner.solver_region_constraint_storage.push(c, span); } fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 583fb1d7db21a..906ffe710e03a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -61,9 +61,13 @@ pub mod region_constraints; pub mod relate; pub mod resolve; pub(crate) mod snapshot; +mod solver_region_constraints; mod type_variable; mod unify_key; +pub(crate) use solver_region_constraints::SolverRegionConstraint; +use solver_region_constraints::SolverRegionConstraintStorage; + /// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper /// around `PredicateObligations<'tcx>`, but it has one important property: /// because `InferOk` is marked with `#[must_use]`, if you have a method @@ -1809,62 +1813,6 @@ impl<'tcx> InferCtxt<'tcx> { } } -type SolverRegionConstraint<'tcx> = - rustc_type_ir::region_constraint::RegionConstraint>; - -#[derive(Clone, Debug)] -struct SolverRegionConstraintStorage<'tcx>(SolverRegionConstraint<'tcx>); - -impl<'tcx> SolverRegionConstraintStorage<'tcx> { - fn new() -> Self { - SolverRegionConstraintStorage(SolverRegionConstraint::And(Box::new([]))) - } - - fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { - self.0.clone() - } - - fn is_and(&self) -> bool { - self.0.is_and() - } - - fn pop(&mut self, previous_was_and: bool) -> Option> { - match &mut self.0 { - SolverRegionConstraint::And(and) => { - let mut and = core::mem::take(and).into_iter().collect::>(); - let popped = and.pop()?; - if previous_was_and { - self.0 = SolverRegionConstraint::And(and.into_boxed_slice()); - } else { - assert_eq!(and.len(), 1); - self.0 = and.pop().unwrap(); - } - Some(popped) - } - _ => unreachable!(), - } - } - - #[instrument(level = "debug")] - fn push(&mut self, constraint: SolverRegionConstraint<'tcx>) { - match core::mem::replace(&mut self.0, SolverRegionConstraint::new_true()) { - SolverRegionConstraint::And(and) => { - let and = - and.into_iter().chain([constraint]).collect::>().into_boxed_slice(); - self.0 = SolverRegionConstraint::And(and); - } - previous => { - self.0 = SolverRegionConstraint::And(Box::new([previous, constraint])); - } - } - } - - #[instrument(level = "debug", skip(self))] - fn overwrite_solver_region_constraint(&mut self, constraint: SolverRegionConstraint<'tcx>) { - self.0 = constraint; - } -} - /// Returns unresolved root variables from `table`, according to `is_unresolved`. fn unresolved_root_variables_of( mut table: UnificationTable<'_, '_, V>, diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 67a85dbdd741b..2bf73e5da7e34 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -215,13 +215,12 @@ impl<'tcx> InferCtxt<'tcx> { pub fn destructure_solver_region_constraints_for_regionck( &self, outlives_env: &OutlivesEnvironment<'tcx>, - span: Span, ) { let assumptions = rustc_type_ir::region_constraint::Assumptions::new( outlives_env.known_type_outlives().into_iter().cloned().collect(), outlives_env.free_region_map().relation.clone(), ); - self.destructure_solver_region_constraints(assumptions, self, span); + self.destructure_solver_region_constraints(assumptions, self); } pub fn destructure_solver_region_constraints_for_borrowck( @@ -230,13 +229,12 @@ impl<'tcx> InferCtxt<'tcx> { conversion: impl TypeOutlivesDelegate<'tcx>, known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], region_outlives: TransitiveRelation, - span: Span, ) { let assumptions = rustc_type_ir::region_constraint::Assumptions::new( known_type_outlives.into_iter().cloned().collect(), region_outlives.maybe_map(|r| Some(Region::new_var(self.tcx, r))).unwrap(), ); - self.destructure_solver_region_constraints(assumptions, conversion, span); + self.destructure_solver_region_constraints(assumptions, conversion); } #[instrument(level = "debug", skip(self, conversion))] @@ -244,14 +242,10 @@ impl<'tcx> InferCtxt<'tcx> { &self, assumptions: rustc_type_ir::region_constraint::Assumptions>, mut conversion: impl TypeOutlivesDelegate<'tcx>, - span: Span, ) { assert!(self.tcx.assumptions_on_binders()); assert!(self.next_trait_solver()); - let origin = SubregionOrigin::SolverRegionConstraint(span); - let category = origin.to_constraint_category(); - let constraint = self.inner.borrow().solver_region_constraint_storage.get_constraint(); debug!(?constraint); let constraint = @@ -269,16 +263,20 @@ impl<'tcx> InferCtxt<'tcx> { use rustc_type_ir::region_constraint::RegionConstraint::*; match c { - Ambiguity => { - self.dcx().err("unable to satisfy constraints involving placeholders due to unknown implied bounds"); + Ambiguity(span) => { + self.dcx() + .struct_span_err( + span, + "unable to satisfy constraints involving placeholders due to unknown implied bounds", + ) + .emit(); } - RegionOutlives(a, b) => { + RegionOutlives(a, b, span) => { + let origin = SubregionOrigin::SolverRegionConstraint(span); + let category = origin.to_constraint_category(); conversion.push_sub_region_constraint( - origin.clone(), - // we flip these because regionck is silly :> - b, - a, - category, + origin, // we flip these because regionck is silly :> + b, a, category, ); } // FIXME(-Zassumptions-on-binders): actually implement OR as an OR @@ -306,7 +304,7 @@ impl<'tcx> InferCtxt<'tcx> { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); if self.tcx.assumptions_on_binders() { - self.destructure_solver_region_constraints_for_regionck(outlives_env, span); + self.destructure_solver_region_constraints_for_regionck(outlives_env); } // Must loop since the process of normalizing may itself register region obligations. diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index eb5b3fe7bfd41..321a7ce4901b4 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -88,8 +88,7 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { ); } UndoLog::OverwriteSolverRegionConstraint { old_constraint } => { - self.solver_region_constraint_storage - .overwrite_solver_region_constraint(old_constraint); + self.solver_region_constraint_storage.overwrite_spanned(old_constraint); } UndoLog::PushTypeOutlivesConstraint => { let popped = self.region_obligations.pop(); diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints.rs b/compiler/rustc_infer/src/infer/solver_region_constraints.rs new file mode 100644 index 0000000000000..885bf53dbe6c2 --- /dev/null +++ b/compiler/rustc_infer/src/infer/solver_region_constraints.rs @@ -0,0 +1,77 @@ +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; +use rustc_type_ir::region_constraint::{ + RegionConstraint as UnspannedRegionConstraint, SpannedRegionConstraint, +}; +use tracing::instrument; + +pub(crate) type SolverRegionConstraint<'tcx> = SpannedRegionConstraint>; + +#[derive(Clone, Debug)] +pub(crate) struct SolverRegionConstraintStorage<'tcx>(SolverRegionConstraint<'tcx>); + +impl<'tcx> SolverRegionConstraintStorage<'tcx> { + pub(crate) fn new() -> Self { + Self(SolverRegionConstraint::And(Box::new([]))) + } + + pub(crate) fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { + self.0.clone() + } + + pub(crate) fn get_unspanned_constraint(&self) -> UnspannedRegionConstraint> { + self.0.clone().without_spans() + } + + pub(crate) fn is_and(&self) -> bool { + self.0.is_and() + } + + pub(crate) fn pop(&mut self, previous_was_and: bool) -> Option> { + match &mut self.0 { + SolverRegionConstraint::And(and) => { + let mut and = core::mem::take(and).into_vec(); + let popped = and.pop()?; + if previous_was_and { + self.0 = SolverRegionConstraint::And(and.into_boxed_slice()); + } else { + assert_eq!(and.len(), 1); + self.0 = and.pop().unwrap(); + } + Some(popped) + } + _ => unreachable!(), + } + } + + #[instrument(level = "debug")] + pub(crate) fn push(&mut self, constraint: UnspannedRegionConstraint>, span: Span) { + let constraint = constraint.with_span(span); + match core::mem::replace(&mut self.0, SolverRegionConstraint::new_true()) { + SolverRegionConstraint::And(and) => { + let and = + and.into_iter().chain([constraint]).collect::>().into_boxed_slice(); + self.0 = SolverRegionConstraint::And(and); + } + previous => { + self.0 = SolverRegionConstraint::And(Box::new([previous, constraint])); + } + } + } + + #[instrument(level = "debug", skip(self))] + pub(crate) fn overwrite( + &mut self, + constraint: UnspannedRegionConstraint>, + span: Span, + ) { + self.overwrite_spanned(constraint.with_span(span)); + } + + pub(crate) fn overwrite_spanned(&mut self, constraint: SolverRegionConstraint<'tcx>) { + self.0 = constraint; + } +} + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs b/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs new file mode 100644 index 0000000000000..339a454753f0b --- /dev/null +++ b/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs @@ -0,0 +1,58 @@ +use rustc_span::{BytePos, DUMMY_SP, Span}; +use rustc_type_ir::region_constraint::evaluate_solver_constraint; + +use super::SolverRegionConstraint; + +fn and(constraints: Vec>) -> SolverRegionConstraint<'static> { + SolverRegionConstraint::And(constraints.into_boxed_slice()) +} + +fn or(constraints: Vec>) -> SolverRegionConstraint<'static> { + SolverRegionConstraint::Or(constraints.into_boxed_slice()) +} + +fn ambiguity() -> SolverRegionConstraint<'static> { + SolverRegionConstraint::Ambiguity(DUMMY_SP) +} + +#[test] +fn evaluation_is_span_agnostic() { + let constraints = [ + ambiguity(), + and(vec![]), + or(vec![]), + and(vec![and(vec![]), ambiguity()]), + and(vec![ambiguity(), or(vec![])]), + or(vec![or(vec![]), ambiguity()]), + or(vec![ambiguity(), and(vec![])]), + and(vec![or(vec![or(vec![]), ambiguity()]), or(vec![ambiguity(), and(vec![])])]), + ]; + + for constraint in constraints { + let expected = evaluate_solver_constraint(&constraint.clone().without_spans()); + let actual = evaluate_solver_constraint(&constraint).without_spans(); + assert_eq!(actual, expected); + } +} + +#[test] +fn evaluation_preserves_first_ambiguity_span() { + let first = Span::with_root_ctxt(BytePos(1), BytePos(2)); + let second = Span::with_root_ctxt(BytePos(3), BytePos(4)); + + for constraint in [ + and(vec![ + SolverRegionConstraint::Ambiguity(first), + SolverRegionConstraint::Ambiguity(second), + ]), + or(vec![ + SolverRegionConstraint::Ambiguity(first), + SolverRegionConstraint::Ambiguity(second), + ]), + ] { + assert!(matches!( + evaluate_solver_constraint(&constraint), + SolverRegionConstraint::Ambiguity(span) if span == first + )); + } +} diff --git a/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt b/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt index 37c747a902d87..8c78b41c5c578 100644 --- a/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt +++ b/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.20) project(RustOffload LANGUAGES CXX) -find_package(LLVM CONFIG REQUIRED) +# If we don't prohibit the default path, CMake will find an incompatible system LLVM installation instead of the one we built. +find_package(LLVM CONFIG REQUIRED NO_DEFAULT_PATH PATHS "${LLVM_DIR}") add_library(RustOffload-${LLVM_VERSION_MAJOR} SHARED OffloadWrapper.cpp diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index cf10b3a30f318..128735965ba73 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -134,7 +134,7 @@ where span, ), ExternalRegionConstraints::NextGen(r) => { - delegate.register_solver_region_constraint(r.clone()) + delegate.register_solver_region_constraint(r.clone(), span) } }; register_new_opaque_types(delegate, opaque_types, span); diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 93e97b53f7720..13988e2c7b918 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -7,6 +7,22 @@ use rustc_type_ir::solve::{ }; use rustc_type_ir::{self as ty, CanonicalizerState, InferCtxtLike, Interner, TypeFoldable}; +/// `SolverDelegate` is one of the two traits in the `rustc_type_ir` shared abstraction layer +/// between rustc and rust-analyzer abstracting over the [InferCtxt][inferctxt-doc], which had to be +/// split due to coherence reasons: +/// - `SolverDelegate` contains the parts depending on trait-solving logic, to provide functionality +/// in `rustc_trait_selection`, and is implemented by a [simple wrapper over +/// `InferCtxt`][inferctxt-wrapper-doc] there, +/// - [InferCtxtLike] contains the other parts, and is implemented [directly on +/// `InferCtxt`][inferctxtlike-impl-doc]. +/// +/// More information can also be found in the dedicated chapter in the dev-guide, in [this +/// section][dev-guide]. +/// +/// [inferctxt-doc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html +/// [inferctxt-wrapper-doc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/solve/delegate/struct.SolverDelegate.html +/// [inferctxtlike-impl-doc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#impl-InferCtxtLike-for-InferCtxt%3C'tcx%3E +/// [dev-guide]: https://rustc-dev-guide.rust-lang.org/solve/sharing-crates-with-rust-analyzer.html#trait-inferctxtlike-and-trait-solverdelegate pub trait SolverDelegate: Deref + Sized { type Infcx: InferCtxtLike; type Interner: Interner; 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 be90d21e715ae..aa7e1e79b5866 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 @@ -1329,7 +1329,7 @@ where } pub(super) fn register_solver_region_constraint(&self, c: RegionConstraint) { - self.delegate.register_solver_region_constraint(c); + self.delegate.register_solver_region_constraint(c, self.origin_span); } pub(super) fn register_ty_outlives(&self, ty: I::Ty, lt: Region) { 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 583dd391dd4d0..2790b563f1ea7 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 @@ -139,7 +139,7 @@ where }); let constraint = evaluate_solver_constraint(&constraint.canonical_form()); - self.delegate.overwrite_solver_region_constraint(constraint.clone()); + self.delegate.overwrite_solver_region_constraint(constraint.clone(), self.origin_span); if constraint.is_false() { Err(NoSolution) @@ -177,13 +177,13 @@ where fn destructure_component(&mut self, c: &Component, r: Region) -> RegionConstraint { use Component::*; match c { - Region(c_r) => RegionConstraint::RegionOutlives(*c_r, r), + Region(c_r) => RegionConstraint::RegionOutlives(*c_r, r, ()), Placeholder(p) => { - RegionConstraint::PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r) + RegionConstraint::PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), 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, + UnresolvedInferenceVariable(_) => RegionConstraint::Ambiguity(()), Param(_) => panic!("Params should have been canonicalized to placeholders"), EscapingAlias(components) => self.destructure_components(components, r), } @@ -204,11 +204,11 @@ where ) -> RegionConstraint { let item_bounds = rustc_type_ir::outlives::declared_bounds_from_definition(self.cx(), alias) - .map(|bound| RegionConstraint::RegionOutlives(bound, r)); + .map(|bound| RegionConstraint::RegionOutlives(bound, r, ())); let item_bound_outlives = RegionConstraint::Or(item_bounds.collect()); let where_clause_outlives = - RegionConstraint::AliasTyOutlivesViaEnv(Binder::dummy((alias, r))); + RegionConstraint::AliasTyOutlivesViaEnv(Binder::dummy((alias, r)), ()); let mut components = Default::default(); rustc_type_ir::outlives::compute_alias_components_recursive( diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 84c128ad0f45f..d62a1627953ef 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -120,7 +120,7 @@ where if self.cx().assumptions_on_binders() { let constraint = - rustc_type_ir::region_constraint::RegionConstraint::RegionOutlives(a, b); + rustc_type_ir::region_constraint::RegionConstraint::RegionOutlives(a, b, ()); self.register_solver_region_constraint(constraint); } else { self.register_region_outlives(a, b, VisibleForLeakCheck::Yes); diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 0d480983ce9e6..7d9dc8ff52fbe 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1282,6 +1282,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ResolutionError::ParamInTyOfConstParam { name } => { self.dcx().create_err(diagnostics::ParamInTyOfConstParam { span, name }) } + ResolutionError::SelfInConstParam => { + self.dcx().create_err(diagnostics::SelfInConstGenericTy { + span, + enable_feature: self.tcx().sess.is_nightly_build(), + }) + } ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { span, @@ -1305,9 +1311,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ForwardGenericParamBanReason::Default => { self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span }) } - ForwardGenericParamBanReason::ConstParamTy => { - self.dcx().create_err(diagnostics::SelfInConstGenericTy { span }) - } + ForwardGenericParamBanReason::ConstParamTy => self + .dcx() + .create_err(diagnostics::SelfInConstGenericTy { span, enable_feature: false }), }, ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index d4ce0dc78ca1f..cd09ece879708 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -386,6 +386,10 @@ pub(crate) struct SelfInGenericParamDefault { pub(crate) struct SelfInConstGenericTy { #[primary_span] pub(crate) span: Span, + #[help( + "add `#![feature(min_adt_const_params)]` to the crate attributes to enable `Self` as a const parameter type" + )] + pub(crate) enable_feature: bool, } #[derive(Diagnostic)] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 2b4d673b2184b..d58ec6b5032b8 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1596,18 +1596,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } RibKind::ConstParamTy => { - if !self.features.generic_const_parameter_types() { + let adt_enabled = self.features.min_adt_const_params() + || self.features.adt_const_params(); + let is_self = matches!(res, Res::SelfTyAlias { .. }); + // We check whether Self depends on generics parameters in `fn type_of` + if self.features.generic_const_parameter_types() + || (adt_enabled && is_self) + { + continue; + } else { if let Some(span) = finalize { - self.report_error( - span, - ResolutionError::ParamInTyOfConstParam { - name: rib_ident.name, - }, - ); + if matches!(res, Res::SelfTyAlias { .. }) { + self.report_error(span, ResolutionError::SelfInConstParam); + } else { + self.report_error( + span, + ResolutionError::ParamInTyOfConstParam { + name: rib_ident.name, + }, + ); + } } return Res::Err; - } else { - continue; } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 180157dbfb950..2b1c0bf80a694 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -299,6 +299,8 @@ enum ResolutionError<'ra> { // problematic to use *forward declared* parameters when the feature is enabled. /// ERROR E0770: the type of const parameters must not depend on other generic parameters. ParamInTyOfConstParam { name: Symbol }, + /// cannot use self in const param + SelfInConstParam, /// generic parameters must not be used inside const evaluations. /// /// This error is only emitted when using `min_const_generics`. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 68e0fec59f06e..38248013557a1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2453,7 +2453,7 @@ options! { dual_proc_macros: bool = (false, parse_bool, [TRACKED], "load proc macros for both target and host, but only link to the target (default: no)"), dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], - "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \ + "dump the dependency graph to `$RUST_DEP_GRAPH` as both a text file and a GraphViz dot file (default: ./dep_graph.{dot, txt}) \ (default: no)"), dump_mir: Option = (None, parse_opt_string, [UNTRACKED], "dump MIR state to file. diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index f92c0e14c8f3a..7f4ca7a572988 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -297,7 +297,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { SubregionOrigin::SolverRegionConstraint(span) => { RegionOriginNote::Plain { span, - msg: msg!("this diagnostic is currently WIP while -Zassumptions-on-binders is incomplete"), + msg: msg!("...so that a higher-ranked lifetime bound can be satisfied"), } .add_to_diag(err); } @@ -569,14 +569,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { notes: instantiated.into_iter().chain(must_outlive).collect(), }) } - SubregionOrigin::SolverRegionConstraint(span) => { - let mut d = self.dcx().struct_span_err( - span, - "unsatisfied lifetime constraint from -Zassumptions-on-binders :3", - ); - d.note("meoow :c"); - d - } + SubregionOrigin::SolverRegionConstraint(span) => self + .dcx() + .struct_span_err(span, "higher-ranked lifetime bound could not be satisfied"), }; if sub.is_error() || sup.is_error() { err.downgrade_to_delayed_bug(); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index bd272dee3b3ab..ab6aa9c58d9c2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -419,6 +419,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { FulfillmentErrorCode::Project(ref e) => { self.report_projection_error(&error.obligation, e) } + FulfillmentErrorCode::Outlives => self + .dcx() + .struct_span_err( + error.obligation.cause.span, + "higher-ranked lifetime bound could not be satisfied", + ) + .emit(), FulfillmentErrorCode::Ambiguity { overflow: None } => { self.maybe_report_ambiguity(&error.obligation, related) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 27be427d28102..59336396ab11c 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -66,6 +66,9 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( let expected_found = ExpectedFound::new(b, a); FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) } + ty::PredicateKind::Clause( + ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::TypeOutlives(_), + ) if infcx.tcx.assumptions_on_binders() => FulfillmentErrorCode::Outlives, ty::PredicateKind::Clause(_) | ty::PredicateKind::DynCompatible(_) | ty::PredicateKind::Ambiguous => { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 941d80db4bbc4..bca336c2a0449 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1066,6 +1066,7 @@ impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for ScrubbedTraitError<'t match error.0.error { FulfillmentErrorCode::Select(_) | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Outlives | FulfillmentErrorCode::Subtype(_, _) | FulfillmentErrorCode::ConstEquate(_, _) => ScrubbedTraitError::TrueError, FulfillmentErrorCode::Ambiguity { overflow: _ } => ScrubbedTraitError::Ambiguity, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 8b4d02725bb0a..f4b64888972bd 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -98,6 +98,7 @@ impl<'tcx> FulfillmentError<'tcx> { match self.code { FulfillmentErrorCode::Select(_) | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Outlives | FulfillmentErrorCode::Subtype(_, _) | FulfillmentErrorCode::ConstEquate(_, _) => true, FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { @@ -114,6 +115,8 @@ pub enum FulfillmentErrorCode<'tcx> { Cycle(PredicateObligations<'tcx>), Select(SelectionError<'tcx>), Project(MismatchedProjectionTypes<'tcx>), + /// An outlives constraint emitted for `-Zassumptions-on-binders` was unsatisfiable. + Outlives, Subtype(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate ConstEquate(ExpectedFound>, TypeError<'tcx>), Ambiguity { @@ -129,6 +132,7 @@ impl<'tcx> Debug for FulfillmentErrorCode<'tcx> { match *self { FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"), FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Outlives => write!(f, "CodeOutlivesError"), FulfillmentErrorCode::Subtype(ref a, ref b) => { write!(f, "CodeSubtypeError({a:?}, {b:?})") } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 1cd070365f651..7a6a7d7380bdd 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -362,6 +362,22 @@ impl From> for TypingMode, + span: ::Span, ); fn universe_of_ty(&self, ty: ty::TyVid) -> Option; @@ -520,6 +537,7 @@ pub trait InferCtxtLike: Sized { fn register_solver_region_constraint( &self, c: crate::region_constraint::RegionConstraint, + span: ::Span, ); fn register_ty_outlives( diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 49899147d5747..1113aa4f6af51 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -24,6 +24,26 @@ use crate::{ TraitRef, search_graph, }; +/// The central trait in the shared abstraction layer, specifying all implementation-specific +/// details for rustc and rust-analyzer. +/// +/// Among its essential responsibilities: +/// - it specifies the concrete types used by each implementation via its associated types; these +/// form the backbone of how each compiler frontend instantiates the shared IR. +/// - it provides the context required by the solver (e.g., querying lang items, enumerating all +/// blanket impls for a trait) +/// - it implements [IrPrint] for formatting and tracing. +/// +/// In rustc, it is [implemented by TyCtxt][interner-impl-doc]. In rust-analyzer, the implementing +/// type is named [DbInterner][dbinterner-code] (as it performs most interning through the salsa +/// database). +/// +/// More information can also be found in the dedicated chapter in the dev-guide, in [this +/// section][dev-guide]. +/// +/// [interner-impl-doc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#impl-Interner-for-TyCtxt%3C'tcx%3E +/// [dbinterner-code]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/interner.rs#L272 +/// [dev-guide]: https://rustc-dev-guide.rust-lang.org/solve/sharing-crates-with-rust-analyzer.html#trait-interner #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] pub trait Interner: Sized diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 116640c776f54..9bd7698b852e1 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,3 +1,24 @@ +//! This crate is an abstraction layer, shared between rustc and rust-analyzer, to help with the +//! overlapping responsibilities (like type inference and trait solving), reduce duplication, and +//! maintain consistent behavior between the two implementations. +//! +//! It defines fundamental interfaces for types, predicates, and the context required by the next +//! trait solver. +//! +//! Both rustc and rust-analyzer immplement these traits for their own concrete implementations, and +//! `rustc_next_trait_solver` is written to be generic over these abstractions. +//! +//! In addition to these interfaces, it also contains components built on top of the abstraction +//! layer, for example elaboration logic, and the search graph machinery used by the solver, as well +//! as items that do not need compiler-specific implementations. +//! +//! Note that rust-analyzer is built with a stable compiler, while rustc uses unstable features, so +//! this crate and some of its dependencies need to separate unstable code under the `nightly` +//! feature. +//! +//! There are more details available in a [dedicated dev-guide +//! chapter](https://rustc-dev-guide.rust-lang.org/solve/sharing-crates-with-rust-analyzer.html). + #![cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir")] // tidy-alphabetical-start #![allow(rustc::direct_use_of_rustc_type_ir)] diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index d34a1ec52d153..2592c0579c741 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -91,11 +91,11 @@ impl Assumptions { } } -#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)] +#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, S)] #[derive(GenericTypeVisitable)] -pub enum RegionConstraint { - Ambiguity, - RegionOutlives(Region, Region), +pub enum RegionConstraint { + Ambiguity(S), + RegionOutlives(Region, Region, S), /// Requirement that a (potentially higher ranked) alias outlives some (potentially higher ranked) /// region due to an assumption in the environment. This cannot be satisfied via component outlives /// or item bounds. @@ -105,7 +105,7 @@ pub enum RegionConstraint { /// /// We eagerly destructure alias outlives requirements into region outlives requirements corresponding to /// component outlives & item bound outlives rules, leaving only param env candidates. - AliasTyOutlivesViaEnv(Binder, Region)>), + AliasTyOutlivesViaEnv(Binder, Region)>, S), /// This is an `I::Ty` for two reasons: /// 1. We need the type visitable impl to be able to `visit_ty` on this so canonicalization /// knows about the placeholder @@ -115,12 +115,19 @@ pub enum RegionConstraint { /// /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe. - PlaceholderTyOutlives(I::Ty, Region), + PlaceholderTyOutlives(I::Ty, Region, S), - And(Box<[RegionConstraint]>), - Or(Box<[RegionConstraint]>), + And(Box<[RegionConstraint]>), + Or(Box<[RegionConstraint]>), } +/// A solver region constraint together with the span that caused each leaf constraint. +/// +/// Solver query responses use [`RegionConstraint`] so source locations do not participate in +/// candidate equality or caching. Spans are attached when responses are applied to an inference +/// context. +pub type SpannedRegionConstraint = RegionConstraint::Span>; + // This is not a derived impl because a perfect derive leads to inductive // cycle causing the trait to never actually be implemented. #[cfg(feature = "nightly")] @@ -141,15 +148,15 @@ where std::mem::discriminant(self).stable_hash(hcx, hasher); match self { - Ambiguity => (), - RegionOutlives(a, b) => { + Ambiguity(_) => (), + RegionOutlives(a, b, _) => { a.stable_hash(hcx, hasher); b.stable_hash(hcx, hasher); } - AliasTyOutlivesViaEnv(outlives) => { + AliasTyOutlivesViaEnv(outlives, _) => { outlives.stable_hash(hcx, hasher); } - PlaceholderTyOutlives(a, b) => { + PlaceholderTyOutlives(a, b, _) => { a.stable_hash(hcx, hasher); b.stable_hash(hcx, hasher); } @@ -167,15 +174,19 @@ where } } -impl TypeFoldable for RegionConstraint { +impl TypeFoldable for RegionConstraint { fn try_fold_with>(self, f: &mut F) -> Result { use RegionConstraint::*; Ok(match self { - Ambiguity => self, - RegionOutlives(a, b) => RegionOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?), - AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.try_fold_with(f)?), - PlaceholderTyOutlives(a, b) => { - PlaceholderTyOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?) + Ambiguity(_) => self, + RegionOutlives(a, b, span) => { + RegionOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?, span) + } + AliasTyOutlivesViaEnv(outlives, span) => { + AliasTyOutlivesViaEnv(outlives.try_fold_with(f)?, span) + } + PlaceholderTyOutlives(a, b, span) => { + PlaceholderTyOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?, span) } And(and) => { let mut new_and = Vec::new(); @@ -197,10 +208,14 @@ impl TypeFoldable for RegionConstraint { fn fold_with>(self, f: &mut F) -> Self { use RegionConstraint::*; match self { - Ambiguity => self, - RegionOutlives(a, b) => RegionOutlives(a.fold_with(f), b.fold_with(f)), - AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.fold_with(f)), - PlaceholderTyOutlives(a, b) => PlaceholderTyOutlives(a.fold_with(f), b.fold_with(f)), + Ambiguity(_) => self, + RegionOutlives(a, b, span) => RegionOutlives(a.fold_with(f), b.fold_with(f), span), + AliasTyOutlivesViaEnv(outlives, span) => { + AliasTyOutlivesViaEnv(outlives.fold_with(f), span) + } + PlaceholderTyOutlives(a, b, span) => { + PlaceholderTyOutlives(a.fold_with(f), b.fold_with(f), span) + } And(and) => { let mut new_and = Vec::new(); for a in and { @@ -219,20 +234,20 @@ impl TypeFoldable for RegionConstraint { } } -impl TypeVisitable for RegionConstraint { +impl TypeVisitable for RegionConstraint { fn visit_with>(&self, f: &mut F) -> F::Result { use RegionConstraint::*; match self { - Ambiguity => (), - RegionOutlives(a, b) => { + Ambiguity(_) => (), + RegionOutlives(a, b, _) => { try_visit!(a.visit_with(f)); try_visit!(b.visit_with(f)); } - AliasTyOutlivesViaEnv(outlives) => { + AliasTyOutlivesViaEnv(outlives, _) => { try_visit!(outlives.visit_with(f)); } - PlaceholderTyOutlives(a, b) => { + PlaceholderTyOutlives(a, b, _) => { try_visit!(a.visit_with(f)); try_visit!(b.visit_with(f)); } @@ -248,13 +263,38 @@ impl TypeVisitable for RegionConstraint { } } -impl Default for RegionConstraint { +impl RegionConstraint { + fn map_spans(self, f: &mut impl FnMut(S) -> T) -> RegionConstraint { + use RegionConstraint::*; + + match self { + Ambiguity(span) => Ambiguity(f(span)), + RegionOutlives(a, b, span) => RegionOutlives(a, b, f(span)), + AliasTyOutlivesViaEnv(outlives, span) => AliasTyOutlivesViaEnv(outlives, f(span)), + PlaceholderTyOutlives(ty, region, span) => PlaceholderTyOutlives(ty, region, f(span)), + And(constraints) => And(constraints.into_iter().map(|c| c.map_spans(f)).collect()), + Or(constraints) => Or(constraints.into_iter().map(|c| c.map_spans(f)).collect()), + } + } + + pub fn without_spans(self) -> RegionConstraint { + self.map_spans(&mut |_| ()) + } +} + +impl RegionConstraint { + pub fn with_span(self, span: S) -> RegionConstraint { + self.map_spans(&mut |_| span.clone()) + } +} + +impl Default for RegionConstraint { fn default() -> Self { Self::new_true() } } -impl RegionConstraint { +impl RegionConstraint { pub fn new_true() -> Self { RegionConstraint::And(Box::new([])) } @@ -281,14 +321,14 @@ impl RegionConstraint { matches!(self, Self::Or(_)) } - pub fn unwrap_or(self) -> Box<[RegionConstraint]> { + pub fn unwrap_or(self) -> Box<[RegionConstraint]> { match self { Self::Or(ors) => ors, _ => panic!("`unwrap_or` on non-Or: {self:?}"), } } - pub fn unwrap_and(self) -> Box<[RegionConstraint]> { + pub fn unwrap_and(self) -> Box<[RegionConstraint]> { match self { Self::And(ands) => ands, _ => panic!("`unwrap_and` on non-And: {self:?}"), @@ -300,10 +340,10 @@ impl RegionConstraint { } pub fn is_ambig(&self) -> bool { - matches!(self, Self::Ambiguity) + matches!(self, Self::Ambiguity(_)) } - pub fn and(self, other: RegionConstraint) -> RegionConstraint { + pub fn and(self, other: RegionConstraint) -> RegionConstraint { use RegionConstraint::*; match (self, other) { @@ -325,9 +365,9 @@ impl RegionConstraint { pub fn canonical_form(self) -> Self { use RegionConstraint::*; - fn permutations( - ors: &[Vec>], - ) -> Vec>> { + fn permutations( + ors: &[Vec>], + ) -> Vec>> { match ors { [] => vec![vec![]], [or1] => { @@ -405,7 +445,7 @@ impl RegionConstraint { fn is_leaf_constraint(&self) -> bool { use RegionConstraint::*; match self { - Ambiguity + Ambiguity(_) | RegionOutlives(..) | AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => true, @@ -504,10 +544,10 @@ fn compute_new_region_constraints, I: Interne for c in constraints { match c { And(..) | Or(..) => unreachable!(), - Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { + Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { new_constraints.push(c.clone()) } - RegionOutlives(r1, r2) => { + RegionOutlives(r1, r2, _) => { regions.insert(r1); regions.insert(r2); region_flows_builder.add(r2, r1); @@ -531,7 +571,7 @@ fn compute_new_region_constraints, I: Interne }; if is_placeholder_like(*r) && is_placeholder_like(*ub) { - new_constraints.push(RegionOutlives(*ub, *r)); + new_constraints.push(RegionOutlives(*ub, *r, ())); } } } @@ -541,57 +581,56 @@ fn compute_new_region_constraints, I: Interne /// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are true/false/ambiguous #[instrument(level = "debug", ret)] -pub fn evaluate_solver_constraint( - constraint: &RegionConstraint, -) -> RegionConstraint { +pub fn evaluate_solver_constraint( + constraint: &RegionConstraint, +) -> RegionConstraint { use RegionConstraint::*; match constraint { - Ambiguity | RegionOutlives(..) | AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => { - constraint.clone() - } + Ambiguity(_) + | RegionOutlives(..) + | AliasTyOutlivesViaEnv(..) + | PlaceholderTyOutlives(..) => constraint.clone(), And(and) => { let mut and_constraints = Vec::new(); - let mut is_ambiguous_constraint = false; + let mut ambiguity = None; for c in and.iter() { let evaluated_constraint = evaluate_solver_constraint(c); if evaluated_constraint.is_true() { // - do nothing } else if evaluated_constraint.is_false() { return RegionConstraint::new_false(); - } else if evaluated_constraint.is_ambig() { - is_ambiguous_constraint = true; + } else if let Ambiguity(span) = evaluated_constraint { + ambiguity.get_or_insert(span); } else { and_constraints.push(evaluated_constraint); } } - if is_ambiguous_constraint { - RegionConstraint::Ambiguity - } else { - RegionConstraint::And(and_constraints.into_boxed_slice()) - } + ambiguity.map_or_else( + || RegionConstraint::And(and_constraints.into_boxed_slice()), + RegionConstraint::Ambiguity, + ) } Or(or) => { let mut or_constraints = Vec::new(); - let mut is_ambiguous_constraint = false; + let mut ambiguity = None; for c in or.iter() { let evaluated_constraint = evaluate_solver_constraint(c); if evaluated_constraint.is_false() { // do nothing } else if evaluated_constraint.is_true() { return RegionConstraint::new_true(); - } else if evaluated_constraint.is_ambig() { - is_ambiguous_constraint = true; + } else if let Ambiguity(span) = evaluated_constraint { + ambiguity.get_or_insert(span); } else { or_constraints.push(evaluated_constraint); } } - if is_ambiguous_constraint { - RegionConstraint::Ambiguity - } else { - RegionConstraint::Or(or_constraints.into_boxed_slice()) - } + ambiguity.map_or_else( + || RegionConstraint::Or(or_constraints.into_boxed_slice()), + RegionConstraint::Ambiguity, + ) } } } @@ -637,11 +676,11 @@ fn pull_region_outlives_constraints_out_of_universe< use RegionConstraint::*; match constraint { - Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { + Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { assert!(max_universe(infcx, constraint.clone()) < u); constraint } - RegionOutlives(region_1, region_2) => { + RegionOutlives(region_1, region_2, ()) => { let region_1_u = max_universe(infcx, region_1); let region_2_u = max_universe(infcx, region_2); @@ -651,7 +690,7 @@ fn pull_region_outlives_constraints_out_of_universe< let assumptions = match assumptions { Some(assumptions) => assumptions, - None => return RegionConstraint::Ambiguity, + None => return RegionConstraint::Ambiguity(()), }; let mut candidates = vec![]; @@ -667,7 +706,7 @@ fn pull_region_outlives_constraints_out_of_universe< // As long as any region outlived by `region_1` outlives any region region which // `region_2` outlives, we know that `region_1: region_2` holds. In other words, // there exists some set of 4 regions for which `'r1: 'i1` `'i1: 'i2` `'i2: 'r2` - candidates.push(RegionOutlives(ub, lb)); + candidates.push(RegionOutlives(ub, lb, ())); } } @@ -690,23 +729,25 @@ fn pull_region_outlives_constraints_out_of_universe< pub fn destructure_type_outlives_constraints_in_root< Infcx: InferCtxtLike, I: Interner, + S: Clone + std::fmt::Debug, >( infcx: &Infcx, - constraint: RegionConstraint, + constraint: RegionConstraint, assumptions: &Assumptions, -) -> RegionConstraint { +) -> RegionConstraint { use RegionConstraint::*; match constraint { - Ambiguity | RegionOutlives(..) => constraint, - PlaceholderTyOutlives(ty, r) => { + Ambiguity(_) | RegionOutlives(..) => constraint, + PlaceholderTyOutlives(ty, r, span) => { Or(regions_outlived_by_placeholder(ty, assumptions, infcx.cx()) - .map(move |assumption_r| RegionOutlives(assumption_r, r)) + .map(move |assumption_r| RegionOutlives(assumption_r, r, span.clone())) .collect::>() .into_boxed_slice()) } - AliasTyOutlivesViaEnv(bound_outlives) => { + AliasTyOutlivesViaEnv(bound_outlives, span) => { alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions) + .with_span(span) } And(constraints) => And(constraints .into_iter() @@ -752,8 +793,8 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< use RegionConstraint::*; match constraint { - Ambiguity | RegionOutlives(..) => constraint, - PlaceholderTyOutlives(ty, region) => { + Ambiguity(_) | RegionOutlives(..) => constraint, + PlaceholderTyOutlives(ty, region, ()) => { let ty_u = max_universe(infcx, ty); let region_u = max_universe(infcx, region); @@ -763,7 +804,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< let assumptions = match assumptions { Some(assumptions) => assumptions, - None => return Ambiguity, + None => return Ambiguity(()), }; let mut candidates = vec![]; @@ -772,7 +813,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< // smaller universe candidates.extend( regions_outlived_by_placeholder(ty, assumptions, infcx.cx()) - .map(move |assumption_r| RegionOutlives(assumption_r, region)), + .map(move |assumption_r| RegionOutlives(assumption_r, region, ())), ); // We can express `!T: 'region` as `!T: 'r` where `'r: 'region`. This is only necessary @@ -782,13 +823,13 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< candidates.extend( regions_outliving::(region, assumptions, infcx.cx()) .filter(|r| max_universe(infcx, *r) < u) - .map(|r| PlaceholderTyOutlives(ty, r)), + .map(|r| PlaceholderTyOutlives(ty, r, ())), ); } Or(candidates.into_boxed_slice()) } - AliasTyOutlivesViaEnv(bound_outlives) => { + AliasTyOutlivesViaEnv(bound_outlives, ()) => { let mut candidates = Vec::new(); // given there can be higher ranked assumptions, e.g. `for<'a> >::Assoc: 'c`, that @@ -824,21 +865,21 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< escaping_outlives, I::BoundVarKinds::from_vars(infcx.cx(), bound_vars), ); - let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives); + let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives, ()); if max_universe(infcx, candidate.clone()) < u { candidates.push(candidate); } else { // `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave // a placeholder type in `u`, so this type-outlives constraint cannot be // handled by the region-outlives-only eager placeholder machinery. - candidates.push(Ambiguity); + candidates.push(Ambiguity(())); } } let assumptions = match assumptions { Some(assumptions) => assumptions, None => { - candidates.push(Ambiguity); + candidates.push(Ambiguity(())); return Or(candidates.into_boxed_slice()); } }; @@ -881,11 +922,11 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< .filter(|r2| max_universe(infcx, *r2) < u) { let candidate = - AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2))); + AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2)), ()); if max_universe(infcx, candidate.clone()) < u { candidates.push(candidate); } else { - candidates.push(Ambiguity); + candidates.push(Ambiguity(())); } } } @@ -894,7 +935,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< // let's be conservative and not let alias outlives' cause NoSolution // in coherence match infcx.typing_mode_raw() { - TypingMode::Coherence => candidates.push(RegionConstraint::Ambiguity), + TypingMode::Coherence => candidates.push(RegionConstraint::Ambiguity(())), TypingMode::Typeck { .. } | TypingMode::ErasedNotCoherence { .. } | TypingMode::PostTypeckUntilBorrowck { .. } @@ -1032,7 +1073,7 @@ fn alias_outlives_candidates_from_assumptions let mut relation = HigherRankedAliasMatcher { infcx, - region_constraints: vec![RegionConstraint::RegionOutlives(r2, r)], + region_constraints: vec![RegionConstraint::RegionOutlives(r2, r, ())], }; // FIXME(#155345): Both sides should be rigid in the future. @@ -1103,8 +1144,8 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeRelation fn regions(&mut self, a: Region, b: Region) -> RelateResult> { if a != b { - self.region_constraints.push(RegionConstraint::RegionOutlives(a, b)); - self.region_constraints.push(RegionConstraint::RegionOutlives(b, a)); + self.region_constraints.push(RegionConstraint::RegionOutlives(a, b, ())); + self.region_constraints.push(RegionConstraint::RegionOutlives(b, a, ())); } Ok(a) } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 5cf05071bc329..09b752491673e 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -255,6 +255,7 @@ pub mod fmt; pub mod intrinsics; #[unstable(feature = "alloc_io", issue = "154046")] pub mod io; +pub mod panicking; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/alloc/src/panicking.rs b/library/alloc/src/panicking.rs new file mode 100644 index 0000000000000..e150ecb6dca2d --- /dev/null +++ b/library/alloc/src/panicking.rs @@ -0,0 +1,29 @@ +#![doc(hidden)] +#![unstable(feature = "std_internals", issue = "none")] + +use core::any::Any; +use core::fmt::Display; + +use crate::boxed::Box; + +/// An internal trait used by std to pass data from std to `panic_unwind` and +/// other panic runtimes. Not intended to be stabilized any time soon, do not +/// use. +pub trait PanicPayload: Display { + /// Take full ownership of the contents. + /// + /// After this method got called, only some dummy default value is left in `self`. + /// Calling this method twice, or calling `get` after calling this method, is an error. + /// + /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only + /// gets a borrowed `dyn PanicPayload`. + fn take_box(&mut self) -> Box; + + /// Just borrow the contents. + fn get(&mut self) -> &(dyn Any + Send); + + /// Tries to borrow the contents as `&str`, if possible without doing any allocations. + fn as_str(&mut self) -> Option<&str> { + None + } +} diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 48c489cc8fa5e..e4a803f28e121 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1606,6 +1606,46 @@ impl Rc { pub unsafe fn decrement_strong_count(ptr: *const T) { unsafe { Self::decrement_strong_count_in(ptr, Global) } } + + /// Gets the number of strong (`Rc`) pointers to the allocation behind the given raw pointer. + /// + /// This method does not consume or drop the `Rc` behind this pointer. + /// + /// # Safety + /// + /// The pointer must point to (and have valid metadata for) the value inside a live `Rc` + /// allocation, such as a pointer returned by [`Rc::into_raw`], + /// [`Rc::into_raw_with_allocator`], or [`Rc::as_ptr`]. + /// `T` must have the same alignment as that value. + /// The associated `Rc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_raw_get_strong)] + /// use std::rc::Rc; + /// + /// let five = Rc::new(5); + /// let _also_five = Rc::clone(&five); + /// let ptr = Rc::into_raw(five); + /// + /// unsafe { + /// assert_eq!(2, Rc::strong_count_from_raw(ptr)); + /// + /// // Convert back to an `Rc` to avoid leaking memory. + /// let five = Rc::from_raw(ptr); + /// assert_eq!(2, Rc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[unstable(feature = "arc_raw_get_strong", issue = "157021")] + pub unsafe fn strong_count_from_raw(ptr: *const T) -> usize { + let offset = unsafe { data_offset(ptr) }; + // Reverse the offset to find the original RcInner. + let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut RcInner }; + unsafe { (*rc_ptr).strong.get() } + } } impl Rc { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b944da8ed47eb..625a29dd9b7a0 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1774,6 +1774,54 @@ impl Arc { pub unsafe fn decrement_strong_count(ptr: *const T) { unsafe { Arc::decrement_strong_count_in(ptr, Global) } } + + /// Gets the number of strong (`Arc`) pointers to the allocation behind the given raw + /// pointer. + /// + /// This method does not consume or drop the `Arc` behind this pointer. + /// + /// # Safety + /// + /// The pointer must point to (and have valid metadata for) the value inside a live `Arc` + /// allocation, such as a pointer returned by [`Arc::into_raw`], + /// [`Arc::into_raw_with_allocator`], or [`Arc::as_ptr`]. + /// `T` must have the same alignment as that value. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method. + /// + /// Using this method correctly also requires extra care: another thread can change the + /// strong count at any time, including between calling this method and acting on the + /// result. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_raw_get_strong)] + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _also_five = Arc::clone(&five); + /// let ptr = Arc::into_raw(five); + /// + /// unsafe { + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// assert_eq!(2, Arc::strong_count_from_raw(ptr)); + /// + /// // Convert back to an `Arc` to avoid leaking memory. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "arc_raw_get_strong", issue = "157021")] + pub unsafe fn strong_count_from_raw(ptr: *const T) -> usize { + let offset = unsafe { data_offset(ptr) }; + // Reverse the offset to find the original ArcInner. + let arc_ptr = unsafe { ptr.byte_sub(offset) as *mut ArcInner }; + unsafe { (*arc_ptr).strong.load(Relaxed) } + } } impl Arc { diff --git a/library/alloc/src/wtf8/mod.rs b/library/alloc/src/wtf8/mod.rs index 36ec32c549763..94624a57754c6 100644 --- a/library/alloc/src/wtf8/mod.rs +++ b/library/alloc/src/wtf8/mod.rs @@ -31,7 +31,7 @@ use crate::string::String; use crate::sync::Arc; use crate::vec::Vec; -/// An owned, growable string of well-formed WTF-8 data. +/// An owned, growable string of [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed) data. /// /// Similar to `String`, but can additionally contain surrogate code points /// if they’re not in a surrogate pair. @@ -104,8 +104,9 @@ impl Wtf8Buf { /// Creates a WTF-8 string from a WTF-8 byte vec. /// - /// Since the byte vec is not checked for valid WTF-8, this function is - /// marked unsafe. + /// # Safety + /// + /// `value` must contain [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed). #[inline] pub unsafe fn from_bytes_unchecked(value: Vec) -> Wtf8Buf { Wtf8Buf { bytes: value, is_known_utf8: false } @@ -147,12 +148,15 @@ impl Wtf8Buf { Ok(ch) => string.push_char(ch), Err(surrogate) => { let surrogate = surrogate.unpaired_surrogate(); - // Surrogates are known to be in the code point range. + // SAFETY: Surrogates are known to be in the code point range. let code_point = unsafe { CodePoint::from_u32_unchecked(surrogate as u32) }; // The string will now contain an unpaired surrogate. string.is_known_utf8 = false; - // Skip the WTF-8 concatenation check, - // surrogate pairs are already decoded by decode_utf16 + // SAFETY: `decode_utf16` reports only unpaired surrogates here, + // so this code point cannot form a surrogate pair with the + // preceding and succeeding contents. The existing buffer is + // well-formed WTF-8, and appending this encoded surrogate + // preserves that invariant. unsafe { string.push_code_point_unchecked(code_point); } @@ -165,6 +169,14 @@ impl Wtf8Buf { /// Appends the given `char` to the end of this string. /// This does **not** include the WTF-8 concatenation check or `is_known_utf8` check. /// Copied from String::push. + /// + /// # Safety + /// + /// `self` must contain [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed), + /// and appending `code_point` must preserve that invariant. In particular, + /// `code_point` must not be a trailing surrogate if `self` ends with a leading surrogate. + /// + /// If `self.is_known_utf8` is true, `code_point` must not be a surrogate. unsafe fn push_code_point_unchecked(&mut self, code_point: CodePoint) { let mut bytes = [0; char::MAX_LEN_UTF8]; let bytes = encode_utf8_raw(code_point.to_u32(), &mut bytes); @@ -173,14 +185,13 @@ impl Wtf8Buf { #[inline] pub fn as_slice(&self) -> &Wtf8 { + // SAFETY: `self` maintains `bytes` as well-formed WTF-8. unsafe { Wtf8::from_bytes_unchecked(&self.bytes) } } #[inline] pub fn as_mut_slice(&mut self) -> &mut Wtf8 { - // Safety: `Wtf8` doesn't expose any way to mutate the bytes that would - // cause them to change from well-formed UTF-8 to ill-formed UTF-8, - // which would break the assumptions of the `is_known_utf8` field. + // SAFETY: `self` maintains `bytes` as well-formed WTF-8. unsafe { Wtf8::from_mut_bytes_unchecked(&mut self.bytes) } } @@ -262,6 +273,7 @@ impl Wtf8Buf { #[inline] pub fn leak<'a>(self) -> &'a mut Wtf8 { + // SAFETY: `self` maintains `bytes` as well-formed WTF-8. unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) } } @@ -336,7 +348,7 @@ impl Wtf8Buf { self.is_known_utf8 = false; } - // No newly paired surrogates at the boundary. + // SAFETY: We have checked that no newly paired surrogates at the boundary. unsafe { self.push_code_point_unchecked(code_point) } } @@ -371,6 +383,7 @@ impl Wtf8Buf { /// the original WTF-8 string is returned instead. pub fn into_string(self) -> Result { if self.is_known_utf8 || self.next_surrogate(0).is_none() { + // SAFETY: We have checked that `self.bytes` contains valid UTF-8. Ok(unsafe { String::from_utf8_unchecked(self.bytes) }) } else { Err(self) @@ -392,18 +405,23 @@ impl Wtf8Buf { self.bytes[surrogate_pos..pos].copy_from_slice("\u{FFFD}".as_bytes()); } } + // SAFETY: Now `self.bytes` contains valid UTF-8. unsafe { String::from_utf8_unchecked(self.bytes) } } /// Converts this `Wtf8Buf` into a boxed `Wtf8`. #[inline] pub fn into_box(self) -> Box { - // SAFETY: relies on `Wtf8` being `repr(transparent)`. + // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and + // `self.bytes.into_boxed_slice()` returns a `Box<[u8]>`. + // Therefore, transmuting `Box<[u8]>` to `Box` is safe. unsafe { mem::transmute(self.bytes.into_boxed_slice()) } } /// Converts a `Box` into a `Wtf8Buf`. pub fn from_box(boxed: Box) -> Wtf8Buf { + // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and `boxed` is + // a `Box`. Therefore, transmuting `Box` to `Box<[u8]>` is safe. let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) }; Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false } } @@ -411,6 +429,13 @@ impl Wtf8Buf { /// Provides plumbing to core `Vec::extend_from_slice`. /// More well behaving alternative to allowing outer types /// full mutable access to the core `Vec`. + /// + /// # Safety + /// + /// `self` and `other` must contain [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed), + /// and appending `other` to `self` must preserve that invariant. + /// In particular, `self` must not end with a leading surrogate, + /// or `other` must not start with a trailing surrogate. #[inline] pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.bytes.extend_from_slice(other); @@ -468,6 +493,8 @@ pub(super) fn to_owned(slice: &Wtf8) -> Wtf8Buf { /// This only copies the data if necessary (if it contains any surrogate). pub(super) fn to_string_lossy(slice: &Wtf8) -> Cow<'_, str> { let Some((surrogate_pos, _)) = slice.next_surrogate(0) else { + // SAFETY: `next_surrogate` found no surrogate, so the well-formed WTF-8 + // bytes are valid UTF-8. return Cow::Borrowed(unsafe { str::from_utf8_unchecked(slice.as_bytes()) }); }; let wtf8_bytes = slice.as_bytes(); @@ -484,6 +511,8 @@ pub(super) fn to_string_lossy(slice: &Wtf8) -> Cow<'_, str> { } None => { utf8_bytes.extend_from_slice(&wtf8_bytes[pos..]); + // SAFETY: Every surrogate was replaced with `"\u{FFFD}"`, + // and the remaining bytes are valid UTF-8, so `utf8_bytes` is valid UTF-8. return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) }); } } @@ -516,12 +545,16 @@ impl Wtf8 { #[rustc_allow_incoherent_impl] pub fn into_box(&self) -> Box { let boxed: Box<[u8]> = self.as_bytes().into(); + // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and `boxed` is + // a `Box<[u8]>`. Therefore, transmuting `Box<[u8]>` to `Box` is safe. unsafe { mem::transmute(boxed) } } #[rustc_allow_incoherent_impl] pub fn empty_box() -> Box { let boxed: Box<[u8]> = Default::default(); + // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and `boxed` is + // an empty `Box<[u8]>`. Therefore, transmuting it to `Box` is safe. unsafe { mem::transmute(boxed) } } @@ -529,12 +562,14 @@ impl Wtf8 { #[rustc_allow_incoherent_impl] pub fn into_arc(&self) -> Arc { let arc: Arc<[u8]> = Arc::from(self.as_bytes()); + // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`. unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Wtf8) } } #[rustc_allow_incoherent_impl] pub fn into_rc(&self) -> Rc { let rc: Rc<[u8]> = Rc::from(self.as_bytes()); + // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`. unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) } } @@ -554,6 +589,7 @@ impl Wtf8 { #[inline] fn decode_surrogate_pair(lead: u16, trail: u16) -> char { let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32); + // SAFETY: The computed `code_point` is in 0x10000..=0x10FFFF and is not a surrogate. unsafe { char::from_u32_unchecked(code_point) } } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 43128302dfef4..a0568dd99a4b0 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -26,7 +26,10 @@ #![stable(feature = "rust1", since = "1.0.0")] mod bytewise; +mod clamp; pub(crate) use bytewise::BytewiseEq; +#[unstable(feature = "clamp_bounds", issue = "147781")] +pub use clamp::ClampBounds; use self::Ordering::*; use crate::marker::{Destruct, PointeeSized}; @@ -1169,6 +1172,35 @@ pub const trait Ord: [const] Eq + [const] PartialOrd + PointeeSized { self } } + + /// Restrict a value to a certain range. + /// + /// This is equal to `max`, `min`, or `clamp`, depending on whether the range is `min..`, + /// `..=max`, or `min..=max`, respectively. Exclusive ranges are not permitted. + /// + /// # Panics + /// + /// Panics on `min..=max` if `min > max`. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_to)] + /// assert_eq!((-3).clamp_to(-2..=1), -2); + /// assert_eq!(0.clamp_to(-2..=1), 0); + /// assert_eq!(2.clamp_to(..=1), 1); + /// assert_eq!(5.clamp_to(7..), 7); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "clamp_to", issue = "147781")] + fn clamp_to(self, range: R) -> Self + where + Self: Sized + [const] Destruct, + R: [const] ClampBounds, + { + range.clamp(self) + } } /// Derive macro generating an impl of the trait [`Ord`]. diff --git a/library/core/src/cmp/clamp.rs b/library/core/src/cmp/clamp.rs new file mode 100644 index 0000000000000..2743c5710d2f6 --- /dev/null +++ b/library/core/src/cmp/clamp.rs @@ -0,0 +1,100 @@ +use crate::marker::Destruct; +use crate::ops::{RangeFrom, RangeFull, RangeInclusive, RangeToInclusive}; + +/// Trait for ranges supported by [`Ord::clamp_to`]. +#[unstable(feature = "clamp_bounds", issue = "147781")] +#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] +pub const trait ClampBounds: Sized { + /// The implementation of [`Ord::clamp_to`]. + fn clamp(self, value: T) -> T + where + T: [const] Destruct; +} + +#[unstable(feature = "clamp_bounds", issue = "147781")] +#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] +const impl ClampBounds for RangeFrom +where + T: [const] Ord, +{ + fn clamp(self, value: T) -> T + where + T: [const] Destruct, + { + value.max(self.start) + } +} + +#[unstable(feature = "clamp_bounds", issue = "147781")] +#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] +const impl ClampBounds for RangeToInclusive +where + T: [const] Ord, +{ + fn clamp(self, value: T) -> T + where + T: [const] Destruct, + { + value.min(self.end) + } +} + +#[unstable(feature = "clamp_bounds", issue = "147781")] +#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] +const impl ClampBounds for RangeInclusive +where + T: [const] Ord, +{ + fn clamp(self, value: T) -> T + where + T: [const] Destruct, + { + let (start, end) = self.into_inner(); + value.clamp(start, end) + } +} + +#[unstable(feature = "clamp_bounds", issue = "147781")] +#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] +const impl ClampBounds for RangeFull { + fn clamp(self, value: T) -> T { + value + } +} + +macro impl_for_float($t:ty) { + #[unstable(feature = "clamp_bounds", issue = "147781")] + #[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] + const impl ClampBounds<$t> for RangeFrom<$t> { + fn clamp(self, value: $t) -> $t { + assert!(!self.start.is_nan(), "start was NaN"); + value.max(self.start) + } + } + + #[unstable(feature = "clamp_bounds", issue = "147781")] + #[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] + const impl ClampBounds<$t> for RangeToInclusive<$t> { + fn clamp(self, value: $t) -> $t { + assert!(!self.end.is_nan(), "end was NaN"); + value.min(self.end) + } + } + + #[unstable(feature = "clamp_bounds", issue = "147781")] + #[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] + const impl ClampBounds<$t> for RangeInclusive<$t> { + fn clamp(self, value: $t) -> $t { + let (start, end) = self.into_inner(); + assert!(start <= end, "start > end, or either was NaN"); + value.clamp(start, end) + } + } +} + +// #[unstable(feature = "f16", issue = "116909")] +impl_for_float!(f16); +impl_for_float!(f32); +impl_for_float!(f64); +// #[unstable(feature = "f128", issue = "116909")] +impl_for_float!(f128); diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 2f38f9b77faf4..111664775ca8d 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -90,13 +90,13 @@ pub enum TypeKind { /// Unions. Union, /// Primitive boolean type. - Bool(Bool), + Bool, /// Primitive character type. - Char(Char), + Char, /// Primitive signed and unsigned integer type. - Int(Int), + Int, /// Primitive floating-point type. - Float(Float), + Float, /// String slice type. Str(Str), /// References. @@ -199,42 +199,6 @@ pub struct Const { pub ty: TypeId, } -/// Compile-time type information about `bool`. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Bool { - // No additional information to provide for now. -} - -/// Compile-time type information about `char`. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Char { - // No additional information to provide for now. -} - -/// Compile-time type information about signed and unsigned integer types. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Int { - /// The bit width of the signed integer type. - pub bits: u32, - /// Whether the integer type is signed. - pub signed: bool, -} - -/// Compile-time type information about floating-point types. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Float { - /// The bit width of the floating-point type. - pub bits: u32, -} - /// Compile-time type information about string slice types. #[derive(Debug)] #[non_exhaustive] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index e876b2d7bd312..fb69fec276f43 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1455,6 +1455,43 @@ impl f128 { self.clamp(-limit, limit) } + /// Restrict a value to a certain range, unless it is NaN. + /// + /// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is + /// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will + /// panic if any bound is NaN. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// Exclusive ranges are not permitted. + /// + /// # Panics + /// + /// Panics on `min..=max` if `min > max`, or if any bound is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128, clamp_to)] + /// # #[cfg(target_has_reliable_f128)] { + /// assert_eq!((-3.0f128).clamp_to(-2.0..=1.0), -2.0); + /// assert_eq!(0.0f128.clamp_to(-2.0..=1.0), 0.0); + /// assert_eq!(2.0f128.clamp_to(..=1.0), 1.0); + /// assert_eq!(5.0f128.clamp_to(7.0..), 7.0); + /// assert!(f128::NAN.clamp_to(1.0..=2.0).is_nan()); + /// # } + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "clamp_to", issue = "147781")] + pub fn clamp_to(self, range: R) -> Self + where + R: crate::cmp::ClampBounds, + { + range.clamp(self) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e649f6643fe59..88d2eb4875e27 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1441,6 +1441,43 @@ impl f16 { self.clamp(-limit, limit) } + /// Restrict a value to a certain range, unless it is NaN. + /// + /// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is + /// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will + /// panic if any bound is NaN. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// Exclusive ranges are not permitted. + /// + /// # Panics + /// + /// Panics on `min..=max` if `min > max`, or if any bound is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16, clamp_to)] + /// # #[cfg(target_has_reliable_f16)] { + /// assert_eq!((-3.0f16).clamp_to(-2.0..=1.0), -2.0); + /// assert_eq!(0.0f16.clamp_to(-2.0..=1.0), 0.0); + /// assert_eq!(2.0f16.clamp_to(..=1.0), 1.0); + /// assert_eq!(5.0f16.clamp_to(7.0..), 7.0); + /// assert!(f16::NAN.clamp_to(1.0..=2.0).is_nan()); + /// # } + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "clamp_to", issue = "147781")] + pub fn clamp_to(self, range: R) -> Self + where + R: crate::cmp::ClampBounds, + { + range.clamp(self) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 66d89b2ae7c1d..f782d3d144f63 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1613,6 +1613,41 @@ impl f32 { self.clamp(-limit, limit) } + /// Restrict a value to a certain range, unless it is NaN. + /// + /// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is + /// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will + /// panic if any bound is NaN. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// Exclusive ranges are not permitted. + /// + /// # Panics + /// + /// Panics on `min..=max` if `min > max`, or if any bound is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_to)] + /// assert_eq!((-3.0f32).clamp_to(-2.0..=1.0), -2.0); + /// assert_eq!(0.0f32.clamp_to(-2.0..=1.0), 0.0); + /// assert_eq!(2.0f32.clamp_to(..=1.0), 1.0); + /// assert_eq!(5.0f32.clamp_to(7.0..), 7.0); + /// assert!(f32::NAN.clamp_to(1.0..=2.0).is_nan()); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "clamp_to", issue = "147781")] + pub fn clamp_to(self, range: R) -> Self + where + R: crate::cmp::ClampBounds, + { + range.clamp(self) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 0a45d6f333ef8..3a0c9ef8d311f 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1595,6 +1595,41 @@ impl f64 { self.clamp(-limit, limit) } + /// Restrict a value to a certain range, unless it is NaN. + /// + /// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is + /// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will + /// panic if any bound is NaN. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// Exclusive ranges are not permitted. + /// + /// # Panics + /// + /// Panics on `min..=max` if `min > max`, or if any bound is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_to)] + /// assert_eq!((-3.0f64).clamp_to(-2.0..=1.0), -2.0); + /// assert_eq!(0.0f64.clamp_to(-2.0..=1.0), 0.0); + /// assert_eq!(2.0f64.clamp_to(..=1.0), 1.0); + /// assert_eq!(5.0f64.clamp_to(7.0..), 7.0); + /// assert!(f64::NAN.clamp_to(1.0..=2.0).is_nan()); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "clamp_to", issue = "147781")] + pub fn clamp_to(self, range: R) -> Self + where + R: crate::cmp::ClampBounds, + { + range.clamp(self) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 4332e7d28c58b..4fcd5f615e993 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -14,7 +14,6 @@ pub use self::panic_info::PanicInfo; pub use self::panic_info::PanicMessage; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; -use crate::any::Any; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] @@ -120,31 +119,6 @@ pub fn abort_on_unwind R, R>(f: F) -> R { f() } -/// An internal trait used by std to pass data from std to `panic_unwind` and -/// other panic runtimes. Not intended to be stabilized any time soon, do not -/// use. -#[unstable(feature = "std_internals", issue = "none")] -#[doc(hidden)] -pub unsafe trait PanicPayload: crate::fmt::Display { - /// Take full ownership of the contents. - /// The return type is actually `Box`, but we cannot use `Box` in core. - /// - /// After this method got called, only some dummy default value is left in `self`. - /// Calling this method twice, or calling `get` after calling this method, is an error. - /// - /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only - /// gets a borrowed `dyn PanicPayload`. - fn take_box(&mut self) -> *mut (dyn Any + Send); - - /// Just borrow the contents. - fn get(&mut self) -> &(dyn Any + Send); - - /// Tries to borrow the contents as `&str`, if possible without doing any allocations. - fn as_str(&mut self) -> Option<&str> { - None - } -} - /// Helper macro for panicking in a `const fn`. /// Invoke as: /// ```rust,ignore (just an example) diff --git a/library/core/src/wtf8.rs b/library/core/src/wtf8.rs index 16554f108c0fa..af0b83c62ca76 100644 --- a/library/core/src/wtf8.rs +++ b/library/core/src/wtf8.rs @@ -1,11 +1,11 @@ -//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/). +//! Implementation of [the WTF-8 encoding](https://wtf-8.codeberg.page/). //! //! This library uses Rust’s type system to maintain -//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed), +//! [well-formedness](https://wtf-8.codeberg.page/#well-formed), //! like the `String` and `&str` types do for UTF-8. //! //! Since [WTF-8 must not be used -//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience), +//! for interchange](https://wtf-8.codeberg.page/#intended-audience), //! this library deliberately does not provide access to the underlying bytes //! of WTF-8 strings, //! nor can it decode WTF-8 from arbitrary bytes. diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1f629f01f38dd..8d1bd234dc1c7 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -13,6 +13,7 @@ #![feature(casefold)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] +#![feature(clamp_to)] #![feature(clone_to_uninit)] #![feature(cmp_minmax)] #![feature(cmp_splat)] diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 74be53c57b46b..f3a69dd857aba 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -223,46 +223,41 @@ fn test_primitives() { use TypeKind::*; const { - let Type { kind: Bool(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let Type { kind: Bool, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); - let Type { kind: Char(_ty), .. } = (const { Type::of::() }) else { panic!() }; + let Type { kind: Char, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); - let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; - assert!(ty.bits == 32); - assert!(ty.signed); + let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); + assert!(ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); - let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; - assert!(ty.bits as usize == size_of::() * 8); - assert!(ty.signed); + let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); + assert!(ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); - let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; - assert!(ty.bits == 32); - assert!(!ty.signed); + let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); + assert!(!ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); - let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; - assert!(ty.bits as usize == size_of::() * 8); - assert!(!ty.signed); + let Type { kind: Int, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); + assert!(!ty_id.is_signed()); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); - let Type { kind: Float(ty), .. } = (const { Type::of::() }) else { panic!() }; - assert!(ty.bits == 32); + let Type { kind: Float, .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); diff --git a/library/coretests/tests/num/floats.rs b/library/coretests/tests/num/floats.rs index cc1d1a0b673dc..e021837c8117a 100644 --- a/library/coretests/tests/num/floats.rs +++ b/library/coretests/tests/num/floats.rs @@ -1321,10 +1321,10 @@ float_test! { name: clamp_min_greater_than_max, attrs: { const: #[cfg(false)], - f16: #[should_panic, cfg(target_has_reliable_f16)], + f16: #[should_panic, cfg(target_has_reliable_f16_math)], f32: #[should_panic], f64: #[should_panic], - f128: #[should_panic, cfg(target_has_reliable_f128)], + f128: #[should_panic, cfg(target_has_reliable_f128_math)], }, test { let _ = Float::ONE.clamp(3.0, 1.0); @@ -1335,10 +1335,10 @@ float_test! { name: clamp_min_is_nan, attrs: { const: #[cfg(false)], - f16: #[should_panic, cfg(target_has_reliable_f16)], + f16: #[should_panic, cfg(target_has_reliable_f16_math)], f32: #[should_panic], f64: #[should_panic], - f128: #[should_panic, cfg(target_has_reliable_f128)], + f128: #[should_panic, cfg(target_has_reliable_f128_math)], }, test { let _ = Float::ONE.clamp(Float::NAN, 1.0); @@ -1349,16 +1349,58 @@ float_test! { name: clamp_max_is_nan, attrs: { const: #[cfg(false)], - f16: #[should_panic, cfg(target_has_reliable_f16)], + f16: #[should_panic, cfg(target_has_reliable_f16_math)], f32: #[should_panic], f64: #[should_panic], - f128: #[should_panic, cfg(target_has_reliable_f128)], + f128: #[should_panic, cfg(target_has_reliable_f128_math)], }, test { let _ = Float::ONE.clamp(3.0, Float::NAN); } } +float_test! { + name: clamp_to_min_greater_than_max, + attrs: { + const: #[cfg(false)], + f16: #[should_panic, cfg(target_has_reliable_f16_math)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128_math)], + }, + test { + let _ = Float::ONE.clamp_to(3.0..=1.0); + } +} + +float_test! { + name: clamp_to_min_is_nan, + attrs: { + const: #[cfg(false)], + f16: #[should_panic, cfg(target_has_reliable_f16_math)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128_math)], + }, + test { + let _ = Float::ONE.clamp_to(Float::NAN..=1.0); + } +} + +float_test! { + name: clamp_to_max_is_nan, + attrs: { + const: #[cfg(false)], + f16: #[should_panic, cfg(target_has_reliable_f16_math)], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(target_has_reliable_f128_math)], + }, + test { + let _ = Float::ONE.clamp_to(3.0..=Float::NAN); + } +} + float_test! { name: total_cmp, attrs: { diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index ecf043ac7071c..a904c6c828204 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -12,10 +12,8 @@ bench = false doc = false [dependencies] +alloc = { path = "../alloc" } core = { path = "../rustc-std-workspace-core", package = "rustc-std-workspace-core" } [target.'cfg(target_os = "android")'.dependencies] libc = { version = "0.2", default-features = false } - -[target.'cfg(any(target_os = "android", target_os = "zkvm"))'.dependencies] -alloc = { path = "../alloc" } diff --git a/library/panic_abort/src/android.rs b/library/panic_abort/src/android.rs index 1cc2077d14bd4..d4c030d059244 100644 --- a/library/panic_abort/src/android.rs +++ b/library/panic_abort/src/android.rs @@ -1,6 +1,6 @@ +use alloc::panicking::PanicPayload; use alloc::string::String; use core::mem::transmute; -use core::panic::PanicPayload; use core::ptr::copy_nonoverlapping; const ANDROID_SET_ABORT_MESSAGE: &[u8] = b"android_set_abort_message\0"; @@ -15,7 +15,7 @@ type SetAbortMessageType = unsafe extern "C" fn(*const libc::c_char) -> (); // // Weakly resolve the symbol for android_set_abort_message. This function is only available // for API >= 21. -pub(crate) unsafe fn android_set_abort_message(payload: &mut dyn PanicPayload) { +pub(crate) fn android_set_abort_message(payload: &mut dyn PanicPayload) { let func_addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, ANDROID_SET_ABORT_MESSAGE.as_ptr() as *const libc::c_char) as usize diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index e62d758e9e5d1..6e9736bc4bd97 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -19,27 +19,23 @@ mod android; #[cfg(target_os = "zkvm")] mod zkvm; +use alloc::boxed::Box; +use alloc::panicking::PanicPayload; use core::any::Any; -use core::panic::PanicPayload; #[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { +pub unsafe fn __rust_panic_cleanup(_: *mut u8) -> Box { unreachable!() } // "Leak" the payload and shim to the relevant abort on the platform in question. #[rustc_std_internal_symbol] -pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { +pub fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { // Android has the ability to attach a message as part of the abort. #[cfg(target_os = "android")] - unsafe { - android::android_set_abort_message(_payload); - } + android::android_set_abort_message(_payload); #[cfg(target_os = "zkvm")] - unsafe { - zkvm::zkvm_set_abort_message(_payload); - } + zkvm::zkvm_set_abort_message(_payload); unsafe extern "Rust" { // This is defined in std::rt. diff --git a/library/panic_abort/src/zkvm.rs b/library/panic_abort/src/zkvm.rs index 7b1e89c6a8e63..5f62e7022efad 100644 --- a/library/panic_abort/src/zkvm.rs +++ b/library/panic_abort/src/zkvm.rs @@ -1,9 +1,9 @@ +use alloc::panicking::PanicPayload; use alloc::string::String; -use core::panic::PanicPayload; // Forward the abort message to zkVM's sys_panic. This is implemented by RISC Zero's // platform crate which exposes system calls specifically for the zkVM. -pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) { +pub(crate) fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) { let payload = payload.get(); let msg = match payload.downcast_ref::<&'static str>() { Some(msg) => msg.as_bytes(), diff --git a/library/panic_unwind/src/dummy.rs b/library/panic_unwind/src/dummy.rs index 3ba0bec71b7bf..45d30a63a3493 100644 --- a/library/panic_unwind/src/dummy.rs +++ b/library/panic_unwind/src/dummy.rs @@ -3,6 +3,7 @@ //! Stubs that simply abort for targets that don't support unwinding otherwise. use alloc::boxed::Box; +use alloc::panicking::PanicPayload; use core::any::Any; unsafe extern "Rust" { @@ -15,6 +16,6 @@ pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { __rust_abort() } -pub(crate) unsafe fn panic(_data: Box) -> u32 { +pub(crate) fn panic(_data: &mut dyn PanicPayload) -> u32 { __rust_abort() } diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index adcbe5170623c..97437a40569f3 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -37,6 +37,7 @@ //! and the last personality routine transfers control to the catch block. use alloc::boxed::Box; +use alloc::panicking::PanicPayload; use core::any::Any; use core::ptr; @@ -58,7 +59,7 @@ struct Exception { cause: Box, } -pub(crate) unsafe fn panic(data: Box) -> u32 { +pub(crate) fn panic(data: &mut dyn PanicPayload) -> u32 { let exception = Box::new(Exception { _uwe: uw::_Unwind_Exception { exception_class: RUST_EXCEPTION_CLASS, @@ -66,7 +67,7 @@ pub(crate) unsafe fn panic(data: Box) -> u32 { private: [core::ptr::null(); _], }, canary: &CANARY, - cause: data, + cause: data.take_box(), }); let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; return unsafe { uw::_Unwind_RaiseException(exception_param) as u32 }; @@ -75,10 +76,8 @@ pub(crate) unsafe fn panic(data: Box) -> u32 { _unwind_code: uw::_Unwind_Reason_Code, exception: *mut uw::_Unwind_Exception, ) { - unsafe { - let _: Box = Box::from_raw(exception as *mut Exception); - super::__rust_drop_panic(); - } + let _: Box = unsafe { Box::from_raw(exception as *mut Exception) }; + super::__rust_drop_panic(); } } diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 3f409be039567..d25d94258a16e 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -28,8 +28,8 @@ #![deny(unsafe_op_in_unsafe_fn)] use alloc::boxed::Box; +use alloc::panicking::PanicPayload; use core::any::Any; -use core::panic::PanicPayload; cfg_select! { any( @@ -68,30 +68,25 @@ cfg_select! { } } -unsafe extern "C" { +unsafe extern "Rust" { /// Handler in std called when a panic object is dropped outside of /// `catch_unwind`. #[rustc_std_internal_symbol] - fn __rust_drop_panic() -> !; + safe fn __rust_drop_panic() -> !; /// Handler in std called when a foreign exception is caught. #[rustc_std_internal_symbol] - fn __rust_foreign_exception() -> !; + safe fn __rust_foreign_exception() -> !; } #[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) { - unsafe { Box::into_raw(imp::cleanup(payload)) } +pub unsafe fn __rust_panic_cleanup(payload: *mut u8) -> Box { + unsafe { imp::cleanup(payload) } } // Entry point for raising an exception, just delegates to the platform-specific // implementation. #[rustc_std_internal_symbol] -pub unsafe fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32 { - unsafe { - let payload = Box::from_raw(payload.take_box()); - - imp::panic(payload) - } +pub fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32 { + imp::panic(payload) } diff --git a/library/panic_unwind/src/miri.rs b/library/panic_unwind/src/miri.rs index d6d4af8218d31..9df6d7322a9be 100644 --- a/library/panic_unwind/src/miri.rs +++ b/library/panic_unwind/src/miri.rs @@ -1,6 +1,7 @@ //! Unwinding panics for Miri. use alloc::boxed::Box; +use alloc::panicking::PanicPayload; use core::any::Any; // The type of the payload that the Miri engine propagates through unwinding for us. @@ -12,10 +13,10 @@ unsafe extern "Rust" { fn miri_start_unwind(payload: *mut u8) -> !; } -pub(crate) unsafe fn panic(payload: Box) -> u32 { +pub(crate) fn panic(payload: &mut dyn PanicPayload) -> u32 { // The payload we pass to `miri_start_unwind` will be exactly the argument we get // in `cleanup` below. So we just box it up once, to get something pointer-sized. - let payload_box: Payload = Box::new(payload); + let payload_box: Payload = Box::new(payload.take_box()); unsafe { miri_start_unwind(Box::into_raw(payload_box) as *mut u8) } } diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index ad99fb8c5b5f5..455cb114bdedb 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -47,6 +47,7 @@ #![allow(nonstandard_style)] use alloc::boxed::Box; +use alloc::panicking::PanicPayload; use core::any::Any; use core::ffi::{c_int, c_uint, c_void}; use core::mem::ManuallyDrop; @@ -298,8 +299,8 @@ cfg_select! { } } -pub(crate) unsafe fn panic(data: Box) -> u32 { - unsafe { throw_exception(Some(data)) } +pub(crate) fn panic(data: &mut dyn PanicPayload) -> u32 { + unsafe { throw_exception(Some(data.take_box())) } } unsafe fn throw_exception(data: Option>) -> ! { @@ -370,13 +371,13 @@ unsafe fn throw_exception(data: Option>) -> ! { } pub(crate) unsafe fn cleanup(payload: *mut u8) -> Box { + // A null payload here means that we got here from the catch (...) of + // __rust_try. This happens when a non-Rust foreign exception is caught. + if payload.is_null() { + super::__rust_foreign_exception(); + } + let exception = payload as *mut Exception; unsafe { - // A null payload here means that we got here from the catch (...) of - // __rust_try. This happens when a non-Rust foreign exception is caught. - if payload.is_null() { - super::__rust_foreign_exception(); - } - let exception = payload as *mut Exception; let canary = (&raw const (*exception).canary).read(); if !core::ptr::eq(canary, &raw const TYPE_DESCRIPTOR) { // A foreign Rust exception. diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 7db6e93e8bd02..356b7daa293f4 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -9,7 +9,8 @@ #![deny(unsafe_op_in_unsafe_fn)] -use core::panic::{Location, PanicPayload}; +use alloc::panicking::PanicPayload; +use core::panic::Location; // make sure to use the stderr output configured // by libtest in the real copy of std @@ -53,17 +54,14 @@ pub static EMPTY_PANIC: fn(&'static str) -> ! = // // One day this may look a little less ad-hoc with the compiler helping out to // hook up these functions, but it is not this day! -#[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "Rust" { #[rustc_std_internal_symbol] - fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); -} + fn __rust_panic_cleanup(payload: *mut u8) -> Box; -unsafe extern "Rust" { /// `PanicPayload` lazily performs allocation only when needed (this avoids /// allocations when using the "abort" panic runtime). #[rustc_std_internal_symbol] - fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32; + safe fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32; } /// This function is called by the panic runtime if FFI code catches a Rust @@ -71,7 +69,7 @@ unsafe extern "Rust" { /// with our panic count. #[cfg(not(test))] #[rustc_std_internal_symbol] -extern "C" fn __rust_drop_panic() -> ! { +fn __rust_drop_panic() -> ! { rtabort!("Rust panics must be rethrown"); } @@ -79,7 +77,7 @@ extern "C" fn __rust_drop_panic() -> ! { /// object which does not correspond to a Rust panic. #[cfg(not(test))] #[rustc_std_internal_symbol] -extern "C" fn __rust_foreign_exception() -> ! { +fn __rust_foreign_exception() -> ! { rtabort!("Rust cannot catch foreign exceptions"); } @@ -559,7 +557,7 @@ pub unsafe fn catch_unwind R>(f: F) -> Result) -> ! { } } - unsafe impl PanicPayload for FormatStringPayload<'_> { - fn take_box(&mut self) -> *mut (dyn Any + Send) { + impl PanicPayload for FormatStringPayload<'_> { + fn take_box(&mut self) -> Box { // We do two allocations here, unfortunately. But (a) they're required with the current // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in // begin_panic below). let contents = mem::take(self.fill()); - Box::into_raw(Box::new(contents)) + Box::new(contents) } fn get(&mut self) -> &(dyn Any + Send) { @@ -654,9 +652,9 @@ pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { struct StaticStrPayload(&'static str); - unsafe impl PanicPayload for StaticStrPayload { - fn take_box(&mut self) -> *mut (dyn Any + Send) { - Box::into_raw(Box::new(self.0)) + impl PanicPayload for StaticStrPayload { + fn take_box(&mut self) -> Box { + Box::new(self.0) } fn get(&mut self) -> &(dyn Any + Send) { @@ -716,18 +714,17 @@ pub const fn begin_panic(msg: M) -> ! { inner: Option, } - unsafe impl PanicPayload for Payload { - fn take_box(&mut self) -> *mut (dyn Any + Send) { + impl PanicPayload for Payload { + fn take_box(&mut self) -> Box { // Note that this should be the only allocation performed in this code path. Currently // this means that panic!() on OOM will invoke this code path, but then again we're not // really ready for panic on OOM anyway. If we do start doing this, then we should // propagate this allocation to be performed in the parent of this thread instead of the // thread that's panicking. - let data = match self.inner.take() { + match self.inner.take() { Some(a) => Box::new(a) as Box, None => process::abort(), - }; - Box::into_raw(data) + } } fn get(&mut self) -> &(dyn Any + Send) { @@ -859,9 +856,9 @@ pub fn resume_unwind(payload: Box) -> ! { struct RewrapBox(Box); - unsafe impl PanicPayload for RewrapBox { - fn take_box(&mut self) -> *mut (dyn Any + Send) { - Box::into_raw(mem::replace(&mut self.0, Box::new(()))) + impl PanicPayload for RewrapBox { + fn take_box(&mut self) -> Box { + mem::replace(&mut self.0, Box::new(())) } fn get(&mut self) -> &(dyn Any + Send) { @@ -884,7 +881,7 @@ pub fn resume_unwind(payload: Box) -> ! { #[cfg_attr(not(test), rustc_std_internal_symbol)] #[cfg(not(panic = "immediate-abort"))] fn rust_panic(msg: &mut dyn PanicPayload) -> ! { - let code = unsafe { __rust_start_panic(msg) }; + let code = __rust_start_panic(msg); rtabort!("failed to initiate panic, error {code}") } diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index d37e57e399660..62cac6afee751 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -347,6 +347,16 @@ impl fmt::Debug for Once { } } +#[stable(feature = "once_default", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +const impl Default for Once { + /// Creates a new `Once` value, same as [`Once::new`]. + #[inline] + fn default() -> Once { + Once::new() + } +} + impl OnceState { /// Returns `true` if the associated [`Once`] was poisoned prior to the /// invocation of the closure passed to [`Once::call_once_force()`]. diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index d59631d5d6ca3..212e8ccdc9d60 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -176,6 +176,12 @@ pub fn enable() { } }; + // We must not set the key if we are in a fiber, since deleting that fiber from a thread + // will cause the destructors to run before thread exit. + if is_thread_a_fiber() { + return; + } + // Setting the key's value to non-zero will cause the dtor callback to be called when the thread exits. unsafe { set(key, ptr::without_provenance(1)) }; } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 7a05a962e2ac0..18b0f3263ad59 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -98,17 +98,16 @@ use crate::fmt; /// run on the thread that causes the process to exit. This is because the /// other threads may be forcibly terminated. /// -/// If a thread is [converted into a fiber], destructors will not be run unless -/// the fiber is [converted back into a thread] before the underlying thread exits. +/// TLS destructors may be leaked if a thread exits while [converted into a fiber], +/// or if Rust TLS destructor support is first needed while running in a fiber. /// /// If a process loads a Rust `cdylib`, it must not cause the Rust TLS destructor support -// to be initialized for the first time during process shutdown. +/// to be initialized for the first time during process shutdown. /// /// When dynamically unloading a Rust `cdylib`, pending TLS destructors may run -// during the unload or may be leaked. +/// during the unload or may be leaked. /// /// [converted into a fiber]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-convertthreadtofiber -/// [converted back into a thread]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-convertfibertothread /// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices /// [`with`]: LocalKey::with #[cfg_attr(not(test), rustc_diagnostic_item = "LocalKey")] diff --git a/library/std/tests/thread_local/tests.rs b/library/std/tests/thread_local/tests.rs index 1a25d91e43bc0..df9b99ef0af64 100644 --- a/library/std/tests/thread_local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -416,9 +416,17 @@ fn fiber_does_not_trigger_dtor() { unsafe extern "system" { fn ConvertFiberToThread() -> i32; fn ConvertThreadToFiber(lpParameter: *const c_void) -> *mut c_void; + fn CreateFiber( + dwStackSize: usize, + lpStartAddress: unsafe extern "system" fn(*mut c_void), + lpParameter: *mut c_void, + ) -> *mut c_void; + fn DeleteFiber(lpFiber: *mut c_void); + fn SwitchToFiber(lpFiber: *mut c_void); } thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); + let signal = Signal::default(); let signal2 = signal.clone(); @@ -438,13 +446,49 @@ fn fiber_does_not_trigger_dtor() { // As long as we stop using fibers before thread teardown, everything works as expected. let signal2 = signal.clone(); let t = thread::spawn(move || unsafe { - let mut signal = Some(signal2); - let _ = ConvertThreadToFiber(ptr::null()); - FOO.with(|f| { - *f.get() = Some(NotifyOnDrop(signal.take().unwrap())); - }); - let _ = ConvertFiberToThread(); + struct FiberData { + main: *mut c_void, + signal: Signal, + } + + unsafe extern "system" fn fiber_start(data: *mut c_void) { + let data = unsafe { &mut *data.cast::() }; + + // Set the value while this fiber is current. + // This must NOT arm the FLS cleanup guard for the fiber. + FOO.with(|f| unsafe { + *f.get() = Some(NotifyOnDrop(data.signal.clone())); + }); + + unsafe { + SwitchToFiber(data.main); + } + } + + let main = ConvertThreadToFiber(ptr::null()); + assert!(!main.is_null()); + + let mut data = FiberData { main, signal: signal2.clone() }; + let foo = CreateFiber(0, fiber_start, ptr::from_mut(&mut data).cast()); + assert!(!foo.is_null()); + + // Run `foo`, which sets FOO while `foo` is the current fiber, + // then switches back to main. + SwitchToFiber(foo); + + // Convert main back to a thread before deleting `foo`. + assert_ne!(ConvertFiberToThread(), 0); + + // Deleting `foo` must not trigger dtors like a thread teardown. + DeleteFiber(foo); + assert!(!signal2.is_set()); + + // Arm the guard now from the normal thread. + // `FOO`'s destructor is already registered, so it will run when the thread exits. + thread_local!(static BAR: UnsafeCell> = UnsafeCell::new(None)); + BAR.with(|_| {}); }); + signal.wait(); assert!(signal.is_set()); t.join().unwrap(); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index b475c9c81494c..11308f613b877 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1174,13 +1174,15 @@ impl CommandLineStep for Rustc { { // jemalloc_sys and rustc_public_bridge are not linked into librustc_driver.so, // so we need to distribute them as rlib to be able to use them. - filename.ends_with(".rlib") - } else { - // Distribute the rest of the rustc crates as rmeta files only to reduce - // the tarball sizes by about 50%. The object files are linked into - // librustc_driver.so, so it is still possible to link against them. - filename.ends_with(".rmeta") + if filename.ends_with(".rlib") { + return true; + } } + + // Distribute the rest of the rustc crates as rmeta files only to reduce + // the tarball sizes by about 50%. The object files are linked into + // librustc_driver.so, so it is still possible to link against them. + filename.ends_with(".rmeta") })), ); @@ -1719,7 +1721,7 @@ impl CommandLineStep for GccCodegenBackend { let _guard = builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, host); - let files = run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyRlib); + let files = run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyDylib); GccCodegenBackendOutput { stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()), @@ -1795,7 +1797,7 @@ impl CommandLineStep for CraneliftCodegenBackend { build_compiler, target, ); - let files = run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyRlib); + let files = run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyDylib); write_codegen_backend_stamp(stamp, files, builder.config.dry_run()) } @@ -2617,13 +2619,11 @@ pub fn add_to_sysroot( /// build stamp, and thus be included in dist archives and copied into sysroots by default. /// Note that some kinds of artifacts are copied automatically (e.g. native libraries). pub enum ArtifactKeepMode { - /// Only keep .rlib files, ignore .rmeta files - OnlyRlib, + /// Only keep .so files, ignore .rlib and .rmeta files + OnlyDylib, /// Only keep .rmeta files, ignore .rlib files OnlyRmeta, /// Keep both .rlib and .rmeta files. - /// This is essentially only useful when using `-Zembed-metadata=no`, in which case both the - /// .rlib and .rmeta files are needed for compilation/linking. BothRlibAndRmeta, /// Custom logic for keeping an artifact /// It receives the filename of an artifact, and returns true if it should be kept. @@ -2679,7 +2679,7 @@ pub fn run_cargo( true } else { match &artifact_keep_mode { - ArtifactKeepMode::OnlyRlib => filename.ends_with(".rlib"), + ArtifactKeepMode::OnlyDylib => false, ArtifactKeepMode::OnlyRmeta => filename.ends_with(".rmeta"), ArtifactKeepMode::BothRlibAndRmeta => { filename.ends_with(".rmeta") || filename.ends_with(".rlib") diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 5d188bcd25570..554b4b30d1d6f 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1299,17 +1299,64 @@ impl CommandLineStep for OmpOffload { builder.config.update_submodule("src/llvm-project"); - // OpenMP/Offload builds currently (LLVM-22) still depend on Clang, although there are - // intentions to loosen this requirement over time. FIXME(offload): re-evaluate on LLVM 23 - let clang_dir = if !builder.config.llvm_clang { + let offload_clang_dir = if !builder.config.llvm_clang { // We must have an external clang to use. - assert!(&builder.build.config.llvm_clang_dir.is_some()); - builder.build.config.llvm_clang_dir.clone() + builder.build.config.offload_clang_dir.clone() } else { // No need to specify it, since we use the in-tree clang None }; + // We currently build libompdevice by accident. It includes bitcode for our amd/nvptx + // targets, and only the latest clang compiler can build those. We could stop building those + // to fix this requirement, but we plan on instead building libc-for-gpu very soon, which + // will have the same clang requirement, so we wouldn't save much. There are two ways in + // which we can find a suitable clang. Either a user enabled the llvm.clang, in which case + // we built our own clang based on the llvm submodule first, this always works. The + // alternative is that the user sets the offload_clang_dir path, in which case they hopefully point + // to a suitable clang, otherwise the build will fail. + let clang_bin_dir = if builder.config.llvm_clang { + llvm_output.host_llvm_config.parent().map(Path::to_path_buf) + } else { + // We expect the following (default) structure of the offload_clang_dir: + // /lib/cmake/clang, with a ClangConfig.cmake inside. + // The clang binary is located in /bin, so we go up three levels to find it. + // This hardcodes the ClangConfig.cmake logic, which isn't great, so we filter for the + // binary and error if we can't find it (presumably because LLVM build layout changed?). + offload_clang_dir + .as_deref() + .and_then(|dir| dir.ancestors().nth(3)) + .map(|prefix| prefix.join("bin")) + } + .filter(|dir| dir.join(exe("clang", target)).exists()); + + let Some(clang_bin_dir) = clang_bin_dir else { + eprintln!( + "Building Offload requires a clang binary. Please either set `llvm.offload-clang-dir` or enable `llvm.clang` to build it." + ); + helpers::exit_process(1); + }; + let clang = clang_bin_dir.join(exe("clang", target)); + let clangxx = clang_bin_dir.join(exe("clang++", target)); + + // This was encountered when using gcc 13 to build the llvm submodule on a server, where no + // clang was available. We first built clang along with llvm, and then switched over to use + // the newly built clang to build the offload runtimes. Since we switched compiler, we have + // to make sure that we're still using the same libstdc++ we used before. Without this + // change, clang picked up a system libstdc++ from a different gcc and failed. + let cxx_lib_dir = builder.cxx(target).ok().and_then(|cxx| { + let stdout = command(&cxx) + .arg("-print-file-name=libstdc++.so") + .cached() + .run_capture_stdout(builder) + .stdout(); + let libstdcxx = PathBuf::from(stdout.trim()); + if !libstdcxx.is_absolute() { + return None; + } + libstdcxx.parent().map(Path::to_path_buf) + }); + // In the context of OpenMP offload, some libraries must be compiled for the gpu target, // some for the host, and others for both. We do not perform a full cross-compilation, since // we don't want to run rustc on a GPU. @@ -1321,7 +1368,6 @@ impl CommandLineStep for OmpOffload { // come with it's own set of default include directories, which are based on a potentially older // LLVM. This can cause issues, so we overwrite it to include headers based on our // `src/llvm-project` submodule instead. - // FIXME(offload): With LLVM-22 we hopefully won't need an external clang anymore. let mut cflags = CcFlags::default(); if !builder.config.llvm_clang { let base = builder.llvm_out(target).join("include"); @@ -1336,9 +1382,18 @@ impl CommandLineStep for OmpOffload { if builder.config.llvm_thin_lto && !target.contains("apple") { ldflags.push_all("-fuse-ld=lld"); } + if *omp_target == *target.triple + && let Some(dir) = &cxx_lib_dir + { + ldflags.push_all(format!("-L{}", dir.display())); + } configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]); + cfg.define("CMAKE_C_COMPILER", &clang) + .define("CMAKE_CXX_COMPILER", &clangxx) + .define("CMAKE_ASM_COMPILER", &clang); + // Re-use the same flags as llvm to control the level of debug information // generated for offload. let profile = get_llvm_profile(&builder.config); @@ -1355,7 +1410,7 @@ impl CommandLineStep for OmpOffload { .define("LLVM_ROOT", builder.llvm_out(target).join("build")) .define("LLVM_DIR", llvm_output.cmake_dir()) .define("LLVM_DEFAULT_TARGET_TRIPLE", omp_target); - if let Some(p) = clang_dir.clone() { + if let Some(p) = offload_clang_dir.clone() { cfg.define("Clang_DIR", p); } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index e308801b2548f..4cf84b3c5a3c7 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3067,8 +3067,14 @@ impl BookTest { let stamp = BuildStamp::new(&builder.cargo_out(test_compiler, mode, target)) .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap()); - let output_paths = - run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyRlib); + let output_paths = run_cargo( + builder, + cargo, + vec![], + &stamp, + vec![], + ArtifactKeepMode::BothRlibAndRmeta, + ); let directories = output_paths .into_iter() .filter_map(|p| p.parent().map(ToOwned::to_owned)) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 93ec4a11f9083..2a53500310aa9 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1219,9 +1219,7 @@ impl Builder<'_> { // Enable usage of unstable features cargo.env("RUSTC_BOOTSTRAP", "1"); - if matches!(mode, Mode::Std) { - cargo.arg("-Zembed-metadata=no"); - } + cargo.arg("-Zembed-metadata=no"); if self.config.dump_bootstrap_shims { prepare_shims_dump_dir(self); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d697f4aef8570..d96300e0789aa 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -172,7 +172,7 @@ pub(crate) struct Config { pub llvm_link_jobs: Option, pub llvm_version_suffix: Option, pub llvm_use_linker: Option, - pub llvm_clang_dir: Option, + pub offload_clang_dir: Option, pub llvm_allow_old_toolchain: bool, pub llvm_polly: bool, pub llvm_clang: bool, @@ -644,7 +644,7 @@ impl Config { use_linker: llvm_use_linker, allow_old_toolchain: llvm_allow_old_toolchain, offload: llvm_offload, - offload_clang_dir: llvm_clang_dir, + offload_clang_dir, polly: llvm_polly, clang: llvm_clang, enable_warnings: llvm_enable_warnings, @@ -1497,7 +1497,6 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to llvm_ci_mode, llvm_clang: llvm_clang.unwrap_or(false), llvm_clang_cl, - llvm_clang_dir: llvm_clang_dir.map(PathBuf::from), llvm_cxxflags, llvm_enable_warnings: llvm_enable_warnings.unwrap_or(false), llvm_enzyme: llvm_enzyme.unwrap_or(false), @@ -1530,6 +1529,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to musl_root: rust_musl_root.map(PathBuf::from), ninja_in_file: llvm_ninja.unwrap_or(true), nodejs: build_nodejs.map(PathBuf::from), + offload_clang_dir: offload_clang_dir.map(PathBuf::from), omit_git_hash, on_fail: flags_on_fail, optimized_compiler_builtins, diff --git a/src/bootstrap/src/core/session.rs b/src/bootstrap/src/core/session.rs index 3e6668258c641..92ec80f94964f 100644 --- a/src/bootstrap/src/core/session.rs +++ b/src/bootstrap/src/core/session.rs @@ -1606,7 +1606,11 @@ impl Build { metadata = t!(fs::metadata(&src), format!("target = {}", src.display())); } else { let link = t!(fs::read_link(src)); - t!(self.symlink_file(link, dst)); + if t!(link.metadata()).is_dir() { + t!(symlink_dir(&self.config, &link, dst)); + } else { + t!(self.symlink_file(link, dst)); + } return; } } diff --git a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md index f6be619bc1094..110fd1331bc44 100644 --- a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md +++ b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md @@ -29,7 +29,7 @@ Since these crates are not published on `crates.io` as part of the compiler's no process, rust-analyzer maintains its own publishing pipeline. It uses the [rustc-auto-publish script][rustc-auto-publish] to publish these crates to `crates.io` with the prefix `ra-ap-rustc_*` -(for example: https://crates.io/crates/ra-ap-rustc_next_trait_solver). +(for example: ). rust-analyzer then depends on these re-published crates in its own build. For trait solving specifically, the primary shared crates are `rustc_type_ir` and diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs index de73f99ef3c0e..0c2ed6585cf45 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.rs +++ b/tests/ui/assumptions_on_binders/alias_outlives.rs @@ -35,7 +35,7 @@ where ::Assoc: 'static; const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() -//~^ ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders +//~^ ERROR: higher-ranked lifetime bound could not be satisfied where ::Assoc: 'a; diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr index 9997bdb98601b..1787c1912ae4f 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ b/tests/ui/assumptions_on_binders/alias_outlives.stderr @@ -1,10 +1,8 @@ -error: unsatisfied lifetime constraint from -Zassumptions-on-binders :3 - --> $DIR/alias_outlives.rs:37:1 +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/alias_outlives.rs:37:45 | LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: meoow :c + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.rs b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.rs new file mode 100644 index 0000000000000..5e51e8a2d416f --- /dev/null +++ b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +fn foo<'a>(_a: &'a u32) +where + for<'b> &'b (): 'a, +{ +} + +fn main() { + foo(&10); + //~^ ERROR: higher-ranked lifetime bound could not be satisfied +} diff --git a/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.stderr b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.stderr new file mode 100644 index 0000000000000..cb06bdd933490 --- /dev/null +++ b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.stderr @@ -0,0 +1,8 @@ +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/higher-ranked-outlives-issue-157732.rs:10:5 + | +LL | foo(&10); + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/allow-self-in-const-generics.rs b/tests/ui/const-generics/allow-self-in-const-generics.rs new file mode 100644 index 0000000000000..d81866c80546d --- /dev/null +++ b/tests/ui/const-generics/allow-self-in-const-generics.rs @@ -0,0 +1,36 @@ +// Allow Self in const generics when Self doesn't depends on generics(#149203) +#![feature(min_adt_const_params)] + +//1 +trait MyTrait { + fn foo(); +} + +impl MyTrait for i32 { + fn foo() {} +} + +//2 +impl Wrap { + fn f() {} + //~^ ERROR the type of const parameters must not depend on other generic parameters + +} +struct Wrap(T); + +//3 +type Foo = Bar; + +#[derive(Eq, PartialEq, core::marker::ConstParamTy)] +struct Bar; + +trait Trait { + fn bar(); +} + +impl Trait for Foo { + fn bar() {} + // FIXME: currently the compiler let this pass + // https://github.com/rust-lang/rust/pull/157949#discussion_r3544858218 +} +fn main(){} diff --git a/tests/ui/const-generics/allow-self-in-const-generics.stderr b/tests/ui/const-generics/allow-self-in-const-generics.stderr new file mode 100644 index 0000000000000..26864e9803306 --- /dev/null +++ b/tests/ui/const-generics/allow-self-in-const-generics.stderr @@ -0,0 +1,9 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/allow-self-in-const-generics.rs:15:19 + | +LL | fn f() {} + | ^^^^ the type `Wrap` must not depend on other generic parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/ban-self-when-feature-not-enabled.rs b/tests/ui/const-generics/ban-self-when-feature-not-enabled.rs new file mode 100644 index 0000000000000..49241033b7251 --- /dev/null +++ b/tests/ui/const-generics/ban-self-when-feature-not-enabled.rs @@ -0,0 +1,13 @@ +// Ban Self in const generics when min_adt_const_params and adt_const_params are not enabled +// #149203 +trait MyTrait { + fn foo(); +} + +impl MyTrait for i32 { + fn foo() {} + //~^ ERROR cannot use `Self` in const parameter type + //~| ERROR associated function `foo` has an incompatible generic parameter for trait `MyTrait` +} + +fn main(){} diff --git a/tests/ui/const-generics/ban-self-when-feature-not-enabled.stderr b/tests/ui/const-generics/ban-self-when-feature-not-enabled.stderr new file mode 100644 index 0000000000000..4961d79900375 --- /dev/null +++ b/tests/ui/const-generics/ban-self-when-feature-not-enabled.stderr @@ -0,0 +1,24 @@ +error: cannot use `Self` in const parameter type + --> $DIR/ban-self-when-feature-not-enabled.rs:8:21 + | +LL | fn foo() {} + | ^^^^ + | + = help: add `#![feature(min_adt_const_params)]` to the crate attributes to enable `Self` as a const parameter type + +error[E0053]: associated function `foo` has an incompatible generic parameter for trait `MyTrait` + --> $DIR/ban-self-when-feature-not-enabled.rs:8:12 + | +LL | trait MyTrait { + | ------- +LL | fn foo(); + | ------------ expected const parameter of type `i32` +... +LL | impl MyTrait for i32 { + | -------------------- +LL | fn foo() {} + | ^^^^^^^^^^^^^ found const parameter of type `{type error}` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/const-generics/gca/direct-const-arg-fn-call.rs b/tests/ui/const-generics/gca/direct-const-arg-fn-call.rs new file mode 100644 index 0000000000000..f4a604e2b4700 --- /dev/null +++ b/tests/ui/const-generics/gca/direct-const-arg-fn-call.rs @@ -0,0 +1,84 @@ +//! Regression test for #157152. +//! +//! Under `min_generic_const_args` with `macroless_generic_const_args`, a braced const +//! argument containing an associated-function call (e.g. `FieldName::len()`, as generated +//! by `tracing`'s logging macros as `FieldName<{ FieldName::len(name) }>`) was lowered as +//! a tuple-struct constructor. Lowering the callee's `Self` type `FieldName`, written +//! without its `const N: usize` argument, then produced a spurious "missing generics" +//! error (E0107) plus follow-on errors, which made `tracing` fail to compile in any crate +//! enabling the feature. +//! +//! It should instead report that the call must be wrapped in a `const` block, and +//! the wrapped form must compile. The same holds for any self type that cannot host a +//! tuple-variant constructor (unions, primitives, foreign types), not just structs. +//@ compile-flags: -Znext-solver + +#![feature(min_generic_const_args, macroless_generic_const_args)] +#![feature(generic_const_args)] +#![feature(extern_types)] +#![expect(incomplete_features)] + +struct FieldName([u8; N]); + +impl FieldName<0> { + const fn len() -> usize { + 5 + } + + const fn len_of(name: &str) -> usize { + name.len() + } +} + +// The associated-function call is not a constructor, so the bare braces are +// rejected with a clear diagnostic instead of a spurious "missing generics" error. +fn bad(_: FieldName<{ FieldName::len() }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +// Wrapping the call in a `const` block makes it an anonymous const and compiles. +fn good(_: FieldName<{ const { FieldName::len() } }>) {} + +// The exact shape from #157152: `tracing`'s macros expand a field name to +// `FieldName::len(stringify!(field))`. Same as `bad` but with a string argument, which +// the diagnostic ignores; the self type is still a bare generic struct. +fn bad_tracing(_: FieldName<{ FieldName::len_of("id") }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +fn good_tracing(_: FieldName<{ const { FieldName::len_of("id") } }>) {} + +union Tag { + bytes: [u8; N], +} + +impl Tag<0> { + const fn width() -> usize { + 7 + } +} + +// Unions behave exactly like structs: the call is an associated function, not a +// constructor, so the bare braces are rejected the same way. +fn bad_union(_: Tag<{ Tag::width() }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +fn good_union(_: Tag<{ const { Tag::width() } }>) {} + +// A primitive can't host a constructor either, and has no generics to omit, so it never +// hits the "missing generics" path. No `good_` counterpart: `from_str_radix` returns a +// `Result`, not a `usize`, so the wrapped form can't form a valid const arg. This case +// only checks that the bare form is rejected. +fn bad_prim(_: FieldName<{ u32::from_str_radix("10", 10) }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +unsafe extern "C" { + type Opaque; +} + +// A foreign type has no constructor and no inherent associated functions. The guard +// rejects it from the self type's resolution alone, before the `foo` segment is resolved. +// Without that, downstream resolution gives an opaque "invalid base path" error (plus an +// E0223) rather than this clear one. +fn bad_foreign(_: FieldName<{ Opaque::foo() }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +fn main() {} diff --git a/tests/ui/const-generics/gca/direct-const-arg-fn-call.stderr b/tests/ui/const-generics/gca/direct-const-arg-fn-call.stderr new file mode 100644 index 0000000000000..b73cc02dae915 --- /dev/null +++ b/tests/ui/const-generics/gca/direct-const-arg-fn-call.stderr @@ -0,0 +1,32 @@ +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:35:23 + | +LL | fn bad(_: FieldName<{ FieldName::len() }>) {} + | ^^^^^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:44:31 + | +LL | fn bad_tracing(_: FieldName<{ FieldName::len_of("id") }>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:61:23 + | +LL | fn bad_union(_: Tag<{ Tag::width() }>) {} + | ^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:70:28 + | +LL | fn bad_prim(_: FieldName<{ u32::from_str_radix("10", 10) }>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:81:31 + | +LL | fn bad_foreign(_: FieldName<{ Opaque::foo() }>) {} + | ^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/derives/debug/imperfect-derive-debug-diagnostic.rs b/tests/ui/derives/debug/imperfect-derive-debug-diagnostic.rs new file mode 100644 index 0000000000000..7abaf5944a618 --- /dev/null +++ b/tests/ui/derives/debug/imperfect-derive-debug-diagnostic.rs @@ -0,0 +1,17 @@ +// Regression test for #52560: point at an imperfect derive when its generated +// bound is unsatisfied. + +use std::fmt::Debug; + +#[derive(Debug)] +struct Foo(B::Item); + +trait Bar { + type Item: Debug; +} + +fn print(value: Foo) { + println!("{value:?}"); //~ ERROR `B` doesn't implement `Debug` +} + +fn main() {} diff --git a/tests/ui/derives/debug/imperfect-derive-debug-diagnostic.stderr b/tests/ui/derives/debug/imperfect-derive-debug-diagnostic.stderr new file mode 100644 index 0000000000000..f59023aa2d9f1 --- /dev/null +++ b/tests/ui/derives/debug/imperfect-derive-debug-diagnostic.stderr @@ -0,0 +1,22 @@ +error[E0277]: `B` doesn't implement `Debug` + --> $DIR/imperfect-derive-debug-diagnostic.rs:14:15 + | +LL | println!("{value:?}"); + | ^^^^^^^^^ `B` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | +note: required for `Foo` to implement `Debug` + --> $DIR/imperfect-derive-debug-diagnostic.rs:7:8 + | +LL | #[derive(Debug)] + | ----- in this derive macro expansion +LL | struct Foo(B::Item); + | ^^^ - type parameter would need to implement `Debug` + = help: consider manually implementing `Debug` to avoid undesired bounds +help: consider further restricting type parameter `B` with trait `Debug` + | +LL | fn print(value: Foo) { + | +++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lint/recommend-literal.rs b/tests/ui/lint/recommend-literal.rs index be074c1114532..4eae569df130f 100644 --- a/tests/ui/lint/recommend-literal.rs +++ b/tests/ui/lint/recommend-literal.rs @@ -1,5 +1,3 @@ -//~vv HELP consider importing this struct - type Real = double; //~^ ERROR cannot find type `double` in this scope //~| HELP perhaps you intended to use this type @@ -16,6 +14,7 @@ fn main() { //~^ ERROR: cannot find type `Bool` in this scope [E0425] //~| HELP a builtin type with a similar name exists //~| HELP perhaps you intended to use this type + //~| HELP: there is an enum variant `std::mem::type_info::TypeKind::Bool`; try using the variant's enum } fn z(a: boolean) { diff --git a/tests/ui/lint/recommend-literal.stderr b/tests/ui/lint/recommend-literal.stderr index 01e993df17a98..351760589cd17 100644 --- a/tests/ui/lint/recommend-literal.stderr +++ b/tests/ui/lint/recommend-literal.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find type `double` in this scope - --> $DIR/recommend-literal.rs:3:13 + --> $DIR/recommend-literal.rs:1:13 | LL | type Real = double; | ^^^^^^ @@ -8,7 +8,7 @@ LL | type Real = double; | help: perhaps you intended to use this type: `f64` error[E0425]: cannot find type `long` in this scope - --> $DIR/recommend-literal.rs:9:12 + --> $DIR/recommend-literal.rs:7:12 | LL | let y: long = 74802374902374923; | ^^^^ @@ -17,7 +17,7 @@ LL | let y: long = 74802374902374923; | help: perhaps you intended to use this type: `i64` error[E0425]: cannot find type `Boolean` in this scope - --> $DIR/recommend-literal.rs:12:13 + --> $DIR/recommend-literal.rs:10:13 | LL | let v1: Boolean = true; | ^^^^^^^ @@ -26,11 +26,16 @@ LL | let v1: Boolean = true; | help: perhaps you intended to use this type: `bool` error[E0425]: cannot find type `Bool` in this scope - --> $DIR/recommend-literal.rs:15:13 + --> $DIR/recommend-literal.rs:13:13 | LL | let v2: Bool = true; | ^^^^ | +help: there is an enum variant `std::mem::type_info::TypeKind::Bool`; try using the variant's enum + | +LL - let v2: Bool = true; +LL + let v2: std::mem::type_info::TypeKind = true; + | help: a builtin type with a similar name exists | LL - let v2: Bool = true; @@ -41,13 +46,9 @@ help: perhaps you intended to use this type LL - let v2: Bool = true; LL + let v2: bool = true; | -help: consider importing this struct - | -LL + use std::mem::type_info::Bool; - | error[E0425]: cannot find type `boolean` in this scope - --> $DIR/recommend-literal.rs:21:9 + --> $DIR/recommend-literal.rs:20:9 | LL | fn z(a: boolean) { | ^^^^^^^ @@ -56,7 +57,7 @@ LL | fn z(a: boolean) { | help: perhaps you intended to use this type: `bool` error[E0425]: cannot find type `byte` in this scope - --> $DIR/recommend-literal.rs:26:11 + --> $DIR/recommend-literal.rs:25:11 | LL | fn a() -> byte { | ^^^^ @@ -65,7 +66,7 @@ LL | fn a() -> byte { | help: perhaps you intended to use this type: `u8` error[E0425]: cannot find type `float` in this scope - --> $DIR/recommend-literal.rs:33:12 + --> $DIR/recommend-literal.rs:32:12 | LL | width: float, | ^^^^^ @@ -74,7 +75,7 @@ LL | width: float, | help: perhaps you intended to use this type: `f32` error[E0425]: cannot find type `int` in this scope - --> $DIR/recommend-literal.rs:36:19 + --> $DIR/recommend-literal.rs:35:19 | LL | depth: Option, | ^^^ not found in this scope @@ -90,7 +91,7 @@ LL | struct Data { | +++++ error[E0425]: cannot find type `short` in this scope - --> $DIR/recommend-literal.rs:42:16 + --> $DIR/recommend-literal.rs:41:16 | LL | impl Stuff for short {} | ^^^^^