diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ca704082a3f18..fd54a153a1609 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,4 @@ + \ No newline at end of file +--> + diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index fcfb297d50aa8..d62f87de61af4 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -328,7 +328,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let Some(annotation_index) = constant.user_ty { if let Err(terr) = self.cx.relate_type_and_user_type( constant.const_.ty(), - ty::Variance::Invariant, + ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, locations, ConstraintCategory::Boring, @@ -451,7 +451,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let Err(terr) = self.cx.relate_type_and_user_type( ty, - ty::Variance::Invariant, + ty::Invariant, user_ty, Locations::All(*span), ConstraintCategory::TypeAnnotation, @@ -1095,7 +1095,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) -> Result<(), NoSolution> { // Use this order of parameters because the sup type is usually the // "expected" type in diagnostics. - self.relate_types(sup, ty::Variance::Contravariant, sub, locations, category) + self.relate_types(sup, ty::Contravariant, sub, locations, category) } #[instrument(skip(self, category), level = "debug")] @@ -1106,7 +1106,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { - self.relate_types(expected, ty::Variance::Invariant, found, locations, category) + self.relate_types(expected, ty::Invariant, found, locations, category) } #[instrument(skip(self), level = "debug")] @@ -1146,7 +1146,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { trace!(?curr_projected_ty); let ty = curr_projected_ty.ty; - self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?; + self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?; Ok(()) } @@ -1248,7 +1248,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Some(annotation_index) = self.rvalue_user_ty(rv) { if let Err(terr) = self.relate_type_and_user_type( rv_ty, - ty::Variance::Invariant, + ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, location.to_locations(), ConstraintCategory::Boring, diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index c531c9b209be5..b9a82046e59ad 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -50,14 +50,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { - NllTypeRelating::new( - self, - locations, - category, - UniverseInfo::other(), - ty::Variance::Invariant, - ) - .relate(a, b)?; + NllTypeRelating::new(self, locations, category, UniverseInfo::other(), ty::Invariant) + .relate(a, b)?; Ok(()) } } @@ -106,15 +100,15 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { fn ambient_covariance(&self) -> bool { match self.ambient_variance { - ty::Variance::Covariant | ty::Variance::Invariant => true, - ty::Variance::Contravariant | ty::Variance::Bivariant => false, + ty::Covariant | ty::Invariant => true, + ty::Contravariant | ty::Bivariant => false, } } fn ambient_contravariance(&self) -> bool { match self.ambient_variance { - ty::Variance::Contravariant | ty::Variance::Invariant => true, - ty::Variance::Covariant | ty::Variance::Bivariant => false, + ty::Contravariant | ty::Invariant => true, + ty::Covariant | ty::Bivariant => false, } } @@ -336,11 +330,7 @@ impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx debug!(?self.ambient_variance); // In a bivariant context this always succeeds. - let r = if self.ambient_variance == ty::Variance::Bivariant { - Ok(a) - } else { - self.relate(a, b) - }; + let r = if self.ambient_variance == ty::Bivariant { Ok(a) } else { self.relate(a, b) }; self.ambient_variance = old_ambient_variance; @@ -474,7 +464,7 @@ impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx } match self.ambient_variance { - ty::Variance::Covariant => { + ty::Covariant => { // Covariance, so we want `for<..> A <: for<..> B` -- // therefore we compare any instantiation of A (i.e., A // instantiated with existentials) against every @@ -489,7 +479,7 @@ impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx })?; } - ty::Variance::Contravariant => { + ty::Contravariant => { // Contravariance, so we want `for<..> A :> for<..> B` -- // therefore we compare every instantiation of A (i.e., A // instantiated with universals) against any @@ -504,7 +494,7 @@ impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx })?; } - ty::Variance::Invariant => { + ty::Invariant => { // Invariant, so we want `for<..> A == for<..> B` -- // therefore we want `exists<..> A == for<..> B` and // `exists<..> B == for<..> A`. @@ -525,7 +515,7 @@ impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx })?; } - ty::Variance::Bivariant => {} + ty::Bivariant => {} } Ok(a) @@ -584,23 +574,23 @@ impl<'bccx, 'tcx> PredicateEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { self.register_predicates([ty::Binder::dummy(match self.ambient_variance { - ty::Variance::Covariant => ty::PredicateKind::AliasRelate( + ty::Covariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Subtype, ), // a :> b is b <: a - ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( + ty::Contravariant => ty::PredicateKind::AliasRelate( b.into(), a.into(), ty::AliasRelationDirection::Subtype, ), - ty::Variance::Invariant => ty::PredicateKind::AliasRelate( + ty::Invariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Equate, ), - ty::Variance::Bivariant => { + ty::Bivariant => { unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") } })]); diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index a6eef9f5662ca..4d0f35618ef27 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -112,25 +112,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Shift ops can have an RHS with a different numeric type. if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) { - let size = left.layout.size.bits(); + let l_bits = left.layout.size.bits(); // Compute the equivalent shift modulo `size` that is in the range `0..size`. (This is // the one MIR operator that does *not* directly map to a single LLVM operation.) let (shift_amount, overflow) = if right.layout.abi.is_signed() { let shift_amount = r_signed(); - let overflow = shift_amount < 0 || shift_amount >= i128::from(size); - // Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result - // of the `as` will be equal modulo `size` (since it is a power of two). - let masked_amount = (shift_amount as u128) % u128::from(size); - assert_eq!(overflow, shift_amount != i128::try_from(masked_amount).unwrap()); - (masked_amount, overflow) + let rem = shift_amount.rem_euclid(l_bits.into()); + // `rem` is guaranteed positive, so the `unwrap` cannot fail + (u128::try_from(rem).unwrap(), rem != shift_amount) } else { let shift_amount = r_unsigned(); - let overflow = shift_amount >= u128::from(size); - let masked_amount = shift_amount % u128::from(size); - assert_eq!(overflow, shift_amount != masked_amount); - (masked_amount, overflow) + let rem = shift_amount.rem_euclid(l_bits.into()); + (rem, rem != shift_amount) }; - let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit + let shift_amount = u32::try_from(shift_amount).unwrap(); // we brought this in the range `0..size` so this will always fit // Compute the shifted result. let result = if left.layout.abi.is_signed() { let l = l_signed(); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 3ab319a037b49..d6f3f4d640bd5 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -45,6 +45,7 @@ hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to ` hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private hir_typeck_dependency_on_unit_never_type_fallback = this function depends on never type fallback being `()` + .note = in edition 2024, the requirement `{$obligation}` will fail .help = specify the types explicitly hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty` diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 6dd34024fd1df..24f039b8e90bc 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -7,7 +7,7 @@ use rustc_errors::{ SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::{ edition::{Edition, LATEST_STABLE_EDITION}, symbol::Ident, @@ -186,7 +186,11 @@ pub enum NeverTypeFallbackFlowingIntoUnsafe { #[derive(LintDiagnostic)] #[help] #[diag(hir_typeck_dependency_on_unit_never_type_fallback)] -pub struct DependencyOnUnitNeverTypeFallback {} +pub struct DependencyOnUnitNeverTypeFallback<'tcx> { + #[note] + pub obligation_span: Span, + pub obligation: ty::Predicate<'tcx>, +} #[derive(Subdiagnostic)] #[multipart_suggestion( diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index b7937f458b332..3cecbfd42757e 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -488,7 +488,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let remaining_errors_if_fallback_to = |fallback| { self.probe(|_| { let obligations = self.fulfillment_cx.borrow().pending_obligations(); - let ocx = ObligationCtxt::new(&self.infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx); ocx.register_obligations(obligations.iter().cloned()); for &diverging_vid in diverging_vids { @@ -506,14 +506,18 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // then this code will be broken by the never type fallback change.qba let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit); if unit_errors.is_empty() - && let never_errors = remaining_errors_if_fallback_to(self.tcx.types.never) - && !never_errors.is_empty() + && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never) + && let [ref mut never_error, ..] = never_errors.as_mut_slice() { + self.adjust_fulfillment_error_for_expr_obligation(never_error); self.tcx.emit_node_span_lint( lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, self.tcx.local_def_id_to_hir_id(self.body_id), self.tcx.def_span(self.body_id), - errors::DependencyOnUnitNeverTypeFallback {}, + errors::DependencyOnUnitNeverTypeFallback { + obligation_span: never_error.obligation.cause.span, + obligation: never_error.obligation.predicate, + }, ) } } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 5e9c3c64e39ed..fc7a22833ff05 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -212,16 +212,16 @@ impl<'a, 'tcx> At<'a, 'tcx> { T: ToTrace<'tcx>, { match variance { - ty::Variance::Covariant => self.sub(define_opaque_types, expected, actual), - ty::Variance::Invariant => self.eq(define_opaque_types, expected, actual), - ty::Variance::Contravariant => self.sup(define_opaque_types, expected, actual), + ty::Covariant => self.sub(define_opaque_types, expected, actual), + ty::Invariant => self.eq(define_opaque_types, expected, actual), + ty::Contravariant => self.sup(define_opaque_types, expected, actual), // We could make this make sense but it's not readily // exposed and I don't feel like dealing with it. Note // that bivariance in general does a bit more than just // *nothing*, it checks that the types are the same // "modulo variance" basically. - ty::Variance::Bivariant => panic!("Bivariant given to `relate()`"), + ty::Bivariant => panic!("Bivariant given to `relate()`"), } } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 7114b888718b3..b8dd501a721b5 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -345,7 +345,7 @@ impl<'tcx> InferCtxt<'tcx> { .args .iter() .enumerate() - .filter(|(i, _)| variances[*i] == ty::Variance::Invariant) + .filter(|(i, _)| variances[*i] == ty::Invariant) .filter_map(|(_, arg)| match arg.unpack() { GenericArgKind::Lifetime(r) => Some(r), GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, @@ -441,7 +441,7 @@ where let variances = self.tcx.variances_of(*def_id); for (v, s) in std::iter::zip(variances, args.iter()) { - if *v != ty::Variance::Bivariant { + if *v != ty::Bivariant { s.visit_with(self); } } diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index 488f435994d4d..c02ab98b2baeb 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -102,9 +102,7 @@ where }; for (idx, s) in args.iter().enumerate() { - if variances.map(|variances| variances[idx]) - != Some(ty::Variance::Bivariant) - { + if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) { s.visit_with(self); } } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 87a2f0b45803d..d6e57d8538772 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -83,16 +83,16 @@ impl<'tcx> InferCtxt<'tcx> { // mention `?0`. if self.next_trait_solver() { let (lhs, rhs, direction) = match instantiation_variance { - ty::Variance::Invariant => { + ty::Invariant => { (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate) } - ty::Variance::Covariant => { + ty::Covariant => { (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype) } - ty::Variance::Contravariant => { + ty::Contravariant => { (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype) } - ty::Variance::Bivariant => unreachable!("bivariant generalization"), + ty::Bivariant => unreachable!("bivariant generalization"), }; relation.register_predicates([ty::PredicateKind::AliasRelate(lhs, rhs, direction)]); @@ -192,7 +192,7 @@ impl<'tcx> InferCtxt<'tcx> { relation.span(), relation.structurally_relate_aliases(), target_vid, - ty::Variance::Invariant, + ty::Invariant, source_ct, )?; @@ -210,14 +210,14 @@ impl<'tcx> InferCtxt<'tcx> { // generalized const and the source. if target_is_expected { relation.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, ty::VarianceDiagInfo::default(), generalized_ct, source_ct, )?; } else { relation.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, ty::VarianceDiagInfo::default(), source_ct, generalized_ct, @@ -411,7 +411,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { a_arg: ty::GenericArgsRef<'tcx>, b_arg: ty::GenericArgsRef<'tcx>, ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { - if self.ambient_variance == ty::Variance::Invariant { + if self.ambient_variance == ty::Invariant { // Avoid fetching the variance if we are in an invariant // context; no need, and it can induce dependency cycles // (e.g., #41849). @@ -667,7 +667,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // structural. ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { let args = self.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, ty::VarianceDiagInfo::default(), args, args, diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs index cc17e60a79b00..067004ecaebbf 100644 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -94,12 +94,7 @@ impl<'tcx> TypeRelation> for Glb<'_, '_, 'tcx> { // When higher-ranked types are involved, computing the GLB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - self.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - a, - b, - )?; + self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; Ok(a) } else { Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs index e9d300d349c62..2184416b4ccef 100644 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -94,12 +94,7 @@ impl<'tcx> TypeRelation> for Lub<'_, '_, 'tcx> { // When higher-ranked types are involved, computing the LUB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - self.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - a, - b, - )?; + self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; Ok(a) } else { Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index f7b2f11e3d707..f2bec9392d542 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -42,7 +42,7 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { a_arg: ty::GenericArgsRef<'tcx>, b_arg: ty::GenericArgsRef<'tcx>, ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { - if self.ambient_variance == ty::Variance::Invariant { + if self.ambient_variance == ty::Invariant { // Avoid fetching the variance if we are in an invariant // context; no need, and it can induce dependency cycles // (e.g., #41849). @@ -325,23 +325,23 @@ impl<'tcx> PredicateEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { self.register_predicates([ty::Binder::dummy(match self.ambient_variance { - ty::Variance::Covariant => ty::PredicateKind::AliasRelate( + ty::Covariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Subtype, ), // a :> b is b <: a - ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( + ty::Contravariant => ty::PredicateKind::AliasRelate( b.into(), a.into(), ty::AliasRelationDirection::Subtype, ), - ty::Variance::Invariant => ty::PredicateKind::AliasRelate( + ty::Invariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Equate, ), - ty::Variance::Bivariant => { + ty::Bivariant => { unreachable!("Expected bivariance to be handled in relate_with_variance") } })]); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index ebe77a1abfd8b..3edc5fe36cd2f 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1490,7 +1490,8 @@ pub enum BinOp { BitOr, /// The `<<` operator (shift left) /// - /// The offset is (uniquely) determined as follows: + /// The offset is given by `RHS.rem_euclid(LHS::BITS)`. + /// In other words, it is (uniquely) determined as follows: /// - it is "equal modulo LHS::BITS" to the RHS /// - it is in the range `0..LHS::BITS` Shl, @@ -1498,7 +1499,8 @@ pub enum BinOp { ShlUnchecked, /// The `>>` operator (shift right) /// - /// The offset is (uniquely) determined as follows: + /// The offset is given by `RHS.rem_euclid(LHS::BITS)`. + /// In other words, it is (uniquely) determined as follows: /// - it is "equal modulo LHS::BITS" to the RHS /// - it is in the range `0..LHS::BITS` /// diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c322c87bce4ad..5a94a53e175ea 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -16,7 +16,6 @@ pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeV pub use self::AssocItemContainer::*; pub use self::BorrowKind::*; pub use self::IntVarValue::*; -pub use self::Variance::*; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::ModChild; use crate::middle::privacy::EffectiveVisibilities; diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 4ae475d35d14d..8c323188826b2 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -142,7 +142,7 @@ impl<'tcx> Value> for &[ty::Variance] { && let Some(def_id) = frame.query.def_id { let n = tcx.generics_of(def_id).own_params.len(); - vec![ty::Variance::Bivariant; n].leak() + vec![ty::Bivariant; n].leak() } else { span_bug!( cycle_error.usage.as_ref().unwrap().0, diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 5413878936983..544f27b84e90d 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -699,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // exactly `T` (i.e., with invariance). The variance field, in // contrast, is intended to be used to relate `T` to the type of // ``. - ty::Variance::Invariant, + ty::Invariant, ), }, ); diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 54eafdd828ae6..95cd703dbb3c0 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -92,7 +92,7 @@ impl<'tcx> Cx<'tcx> { kind: PatKind::AscribeUserType { ascription: Ascription { annotation, - variance: ty::Variance::Covariant, + variance: ty::Covariant, }, subpattern: pattern, }, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 158ca91fcf138..93db1f618533b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -525,7 +525,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; kind = PatKind::AscribeUserType { subpattern: Box::new(Pat { span, ty, kind }), - ascription: Ascription { annotation, variance: ty::Variance::Covariant }, + ascription: Ascription { annotation, variance: ty::Covariant }, }; } @@ -612,7 +612,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { annotation, // Note that use `Contravariant` here. See the // `variance` field documentation for details. - variance: ty::Variance::Contravariant, + variance: ty::Contravariant, }, }, ty: const_.ty(), diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index fe2237dd2e97a..2cbe0a01e9ece 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -225,13 +225,8 @@ impl<'tcx> Inliner<'tcx> { // Normally, this shouldn't be required, but trait normalization failure can create a // validation ICE. let output_type = callee_body.return_ty(); - if !util::relate_types( - self.tcx, - self.param_env, - ty::Variance::Covariant, - output_type, - destination_ty, - ) { + if !util::relate_types(self.tcx, self.param_env, ty::Covariant, output_type, destination_ty) + { trace!(?output_type, ?destination_ty); return Err("failed to normalize return type"); } @@ -261,13 +256,8 @@ impl<'tcx> Inliner<'tcx> { self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; - if !util::relate_types( - self.tcx, - self.param_env, - ty::Variance::Covariant, - input_type, - arg_ty, - ) { + if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty) + { trace!(?arg_ty, ?input_type); return Err("failed to normalize tuple argument type"); } @@ -276,13 +266,8 @@ impl<'tcx> Inliner<'tcx> { for (arg, input) in args.iter().zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; let arg_ty = arg.node.ty(&caller_body.local_decls, self.tcx); - if !util::relate_types( - self.tcx, - self.param_env, - ty::Variance::Covariant, - input_type, - arg_ty, - ) { + if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty) + { trace!(?arg_ty, ?input_type); return Err("failed to normalize argument type"); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b0adc3775f667..fa711d932b63b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3281,17 +3281,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } self.visit_path(&delegation.path, delegation.id); if let Some(body) = &delegation.body { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - - let span = delegation.path.segments.last().unwrap().ident.span; - self.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); - self.visit_block(body); + self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { + // `PatBoundCtx` is not necessary in this context + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + + let span = delegation.path.segments.last().unwrap().ident.span; + this.fresh_binding( + Ident::new(kw::SelfLower, span), + delegation.id, + PatternSource::FnParam, + &mut bindings, + ); + this.visit_block(body); + }); } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 9eeb0da7ed2fd..be24755d4c5a0 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1021,12 +1021,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }, ); let is_assoc_fn = self.self_type_is_available(); + let self_from_macro = "a `self` parameter, but a macro invocation can only \ + access identifiers it receives from parameters"; if let Some((fn_kind, span)) = &self.diag_metadata.current_function { // The current function has a `self` parameter, but we were unable to resolve // a reference to `self`. This can only happen if the `self` identifier we // are resolving came from a different hygiene context. if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) { - err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); + err.span_label(*span, format!("this function has {self_from_macro}")); } else { let doesnt = if is_assoc_fn { let (span, sugg) = fn_kind @@ -1068,14 +1070,18 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } } else if let Some(item_kind) = self.diag_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); + if matches!(item_kind.kind, ItemKind::Delegation(..)) { + err.span_label(item_kind.span, format!("delegation supports {self_from_macro}")); + } else { + err.span_label( + item_kind.ident.span, + format!( + "`self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); + } } true } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 73bc87dc9abf9..e3cd7187e7731 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -866,10 +866,10 @@ impl<'tcx> Stable<'tcx> for ty::Variance { type T = stable_mir::mir::Variance; fn stable(&self, _: &mut Tables<'_>) -> Self::T { match self { - ty::Variance::Bivariant => stable_mir::mir::Variance::Bivariant, - ty::Variance::Contravariant => stable_mir::mir::Variance::Contravariant, - ty::Variance::Covariant => stable_mir::mir::Variance::Covariant, - ty::Variance::Invariant => stable_mir::mir::Variance::Invariant, + ty::Bivariant => stable_mir::mir::Variance::Bivariant, + ty::Contravariant => stable_mir::mir::Variance::Contravariant, + ty::Covariant => stable_mir::mir::Variance::Covariant, + ty::Invariant => stable_mir::mir::Variance::Invariant, } } } diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 59a8de99b8f6d..4e52caa5a5b8e 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -61,8 +61,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { trace!(?lhs, ?rhs); let variance = match direction { - ty::AliasRelationDirection::Equate => ty::Variance::Invariant, - ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, + ty::AliasRelationDirection::Equate => ty::Invariant, + ty::AliasRelationDirection::Subtype => ty::Covariant, }; match (lhs.to_alias_term(), rhs.to_alias_term()) { (None, None) => { @@ -78,7 +78,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { self.relate_rigid_alias_non_alias( param_env, alias, - variance.xform(ty::Variance::Contravariant), + variance.xform(ty::Contravariant), lhs, )?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index a7fac5fc3ba82..c3e4057f5e096 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -40,7 +40,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { Ok(res) => Ok(res), Err(NoSolution) => { let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal; - self.relate_rigid_alias_non_alias(param_env, alias, ty::Variance::Invariant, term)?; + self.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a026b233c68a5..c53939bfe602f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -239,24 +239,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(()); } - let all_bounds = stack + let bounds = stack .obligation .param_env .caller_bounds() .iter() .filter(|p| !p.references_error()) - .filter_map(|p| p.as_trait_clause()); - - // Micro-optimization: filter out predicates relating to different traits. - let matching_bounds = - all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id()); + .filter_map(|p| p.as_trait_clause()) + // Micro-optimization: filter out predicates relating to different traits. + .filter(|p| p.def_id() == stack.obligation.predicate.def_id()) + .filter(|p| p.polarity() == stack.obligation.predicate.polarity()); // Keep only those bounds which may apply, and propagate overflow if it occurs. - for bound in matching_bounds { - if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity { - continue; - } - + for bound in bounds { // FIXME(oli-obk): it is suspicious that we are dropping the constness and // polarity here. let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?; diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 7f0c3df381daf..4775a0f8cbbe4 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -74,6 +74,7 @@ pub use DynKind::*; pub use InferTy::*; pub use RegionKind::*; pub use TyKind::*; +pub use Variance::*; rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index cae1d13020d34..8a6ba87b60e06 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -128,7 +128,7 @@ pub fn relate_args_invariantly>( b_arg: I::GenericArgs, ) -> RelateResult { relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| { - relation.relate_with_variance(ty::Variance::Invariant, VarianceDiagInfo::default(), a, b) + relation.relate_with_variance(ty::Invariant, VarianceDiagInfo::default(), a, b) })) } @@ -145,7 +145,7 @@ pub fn relate_args_with_variances>( let mut cached_ty = None; let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| { let variance = variances[i]; - let variance_info = if variance == ty::Variance::Invariant && fetch_ty_for_diag { + let variance_info = if variance == ty::Invariant && fetch_ty_for_diag { let ty = *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, &a_arg)); VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } @@ -191,7 +191,7 @@ impl Relate for ty::FnSig { relation.relate(a, b) } else { relation.relate_with_variance( - ty::Variance::Contravariant, + ty::Contravariant, VarianceDiagInfo::default(), a, b, @@ -311,13 +311,13 @@ impl Relate for ty::ExistentialProjection { })) } else { let term = relation.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, VarianceDiagInfo::default(), a.term, b.term, )?; let args = relation.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, VarianceDiagInfo::default(), a.args, b.args, @@ -466,9 +466,9 @@ pub fn structurally_relate_tys>( } let (variance, info) = match a_mutbl { - Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None), + Mutability::Not => (ty::Covariant, VarianceDiagInfo::None), Mutability::Mut => { - (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + (ty::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) } }; @@ -483,9 +483,9 @@ pub fn structurally_relate_tys>( } let (variance, info) = match a_mutbl { - Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None), + Mutability::Not => (ty::Covariant, VarianceDiagInfo::None), Mutability::Mut => { - (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + (ty::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) } }; @@ -612,7 +612,7 @@ pub fn structurally_relate_consts>( } let args = relation.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, VarianceDiagInfo::default(), au.args, bu.args, diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 2a13b1a8ae2de..9a38c42f43a02 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -133,7 +133,8 @@ //! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at //! most one thread at a time is able to access some data. //! -//! - [`Once`]: Used for a thread-safe, one-time global initialization routine +//! - [`Once`]: Used for a thread-safe, one-time global initialization routine. +//! Mostly useful for implementing other types like `OnceLock`. //! //! - [`OnceLock`]: Used for thread-safe, one-time initialization of a //! variable, with potentially different initializers based on the caller. diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 608229fd674d8..9d969af8c6d84 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -10,9 +10,15 @@ use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; -/// A synchronization primitive which can be used to run a one-time global -/// initialization. Useful for one-time initialization for FFI or related -/// functionality. This type can only be constructed with [`Once::new()`]. +/// A low-level synchronization primitive for one-time global execution. +/// +/// Previously this was the only "execute once" synchronization in `std`. +/// Other libraries implemented novel synchronizing types with `Once`, like +/// [`OnceLock`] or [`LazyLock`], before those were added to `std`. +/// `OnceLock` in particular supersedes `Once` in functionality and should +/// be preferred for the common case where the `Once` is associated with data. +/// +/// This type can only be constructed with [`Once::new()`]. /// /// # Examples /// @@ -25,6 +31,9 @@ use crate::sys::sync as sys; /// // run initialization here /// }); /// ``` +/// +/// [`OnceLock`]: crate::sync::OnceLock +/// [`LazyLock`]: crate::sync::LazyLock #[stable(feature = "rust1", since = "1.0.0")] pub struct Once { inner: sys::Once, diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 6cf31f2e61e1c..e0a9674ae5a90 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -823,19 +823,29 @@ impl Step for LibcxxVersionTool { fn run(self, builder: &Builder<'_>) -> LibcxxVersion { let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version"); - let _ = fs::remove_dir_all(&out_dir); - t!(fs::create_dir_all(&out_dir)); + let executable = out_dir.join(exe("libcxx-version", self.target)); - let compiler = builder.cxx(self.target).unwrap(); - let mut cmd = Command::new(compiler); + // This is a sanity-check specific step, which means it is frequently called (when using + // CI LLVM), and compiling `src/tools/libcxx-version/main.cpp` at the beginning of the bootstrap + // invocation adds a fair amount of overhead to the process (see https://github.com/rust-lang/rust/issues/126423). + // Therefore, we want to avoid recompiling this file unnecessarily. + if !executable.exists() { + if !out_dir.exists() { + t!(fs::create_dir_all(&out_dir)); + } - let executable = out_dir.join(exe("libcxx-version", self.target)); - cmd.arg("-o").arg(&executable).arg(builder.src.join("src/tools/libcxx-version/main.cpp")); + let compiler = builder.cxx(self.target).unwrap(); + let mut cmd = Command::new(compiler); - builder.run_cmd(&mut cmd); + cmd.arg("-o") + .arg(&executable) + .arg(builder.src.join("src/tools/libcxx-version/main.cpp")); - if !executable.exists() { - panic!("Something went wrong. {} is not present", executable.display()); + builder.run_cmd(&mut cmd); + + if !executable.exists() { + panic!("Something went wrong. {} is not present", executable.display()); + } } let version_output = output(&mut Command::new(executable)); diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index da80f10b7dddd..5a0be2948a19e 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -128,6 +128,9 @@ pub fn check(build: &mut Build) { eprintln!( "Consider upgrading libstdc++ or disabling the `llvm.download-ci-llvm` option." ); + eprintln!( + "If you choose to upgrade libstdc++, run `x clean` or delete `build/host/libcxx-version` manually after the upgrade." + ); } } tool::LibcxxVersion::Llvm(_) => { diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index c437619a76eab..4b4f2f83062f0 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -151,6 +151,21 @@ platform. For example `cargo miri test --target s390x-unknown-linux-gnu` will run your test suite on a big-endian target, which is useful for testing endian-sensitive code. +### Testing multiple different executions + +Certain parts of the execution are picked randomly by Miri, such as the exact base address +allocations are stored at and the interleaving of concurrently executing threads. Sometimes, it can +be useful to explore multiple different execution, e.g. to make sure that your code does not depend +on incidental "super-alignment" of new allocations and to test different thread interleavings. +This can be done with the `--many-seeds` flag: + +``` +cargo miri test --many-seeds # tries the seeds in 0..64 +cargo miri test --many-seeds=0..16 +``` + +The default of 64 different seeds is quite slow, so you probably want to specify a smaller range. + ### Running Miri on CI When running Miri on CI, use the following snippet to install a nightly toolchain with the Miri @@ -183,23 +198,6 @@ Here is an example job for GitHub Actions: The explicit `cargo miri setup` helps to keep the output of the actual test step clean. -### Testing for alignment issues - -Miri can sometimes miss misaligned accesses since allocations can "happen to be" -aligned just right. You can use `-Zmiri-symbolic-alignment-check` to definitely -catch all such issues, but that flag will also cause false positives when code -does manual pointer arithmetic to account for alignment. Another alternative is -to call Miri with various values for `-Zmiri-seed`; that will alter the -randomness that is used to determine allocation base addresses. The following -snippet calls Miri in a loop with different values for the seed: - -``` -for SEED in $(seq 0 255); do - echo "Trying seed: $SEED" - MIRIFLAGS=-Zmiri-seed=$SEED cargo miri test || { echo "Failing seed: $SEED"; break; }; -done -``` - ### Supported targets Miri does not support all targets supported by Rust. The good news, however, is diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index e2fc2a0c2779b..3c36f606d8452 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -1,10 +1,10 @@ //! Implements the various phases of `cargo miri run/test`. -use std::env; use std::fs::{self, File}; -use std::io::BufReader; +use std::io::{BufReader, Write}; use std::path::{Path, PathBuf}; use std::process::Command; +use std::{env, thread}; use rustc_version::VersionMeta; @@ -34,6 +34,8 @@ Examples: "; +const DEFAULT_MANY_SEEDS: &str = "0..64"; + fn show_help() { println!("{CARGO_MIRI_HELP}"); } @@ -119,7 +121,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // describes an alternative // approach that uses `cargo check`, making that part easier but target and binary handling // harder. - let cargo_miri_path = std::env::current_exe() + let cargo_miri_path = env::current_exe() .expect("current executable path invalid") .into_os_string() .into_string() @@ -163,14 +165,22 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { let target_dir = get_target_dir(&metadata); cmd.arg("--target-dir").arg(target_dir); + // Store many-seeds argument. + let mut many_seeds = None; // *After* we set all the flags that need setting, forward everything else. Make sure to skip - // `--target-dir` (which would otherwise be set twice). + // `--target-dir` (which would otherwise be set twice) and `--many-seeds` (which is our flag, not cargo's). for arg in ArgSplitFlagValue::from_string_iter(&mut args, "--target-dir").filter_map(Result::err) { - cmd.arg(arg); + if arg == "--many-seeds" { + many_seeds = Some(DEFAULT_MANY_SEEDS.to_owned()); + } else if let Some(val) = arg.strip_prefix("--many-seeds=") { + many_seeds = Some(val.to_owned()); + } else { + cmd.arg(arg); + } } - // Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo. + // Forward all further arguments after `--` (not consumed by `ArgSplitFlagValue`) to cargo. cmd.args(args); // Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation, @@ -222,6 +232,9 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // Forward some crucial information to our own re-invocations. cmd.env("MIRI_SYSROOT", miri_sysroot); cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata)); + if let Some(many_seeds) = many_seeds { + cmd.env("MIRI_MANY_SEEDS", many_seeds); + } if verbose > 0 { cmd.env("MIRI_VERBOSE", verbose.to_string()); // This makes the other phases verbose. } @@ -309,7 +322,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { } } - let verbose = std::env::var("MIRI_VERBOSE") + let verbose = env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); let target_crate = is_target_crate(); @@ -489,7 +502,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { // This is a host crate. // When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly // due to bootstrap complications. - if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") { + if let Some(sysroot) = env::var_os("MIRI_HOST_SYSROOT") { cmd.arg("--sysroot").arg(sysroot); } @@ -532,7 +545,7 @@ pub enum RunnerPhase { } pub fn phase_runner(mut binary_args: impl Iterator, phase: RunnerPhase) { - let verbose = std::env::var("MIRI_VERBOSE") + let verbose = env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); let binary = binary_args.next().unwrap(); @@ -541,6 +554,7 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner "file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`", binary )); let file = BufReader::new(file); + let binary_args = binary_args.collect::>(); let info = serde_json::from_reader(file).unwrap_or_else(|_| { show_error!("file {:?} contains outdated or invalid JSON; try `cargo clean`", binary) @@ -555,84 +569,114 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner } }; - let mut cmd = miri(); - - // Set missing env vars. We prefer build-time env vars over run-time ones; see - // for the kind of issue that fixes. - for (name, val) in info.env { - // `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time - // the program is being run, that jobserver no longer exists (cargo only runs the jobserver - // for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this. - // Also see . - if name == "CARGO_MAKEFLAGS" { - continue; - } - if let Some(old_val) = env::var_os(&name) { - if old_val == val { - // This one did not actually change, no need to re-set it. - // (This keeps the `debug_cmd` below more manageable.) + let many_seeds = env::var("MIRI_MANY_SEEDS"); + run_many_seeds(many_seeds.ok(), |seed| { + let mut cmd = miri(); + + // Set missing env vars. We prefer build-time env vars over run-time ones; see + // for the kind of issue that fixes. + for (name, val) in &info.env { + // `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time + // the program is being run, that jobserver no longer exists (cargo only runs the jobserver + // for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this. + // Also see . + if name == "CARGO_MAKEFLAGS" { continue; - } else if verbose > 0 { - eprintln!( - "[cargo-miri runner] Overwriting run-time env var {name:?}={old_val:?} with build-time value {val:?}" - ); } + if let Some(old_val) = env::var_os(name) { + if *old_val == *val { + // This one did not actually change, no need to re-set it. + // (This keeps the `debug_cmd` below more manageable.) + continue; + } else if verbose > 0 { + eprintln!( + "[cargo-miri runner] Overwriting run-time env var {name:?}={old_val:?} with build-time value {val:?}" + ); + } + } + cmd.env(name, val); } - cmd.env(name, val); - } - if phase != RunnerPhase::Rustdoc { - // Set the sysroot. Not necessary in rustdoc, where we already set the sysroot in - // `phase_rustdoc`. rustdoc will forward that flag when invoking rustc (i.e., us), so the - // flag is present in `info.args`. - cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); - } - // Forward rustc arguments. - // We need to patch "--extern" filenames because we forced a check-only - // build without cargo knowing about that: replace `.rlib` suffix by - // `.rmeta`. - // We also need to remove `--error-format` as cargo specifies that to be JSON, - // but when we run here, cargo does not interpret the JSON any more. `--json` - // then also needs to be dropped. - let mut args = info.args.into_iter(); - while let Some(arg) = args.next() { - if arg == "--extern" { - forward_patched_extern_arg(&mut args, &mut cmd); - } else if let Some(suffix) = arg.strip_prefix("--error-format") { - assert!(suffix.starts_with('=')); - // Drop this argument. - } else if let Some(suffix) = arg.strip_prefix("--json") { - assert!(suffix.starts_with('=')); - // Drop this argument. - } else { - cmd.arg(arg); + if phase != RunnerPhase::Rustdoc { + // Set the sysroot. Not necessary in rustdoc, where we already set the sysroot in + // `phase_rustdoc`. rustdoc will forward that flag when invoking rustc (i.e., us), so the + // flag is present in `info.args`. + cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); + } + // Forward rustc arguments. + // We need to patch "--extern" filenames because we forced a check-only + // build without cargo knowing about that: replace `.rlib` suffix by + // `.rmeta`. + // We also need to remove `--error-format` as cargo specifies that to be JSON, + // but when we run here, cargo does not interpret the JSON any more. `--json` + // then also needs to be dropped. + let mut args = info.args.iter(); + while let Some(arg) = args.next() { + if arg == "--extern" { + forward_patched_extern_arg(&mut (&mut args).cloned(), &mut cmd); + } else if let Some(suffix) = arg.strip_prefix("--error-format") { + assert!(suffix.starts_with('=')); + // Drop this argument. + } else if let Some(suffix) = arg.strip_prefix("--json") { + assert!(suffix.starts_with('=')); + // Drop this argument. + } else { + cmd.arg(arg); + } + } + // Respect `MIRIFLAGS`. + if let Ok(a) = env::var("MIRIFLAGS") { + let args = flagsplit(&a); + cmd.args(args); + } + // Set the current seed. + if let Some(seed) = seed { + eprintln!("Trying seed: {seed}"); + cmd.arg(format!("-Zmiri-seed={seed}")); } - } - // Respect `MIRIFLAGS`. - if let Ok(a) = env::var("MIRIFLAGS") { - let args = flagsplit(&a); - cmd.args(args); - } - - // Then pass binary arguments. - cmd.arg("--"); - cmd.args(binary_args); - - // Make sure we use the build-time working directory for interpreting Miri/rustc arguments. - // But then we need to switch to the run-time one, which we instruct Miri to do by setting `MIRI_CWD`. - cmd.current_dir(info.current_dir); - cmd.env("MIRI_CWD", env::current_dir().unwrap()); - // Run it. - debug_cmd("[cargo-miri runner]", verbose, &cmd); - match phase { - RunnerPhase::Rustdoc => exec_with_pipe(cmd, &info.stdin, format!("{binary}.stdin")), - RunnerPhase::Cargo => exec(cmd), - } + // Then pass binary arguments. + cmd.arg("--"); + cmd.args(&binary_args); + + // Make sure we use the build-time working directory for interpreting Miri/rustc arguments. + // But then we need to switch to the run-time one, which we instruct Miri to do by setting `MIRI_CWD`. + cmd.current_dir(&info.current_dir); + cmd.env("MIRI_CWD", env::current_dir().unwrap()); + + // Run it. + debug_cmd("[cargo-miri runner]", verbose, &cmd); + + match phase { + RunnerPhase::Rustdoc => { + cmd.stdin(std::process::Stdio::piped()); + let mut child = cmd.spawn().expect("failed to spawn process"); + let child_stdin = child.stdin.take().unwrap(); + // Write stdin in a background thread, as it may block. + let exit_status = thread::scope(|s| { + s.spawn(|| { + let mut child_stdin = child_stdin; + // Ignore failure, it is most likely due to the process having terminated. + let _ = child_stdin.write_all(&info.stdin); + }); + child.wait().expect("failed to run command") + }); + if !exit_status.success() { + std::process::exit(exit_status.code().unwrap_or(-1)); + } + } + RunnerPhase::Cargo => { + let exit_status = cmd.status().expect("failed to run command"); + if !exit_status.success() { + std::process::exit(exit_status.code().unwrap_or(-1)); + } + } + } + }); } pub fn phase_rustdoc(mut args: impl Iterator) { - let verbose = std::env::var("MIRI_VERBOSE") + let verbose = env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); // phase_cargo_miri sets the RUSTDOC env var to ourselves, and puts a backup @@ -681,7 +725,7 @@ pub fn phase_rustdoc(mut args: impl Iterator) { cmd.arg("--cfg").arg("miri"); // Make rustdoc call us back. - let cargo_miri_path = std::env::current_exe().expect("current executable path invalid"); + let cargo_miri_path = env::current_exe().expect("current executable path invalid"); cmd.arg("--test-builder").arg(&cargo_miri_path); // invoked by forwarding most arguments cmd.arg("--runtool").arg(&cargo_miri_path); // invoked with just a single path argument diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs index f36cff1f7981e..5f2794e2244f4 100644 --- a/src/tools/miri/cargo-miri/src/util.rs +++ b/src/tools/miri/cargo-miri/src/util.rs @@ -171,11 +171,16 @@ where drop(path); // We don't need the path, we can pipe the bytes directly cmd.stdin(std::process::Stdio::piped()); let mut child = cmd.spawn().expect("failed to spawn process"); - { - let stdin = child.stdin.as_mut().expect("failed to open stdin"); - stdin.write_all(input).expect("failed to write out test source"); - } - let exit_status = child.wait().expect("failed to run command"); + let child_stdin = child.stdin.take().unwrap(); + // Write stdin in a background thread, as it may block. + let exit_status = std::thread::scope(|s| { + s.spawn(|| { + let mut child_stdin = child_stdin; + // Ignore failure, it is most likely due to the process having terminated. + let _ = child_stdin.write_all(input); + }); + child.wait().expect("failed to run command") + }); std::process::exit(exit_status.code().unwrap_or(-1)) } } @@ -317,3 +322,24 @@ pub fn clean_target_dir(meta: &Metadata) { remove_dir_all_idem(&target_dir).unwrap_or_else(|err| show_error!("{}", err)) } + +/// Run `f` according to the many-seeds argument. In single-seed mode, `f` will only +/// be called once, with `None`. +pub fn run_many_seeds(many_seeds: Option, f: impl Fn(Option)) { + let Some(many_seeds) = many_seeds else { + return f(None); + }; + let (from, to) = many_seeds + .split_once("..") + .unwrap_or_else(|| show_error!("invalid format for `--many-seeds`: expected `from..to`")); + let from: u32 = if from.is_empty() { + 0 + } else { + from.parse().unwrap_or_else(|_| show_error!("invalid `from` in `--many-seeds=from..to")) + }; + let to: u32 = + to.parse().unwrap_or_else(|_| show_error!("invalid `to` in `--many-seeds=from..to")); + for seed in from..to { + f(Some(seed)); + } +} diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index d436ef0c5aaa3..c4f0d808d93e1 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -98,7 +98,7 @@ Build miri, set up a sysroot and then run the test suite. Build miri, set up a sysroot and then run the driver with the given . (Also respects MIRIFLAGS environment variable.) If `--many-seeds` is present, Miri is run many times in parallel with different seeds. -The range defaults to `0..256`. +The range defaults to `0..64`. ./miri fmt : Format all sources and tests. are passed to `rustfmt`. @@ -180,17 +180,16 @@ fn main() -> Result<()> { dep = true; } else if args.get_long_flag("verbose")? || args.get_short_flag('v')? { verbose = true; - } else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..256")? { + } else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..64")? { let (from, to) = val.split_once("..").ok_or_else(|| { - anyhow!("invalid format for `--many-seeds-range`: expected `from..to`") + anyhow!("invalid format for `--many-seeds`: expected `from..to`") })?; let from: u32 = if from.is_empty() { 0 } else { - from.parse().context("invalid `from` in `--many-seeds-range=from..to")? + from.parse().context("invalid `from` in `--many-seeds=from..to")? }; - let to: u32 = - to.parse().context("invalid `to` in `--many-seeds-range=from..to")?; + let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?; many_seeds = Some(from..to); } else if let Some(val) = args.get_long_opt("target")? { target = Some(val); diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 0f738653b14a7..e49680ba75a5e 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -565cadb514d35e7b851540edbc172af0f606014f +f6b4b71ef10307201b52c17b0f9dcf9557cd90ba diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 76a045c4dc166..91865a2192cf0 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -45,7 +45,7 @@ macro_rules! declare_id { // We use 0 as a sentinel value (see the comment above) and, // therefore, need to shift by one when converting from an index // into a vector. - let shifted_idx = u32::try_from(idx).unwrap().checked_add(1).unwrap(); + let shifted_idx = u32::try_from(idx).unwrap().strict_add(1); $name(std::num::NonZero::new(shifted_idx).unwrap()) } fn index(self) -> usize { @@ -350,7 +350,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { mutex.owner = Some(thread); } - mutex.lock_count = mutex.lock_count.checked_add(1).unwrap(); + mutex.lock_count = mutex.lock_count.strict_add(1); if let Some(data_race) = &this.machine.data_race { data_race.acquire_clock(&mutex.clock, &this.machine.threads); } @@ -370,9 +370,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(None); } let old_lock_count = mutex.lock_count; - mutex.lock_count = old_lock_count - .checked_sub(1) - .expect("invariant violation: lock_count == 0 iff the thread is unlocked"); + mutex.lock_count = old_lock_count.strict_sub(1); if mutex.lock_count == 0 { mutex.owner = None; // The mutex is completely unlocked. Try transferring ownership @@ -450,7 +448,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("rwlock_reader_lock: {:?} now also held (one more time) by {:?}", id, thread); let rwlock = &mut this.machine.sync.rwlocks[id]; let count = rwlock.readers.entry(thread).or_insert(0); - *count = count.checked_add(1).expect("the reader counter overflowed"); + *count = count.strict_add(1); if let Some(data_race) = &this.machine.data_race { data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads); } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index c3b34f0c0c6ba..a5afd029b6519 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -643,10 +643,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(index_len as u64, dest_len); for i in 0..dest_len { - let src_index: u64 = index[usize::try_from(i).unwrap()] - .unwrap_leaf() - .to_u32() - .into(); + let src_index: u64 = + index[usize::try_from(i).unwrap()].unwrap_leaf().to_u32().into(); let dest = this.project_index(&dest, i)?; let val = if src_index < left_len { diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 68eca5a3a0ffd..533992e35ab17 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -142,7 +142,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { os_str: &OsStr, memkind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { - let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. + let size = u64::try_from(os_str.len()).unwrap().strict_add(1); // Make space for `0` terminator. let this = self.eval_context_mut(); let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u8, size); @@ -158,7 +158,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { os_str: &OsStr, memkind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { - let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. + let size = u64::try_from(os_str.len()).unwrap().strict_add(1); // Make space for `0x0000` terminator. let this = self.eval_context_mut(); let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size); diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index c8805f054ecca..262e71756c64f 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -893,7 +893,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dirent64_layout = this.libc_ty_layout("dirent64"); let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes(); - let size = d_name_offset.checked_add(name_len).unwrap(); + let size = d_name_offset.strict_add(name_len); let entry = this.allocate_ptr( Size::from_bytes(size), @@ -994,7 +994,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { name_place.ptr(), name_place.layout.size.bytes(), )?; - let file_name_len = file_name_buf_len.checked_sub(1).unwrap(); + let file_name_len = file_name_buf_len.strict_sub(1); if !name_fits { throw_unsup_format!( "a directory entry had a name too large to fit in libc::dirent" diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index aa4dc98287016..a5661460e95cf 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -57,10 +57,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let flags = this.read_scalar(flags)?.to_i32()?; let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC"); - if flags == epoll_cloexec { - // Miri does not support exec, so this flag has no effect. - } else if flags != 0 { - throw_unsup_format!("epoll_create1 flags {flags} are not implemented"); + + // Miri does not support exec, so EPOLL_CLOEXEC flag has no effect. + if flags != epoll_cloexec && flags != 0 { + throw_unsup_format!( + "epoll_create1: flag {:#x} is unsupported, only 0 or EPOLL_CLOEXEC are allowed", + flags + ); } let fd = this.machine.fds.insert_fd(FileDescriptor::new(Epoll::default())); diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index 97bf6348f0b0d..c639ea2f84603 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1,15 +1,38 @@ +use std::cell::RefCell; +use std::collections::VecDeque; use std::io; +use std::io::{Error, ErrorKind, Read}; +use std::rc::{Rc, Weak}; use crate::shims::unix::*; -use crate::*; +use crate::{concurrency::VClock, *}; use self::fd::FileDescriptor; +/// The maximum capacity of the socketpair buffer in bytes. +/// This number is arbitrary as the value can always +/// be configured in the real system. +const MAX_SOCKETPAIR_BUFFER_CAPACITY: usize = 212992; + /// Pair of connected sockets. -/// -/// We currently don't allow sending any data through this pair, so this can be just a dummy. #[derive(Debug)] -struct SocketPair; +struct SocketPair { + // By making the write link weak, a `write` can detect when all readers are + // gone, and trigger EPIPE as appropriate. + writebuf: Weak>, + readbuf: Rc>, + is_nonblock: bool, +} + +#[derive(Debug)] +struct Buffer { + buf: VecDeque, + clock: VClock, + /// Indicates if there is at least one active writer to this buffer. + /// If all writers of this buffer are dropped, buf_has_writer becomes false and we + /// indicate EOF instead of blocking. + buf_has_writer: bool, +} impl FileDescription for SocketPair { fn name(&self) -> &'static str { @@ -20,17 +43,102 @@ impl FileDescription for SocketPair { self: Box, _communicate_allowed: bool, ) -> InterpResult<'tcx, io::Result<()>> { + // This is used to signal socketfd of other side that there is no writer to its readbuf. + // If the upgrade fails, there is no need to update as all read ends have been dropped. + if let Some(writebuf) = self.writebuf.upgrade() { + writebuf.borrow_mut().buf_has_writer = false; + }; Ok(Ok(())) } + + fn read<'tcx>( + &mut self, + _communicate_allowed: bool, + bytes: &mut [u8], + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result> { + let request_byte_size = bytes.len(); + let mut readbuf = self.readbuf.borrow_mut(); + + // Always succeed on read size 0. + if request_byte_size == 0 { + return Ok(Ok(0)); + } + + if readbuf.buf.is_empty() { + if !readbuf.buf_has_writer { + // Socketpair with no writer and empty buffer. + // 0 bytes successfully read indicates end-of-file. + return Ok(Ok(0)); + } else { + if self.is_nonblock { + // Non-blocking socketpair with writer and empty buffer. + // https://linux.die.net/man/2/read + // EAGAIN or EWOULDBLOCK can be returned for socket, + // POSIX.1-2001 allows either error to be returned for this case. + // Since there is no ErrorKind for EAGAIN, WouldBlock is used. + return Ok(Err(Error::from(ErrorKind::WouldBlock))); + } else { + // Blocking socketpair with writer and empty buffer. + // FIXME: blocking is currently not supported + throw_unsup_format!("socketpair read: blocking isn't supported yet"); + } + } + } + + // Synchronize with all previous writes to this buffer. + // FIXME: this over-synchronizes; a more precise approach would be to + // only sync with the writes whose data we will read. + ecx.acquire_clock(&readbuf.clock); + // Do full read / partial read based on the space available. + // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior. + let actual_read_size = readbuf.buf.read(bytes).unwrap(); + return Ok(Ok(actual_read_size)); + } + + fn write<'tcx>( + &mut self, + _communicate_allowed: bool, + bytes: &[u8], + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result> { + let write_size = bytes.len(); + // Always succeed on write size 0. + // ("If count is zero and fd refers to a file other than a regular file, the results are not specified.") + if write_size == 0 { + return Ok(Ok(0)); + } + + let Some(writebuf) = self.writebuf.upgrade() else { + // If the upgrade from Weak to Rc fails, it indicates that all read ends have been + // closed. + return Ok(Err(Error::from(ErrorKind::BrokenPipe))); + }; + let mut writebuf = writebuf.borrow_mut(); + let data_size = writebuf.buf.len(); + let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.checked_sub(data_size).unwrap(); + if available_space == 0 { + if self.is_nonblock { + // Non-blocking socketpair with a full buffer. + return Ok(Err(Error::from(ErrorKind::WouldBlock))); + } else { + // Blocking socketpair with a full buffer. + throw_unsup_format!("socketpair write: blocking isn't supported yet"); + } + } + // Remember this clock so `read` can synchronize with us. + if let Some(clock) = &ecx.release_clock() { + writebuf.clock.join(clock); + } + // Do full write / partial write based on the space available. + let actual_write_size = write_size.min(available_space); + writebuf.buf.extend(&bytes[..actual_write_size]); + return Ok(Ok(actual_write_size)); + } } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Currently this function this function is a stub. Eventually we need to - /// properly implement an FD type for sockets and have this function create - /// two sockets and associated FDs such that writing to one will produce - /// data that can be read from the other. - /// /// For more information on the arguments see the socketpair manpage: /// fn socketpair( @@ -42,17 +150,80 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let _domain = this.read_scalar(domain)?.to_i32()?; - let _type_ = this.read_scalar(type_)?.to_i32()?; - let _protocol = this.read_scalar(protocol)?.to_i32()?; + let domain = this.read_scalar(domain)?.to_i32()?; + let mut type_ = this.read_scalar(type_)?.to_i32()?; + let protocol = this.read_scalar(protocol)?.to_i32()?; let sv = this.deref_pointer(sv)?; - // FIXME: fail on unsupported inputs + let mut is_sock_nonblock = false; + + // Parse and remove the type flags that we support. If type != 0 after removing, + // unsupported flags are used. + if type_ & this.eval_libc_i32("SOCK_STREAM") == this.eval_libc_i32("SOCK_STREAM") { + type_ &= !(this.eval_libc_i32("SOCK_STREAM")); + } + + // SOCK_NONBLOCK only exists on Linux. + if this.tcx.sess.target.os == "linux" { + if type_ & this.eval_libc_i32("SOCK_NONBLOCK") == this.eval_libc_i32("SOCK_NONBLOCK") { + is_sock_nonblock = true; + type_ &= !(this.eval_libc_i32("SOCK_NONBLOCK")); + } + if type_ & this.eval_libc_i32("SOCK_CLOEXEC") == this.eval_libc_i32("SOCK_CLOEXEC") { + type_ &= !(this.eval_libc_i32("SOCK_CLOEXEC")); + } + } + + // Fail on unsupported input. + // AF_UNIX and AF_LOCAL are synonyms, so we accept both in case + // their values differ. + if domain != this.eval_libc_i32("AF_UNIX") && domain != this.eval_libc_i32("AF_LOCAL") { + throw_unsup_format!( + "socketpair: domain {:#x} is unsupported, only AF_UNIX \ + and AF_LOCAL are allowed", + domain + ); + } else if type_ != 0 { + throw_unsup_format!( + "socketpair: type {:#x} is unsupported, only SOCK_STREAM, \ + SOCK_CLOEXEC and SOCK_NONBLOCK are allowed", + type_ + ); + } else if protocol != 0 { + throw_unsup_format!( + "socketpair: socket protocol {protocol} is unsupported, \ + only 0 is allowed", + ); + } + + let buffer1 = Rc::new(RefCell::new(Buffer { + buf: VecDeque::new(), + clock: VClock::default(), + buf_has_writer: true, + })); + + let buffer2 = Rc::new(RefCell::new(Buffer { + buf: VecDeque::new(), + clock: VClock::default(), + buf_has_writer: true, + })); + + let socketpair_0 = SocketPair { + writebuf: Rc::downgrade(&buffer1), + readbuf: Rc::clone(&buffer2), + is_nonblock: is_sock_nonblock, + }; + + let socketpair_1 = SocketPair { + writebuf: Rc::downgrade(&buffer2), + readbuf: Rc::clone(&buffer1), + is_nonblock: is_sock_nonblock, + }; let fds = &mut this.machine.fds; - let sv0 = fds.insert_fd(FileDescriptor::new(SocketPair)); + let sv0 = fds.insert_fd(FileDescriptor::new(socketpair_0)); let sv0 = Scalar::from_int(sv0, sv.layout.size); - let sv1 = fds.insert_fd(FileDescriptor::new(SocketPair)); + let sv1 = fds.insert_fd(FileDescriptor::new(socketpair_1)); let sv1 = Scalar::from_int(sv1, sv.layout.size); this.write_scalar(sv0, &sv)?; diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 07d737e06635e..0d2977b7b6fe8 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -176,8 +176,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // of 4. let chunk_base = i & !0b11; let src_i = u64::from(this.read_scalar(&control)?.to_u32()? & 0b11) - .checked_add(chunk_base) - .unwrap(); + .strict_add(chunk_base); this.copy_op( &this.project_index(&data, src_i)?, @@ -210,9 +209,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // second instead of the first, ask Intel). To read the value from the current // chunk, add the destination index truncated to a multiple of 2. let chunk_base = i & !1; - let src_i = ((this.read_scalar(&control)?.to_u64()? >> 1) & 1) - .checked_add(chunk_base) - .unwrap(); + let src_i = + ((this.read_scalar(&control)?.to_u64()? >> 1) & 1).strict_add(chunk_base); this.copy_op( &this.project_index(&data, src_i)?, diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index f2d120df21c41..b71aec02166be 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -18,6 +18,7 @@ mod sse; mod sse2; mod sse3; mod sse41; +mod sse42; mod ssse3; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -137,6 +138,11 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this, link_name, abi, args, dest, ); } + name if name.starts_with("sse42.") => { + return sse42::EvalContextExt::emulate_x86_sse42_intrinsic( + this, link_name, abi, args, dest, + ); + } name if name.starts_with("aesni.") => { return aesni::EvalContextExt::emulate_x86_aesni_intrinsic( this, link_name, abi, args, dest, diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs new file mode 100644 index 0000000000000..4e50d5e5dcfbf --- /dev/null +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -0,0 +1,500 @@ +use rustc_middle::mir; +use rustc_middle::ty::layout::LayoutOf as _; +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::abi::Size; +use rustc_target::spec::abi::Abi; + +use crate::*; + +/// A bitmask constant for scrutinizing the immediate byte provided +/// to the string comparison intrinsics. It distinuishes between +/// 16-bit integers and 8-bit integers. See [`compare_strings`] +/// for more details about the immediate byte. +const USE_WORDS: u8 = 1; + +/// A bitmask constant for scrutinizing the immediate byte provided +/// to the string comparison intrinsics. It distinuishes between +/// signed integers and unsigned integers. See [`compare_strings`] +/// for more details about the immediate byte. +const USE_SIGNED: u8 = 2; + +/// The main worker for the string comparison intrinsics, where the given +/// strings are analyzed according to the given immediate byte. +/// +/// # Arguments +/// +/// * `str1` - The first string argument. It is always a length 16 array of bytes +/// or a length 8 array of two-byte words. +/// * `str2` - The second string argument. It is always a length 16 array of bytes +/// or a length 8 array of two-byte words. +/// * `len` is the length values of the supplied strings. It is distinct from the operand length +/// in that it describes how much of `str1` and `str2` will be used for the calculation and may +/// be smaller than the array length of `str1` and `str2`. The string length is counted in bytes +/// if using byte operands and in two-byte words when using two-byte word operands. +/// If the value is `None`, the length of a string is determined by the first +/// null value inside the string. +/// * `imm` is the immediate byte argument supplied to the intrinsic. The byte influences +/// the operation as follows: +/// +/// ```text +/// 0babccddef +/// || | |||- Use of bytes vs use of two-byte words inside the operation. +/// || | || +/// || | ||- Use of signed values versus use of unsigned values. +/// || | | +/// || | |- The comparison operation performed. A total of four operations are available. +/// || | * Equal any: Checks which characters of `str2` are inside `str1`. +/// || | * String ranges: Check if characters in `str2` are inside the provided character ranges. +/// || | Adjacent characters in `str1` constitute one range. +/// || | * String comparison: Mark positions where `str1` and `str2` have the same character. +/// || | * Substring search: Mark positions where `str1` is a substring in `str2`. +/// || | +/// || |- Result Polarity. The result bits may be subjected to a bitwise complement +/// || if these bits are set. +/// || +/// ||- Output selection. This bit has two meanings depending on the instruction. +/// | If the instruction is generating a mask, it distinguishes between a bit mask +/// | and a byte mask. Otherwise it distinguishes between the most significand bit +/// | and the least significand bit when generating an index. +/// | +/// |- This bit is ignored. It is expected that this bit is set to zero, but it is +/// not a requirement. +/// ``` +/// +/// # Returns +/// +/// A result mask. The bit at index `i` inside the mask is set if 'str2' starting at `i` +/// fulfills the test as defined inside the immediate byte. +/// The mask may be negated if negation flags inside the immediate byte are set. +/// +/// For more information, see the Intel Software Developer's Manual, Vol. 2b, Chapter 4.1. +#[allow(clippy::arithmetic_side_effects)] +fn compare_strings<'tcx>( + this: &mut MiriInterpCx<'tcx>, + str1: &OpTy<'tcx>, + str2: &OpTy<'tcx>, + len: Option<(u64, u64)>, + imm: u8, +) -> InterpResult<'tcx, i32> { + let default_len = default_len::(imm); + let (len1, len2) = if let Some(t) = len { + t + } else { + let len1 = implicit_len(this, str1, imm)?.unwrap_or(default_len); + let len2 = implicit_len(this, str2, imm)?.unwrap_or(default_len); + (len1, len2) + }; + + let mut result = 0; + match (imm >> 2) & 3 { + 0 => { + // Equal any: Checks which characters of `str2` are inside `str1`. + for i in 0..len2 { + let ch2 = this.read_immediate(&this.project_index(str2, i)?)?; + + for j in 0..len1 { + let ch1 = this.read_immediate(&this.project_index(str1, j)?)?; + + let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?; + if eq.to_scalar().to_bool()? { + result |= 1 << i; + break; + } + } + } + } + 1 => { + // String ranges: Check if characters in `str2` are inside the provided character ranges. + // Adjacent characters in `str1` constitute one range. + let len1 = len1 - (len1 & 1); + let get_ch = |ch: Scalar| -> InterpResult<'tcx, i32> { + let result = match (imm & USE_WORDS != 0, imm & USE_SIGNED != 0) { + (true, true) => i32::from(ch.to_i16()?), + (true, false) => i32::from(ch.to_u16()?), + (false, true) => i32::from(ch.to_i8()?), + (false, false) => i32::from(ch.to_u8()?), + }; + Ok(result) + }; + + for i in 0..len2 { + for j in (0..len1).step_by(2) { + let ch2 = get_ch(this.read_scalar(&this.project_index(str2, i)?)?)?; + let ch1_1 = get_ch(this.read_scalar(&this.project_index(str1, j)?)?)?; + let ch1_2 = get_ch(this.read_scalar(&this.project_index(str1, j + 1)?)?)?; + + if ch1_1 <= ch2 && ch2 <= ch1_2 { + result |= 1 << i; + } + } + } + } + 2 => { + // String comparison: Mark positions where `str1` and `str2` have the same character. + result = (1 << default_len) - 1; + result ^= (1 << len1.max(len2)) - 1; + + for i in 0..len1.min(len2) { + let ch1 = this.read_immediate(&this.project_index(str1, i)?)?; + let ch2 = this.read_immediate(&this.project_index(str2, i)?)?; + let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?; + result |= i32::from(eq.to_scalar().to_bool()?) << i; + } + } + 3 => { + // Substring search: Mark positions where `str1` is a substring in `str2`. + if len1 == 0 { + result = (1 << default_len) - 1; + } else if len1 <= len2 { + for i in 0..len2 { + if len1 > len2 - i { + break; + } + + result |= 1 << i; + + for j in 0..len1 { + let k = i + j; + + if k >= default_len { + break; + } else { + let ch1 = this.read_immediate(&this.project_index(str1, j)?)?; + let ch2 = this.read_immediate(&this.project_index(str2, k)?)?; + let ne = this.binary_op(mir::BinOp::Ne, &ch1, &ch2)?; + + if ne.to_scalar().to_bool()? { + result &= !(1 << i); + break; + } + } + } + } + } + } + _ => unreachable!(), + } + + // Polarity: Possibly perform a bitwise complement on the result. + match (imm >> 4) & 3 { + 3 => result ^= (1 << len1) - 1, + 1 => result ^= (1 << default_len) - 1, + _ => (), + } + + Ok(result) +} + +/// Obtain the arguments of the intrinsic based on its name. +/// The result is a tuple with the following values: +/// * The first string argument. +/// * The second string argument. +/// * The string length values, if the intrinsic requires them. +/// * The immediate instruction byte. +/// +/// The string arguments will be transmuted into arrays of bytes +/// or two-byte words, depending on the value of the immediate byte. +/// Originally, they are [__m128i](https://doc.rust-lang.org/stable/core/arch/x86_64/struct.__m128i.html) values +/// corresponding to the x86 128-bit integer SIMD type. +fn deconstruct_args<'tcx>( + unprefixed_name: &str, + this: &mut MiriInterpCx<'tcx>, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], +) -> InterpResult<'tcx, (OpTy<'tcx>, OpTy<'tcx>, Option<(u64, u64)>, u8)> { + let array_layout_fn = |this: &mut MiriInterpCx<'tcx>, imm: u8| { + if imm & USE_WORDS != 0 { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8)) + } else { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16)) + } + }; + + // The fourth letter of each string comparison intrinsic is either 'e' for "explicit" or 'i' for "implicit". + // The distinction will correspond to the intrinsics type signature. In this constext, "explicit" and "implicit" + // refer to the way the string length is determined. The length is either passed explicitly in the "explicit" + // case or determined by a null terminator in the "implicit" case. + let is_explicit = match unprefixed_name.as_bytes().get(4) { + Some(&b'e') => true, + Some(&b'i') => false, + _ => unreachable!(), + }; + + if is_explicit { + let [str1, len1, str2, len2, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let imm = this.read_scalar(imm)?.to_u8()?; + + let default_len = default_len::(imm); + let len1 = u64::from(this.read_scalar(len1)?.to_u32()?.min(default_len)); + let len2 = u64::from(this.read_scalar(len2)?.to_u32()?.min(default_len)); + + let array_layout = array_layout_fn(this, imm)?; + let str1 = str1.transmute(array_layout, this)?; + let str2 = str2.transmute(array_layout, this)?; + + Ok((str1, str2, Some((len1, len2)), imm)) + } else { + let [str1, str2, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let imm = this.read_scalar(imm)?.to_u8()?; + + let array_layout = array_layout_fn(this, imm)?; + let str1 = str1.transmute(array_layout, this)?; + let str2 = str2.transmute(array_layout, this)?; + + Ok((str1, str2, None, imm)) + } +} + +/// Calculate the c-style string length for a given string `str`. +/// The string is either a length 16 array of bytes a length 8 array of two-byte words. +fn implicit_len<'tcx>( + this: &mut MiriInterpCx<'tcx>, + str: &OpTy<'tcx>, + imm: u8, +) -> InterpResult<'tcx, Option> { + let mut result = None; + let zero = ImmTy::from_int(0, str.layout.field(this, 0)); + + for i in 0..default_len::(imm) { + let ch = this.read_immediate(&this.project_index(str, i)?)?; + let is_zero = this.binary_op(mir::BinOp::Eq, &ch, &zero)?; + if is_zero.to_scalar().to_bool()? { + result = Some(i); + break; + } + } + Ok(result) +} + +#[inline] +fn default_len>(imm: u8) -> T { + if imm & USE_WORDS != 0 { T::from(8u8) } else { T::from(16u8) } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_x86_sse42_intrinsic( + &mut self, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "sse4.2")?; + // Prefix should have already been checked. + let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse42.").unwrap(); + + match unprefixed_name { + // Used to implement the `_mm_cmpestrm` and the `_mm_cmpistrm` functions. + // These functions compare the input strings and return the resulting mask. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1044,922 + "pcmpistrm128" | "pcmpestrm128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + + // The sixth bit inside the immediate byte distiguishes + // between a bit mask or a byte mask when generating a mask. + if imm & 0b100_0000 != 0 { + let (array_layout, size) = if imm & USE_WORDS != 0 { + (this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8))?, 2) + } else { + (this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16))?, 1) + }; + let size = Size::from_bytes(size); + let dest = dest.transmute(array_layout, this)?; + + for i in 0..default_len::(imm) { + let result = helpers::bool_to_simd_element(mask & (1 << i) != 0, size); + this.write_scalar(result, &this.project_index(&dest, i)?)?; + } + } else { + let layout = this.layout_of(this.tcx.types.i128)?; + let dest = dest.transmute(layout, this)?; + this.write_scalar(Scalar::from_i128(i128::from(mask)), &dest)?; + } + } + + // Used to implement the `_mm_cmpestra` and the `_mm_cmpistra` functions. + // These functions compare the input strings and return `1` if the end of the second + // input string is not reached and the resulting mask is zero, and `0` otherwise. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=919,1041 + "pcmpistria128" | "pcmpestria128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let result = if compare_strings(this, &str1, &str2, len, imm)? != 0 { + false + } else if let Some((_, len)) = len { + len >= default_len::(imm) + } else { + implicit_len(this, &str1, imm)?.is_some() + }; + + this.write_scalar(Scalar::from_i32(i32::from(result)), dest)?; + } + + // Used to implement the `_mm_cmpestri` and the `_mm_cmpistri` functions. + // These functions compare the input strings and return the bit index + // for most significant or least significant bit of the resulting mask. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=921,1043 + "pcmpistri128" | "pcmpestri128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + + let len = default_len::(imm); + // The sixth bit inside the immediate byte distiguishes between the least + // significant bit and the most significant bit when generating an index. + let result = if imm & 0b100_0000 != 0 { + // most significant bit + 31u32.wrapping_sub(mask.leading_zeros()).min(len) + } else { + // least significant bit + mask.trailing_zeros().min(len) + }; + this.write_scalar(Scalar::from_i32(i32::try_from(result).unwrap()), dest)?; + } + + // Used to implement the `_mm_cmpestro` and the `_mm_cmpistro` functions. + // These functions compare the input strings and return the lowest bit of the + // resulting mask. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=923,1045 + "pcmpistrio128" | "pcmpestrio128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + this.write_scalar(Scalar::from_i32(mask & 1), dest)?; + } + + // Used to implement the `_mm_cmpestrc` and the `_mm_cmpistrc` functions. + // These functions compare the input strings and return `1` if the resulting + // mask was non-zero, and `0` otherwise. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=920,1042 + "pcmpistric128" | "pcmpestric128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + this.write_scalar(Scalar::from_i32(i32::from(mask != 0)), dest)?; + } + + // Used to implement the `_mm_cmpistrz` and the `_mm_cmpistrs` functions. + // These functions return `1` if the string end has been reached and `0` otherwise. + // Since these functions define the string length implicitly, it is equal to a + // search for a null terminator (see `deconstruct_args` for more details). + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925 + "pcmpistriz128" | "pcmpistris128" => { + let [str1, str2, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let imm = this.read_scalar(imm)?.to_u8()?; + + let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 }; + let array_layout = if imm & USE_WORDS != 0 { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8))? + } else { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16))? + }; + let str = str.transmute(array_layout, this)?; + let result = implicit_len(this, &str, imm)?.is_some(); + + this.write_scalar(Scalar::from_i32(i32::from(result)), dest)?; + } + + // Used to implement the `_mm_cmpestrz` and the `_mm_cmpestrs` functions. + // These functions return 1 if the explicitly passed string length is smaller + // than 16 for byte-sized operands or 8 for word-sized operands. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047 + "pcmpestriz128" | "pcmpestris128" => { + let [_, len1, _, len2, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 }; + let len = this.read_scalar(len)?.to_i32()?; + let imm = this.read_scalar(imm)?.to_u8()?; + this.write_scalar( + Scalar::from_i32(i32::from(len < default_len::(imm))), + dest, + )?; + } + + // Used to implement the `_mm_crc32_u{8, 16, 32, 64}` functions. + // These functions calculate a 32-bit CRC using `0x11EDC6F41` + // as the polynomial, also known as CRC32C. + // https://datatracker.ietf.org/doc/html/rfc3720#section-12.1 + "crc32.32.8" | "crc32.32.16" | "crc32.32.32" | "crc32.64.64" => { + let bit_size = match unprefixed_name { + "crc32.32.8" => 8, + "crc32.32.16" => 16, + "crc32.32.32" => 32, + "crc32.64.64" => 64, + _ => unreachable!(), + }; + + if bit_size == 64 && this.tcx.sess.target.arch != "x86_64" { + return Ok(EmulateItemResult::NotSupported); + } + + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let left = this.read_scalar(left)?; + let right = this.read_scalar(right)?; + + let crc = if bit_size == 64 { + // The 64-bit version will only consider the lower 32 bits, + // while the upper 32 bits get discarded. + #[allow(clippy::cast_possible_truncation)] + u128::from((left.to_u64()? as u32).reverse_bits()) + } else { + u128::from(left.to_u32()?.reverse_bits()) + }; + let v = match bit_size { + 8 => u128::from(right.to_u8()?.reverse_bits()), + 16 => u128::from(right.to_u16()?.reverse_bits()), + 32 => u128::from(right.to_u32()?.reverse_bits()), + 64 => u128::from(right.to_u64()?.reverse_bits()), + _ => unreachable!(), + }; + + // Perform polynomial division modulo 2. + // The algorithm for the division is an adapted version of the + // schoolbook division algorithm used for normal integer or polynomial + // division. In this context, the quotient is not calculated, since + // only the remainder is needed. + // + // The algorithm works as follows: + // 1. Pull down digits until division can be performed. In the context of division + // modulo 2 it means locating the most significant digit of the dividend and shifting + // the divisor such that the position of the divisors most significand digit and the + // dividends most significand digit match. + // 2. Perform a division and determine the remainder. Since it is arithmetic modulo 2, + // this operation is a simple bitwise exclusive or. + // 3. Repeat steps 1. and 2. until the full remainder is calculated. This is the case + // once the degree of the remainder polynomial is smaller than the degree of the + // divisor polynomial. In other words, the number of leading zeros of the remainder + // is larger than the number of leading zeros of the divisor. It is important to + // note that standard arithmetic comparison is not applicable here: + // 0b10011 / 0b11111 = 0b01100 is a valid division, even though the dividend is + // smaller than the divisor. + let mut dividend = (crc << bit_size) ^ (v << 32); + const POLYNOMIAL: u128 = 0x11EDC6F41; + while dividend.leading_zeros() <= POLYNOMIAL.leading_zeros() { + dividend ^= + (POLYNOMIAL << POLYNOMIAL.leading_zeros()) >> dividend.leading_zeros(); + } + + let result = u32::try_from(dividend).unwrap().reverse_bits(); + let result = if bit_size == 64 { + Scalar::from_u64(u64::from(result)) + } else { + Scalar::from_u32(result) + }; + + this.write_scalar(result, dest)?; + } + _ => return Ok(EmulateItemResult::NotSupported), + } + Ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.rs new file mode 100644 index 0000000000000..c28a6d966fe55 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.rs @@ -0,0 +1,12 @@ +//@ignore-target-windows: no libc socketpair on Windows + +// This is temporarily here because blocking on fd is not supported yet. +// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair + +fn main() { + let mut fds = [-1, -1]; + let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + // The read below will be blocked because the buffer is empty. + let mut buf: [u8; 3] = [0; 3]; + let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; //~ERROR: blocking isn't supported +} diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr new file mode 100644 index 0000000000000..b5ed72d9f1b18 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: socketpair read: blocking isn't supported yet + --> $DIR/socketpair_read_blocking.rs:LL:CC + | +LL | let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair read: blocking isn't supported yet + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/socketpair_read_blocking.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.rs new file mode 100644 index 0000000000000..e2fbc0ae4b426 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.rs @@ -0,0 +1,16 @@ +//@ignore-target-windows: no libc socketpair on Windows +// This is temporarily here because blocking on fd is not supported yet. +// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair +fn main() { + let mut fds = [-1, -1]; + let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + // Write size > buffer capacity + // Used up all the space in the buffer. + let arr1: [u8; 212992] = [1; 212992]; + let _ = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; + let data = "abc".as_bytes().as_ptr(); + // The write below will be blocked as the buffer is full. + let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; //~ERROR: blocking isn't supported + let mut buf: [u8; 3] = [0; 3]; + let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; +} diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr new file mode 100644 index 0000000000000..7b3a0d27636de --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: socketpair write: blocking isn't supported yet + --> $DIR/socketpair_write_blocking.rs:LL:CC + | +LL | let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair write: blocking isn't supported yet + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/socketpair_write_blocking.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs b/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs index faf5fb699a6a1..ed451a435b958 100644 --- a/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs +++ b/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs @@ -3,11 +3,17 @@ #[derive(Copy, Clone)] #[allow(unused)] -enum E {A, B, C } +enum E { + A, + B, + C, +} -fn cast(ptr: *const E) { unsafe { - let _val = *ptr as u32; //~ERROR: enum value has invalid tag -}} +fn cast(ptr: *const E) { + unsafe { + let _val = *ptr as u32; //~ERROR: enum value has invalid tag + } +} pub fn main() { let v = u32::MAX; diff --git a/src/tools/miri/tests/fail/validity/invalid_enum_cast.stderr b/src/tools/miri/tests/fail/validity/invalid_enum_cast.stderr index d898887604581..30afb5e8087b4 100644 --- a/src/tools/miri/tests/fail/validity/invalid_enum_cast.stderr +++ b/src/tools/miri/tests/fail/validity/invalid_enum_cast.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: enum value has invalid tag: 0xff --> $DIR/invalid_enum_cast.rs:LL:CC | -LL | let _val = *ptr as u32; - | ^^^^^^^^^^^ enum value has invalid tag: 0xff +LL | let _val = *ptr as u32; + | ^^^^^^^^^^^ enum value has invalid tag: 0xff | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs new file mode 100644 index 0000000000000..324c0127ee976 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -0,0 +1,124 @@ +//@ignore-target-windows: No libc socketpair on Windows +// test_race depends on a deterministic schedule. +//@compile-flags: -Zmiri-preemption-rate=0 +use std::thread; +fn main() { + test_socketpair(); + test_socketpair_threaded(); + test_race(); +} + +fn test_socketpair() { + let mut fds = [-1, -1]; + let mut res = + unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + + // Read size == data available in buffer. + let data = "abcde".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + let mut buf: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf, "abcde".as_bytes()); + + // Read size > data available in buffer. + let data = "abc".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3).try_into().unwrap() }; + assert_eq!(res, 3); + let mut buf2: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 3); + assert_eq!(&buf2[0..3], "abc".as_bytes()); + + // Test read and write from another direction. + // Read size == data available in buffer. + let data = "12345".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + let mut buf3: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf3, "12345".as_bytes()); + + // Read size > data available in buffer. + let data = "123".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3).try_into().unwrap() }; + assert_eq!(res, 3); + let mut buf4: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 3); + assert_eq!(&buf4[0..3], "123".as_bytes()); +} + +fn test_socketpair_threaded() { + let mut fds = [-1, -1]; + let mut res = + unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + + let data = "abcde".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + let thread1 = thread::spawn(move || { + let mut buf: [u8; 5] = [0; 5]; + let res: i64 = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + .try_into() + .unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf, "abcde".as_bytes()); + }); + thread1.join().unwrap(); + + // Read and write from different direction + let thread2 = thread::spawn(move || { + let data = "12345".as_bytes().as_ptr(); + let res: i64 = + unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + }); + thread2.join().unwrap(); + let mut buf: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf, "12345".as_bytes()); +} +fn test_race() { + static mut VAL: u8 = 0; + let mut fds = [-1, -1]; + let mut res = + unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + let thread1 = thread::spawn(move || { + let mut buf: [u8; 1] = [0; 1]; + // write() from the main thread will occur before the read() here + // because preemption is disabled and the main thread yields after write(). + let res: i32 = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + .try_into() + .unwrap() + }; + assert_eq!(res, 1); + assert_eq!(buf, "a".as_bytes()); + unsafe { assert_eq!(VAL, 1) }; + }); + unsafe { VAL = 1 }; + let data = "a".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 1).try_into().unwrap() }; + assert_eq!(res, 1); + thread::yield_now(); + thread1.join().unwrap(); +} diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs new file mode 100644 index 0000000000000..3ac53ea8b933d --- /dev/null +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs @@ -0,0 +1,443 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=+sse4.2 + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; +use std::mem::transmute; + +fn main() { + assert!(is_x86_feature_detected!("sse4.2")); + + unsafe { + test_sse42(); + } +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_sse42() { + // Mostly copied from library/stdarch/crates/core_arch/src/x86/sse42.rs + + test_crc(); + test_cmp(); + test_str(); +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_crc() { + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u8() { + let crc = 0x2aa1e72b; + let v = 0x2a; + let i = _mm_crc32_u8(crc, v); + assert_eq!(i, 0xf24122e4); + + let crc = 0x61343ec4; + let v = 0xef; + let i = _mm_crc32_u8(crc, v); + assert_eq!(i, 0xb95511db); + + let crc = 0xbadeafe; + let v = 0xc0; + let i = _mm_crc32_u8(crc, v); + assert_eq!(i, 0x9c905b7c); + } + test_mm_crc32_u8(); + + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u16() { + let crc = 0x8ecec3b5; + let v = 0x22b; + let i = _mm_crc32_u16(crc, v); + assert_eq!(i, 0x13bb2fb); + + let crc = 0x150bc664; + let v = 0xa6c0; + let i = _mm_crc32_u16(crc, v); + assert_eq!(i, 0xab04fe4e); + + let crc = 0xbadeafe; + let v = 0xc0fe; + let i = _mm_crc32_u16(crc, v); + assert_eq!(i, 0x4b5fad4b); + } + test_mm_crc32_u16(); + + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u32() { + let crc = 0xae2912c8; + let v = 0x845fed; + let i = _mm_crc32_u32(crc, v); + assert_eq!(i, 0xffae2ed1); + + let crc = 0x1a198fe3; + let v = 0x885585c2; + let i = _mm_crc32_u32(crc, v); + assert_eq!(i, 0x22443a7b); + + let crc = 0xbadeafe; + let v = 0xc0febeef; + let i = _mm_crc32_u32(crc, v); + assert_eq!(i, 0xb309502f); + } + test_mm_crc32_u32(); + + #[cfg(target_arch = "x86_64")] + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u64() { + let crc = 0x7819dccd3e824; + let v = 0x2a22b845fed; + let i = _mm_crc32_u64(crc, v); + assert_eq!(i, 0xbb6cdc6c); + + let crc = 0x6dd960387fe13819; + let v = 0x1a7ea8fb571746b0; + let i = _mm_crc32_u64(crc, v); + assert_eq!(i, 0x315b4f6); + + let crc = 0xbadeafe; + let v = 0xc0febeefdadafefe; + let i = _mm_crc32_u64(crc, v); + assert_eq!(i, 0x5b44f54f); + } + #[cfg(not(target_arch = "x86_64"))] + unsafe fn test_mm_crc32_u64() {} + test_mm_crc32_u64(); +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_cmp() { + let a = _mm_set_epi64x(0x2a, 0); + let b = _mm_set1_epi64x(0x00); + let i = _mm_cmpgt_epi64(a, b); + assert_eq_m128i(i, _mm_set_epi64x(0xffffffffffffffffu64 as i64, 0x00)); +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_str() { + #[target_feature(enable = "sse4.2")] + unsafe fn str_to_m128i(s: &[u8]) -> __m128i { + assert!(s.len() <= 16); + let slice = &mut [0u8; 16]; + std::ptr::copy_nonoverlapping(s.as_ptr(), slice.as_mut_ptr(), s.len()); + _mm_loadu_si128(slice.as_ptr() as *const _) + } + + // Test the `_mm_cmpistrm` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrm() { + let a = str_to_m128i(b"Hello! Good-Bye!"); + let b = str_to_m128i(b"hello! good-bye!"); + let i = _mm_cmpistrm::<_SIDD_UNIT_MASK>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + 0x00, !0, !0, !0, !0, !0, !0, 0x00, + !0, !0, !0, !0, 0x00, !0, !0, !0, + ); + assert_eq_m128i(i, res); + } + test_mm_cmpistrm(); + + // Test the `_mm_cmpistri` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistri() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b" Hello "); + let i = _mm_cmpistri::<_SIDD_CMP_EQUAL_ORDERED>(a, b); + assert_eq!(3, i); + } + test_mm_cmpistri(); + + // Test the `_mm_cmpistrz` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrz() { + let a = str_to_m128i(b""); + let b = str_to_m128i(b"Hello"); + let i = _mm_cmpistrz::<_SIDD_CMP_EQUAL_ORDERED>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistrz(); + + // Test the `_mm_cmpistrc` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrc() { + let a = str_to_m128i(b" "); + let b = str_to_m128i(b" ! "); + let i = _mm_cmpistrc::<_SIDD_UNIT_MASK>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistrc(); + + // Test the `_mm_cmpistrs` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrs() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b""); + let i = _mm_cmpistrs::<_SIDD_CMP_EQUAL_ORDERED>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistrs(); + + // Test the `_mm_cmpistro` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistro() { + #[rustfmt::skip] + let a_bytes = _mm_setr_epi8( + 0x00, 0x47, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + #[rustfmt::skip] + let b_bytes = _mm_setr_epi8( + 0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + let a = a_bytes; + let b = b_bytes; + let i = _mm_cmpistro::<{ _SIDD_UWORD_OPS | _SIDD_UNIT_MASK }>(a, b); + assert_eq!(0, i); + } + test_mm_cmpistro(); + + // Test the `_mm_cmpistra` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistra() { + let a = str_to_m128i(b""); + let b = str_to_m128i(b"Hello!!!!!!!!!!!"); + let i = _mm_cmpistra::<_SIDD_UNIT_MASK>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistra(); + + // Test the `_mm_cmpestrm` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrm() { + let a = str_to_m128i(b"Hello!"); + let b = str_to_m128i(b"Hello."); + let i = _mm_cmpestrm::<_SIDD_UNIT_MASK>(a, 5, b, 5); + #[rustfmt::skip] + let r = _mm_setr_epi8( + !0, !0, !0, !0, !0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, r); + } + test_mm_cmpestrm(); + + // Test the `_mm_cmpestri` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestri() { + let a = str_to_m128i(b"bar - garbage"); + let b = str_to_m128i(b"foobar"); + let i = _mm_cmpestri::<_SIDD_CMP_EQUAL_ORDERED>(a, 3, b, 6); + assert_eq!(3, i); + } + test_mm_cmpestri(); + + // Test the `_mm_cmpestrz` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrz() { + let a = str_to_m128i(b""); + let b = str_to_m128i(b"Hello"); + let i = _mm_cmpestrz::<_SIDD_CMP_EQUAL_ORDERED>(a, 16, b, 6); + assert_eq!(1, i); + } + test_mm_cmpestrz(); + + // Test the `_mm_cmpestrs` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrc() { + let va = str_to_m128i(b"!!!!!!!!"); + let vb = str_to_m128i(b" "); + let i = _mm_cmpestrc::<_SIDD_UNIT_MASK>(va, 7, vb, 7); + assert_eq!(0, i); + } + test_mm_cmpestrc(); + + // Test the `_mm_cmpestrs` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrs() { + #[rustfmt::skip] + let a_bytes = _mm_setr_epi8( + 0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + let a = a_bytes; + let b = _mm_set1_epi8(0x00); + let i = _mm_cmpestrs::<_SIDD_UWORD_OPS>(a, 8, b, 0); + assert_eq!(0, i); + } + test_mm_cmpestrs(); + + // Test the `_mm_cmpestro` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestro() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b"World"); + let i = _mm_cmpestro::<_SIDD_UBYTE_OPS>(a, 5, b, 5); + assert_eq!(0, i); + } + test_mm_cmpestro(); + + // Test the `_mm_cmpestra` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestra() { + let a = str_to_m128i(b"Cannot match a"); + let b = str_to_m128i(b"Null after 14"); + let i = _mm_cmpestra::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_UNIT_MASK }>(a, 14, b, 16); + assert_eq!(1, i); + } + test_mm_cmpestra(); + + // Additional tests not inside the standard library. + + // Test the subset functionality of the intrinsic. + unsafe fn test_subset() { + let a = str_to_m128i(b"ABCDEFG"); + let b = str_to_m128i(b"ABC UVW XYZ EFG"); + + let i = _mm_cmpistrm::<{ _SIDD_CMP_EQUAL_ANY | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, !0, !0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, !0, !0, !0, 0x00, + ); + assert_eq_m128i(i, res); + } + test_subset(); + + // Properly test index generation. + unsafe fn test_index() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b"Hello Hello H"); + + let i = _mm_cmpistri::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_LEAST_SIGNIFICANT }>(a, b); + assert_eq!(i, 0); + + let i = _mm_cmpistri::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_MOST_SIGNIFICANT }>(a, b); + assert_eq!(i, 15); + + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b" "); + let i = _mm_cmpistri::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_MOST_SIGNIFICANT }>(a, b); + assert_eq!(i, 16); + } + test_index(); + + // Properly test the substring functionality of the intrinsics. + #[target_feature(enable = "sse4.2")] + unsafe fn test_substring() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b"Hello Hello H"); + + let i = _mm_cmpistrm::<{ _SIDD_CMP_EQUAL_ORDERED | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, 0x00, 0x00, 0x00, 0x00, 0x00, !0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, res); + } + test_substring(); + + // Test the range functionality of the intrinsics. + // Will also test signed values and word-sized values. + #[target_feature(enable = "sse4.2")] + unsafe fn test_ranges() { + let a = _mm_setr_epi16(0, 1, 7, 8, 0, 0, -100, 100); + let b = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 2, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, 0, 0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 3, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, 0, 0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 4, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, !0, !0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 6, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, !0, !0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 8, b, 8); + let res = _mm_setr_epi16(!0, !0, !0, !0, !0, !0, !0, !0); + assert_eq_m128i(i, res); + } + test_ranges(); + + // Confirm that the polarity bits work as indended. + #[target_feature(enable = "sse4.2")] + unsafe fn test_polarity() { + let a = str_to_m128i(b"Hello!"); + let b = str_to_m128i(b"hello?"); + + let i = _mm_cmpistrm::< + { (_SIDD_MASKED_NEGATIVE_POLARITY ^ _SIDD_NEGATIVE_POLARITY) | _SIDD_UNIT_MASK }, + >(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + 0x00, !0, !0, !0, !0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, res); + + let i = _mm_cmpistrm::<{ _SIDD_MASKED_NEGATIVE_POLARITY | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, 0x00, 0x00, 0x00, 0x00, !0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, res); + + let i = _mm_cmpistrm::<{ _SIDD_NEGATIVE_POLARITY | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, 0x00, 0x00, 0x00, 0x00, !0, !0, !0, + !0, !0, !0, !0, !0, !0, !0, !0, + ); + assert_eq_m128i(i, res); + } + test_polarity(); + + // Test the code path in which the intrinsic is supposed to + // return a bit mask instead of a byte mask. + #[target_feature(enable = "sse4.2")] + unsafe fn test_bitmask() { + let a = str_to_m128i(b"Hello! Good-Bye!"); + let b = str_to_m128i(b"hello! good-bye!"); + + let i = _mm_cmpistrm::<0>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi32(0b11101111_01111110, 0, 0, 0); + assert_eq_m128i(i, res); + + let i = _mm_cmpistrm::<_SIDD_MASKED_NEGATIVE_POLARITY>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi32(0b00010000_10000001, 0, 0, 0); + assert_eq_m128i(i, res); + } + test_bitmask(); +} + +#[track_caller] +#[target_feature(enable = "sse2")] +pub unsafe fn assert_eq_m128i(a: __m128i, b: __m128i) { + assert_eq!(transmute::<_, [u64; 2]>(a), transmute::<_, [u64; 2]>(b)) +} diff --git a/tests/ui/delegation/macro-inside-list.rs b/tests/ui/delegation/macro-inside-list.rs index 16c74d396ef00..d07a4e47dd4cd 100644 --- a/tests/ui/delegation/macro-inside-list.rs +++ b/tests/ui/delegation/macro-inside-list.rs @@ -14,9 +14,9 @@ struct S(u8); // Macro expansion works inside delegation items. macro_rules! u8 { () => { u8 } } -macro_rules! self_0 { () => { &self.0 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } impl Trait for S { - reuse ::{foo, bar} { self_0!() } + reuse ::{foo, bar} { self_0!(self) } } fn main() { diff --git a/tests/ui/delegation/not-supported.stderr b/tests/ui/delegation/not-supported.stderr index 339a8418b33a2..4ce01fd5d8827 100644 --- a/tests/ui/delegation/not-supported.stderr +++ b/tests/ui/delegation/not-supported.stderr @@ -124,6 +124,11 @@ LL | fn opaque_ret() -> impl Trait { unimplemented!() } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: opaque::Trait` will fail + --> $DIR/not-supported.rs:80:28 + | +LL | fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` @@ -154,6 +159,11 @@ LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: opaque::Trait` will fail + --> $DIR/not-supported.rs:72:32 + | +LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^ error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` --> $DIR/not-supported.rs:90:24 diff --git a/tests/ui/delegation/self-hygiene.rs b/tests/ui/delegation/self-hygiene.rs new file mode 100644 index 0000000000000..dac6c319416a3 --- /dev/null +++ b/tests/ui/delegation/self-hygiene.rs @@ -0,0 +1,20 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +macro_rules! emit_self { () => { self } } +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` + +struct S; +impl S { + fn method(self) { + emit_self!(); + } +} + +fn foo(arg: u8) {} +reuse foo as bar { + emit_self!() +} + +fn main() {} diff --git a/tests/ui/delegation/self-hygiene.stderr b/tests/ui/delegation/self-hygiene.stderr new file mode 100644 index 0000000000000..fa64b7d1d7f7b --- /dev/null +++ b/tests/ui/delegation/self-hygiene.stderr @@ -0,0 +1,31 @@ +error[E0424]: expected value, found module `self` + --> $DIR/self-hygiene.rs:4:34 + | +LL | macro_rules! emit_self { () => { self } } + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter +... +LL | / fn method(self) { +LL | | emit_self!(); + | | ------------ in this macro invocation +LL | | } + | |_____- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters + | + = note: this error originates in the macro `emit_self` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0424]: expected value, found module `self` + --> $DIR/self-hygiene.rs:4:34 + | +LL | macro_rules! emit_self { () => { self } } + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter +... +LL | / reuse foo as bar { +LL | | emit_self!() + | | ------------ in this macro invocation +LL | | } + | |_- delegation supports a `self` parameter, but a macro invocation can only access identifiers it receives from parameters + | + = note: this error originates in the macro `emit_self` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0424`. diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr index 92c233a0d9cfb..134fd098b7e4a 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -7,6 +7,11 @@ LL | fn m() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/never-type-fallback-breaking.rs:19:17 + | +LL | true => Default::default(), + | ^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: this function depends on never type fallback being `()` @@ -18,6 +23,11 @@ LL | fn q() -> Option<()> { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/never-type-fallback-breaking.rs:34:5 + | +LL | deserialize()?; + | ^^^^^^^^^^^^^ warning: 2 warnings emitted diff --git a/tests/ui/never_type/defaulted-never-note.nofallback.stderr b/tests/ui/never_type/defaulted-never-note.nofallback.stderr index b69b8dda8f1ee..d88615186dd63 100644 --- a/tests/ui/never_type/defaulted-never-note.nofallback.stderr +++ b/tests/ui/never_type/defaulted-never-note.nofallback.stderr @@ -7,6 +7,11 @@ LL | fn smeg() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: ImplementedForUnitButNotNever` will fail + --> $DIR/defaulted-never-note.rs:32:9 + | +LL | foo(_x); + | ^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: 1 warning emitted diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr index 36c82b6d1bf64..ec49137ba7953 100644 --- a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -7,6 +7,11 @@ LL | fn def() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/dependency-on-fallback-to-unit.rs:12:19 + | +LL | false => <_>::default(), + | ^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: this function depends on never type fallback being `()` @@ -18,6 +23,11 @@ LL | fn question_mark() -> Result<(), ()> { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/dependency-on-fallback-to-unit.rs:22:5 + | +LL | deserialize()?; + | ^^^^^^^^^^^^^ warning: 2 warnings emitted diff --git a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr index 5fbdc04ed3b80..2a3c5edc21847 100644 --- a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr @@ -7,6 +7,11 @@ LL | fn assignment() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: UnitDefault` will fail + --> $DIR/diverging-fallback-control-flow.rs:36:13 + | +LL | x = UnitDefault::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: this function depends on never type fallback being `()` @@ -18,6 +23,11 @@ LL | fn assignment_rev() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: UnitDefault` will fail + --> $DIR/diverging-fallback-control-flow.rs:50:13 + | +LL | x = UnitDefault::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ warning: 2 warnings emitted diff --git a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr index d11097323b3fb..11245cc7aabf9 100644 --- a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr @@ -7,6 +7,11 @@ LL | fn main() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: Test` will fail + --> $DIR/diverging-fallback-no-leak.rs:20:23 + | +LL | unconstrained_arg(return); + | ^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: 1 warning emitted diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr index 750bcfb7f89dc..b485c94df4d6f 100644 --- a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr @@ -7,6 +7,11 @@ LL | fn main() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: UnitReturn` will fail + --> $DIR/diverging-fallback-unconstrained-return.rs:39:23 + | +LL | let _ = if true { unconstrained_return() } else { panic!() }; + | ^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: 1 warning emitted diff --git a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr index 9f0b9f6daeae3..3fb5536dee7e3 100644 --- a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr +++ b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr @@ -7,6 +7,11 @@ LL | fn main() { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: Bar` will fail + --> $DIR/fallback-closure-ret.rs:24:5 + | +LL | foo(|| panic!()); + | ^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: 1 warning emitted diff --git a/tests/ui/never_type/impl_trait_fallback.stderr b/tests/ui/never_type/impl_trait_fallback.stderr index 8763894033243..4496746e018e6 100644 --- a/tests/ui/never_type/impl_trait_fallback.stderr +++ b/tests/ui/never_type/impl_trait_fallback.stderr @@ -7,6 +7,11 @@ LL | fn should_ret_unit() -> impl T { = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #123748 = help: specify the types explicitly +note: in edition 2024, the requirement `!: T` will fail + --> $DIR/impl_trait_fallback.rs:8:25 + | +LL | fn should_ret_unit() -> impl T { + | ^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default warning: 1 warning emitted