mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Auto merge of #126514 - matthiaskrgr:rollup-pnwi8ns, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #126354 (Use `Variance` glob imported variants everywhere) - #126367 (Point out failing never obligation for `DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK`) - #126469 (MIR Shl/Shr: the offset can be computed with rem_euclid) - #126471 (Use a consistent way to filter out bounds instead of splitting it into three places) - #126472 (build `libcxx-version` only when it doesn't exist) - #126497 (delegation: Fix hygiene for `self`) - #126501 (make bors ignore comments in PR template) - #126509 (std: suggest OnceLock over Once) - #126512 (Miri subtree update) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
687a68d679
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
@ -1,3 +1,4 @@
|
||||
<!-- homu-ignore:start -->
|
||||
<!--
|
||||
If this PR is related to an unstable feature or an otherwise tracked effort,
|
||||
please link to the relevant tracking issue here. If you don't know of a related
|
||||
@ -7,4 +8,5 @@ This PR will get automatically assigned to a reviewer. In case you would like
|
||||
a specific user to review your work, you can assign it to them by using
|
||||
|
||||
r? <reviewer name>
|
||||
-->
|
||||
-->
|
||||
<!-- homu-ignore:end -->
|
||||
|
@ -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,
|
||||
|
@ -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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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?)")
|
||||
}
|
||||
})]);
|
||||
|
@ -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();
|
||||
|
@ -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`
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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()`"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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,
|
||||
|
@ -94,12 +94,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> 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())?))
|
||||
|
@ -94,12 +94,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> 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())?))
|
||||
|
@ -42,7 +42,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> 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")
|
||||
}
|
||||
})]);
|
||||
|
@ -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`
|
||||
///
|
||||
|
@ -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;
|
||||
|
@ -142,7 +142,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> 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,
|
||||
|
@ -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
|
||||
// `<expr>`.
|
||||
ty::Variance::Invariant,
|
||||
ty::Invariant,
|
||||
),
|
||||
},
|
||||
);
|
||||
|
@ -92,7 +92,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
kind: PatKind::AscribeUserType {
|
||||
ascription: Ascription {
|
||||
annotation,
|
||||
variance: ty::Variance::Covariant,
|
||||
variance: ty::Covariant,
|
||||
},
|
||||
subpattern: pattern,
|
||||
},
|
||||
|
@ -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(),
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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())];
|
||||
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;
|
||||
self.fresh_binding(
|
||||
Ident::new(kw::SelfLower, span),
|
||||
delegation.id,
|
||||
PatternSource::FnParam,
|
||||
&mut bindings,
|
||||
);
|
||||
self.visit_block(body);
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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))?;
|
||||
|
@ -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
|
||||
|
@ -128,7 +128,7 @@ pub fn relate_args_invariantly<I: Interner, R: TypeRelation<I>>(
|
||||
b_arg: I::GenericArgs,
|
||||
) -> RelateResult<I, I::GenericArgs> {
|
||||
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<I: Interner, R: TypeRelation<I>>(
|
||||
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<I: Interner> Relate<I> for ty::FnSig<I> {
|
||||
relation.relate(a, b)
|
||||
} else {
|
||||
relation.relate_with_variance(
|
||||
ty::Variance::Contravariant,
|
||||
ty::Contravariant,
|
||||
VarianceDiagInfo::default(),
|
||||
a,
|
||||
b,
|
||||
@ -311,13 +311,13 @@ impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> {
|
||||
}))
|
||||
} 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<I: Interner, R: TypeRelation<I>>(
|
||||
}
|
||||
|
||||
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<I: Interner, R: TypeRelation<I>>(
|
||||
}
|
||||
|
||||
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<I: Interner, R: TypeRelation<I>>(
|
||||
}
|
||||
|
||||
let args = relation.relate_with_variance(
|
||||
ty::Variance::Invariant,
|
||||
ty::Invariant,
|
||||
VarianceDiagInfo::default(),
|
||||
au.args,
|
||||
bu.args,
|
||||
|
@ -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.
|
||||
|
@ -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<T>`] or [`LazyLock<T, F>`], before those were added to `std`.
|
||||
/// `OnceLock<T>` 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<T>`]: crate::sync::OnceLock
|
||||
/// [`LazyLock<T, F>`]: crate::sync::LazyLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Once {
|
||||
inner: sys::Once,
|
||||
|
@ -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 compiler = builder.cxx(self.target).unwrap();
|
||||
let mut cmd = Command::new(compiler);
|
||||
|
||||
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"));
|
||||
|
||||
builder.run_cmd(&mut cmd);
|
||||
|
||||
// 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() {
|
||||
panic!("Something went wrong. {} is not present", executable.display());
|
||||
if !out_dir.exists() {
|
||||
t!(fs::create_dir_all(&out_dir));
|
||||
}
|
||||
|
||||
let compiler = builder.cxx(self.target).unwrap();
|
||||
let mut cmd = Command::new(compiler);
|
||||
|
||||
cmd.arg("-o")
|
||||
.arg(&executable)
|
||||
.arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
|
||||
|
||||
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));
|
||||
|
@ -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(_) => {
|
||||
|
@ -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
|
||||
|
@ -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<Item = String>) {
|
||||
// <https://github.com/rust-lang/miri/pull/1540#issuecomment-693553191> 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<Item = String>) {
|
||||
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<Item = String>) {
|
||||
// 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<Item = String>, 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<Item = String>, 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<Item = String>, 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<Item = String>, 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::<Vec<_>>();
|
||||
|
||||
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<Item = String>, phase: Runner
|
||||
}
|
||||
};
|
||||
|
||||
let mut cmd = miri();
|
||||
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
|
||||
// <https://github.com/rust-lang/miri/issues/1661> 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 <https://github.com/rust-lang/rust/pull/113730>.
|
||||
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.)
|
||||
// Set missing env vars. We prefer build-time env vars over run-time ones; see
|
||||
// <https://github.com/rust-lang/miri/issues/1661> 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 <https://github.com/rust-lang/rust/pull/113730>.
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
// 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);
|
||||
// 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());
|
||||
// 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),
|
||||
}
|
||||
// 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<Item = String>) {
|
||||
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<Item = String>) {
|
||||
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
|
||||
|
||||
|
@ -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<String>, f: impl Fn(Option<u32>)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -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 <flags>.
|
||||
(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 <flags>:
|
||||
Format all sources and tests. <flags> 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);
|
||||
|
@ -1 +1 @@
|
||||
565cadb514d35e7b851540edbc172af0f606014f
|
||||
f6b4b71ef10307201b52c17b0f9dcf9557cd90ba
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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()));
|
||||
|
@ -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<RefCell<Buffer>>,
|
||||
readbuf: Rc<RefCell<Buffer>>,
|
||||
is_nonblock: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Buffer {
|
||||
buf: VecDeque<u8>,
|
||||
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<Self>,
|
||||
_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<usize>> {
|
||||
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<usize>> {
|
||||
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:
|
||||
/// <https://linux.die.net/man/2/socketpair>
|
||||
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)?;
|
||||
|
@ -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)?,
|
||||
|
@ -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,
|
||||
|
500
src/tools/miri/src/shims/x86/sse42.rs
Normal file
500
src/tools/miri/src/shims/x86/sse42.rs
Normal file
@ -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::<u64>(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::<u32>(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<u64>> {
|
||||
let mut result = None;
|
||||
let zero = ImmTy::from_int(0, str.layout.field(this, 0));
|
||||
|
||||
for i in 0..default_len::<u64>(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<T: From<u8>>(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::<u64>(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::<u64>(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::<u32>(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::<i32>(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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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) };
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
124
src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs
Normal file
124
src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs
Normal file
@ -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();
|
||||
}
|
443
src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs
Normal file
443
src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs
Normal file
@ -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))
|
||||
}
|
@ -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 <u8!() as Trait>::{foo, bar} { self_0!() }
|
||||
reuse <u8!() as Trait>::{foo, bar} { self_0!(self) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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::<impl at $DIR/not-supported.rs:86:5: 86:24>::{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 <https://github.com/rust-lang/rust/issues/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::<impl at $DIR/not-supported.rs:89:5: 89:25>::{synthetic#0}`
|
||||
--> $DIR/not-supported.rs:90:24
|
||||
|
20
tests/ui/delegation/self-hygiene.rs
Normal file
20
tests/ui/delegation/self-hygiene.rs
Normal file
@ -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() {}
|
31
tests/ui/delegation/self-hygiene.stderr
Normal file
31
tests/ui/delegation/self-hygiene.stderr
Normal file
@ -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`.
|
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
@ -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 <https://github.com/rust-lang/rust/issues/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
|
||||
|
Loading…
Reference in New Issue
Block a user