diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs index ad4e006c21ae8..17482cc0cbd22 100644 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs @@ -284,7 +284,7 @@ where return; } - match ty.kind() { + match *ty.kind() { ty::Closure(_, args) => { // Skip lifetime parameters of the enclosing item(s) @@ -316,10 +316,12 @@ where args.as_coroutine().resume_ty().visit_with(self); } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - // Skip lifetime parameters that are not captures. - let variances = self.tcx.variances_of(*def_id); - + ty::Alias(kind, ty::AliasTy { def_id, args, .. }) + if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) => + { + // Skip lifetime parameters that are not captured, since they do + // not need member constraints registered for them; we'll erase + // them (and hopefully in the future replace them with placeholders). for (v, s) in std::iter::zip(variances, args.iter()) { if *v != ty::Bivariant { s.visit_with(self); diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 5c1c38aeb9534..3e48e8d15c3cd 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -347,9 +347,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // yield an object-type (e.g., `&Object` or `Box` // etc). - // FIXME: this feels, like, super dubious - self.fcx - .autoderef(self.span, self_ty) + let mut autoderef = self.fcx.autoderef(self.span, self_ty); + + // We don't need to gate this behind arbitrary self types + // per se, but it does make things a bit more gated. + if self.tcx.features().arbitrary_self_types() + || self.tcx.features().arbitrary_self_types_pointers() + { + autoderef = autoderef.use_receiver_trait(); + } + + autoderef .include_raw_pointers() .find_map(|(ty, _)| match ty.kind() { ty::Dynamic(data, ..) => Some(closure( diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index c02ab98b2baeb..379410641fe5b 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -48,7 +48,7 @@ where return ty.super_visit_with(self); } - match ty.kind() { + match *ty.kind() { // We can prove that an alias is live two ways: // 1. All the components are live. // @@ -95,11 +95,9 @@ where assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS)); r.visit_with(self); } else { - // Skip lifetime parameters that are not captures. - let variances = match kind { - ty::Opaque => Some(self.tcx.variances_of(*def_id)), - _ => None, - }; + // Skip lifetime parameters that are not captured, since they do + // not need to be live. + let variances = tcx.opt_alias_variances(kind, def_id); for (idx, s) in args.iter().enumerate() { if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 84e51b18dc5b6..86a47426049a6 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -392,13 +392,13 @@ where // the problem is to add `T: 'r`, which isn't true. So, if there are no // inference variables, we use a verify constraint instead of adding // edges, which winds up enforcing the same condition. - let is_opaque = alias_ty.kind(self.tcx) == ty::Opaque; + let kind = alias_ty.kind(self.tcx); if approx_env_bounds.is_empty() && trait_bounds.is_empty() - && (alias_ty.has_infer_regions() || is_opaque) + && (alias_ty.has_infer_regions() || kind == ty::Opaque) { debug!("no declared bounds"); - let opt_variances = is_opaque.then(|| self.tcx.variances_of(alias_ty.def_id)); + let opt_variances = self.tcx.opt_alias_variances(kind, alias_ty.def_id); self.args_must_outlive(alias_ty.args, origin, region, opt_variances); return; } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index d68f3639176f9..c14c288c6e4e6 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -102,12 +102,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> { - let alias_ty_as_ty = alias_ty.to_ty(self.tcx); - // Search the env for where clauses like `P: 'a`. let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| { if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() - && ty == alias_ty_as_ty + && let ty::Alias(_, alias_ty_from_bound) = *ty.kind() + && alias_ty_from_bound == alias_ty { // Micro-optimize if this is an exact match (this // occurs often when there are no region variables @@ -127,7 +126,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // see the extensive comment in projection_must_outlive let recursive_bound = { let mut components = smallvec![]; - compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components); + let kind = alias_ty.kind(self.tcx); + compute_alias_components_recursive(self.tcx, kind, alias_ty, &mut components); self.bound_from_components(&components) }; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index eda9c4e03fec2..e5adcdb244f6d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -827,7 +827,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { if tcx.sess.opts.unstable_opts.input_stats { rustc_passes::input_stats::print_hir_stats(tcx); } - #[cfg(debug_assertions)] + // When using rustdoc's "jump to def" feature, it enters this code and `check_crate` + // is not defined. So we need to cfg it out. + #[cfg(all(not(doc), debug_assertions))] rustc_passes::hir_id_validator::check_crate(tcx); let sess = tcx.sess; sess.time("misc_checking_1", || { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 55c6a122d35d5..480d97e377a40 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -390,9 +390,6 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent -lint_improper_ctypes_pat_help = consider using the base type instead - -lint_improper_ctypes_pat_reason = pattern types have no C equivalent lint_improper_ctypes_slice_help = consider using a raw pointer instead lint_improper_ctypes_slice_reason = slices have no C equivalent diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 45b188205d228..636779fe9b4cf 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -241,10 +241,7 @@ fn structurally_same_type_impl<'tcx>( if let ty::Adt(def, args) = *ty.kind() { let is_transparent = def.repr().transparent(); let is_non_null = types::nonnull_optimization_guaranteed(tcx, def); - debug!( - "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}", - ty, is_transparent, is_non_null - ); + debug!(?ty, is_transparent, is_non_null); if is_transparent && !is_non_null { debug_assert_eq!(def.variants().len(), 1); let v = &def.variant(FIRST_VARIANT); @@ -378,14 +375,14 @@ fn structurally_same_type_impl<'tcx>( // An Adt and a primitive or pointer type. This can be FFI-safe if non-null // enum layout optimisation is being applied. - (Adt(..), _) if is_primitive_or_pointer(b) => { + (Adt(..) | Pat(..), _) if is_primitive_or_pointer(b) => { if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) { a_inner == b } else { false } } - (_, Adt(..)) if is_primitive_or_pointer(a) => { + (_, Adt(..) | Pat(..)) if is_primitive_or_pointer(a) => { if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) { b_inner == a } else { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index fd6c17735c5a2..f62a523d92450 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -33,6 +33,7 @@ #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(trait_upcasting)] +#![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 80007f34db356..a68140fa12f3a 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -870,6 +870,37 @@ fn ty_is_known_nonnull<'tcx>( .filter_map(|variant| transparent_newtype_field(tcx, variant)) .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode)) } + ty::Pat(base, pat) => { + ty_is_known_nonnull(tcx, typing_env, *base, mode) + || Option::unwrap_or_default( + try { + match **pat { + ty::PatternKind::Range { start, end, include_end } => { + match (start, end) { + (Some(start), None) => { + start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0 + } + (Some(start), Some(end)) => { + let start = + start.try_to_value()?.try_to_bits(tcx, typing_env)?; + let end = + end.try_to_value()?.try_to_bits(tcx, typing_env)?; + + if include_end { + // This also works for negative numbers, as we just need + // to ensure we aren't wrapping over zero. + start > 0 && end >= start + } else { + start > 0 && end > start + } + } + _ => false, + } + } + } + }, + ) + } _ => false, } } @@ -900,9 +931,8 @@ fn get_nullable_type<'tcx>( }; return get_nullable_type(tcx, typing_env, inner_field_ty); } - ty::Int(ty) => Ty::new_int(tcx, ty), - ty::Uint(ty) => Ty::new_uint(tcx, ty), - ty::RawPtr(ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), + ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base), + ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => ty, // As these types are always non-null, the nullable equivalent of // `Option` of these types are their raw pointer counterparts. ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), @@ -958,63 +988,69 @@ pub(crate) fn repr_nullable_ptr<'tcx>( ckind: CItemKind, ) -> Option> { debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty); - if let ty::Adt(ty_def, args) = ty.kind() { - let field_ty = match &ty_def.variants().raw[..] { - [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) { - ([], [field]) | ([field], []) => field.ty(tcx, args), - ([field1], [field2]) => { - let ty1 = field1.ty(tcx, args); - let ty2 = field2.ty(tcx, args); - - if is_niche_optimization_candidate(tcx, typing_env, ty1) { - ty2 - } else if is_niche_optimization_candidate(tcx, typing_env, ty2) { - ty1 - } else { - return None; + match ty.kind() { + ty::Adt(ty_def, args) => { + let field_ty = match &ty_def.variants().raw[..] { + [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) { + ([], [field]) | ([field], []) => field.ty(tcx, args), + ([field1], [field2]) => { + let ty1 = field1.ty(tcx, args); + let ty2 = field2.ty(tcx, args); + + if is_niche_optimization_candidate(tcx, typing_env, ty1) { + ty2 + } else if is_niche_optimization_candidate(tcx, typing_env, ty2) { + ty1 + } else { + return None; + } } - } + _ => return None, + }, _ => return None, - }, - _ => return None, - }; + }; - if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) { - return None; - } + if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) { + return None; + } - // At this point, the field's type is known to be nonnull and the parent enum is Option-like. - // If the computed size for the field and the enum are different, the nonnull optimization isn't - // being applied (and we've got a problem somewhere). - let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok(); - if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) { - bug!("improper_ctypes: Option nonnull optimization not applied?"); - } + // At this point, the field's type is known to be nonnull and the parent enum is Option-like. + // If the computed size for the field and the enum are different, the nonnull optimization isn't + // being applied (and we've got a problem somewhere). + let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok(); + if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) { + bug!("improper_ctypes: Option nonnull optimization not applied?"); + } - // Return the nullable type this Option-like enum can be safely represented with. - let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty)); - if field_ty_layout.is_err() && !field_ty.has_non_region_param() { - bug!("should be able to compute the layout of non-polymorphic type"); - } + // Return the nullable type this Option-like enum can be safely represented with. + let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty)); + if field_ty_layout.is_err() && !field_ty.has_non_region_param() { + bug!("should be able to compute the layout of non-polymorphic type"); + } - let field_ty_abi = &field_ty_layout.ok()?.backend_repr; - if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi { - match field_ty_scalar.valid_range(&tcx) { - WrappingRange { start: 0, end } - if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 => - { - return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); - } - WrappingRange { start: 1, .. } => { - return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); - } - WrappingRange { start, end } => { - unreachable!("Unhandled start and end range: ({}, {})", start, end) - } - }; + let field_ty_abi = &field_ty_layout.ok()?.backend_repr; + if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi { + match field_ty_scalar.valid_range(&tcx) { + WrappingRange { start: 0, end } + if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 => + { + return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); + } + WrappingRange { start: 1, .. } => { + return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); + } + WrappingRange { start, end } => { + unreachable!("Unhandled start and end range: ({}, {})", start, end) + } + }; + } + None } + ty::Pat(base, pat) => match **pat { + ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, *base), + }, + _ => None, } - None } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { @@ -1249,11 +1285,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_char_help), }, - ty::Pat(..) => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_pat_reason, - help: Some(fluent::lint_improper_ctypes_pat_help), - }, + // It's just extra invariants on the type that you need to uphold, + // but only the base type is relevant for being representable in FFI. + ty::Pat(base, ..) => self.check_type_for_ffi(acc, base), ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c6fc5f98f56f2..82b4fe193f014 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -194,6 +194,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.variances_of(def_id) } + fn opt_alias_variances( + self, + kind: impl Into, + def_id: DefId, + ) -> Option<&'tcx [ty::Variance]> { + self.opt_alias_variances(kind, def_id) + } + fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { self.type_of(def_id) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 6fd0fb9a0a4f0..398ed57faeb4e 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -948,6 +948,29 @@ impl<'tcx> TyCtxt<'tcx> { ty } + + // Computes the variances for an alias (opaque or RPITIT) that represent + // its (un)captured regions. + pub fn opt_alias_variances( + self, + kind: impl Into, + def_id: DefId, + ) -> Option<&'tcx [ty::Variance]> { + match kind.into() { + ty::AliasTermKind::ProjectionTy => { + if self.is_impl_trait_in_trait(def_id) { + Some(self.variances_of(def_id)) + } else { + None + } + } + ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)), + ty::AliasTermKind::InherentTy + | ty::AliasTermKind::WeakTy + | ty::AliasTermKind::UnevaluatedConst + | ty::AliasTermKind::ProjectionConst => None, + } + } } struct OpaqueTypeExpander<'tcx> { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index a6c72da0c564a..aae2d2e96b96f 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -141,6 +141,12 @@ pub trait Interner: type VariancesOf: Copy + Debug + SliceLike; fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf; + fn opt_alias_variances( + self, + kind: impl Into, + def_id: Self::DefId, + ) -> Option; + fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; type AdtDef: AdtDef; diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index c26e211a79402..b09a378d34143 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -148,7 +148,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // trait-ref. Therefore, if we see any higher-ranked regions, // we simply fallback to the most restrictive rule, which // requires that `Pi: 'a` for all `i`. - ty::Alias(_, alias_ty) => { + ty::Alias(kind, alias_ty) => { if !alias_ty.has_escaping_bound_vars() { // best case: no escaping regions, so push the // projection and skip the subtree (thus generating no @@ -162,7 +162,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // OutlivesProjectionComponents. Continue walking // through and constrain Pi. let mut subcomponents = smallvec![]; - compute_alias_components_recursive(self.cx, ty, &mut subcomponents); + compute_alias_components_recursive(self.cx, kind, alias_ty, &mut subcomponents); self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect())); } } @@ -217,21 +217,17 @@ impl TypeVisitor for OutlivesCollector<'_, I> { } } -/// Collect [Component]s for *all* the args of `parent`. +/// Collect [Component]s for *all* the args of `alias_ty`. /// -/// This should not be used to get the components of `parent` itself. +/// This should not be used to get the components of `alias_ty` itself. /// Use [push_outlives_components] instead. pub fn compute_alias_components_recursive( cx: I, - alias_ty: I::Ty, + kind: ty::AliasTyKind, + alias_ty: ty::AliasTy, out: &mut SmallVec<[Component; 4]>, ) { - let ty::Alias(kind, alias_ty) = alias_ty.kind() else { - unreachable!("can only call `compute_alias_components_recursive` on an alias type") - }; - - let opt_variances = - if kind == ty::Opaque { Some(cx.variances_of(alias_ty.def_id)) } else { None }; + let opt_variances = cx.opt_alias_variances(kind, alias_ty.def_id); let mut visitor = OutlivesCollector { cx, out, visited: Default::default() }; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 9a80f97e274d2..f75b292760027 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -469,6 +469,17 @@ impl AliasTermKind { } } +impl From for AliasTermKind { + fn from(value: ty::AliasTyKind) -> Self { + match value { + ty::Projection => AliasTermKind::ProjectionTy, + ty::Opaque => AliasTermKind::OpaqueTy, + ty::Weak => AliasTermKind::WeakTy, + ty::Inherent => AliasTermKind::InherentTy, + } + } +} + /// Represents the unprojected term of a projection goal. /// /// * For a projection, this would be `>::N<...>`. diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 5b696ee5ed400..d065384b58e23 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -236,28 +236,14 @@ impl Relate for ty::AliasTy { ExpectedFound::new(a, b) })) } else { - let args = match a.kind(relation.cx()) { - ty::Opaque => relate_args_with_variances( - relation, - a.def_id, - relation.cx().variances_of(a.def_id), - a.args, - b.args, + let cx = relation.cx(); + let args = if let Some(variances) = cx.opt_alias_variances(a.kind(cx), a.def_id) { + relate_args_with_variances( + relation, a.def_id, variances, a.args, b.args, false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )?, - ty::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => { - relate_args_with_variances( - relation, - a.def_id, - relation.cx().variances_of(a.def_id), - a.args, - b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )? - } - ty::Projection | ty::Weak | ty::Inherent => { - relate_args_invariantly(relation, a.args, b.args)? - } + )? + } else { + relate_args_invariantly(relation, a.args, b.args)? }; Ok(ty::AliasTy::new_from_args(relation.cx(), a.def_id, args)) } diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index d318d457f3569..a5c3983cd89ec 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -5,14 +5,15 @@ use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToS use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); +const BASE_PORT: u16 = 19600; pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) } @@ -30,31 +31,3 @@ pub fn tsa(a: A) -> Result, String> { Err(e) => Err(e.to_string()), } } - -// The bots run multiple builds at the same time, and these builds -// all want to use ports. This function figures out which workspace -// it is running in and assigns a port range based on it. -fn base_port() -> u16 { - let cwd = if cfg!(target_env = "sgx") { - String::from("sgx") - } else { - env::current_dir().unwrap().into_os_string().into_string().unwrap() - }; - let dirs = [ - "32-opt", - "32-nopt", - "musl-64-opt", - "cross-opt", - "64-opt", - "64-nopt", - "64-opt-vg", - "64-debug-opt", - "all-opt", - "snap3", - "dist", - "sgx", - ]; - dirs.iter().enumerate().find(|&(_, dir)| cwd.contains(dir)).map(|p| p.0).unwrap_or(0) as u16 - * 1000 - + 19600 -} diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 1323aba38b7cc..9b87259abefc1 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -391,154 +391,6 @@ fn test_interior_nul_in_env_value_is_error() { } } -/// Tests that process creation flags work by debugging a process. -/// Other creation flags make it hard or impossible to detect -/// behavioral changes in the process. -#[test] -#[cfg(windows)] -fn test_creation_flags() { - use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, INFINITE}; - #[repr(C)] - struct DEBUG_EVENT { - pub event_code: u32, - pub process_id: u32, - pub thread_id: u32, - // This is a union in the real struct, but we don't - // need this data for the purposes of this test. - pub _junk: [u8; 164], - } - - extern "system" { - fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL; - fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL; - } - - const DEBUG_PROCESS: u32 = 1; - const EXIT_PROCESS_DEBUG_EVENT: u32 = 5; - const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001; - - let mut child = Command::new("cmd") - .creation_flags(DEBUG_PROCESS) - .stdin(Stdio::piped()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn() - .unwrap(); - child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); - let mut events = 0; - let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; - loop { - if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { - panic!("WaitForDebugEvent failed!"); - } - events += 1; - - if event.event_code == EXIT_PROCESS_DEBUG_EVENT { - break; - } - - if unsafe { - ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED) - } == 0 - { - panic!("ContinueDebugEvent failed!"); - } - } - assert!(events > 0); -} - -/// Tests proc thread attributes by spawning a process with a custom parent process, -/// then comparing the parent process ID with the expected parent process ID. -#[test] -#[cfg(windows)] -fn test_proc_thread_attributes() { - use crate::mem; - use crate::os::windows::io::AsRawHandle; - use crate::os::windows::process::{CommandExt, ProcThreadAttributeList}; - use crate::sys::c::{BOOL, CloseHandle, HANDLE}; - use crate::sys::cvt; - - #[repr(C)] - #[allow(non_snake_case)] - struct PROCESSENTRY32W { - dwSize: u32, - cntUsage: u32, - th32ProcessID: u32, - th32DefaultHeapID: usize, - th32ModuleID: u32, - cntThreads: u32, - th32ParentProcessID: u32, - pcPriClassBase: i32, - dwFlags: u32, - szExeFile: [u16; 260], - } - - extern "system" { - fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE; - fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - } - - const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; - const TH32CS_SNAPPROCESS: u32 = 0x00000002; - - struct ProcessDropGuard(crate::process::Child); - - impl Drop for ProcessDropGuard { - fn drop(&mut self) { - let _ = self.0.kill(); - } - } - - let mut parent = Command::new("cmd"); - parent.stdout(Stdio::null()).stderr(Stdio::null()); - - let parent = ProcessDropGuard(parent.spawn().unwrap()); - - let mut child_cmd = Command::new("cmd"); - child_cmd.stdout(Stdio::null()).stderr(Stdio::null()); - - let parent_process_handle = parent.0.as_raw_handle(); - - let mut attribute_list = ProcThreadAttributeList::build() - .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle) - .finish() - .unwrap(); - - let child = ProcessDropGuard(child_cmd.spawn_with_attributes(&mut attribute_list).unwrap()); - - let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; - - let mut process_entry = PROCESSENTRY32W { - dwSize: mem::size_of::() as u32, - cntUsage: 0, - th32ProcessID: 0, - th32DefaultHeapID: 0, - th32ModuleID: 0, - cntThreads: 0, - th32ParentProcessID: 0, - pcPriClassBase: 0, - dwFlags: 0, - szExeFile: [0; 260], - }; - - unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - - loop { - if child.0.id() == process_entry.th32ProcessID { - break; - } - unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - } - - unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap(); - - assert_eq!(parent.0.id(), process_entry.th32ParentProcessID); - - drop(child) -} - #[test] fn test_command_implements_send_sync() { fn take_send_sync_type(_: T) {} diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs new file mode 100644 index 0000000000000..87e6106468fdb --- /dev/null +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -0,0 +1,369 @@ +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::sys::unsupported; +use crate::time::Duration; + +pub struct TcpStream(!); + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn write(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result> { + self.0 + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn nodelay(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct TcpListener(!); + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn only_v6(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + self.0 + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct LookupHost(!); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } +} + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + #[allow(dead_code)] + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + #[allow(dead_code)] + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } +} diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs index 5aa197fbc0df3..646679a1cc8b9 100644 --- a/library/std/src/sys/net/mod.rs +++ b/library/std/src/sys/net/mod.rs @@ -25,6 +25,11 @@ cfg_if::cfg_if! { mod xous; pub use xous::*; } + } else if #[cfg(target_os = "uefi")] { + mod connection { + mod uefi; + pub use uefi::*; + } } else { mod connection { mod unsupported; diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 0eb4080c053a7..dedcc139ae198 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -830,7 +830,8 @@ impl Step for Rustc { cargo.rustdocflag("--show-type-layout"); // FIXME: `--generate-link-to-definition` tries to resolve cfged out code // see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222 - // cargo.rustdocflag("--generate-link-to-definition"); + // If there is any bug, please comment out the next line. + cargo.rustdocflag("--generate-link-to-definition"); compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates); cargo.arg("-Zskip-rustdoc-fingerprint"); diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs new file mode 100644 index 0000000000000..6f7e1a0eaefae --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs @@ -0,0 +1,19 @@ +//@ check-pass + +// Ensure that we skip uncaptured args from RPITITs when comptuing outlives. + +#![feature(precise_capturing_in_traits)] + +struct Invariant(*mut T); + +trait Foo { + fn hello<'s: 's>(&'s self) -> Invariant>; +} + +fn outlives_static(_: impl Sized + 'static) {} + +fn hello<'s, T: Foo + 'static>(x: &'s T) { + outlives_static(x.hello()); +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs new file mode 100644 index 0000000000000..94d81d766f725 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs @@ -0,0 +1,18 @@ +//@ check-pass + +// Ensure that we skip uncaptured args from RPITITs when collecting the regions +// to enforce member constraints in opaque type inference. + +#![feature(precise_capturing_in_traits)] + +struct Invariant(*mut T); + +trait Foo { + fn hello<'s: 's>(&'s self) -> Invariant>; +} + +fn hello<'s, T: Foo>(x: &'s T) -> Invariant { + x.hello() +} + +fn main() {} diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs index 9bbb20246df9d..e4477c9620221 100644 --- a/tests/ui/lint/clashing-extern-fn.rs +++ b/tests/ui/lint/clashing-extern-fn.rs @@ -1,7 +1,7 @@ //@ check-pass //@ aux-build:external_extern_fn.rs #![crate_type = "lib"] - +#![feature(pattern_type_macro, pattern_types)] mod redeclared_different_signature { mod a { extern "C" { @@ -490,3 +490,33 @@ mod hidden_niche { } } } + +mod pattern_types { + mod a { + use std::pat::pattern_type; + #[repr(transparent)] + struct NonZeroUsize(pattern_type!(usize is 1..)); + extern "C" { + fn pt_non_zero_usize() -> pattern_type!(usize is 1..); + fn pt_non_zero_usize_opt() -> Option; + fn pt_non_zero_usize_opt_full_range() -> Option; + //~^ WARN not FFI-safe + fn pt_non_null_ptr() -> pattern_type!(usize is 1..); + fn pt_non_zero_usize_wrapper() -> NonZeroUsize; + fn pt_non_zero_usize_wrapper_opt() -> Option; + } + } + mod b { + extern "C" { + // If there's a clash in either of these cases you're either gaining an incorrect + // invariant that the value is non-zero, or you're missing out on that invariant. Both + // cases are warning for, from both a caller-convenience and optimisation perspective. + fn pt_non_zero_usize() -> usize; + fn pt_non_zero_usize_opt() -> usize; + fn pt_non_null_ptr() -> *const (); + //~^ WARN `pt_non_null_ptr` redeclared with a different signature + fn pt_non_zero_usize_wrapper() -> usize; + fn pt_non_zero_usize_wrapper_opt() -> usize; + } + } +} diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 48dd1adbc1fa2..118b18b224c05 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -17,6 +17,15 @@ LL | fn hidden_niche_unsafe_cell() -> Option`, which is not FFI-safe + --> $DIR/clashing-extern-fn.rs:502:54 + | +LL | fn pt_non_zero_usize_opt_full_range() -> Option; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + = note: enum has no representation hint + warning: `clash` redeclared with a different signature --> $DIR/clashing-extern-fn.rs:13:13 | @@ -258,5 +267,17 @@ LL | fn hidden_niche_unsafe_cell() -> Option usize` found `unsafe extern "C" fn() -> Option>>` -warning: 22 warnings emitted +warning: `pt_non_null_ptr` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:516:13 + | +LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); + | ---------------------------------------------------- `pt_non_null_ptr` previously declared here +... +LL | fn pt_non_null_ptr() -> *const (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> (usize) is 1..=` + found `unsafe extern "C" fn() -> *const ()` + +warning: 24 warnings emitted diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index 19af1de95760b..0d19d5b534713 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -94,6 +94,7 @@ extern "C" { fn option_transparent_union(x: Option>>); //~^ ERROR `extern` block uses type fn option_repr_rust(x: Option>>); //~ ERROR `extern` block uses type + fn option_u8(x: Option); //~ ERROR `extern` block uses type fn result_ref_t(x: Result<&'static u8, ()>); fn result_fn_t(x: Result); diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index 8e92e7e694627..a491bd1960563 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -79,8 +79,17 @@ LL | fn option_repr_rust(x: Option>>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint +error: `extern` block uses type `Option`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:97:21 + | +LL | fn option_u8(x: Option); + | ^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + = note: enum has no representation hint + error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:106:33 + --> $DIR/lint-ctypes-enum.rs:107:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -88,7 +97,7 @@ LL | fn result_nonzero_u128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:113:33 + --> $DIR/lint-ctypes-enum.rs:114:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -96,7 +105,7 @@ LL | fn result_nonzero_i128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:118:38 + --> $DIR/lint-ctypes-enum.rs:119:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -105,7 +114,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:120:30 + --> $DIR/lint-ctypes-enum.rs:121:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -114,7 +123,7 @@ LL | fn result_repr_rust_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:124:51 + --> $DIR/lint-ctypes-enum.rs:125:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +132,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:126:53 + --> $DIR/lint-ctypes-enum.rs:127:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +141,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result = note: enum has no representation hint error: `extern` block uses type `Result, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:128:51 + --> $DIR/lint-ctypes-enum.rs:129:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -141,7 +150,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:131:49 + --> $DIR/lint-ctypes-enum.rs:132:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -150,7 +159,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result, Fi = note: enum has no representation hint error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:133:30 + --> $DIR/lint-ctypes-enum.rs:134:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +168,7 @@ LL | fn result_cascading_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:144:33 + --> $DIR/lint-ctypes-enum.rs:145:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -167,7 +176,7 @@ LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:151:33 + --> $DIR/lint-ctypes-enum.rs:152:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -175,7 +184,7 @@ LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:156:38 + --> $DIR/lint-ctypes-enum.rs:157:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -184,7 +193,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:158:30 + --> $DIR/lint-ctypes-enum.rs:159:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -193,7 +202,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust>>); = note: enum has no representation hint error: `extern` block uses type `Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:162:51 + --> $DIR/lint-ctypes-enum.rs:163:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +211,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:164:53 + --> $DIR/lint-ctypes-enum.rs:165:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -211,7 +220,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:166:51 + --> $DIR/lint-ctypes-enum.rs:167:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -220,7 +229,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:169:49 + --> $DIR/lint-ctypes-enum.rs:170:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -229,7 +238,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:171:30 + --> $DIR/lint-ctypes-enum.rs:172:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -238,7 +247,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:173:27 + --> $DIR/lint-ctypes-enum.rs:174:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe @@ -246,5 +255,5 @@ LL | fn result_unit_t_e(x: Result<(), ()>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/process/win-creation-flags.rs b/tests/ui/process/win-creation-flags.rs new file mode 100644 index 0000000000000..0c7c6883d68e3 --- /dev/null +++ b/tests/ui/process/win-creation-flags.rs @@ -0,0 +1,51 @@ +// Test that windows `creation_flags` extension to `Command` works. + +//@ run-pass +//@ only-windows +//@ needs-subprocess + +use std::env; +use std::os::windows::process::CommandExt; +use std::process::{Command, exit}; + +fn main() { + if env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let exe = env::current_exe().unwrap(); + + // Use the DETACH_PROCESS to create a subprocess that isn't attached to the console. + // The subprocess's exit status will be 0 if it's detached. + let status = Command::new(&exe) + .arg("--child") + .creation_flags(DETACH_PROCESS) + .spawn() + .unwrap() + .wait() + .unwrap(); + assert_eq!(status.code(), Some(0)); + + // Try without DETACH_PROCESS to ensure this test works. + let status = Command::new(&exe).arg("--child").spawn().unwrap().wait().unwrap(); + assert_eq!(status.code(), Some(1)); +} + +// exits with 1 if the console is attached or 0 otherwise +fn child() { + // Get the attached console's code page. + // This will fail (return 0) if no console is attached. + let has_console = GetConsoleCP() != 0; + exit(has_console as i32); +} + +// Windows API definitions. +const DETACH_PROCESS: u32 = 0x00000008; +#[link(name = "kernel32")] +unsafe extern "system" { + safe fn GetConsoleCP() -> u32; +} diff --git a/tests/ui/process/win-proc-thread-attributes.rs b/tests/ui/process/win-proc-thread-attributes.rs new file mode 100644 index 0000000000000..91bb0b17e9539 --- /dev/null +++ b/tests/ui/process/win-proc-thread-attributes.rs @@ -0,0 +1,118 @@ +// Tests proc thread attributes by spawning a process with a custom parent process, +// then comparing the parent process ID with the expected parent process ID. + +//@ run-pass +//@ only-windows +//@ needs-subprocess +//@ edition: 2021 + +#![feature(windows_process_extensions_raw_attribute)] + +use std::os::windows::io::AsRawHandle; +use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; +use std::process::{Child, Command}; +use std::{env, mem, ptr, thread, time}; + +// Make a best effort to ensure child processes always exit. +struct ProcessDropGuard(Child); +impl Drop for ProcessDropGuard { + fn drop(&mut self) { + let _ = self.0.kill(); + } +} + +fn main() { + if env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let exe = env::current_exe().unwrap(); + + let (fake_parent_id, child_parent_id) = { + // Create a process to be our fake parent process. + let fake_parent = Command::new(&exe).arg("--child").spawn().unwrap(); + let fake_parent = ProcessDropGuard(fake_parent); + let parent_handle = fake_parent.0.as_raw_handle(); + + // Create another process with the parent process set to the fake. + let mut attribute_list = ProcThreadAttributeList::build() + .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_handle) + .finish() + .unwrap(); + let child = + Command::new(&exe).arg("--child").spawn_with_attributes(&mut attribute_list).unwrap(); + let child = ProcessDropGuard(child); + + // Return the fake's process id and the child's parent's id. + (process_info(&fake_parent.0).process_id(), process_info(&child.0).parent_id()) + }; + + assert_eq!(fake_parent_id, child_parent_id); +} + +// A process that stays running until killed. +fn child() { + // Don't wait forever if something goes wrong. + thread::sleep(time::Duration::from_secs(60)); +} + +fn process_info(child: &Child) -> PROCESS_BASIC_INFORMATION { + unsafe { + let mut info: PROCESS_BASIC_INFORMATION = mem::zeroed(); + let result = NtQueryInformationProcess( + child.as_raw_handle(), + ProcessBasicInformation, + ptr::from_mut(&mut info).cast(), + mem::size_of_val(&info).try_into().unwrap(), + ptr::null_mut(), + ); + assert_eq!(result, 0); + info + } +} + +// Windows API +mod winapi { + #![allow(nonstandard_style)] + use std::ffi::c_void; + + pub type HANDLE = *mut c_void; + type NTSTATUS = i32; + type PROCESSINFOCLASS = i32; + + pub const ProcessBasicInformation: i32 = 0; + pub const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; + #[repr(C)] + pub struct PROCESS_BASIC_INFORMATION { + pub ExitStatus: NTSTATUS, + pub PebBaseAddress: *mut (), + pub AffinityMask: usize, + pub BasePriority: i32, + pub UniqueProcessId: usize, + pub InheritedFromUniqueProcessId: usize, + } + impl PROCESS_BASIC_INFORMATION { + pub fn parent_id(&self) -> usize { + self.InheritedFromUniqueProcessId + } + pub fn process_id(&self) -> usize { + self.UniqueProcessId + } + } + + #[link(name = "ntdll")] + extern "system" { + pub fn NtQueryInformationProcess( + ProcessHandle: HANDLE, + ProcessInformationClass: PROCESSINFOCLASS, + ProcessInformation: *mut c_void, + ProcessInformationLength: u32, + ReturnLength: *mut u32, + ) -> NTSTATUS; + } +} +use winapi::*; diff --git a/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs new file mode 100644 index 0000000000000..f9e346ea11e21 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs @@ -0,0 +1,33 @@ +//@ check-pass + +#![feature(derive_coerce_pointee)] +#![feature(arbitrary_self_types)] + +use std::marker::CoercePointee; +use std::ops::Receiver; + +// `CoercePointee` isn't needed here, it's just a simpler +// (and more conceptual) way of deriving `DispatchFromDyn`. +// You could think of `MyDispatcher` as a smart pointer +// that just doesn't deref to its target type. +#[derive(CoercePointee)] +#[repr(transparent)] +struct MyDispatcher(*const T); + +impl Receiver for MyDispatcher { + type Target = T; +} +struct Test; + +trait Trait { + fn test(self: MyDispatcher); +} + +impl Trait for Test { + fn test(self: MyDispatcher) { + todo!() + } +} +fn main() { + MyDispatcher::(core::ptr::null_mut::()).test(); +} diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs index 04f1c1a65d5cb..4a7f2bee9d954 100644 --- a/tests/ui/wait-forked-but-failed-child.rs +++ b/tests/ui/wait-forked-but-failed-child.rs @@ -31,8 +31,17 @@ fn find_zombies() { // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html let ps_cmd_output = Command::new("ps").args(&["-A", "-o", "pid,ppid,args"]).output().unwrap(); let ps_output = String::from_utf8_lossy(&ps_cmd_output.stdout); + // On AIX, the PPID is not always present, such as when a process is blocked + // (marked as ), or if a process is idle. In these situations, + // the PPID column contains a "-" for the respective process. + // Filter out any lines that have a "-" as the PPID as the PPID is + // expected to be an integer. + let filtered_ps: Vec<_> = ps_output + .lines() + .filter(|line| line.split_whitespace().nth(1) != Some("-")) + .collect(); - for (line_no, line) in ps_output.split('\n').enumerate() { + for (line_no, line) in filtered_ps.into_iter().enumerate() { if 0 < line_no && 0 < line.len() && my_pid == line.split(' ').filter(|w| 0 < w.len()).nth(1) .expect("1st column should be PPID")