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:
bors 2024-06-15 12:38:17 +00:00
commit 687a68d679
67 changed files with 1811 additions and 328 deletions

View file

@ -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 -->

View file

@ -328,7 +328,7 @@ fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, location: Location)
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 @@ fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'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 @@ fn sub_types(
) -> 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 @@ fn eq_types(
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 @@ fn relate_type_and_user_type(
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 @@ fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Lo
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,

View file

@ -50,14 +50,8 @@ pub(super) fn eq_args(
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 @@ pub fn new(
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 @@ fn relate_with_variance<T: Relate<TyCtxt<'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 @@ fn binders<T>(
}
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 @@ fn binders<T>(
})?;
}
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 @@ fn binders<T>(
})?;
}
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 @@ fn binders<T>(
})?;
}
ty::Variance::Bivariant => {}
ty::Bivariant => {}
}
Ok(a)
@ -584,23 +574,23 @@ fn register_goals(
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?)")
}
})]);

View file

@ -112,25 +112,20 @@ fn binary_int_op(
// 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();

View file

@ -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`

View file

@ -7,7 +7,7 @@
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(

View file

@ -488,7 +488,7 @@ fn lint_obligations_broken_by_never_type_fallback_change(
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 @@ fn lint_obligations_broken_by_never_type_fallback_change(
// 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,
},
)
}
}

View file

@ -212,16 +212,16 @@ pub fn relate<T>(
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()`"),
}
}

View file

@ -345,7 +345,7 @@ pub fn register_member_constraints(
.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 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) {
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);
}
}

View file

@ -102,9 +102,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) {
};
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);
}
}

View file

@ -83,16 +83,16 @@ pub fn instantiate_ty_var<R: PredicateEmittingRelation<'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 @@ pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<'tcx>>(
relation.span(),
relation.structurally_relate_aliases(),
target_vid,
ty::Variance::Invariant,
ty::Invariant,
source_ct,
)?;
@ -210,14 +210,14 @@ pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<'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 @@ fn relate_item_args(
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 @@ fn consts(
// structural.
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
let args = self.relate_with_variance(
ty::Variance::Invariant,
ty::Invariant,
ty::VarianceDiagInfo::default(),
args,
args,

View file

@ -94,12 +94,7 @@ fn binders<T>(
// 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())?))

View file

@ -94,12 +94,7 @@ fn binders<T>(
// 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())?))

View file

@ -42,7 +42,7 @@ fn relate_item_args(
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 @@ fn register_goals(
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")
}
})]);

View file

@ -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`
///

View file

@ -16,7 +16,6 @@
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;

View file

@ -142,7 +142,7 @@ fn from_cycle_error(
&& 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,

View file

@ -699,7 +699,7 @@ pub(super) fn expr_into_pattern(
// 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,
),
},
);

View file

@ -92,7 +92,7 @@ fn mirror_stmts(
kind: PatKind::AscribeUserType {
ascription: Ascription {
annotation,
variance: ty::Variance::Covariant,
variance: ty::Covariant,
},
subpattern: pattern,
},

View file

@ -525,7 +525,7 @@ fn lower_variant_or_leaf(
};
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 @@ fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) ->
annotation,
// Note that use `Contravariant` here. See the
// `variance` field documentation for details.
variance: ty::Variance::Contravariant,
variance: ty::Contravariant,
},
},
ty: const_.ty(),

View file

@ -225,13 +225,8 @@ fn try_inlining(
// 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 @@ fn try_inlining(
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 @@ fn try_inlining(
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");
}

View file

@ -3281,17 +3281,19 @@ fn resolve_delegation(&mut self, delegation: &'ast Delegation) {
}
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);
});
}
}

View file

@ -1021,12 +1021,14 @@ fn suggest_self_value(
},
);
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 @@ fn suggest_self_value(
}
}
} 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
}

View file

@ -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,
}
}
}

View file

@ -61,8 +61,8 @@ pub(super) fn compute_alias_relate_goal(
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 @@ pub(super) fn compute_alias_relate_goal(
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)

View file

@ -40,7 +40,7 @@ pub(super) fn compute_normalizes_to_goal(
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)
}
}

View file

@ -239,24 +239,19 @@ fn assemble_candidates_from_caller_bounds<'o>(
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))?;

View file

@ -74,6 +74,7 @@
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

View file

@ -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 @@ fn relate<R: TypeRelation<I>>(
relation.relate(a, b)
} else {
relation.relate_with_variance(
ty::Variance::Contravariant,
ty::Contravariant,
VarianceDiagInfo::default(),
a,
b,
@ -311,13 +311,13 @@ fn relate<R: TypeRelation<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,

View file

@ -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.

View file

@ -10,9 +10,15 @@
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 @@
/// // 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,

View file

@ -823,19 +823,29 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
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));

View file

@ -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(_) => {

View file

@ -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

View file

@ -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 @@
";
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 @@ fn out_filenames() -> Vec<PathBuf> {
}
}
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 @@ fn out_filenames() -> Vec<PathBuf> {
// 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

View file

@ -171,11 +171,16 @@ pub fn exec_with_pipe<P>(mut cmd: Command, input: &[u8], path: P) -> !
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));
}
}

View file

@ -98,7 +98,7 @@ pub enum Command {
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);

View file

@ -1 +1 @@
565cadb514d35e7b851540edbc172af0f606014f
f6b4b71ef10307201b52c17b0f9dcf9557cd90ba

View file

@ -45,7 +45,7 @@ fn new(idx: usize) -> Self {
// 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 @@ fn mutex_lock(&mut self, id: MutexId) {
} 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 @@ fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option<usize>> {
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 @@ fn rwlock_reader_lock(&mut self, id: RwLockId) {
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);
}

View file

@ -643,10 +643,8 @@ enum Op {
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 {

View file

@ -142,7 +142,7 @@ fn alloc_os_str_as_c_str(
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 @@ fn alloc_os_str_as_wide_str(
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);

View file

@ -893,7 +893,7 @@ fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar
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 @@ fn macos_fbsd_readdir_r(
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"

View file

@ -57,10 +57,13 @@ fn epoll_create1(&mut self, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
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()));

View file

@ -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 @@ fn close<'tcx>(
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 @@ fn socketpair(
) -> 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)?;

View file

@ -176,8 +176,7 @@ fn emulate_x86_avx_intrinsic(
// 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 @@ fn emulate_x86_avx_intrinsic(
// 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)?,

View file

@ -18,6 +18,7 @@
mod sse2;
mod sse3;
mod sse41;
mod sse42;
mod ssse3;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@ -137,6 +138,11 @@ fn emulate_x86_intrinsic(
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,

View 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)
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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) };
}

View file

@ -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

View file

@ -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;

View file

@ -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

View 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();
}

View 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))
}

View file

@ -14,9 +14,9 @@ impl Trait for 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() {

View file

@ -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

View 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() {}

View 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`.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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