mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #132237 - matthiaskrgr:rollup-ulogwtd, r=matthiaskrgr
Rollup of 5 pull requests Successful merges: - #132043 (Simplify param handling in `resolve_bound_vars`) - #132214 (Cleanup: Move an impl-Trait check from AST validation to AST lowering) - #132221 (Clean up some comments on lint implementation) - #132228 (Revert "ci update freebsd version proposal, freebsd 12 being eol.") - #132234 (Miri subtree update) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
df4ca44d3f
@ -34,7 +34,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
modifiers: Option<ast::TraitBoundModifiers>,
|
||||
) -> hir::QPath<'hir> {
|
||||
let qself_position = qself.as_ref().map(|q| q.position);
|
||||
let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
|
||||
let qself = qself
|
||||
.as_ref()
|
||||
// Reject cases like `<impl Trait>::Assoc` and `<impl Trait as Trait>::Assoc`.
|
||||
.map(|q| self.lower_ty(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)));
|
||||
|
||||
let partial_res =
|
||||
self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
|
||||
@ -75,6 +78,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
None
|
||||
};
|
||||
|
||||
// Only permit `impl Trait` in the final segment. E.g., we permit `Option<impl Trait>`,
|
||||
// `option::Option<T>::Xyz<impl Trait>` and reject `option::Option<impl Trait>::Xyz`.
|
||||
let itctx = |i| {
|
||||
if i + 1 == p.segments.len() {
|
||||
itctx
|
||||
} else {
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path)
|
||||
}
|
||||
};
|
||||
|
||||
let path_span_lo = p.span.shrink_to_lo();
|
||||
let proj_start = p.segments.len() - unresolved_segments;
|
||||
let path = self.arena.alloc(hir::Path {
|
||||
@ -121,7 +134,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
segment,
|
||||
param_mode,
|
||||
generic_args_mode,
|
||||
itctx,
|
||||
itctx(i),
|
||||
bound_modifier_allowed_features.clone(),
|
||||
)
|
||||
},
|
||||
@ -185,7 +198,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
segment,
|
||||
param_mode,
|
||||
generic_args_mode,
|
||||
itctx,
|
||||
itctx(i),
|
||||
None,
|
||||
));
|
||||
let qpath = hir::QPath::TypeRelative(ty, hir_segment);
|
||||
|
@ -146,8 +146,6 @@ ast_passes_generic_before_constraints = generic arguments must come before the f
|
||||
|
||||
ast_passes_generic_default_trailing = generic parameters with a default must be trailing
|
||||
|
||||
ast_passes_impl_trait_path = `impl Trait` is not allowed in path parameters
|
||||
|
||||
ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
|
||||
.help = remove one of these features
|
||||
|
||||
|
@ -80,10 +80,6 @@ struct AstValidator<'a> {
|
||||
|
||||
disallow_tilde_const: Option<TildeConstReason>,
|
||||
|
||||
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
|
||||
/// or `Foo::Bar<impl Trait>`
|
||||
is_impl_trait_banned: bool,
|
||||
|
||||
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
|
||||
extern_mod_safety: Option<Safety>,
|
||||
|
||||
@ -123,12 +119,6 @@ impl<'a> AstValidator<'a> {
|
||||
self.extern_mod_safety = old;
|
||||
}
|
||||
|
||||
fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
|
||||
let old = mem::replace(&mut self.is_impl_trait_banned, true);
|
||||
f(self);
|
||||
self.is_impl_trait_banned = old;
|
||||
}
|
||||
|
||||
fn with_tilde_const(
|
||||
&mut self,
|
||||
disallowed: Option<TildeConstReason>,
|
||||
@ -213,37 +203,6 @@ impl<'a> AstValidator<'a> {
|
||||
.with_tilde_const(Some(TildeConstReason::TraitObject), |this| {
|
||||
visit::walk_ty(this, t)
|
||||
}),
|
||||
TyKind::Path(qself, path) => {
|
||||
// We allow these:
|
||||
// - `Option<impl Trait>`
|
||||
// - `option::Option<impl Trait>`
|
||||
// - `option::Option<T>::Foo<impl Trait>`
|
||||
//
|
||||
// But not these:
|
||||
// - `<impl Trait>::Foo`
|
||||
// - `option::Option<impl Trait>::Foo`.
|
||||
//
|
||||
// To implement this, we disallow `impl Trait` from `qself`
|
||||
// (for cases like `<impl Trait>::Foo>`)
|
||||
// but we allow `impl Trait` in `GenericArgs`
|
||||
// iff there are no more PathSegments.
|
||||
if let Some(qself) = qself {
|
||||
// `impl Trait` in `qself` is always illegal
|
||||
self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
|
||||
}
|
||||
|
||||
// Note that there should be a call to visit_path here,
|
||||
// so if any logic is added to process `Path`s a call to it should be
|
||||
// added both in visit_path and here. This code mirrors visit::walk_path.
|
||||
for (i, segment) in path.segments.iter().enumerate() {
|
||||
// Allow `impl Trait` iff we're on the final path segment
|
||||
if i == path.segments.len() - 1 {
|
||||
self.visit_path_segment(segment);
|
||||
} else {
|
||||
self.with_banned_impl_trait(|this| this.visit_path_segment(segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => visit::walk_ty(self, t),
|
||||
}
|
||||
}
|
||||
@ -737,10 +696,6 @@ impl<'a> AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
TyKind::ImplTrait(_, bounds) => {
|
||||
if self.is_impl_trait_banned {
|
||||
self.dcx().emit_err(errors::ImplTraitPath { span: ty.span });
|
||||
}
|
||||
|
||||
if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
|
||||
self.dcx().emit_err(errors::NestedImplTrait {
|
||||
span: ty.span,
|
||||
@ -1729,7 +1684,6 @@ pub fn check_crate(
|
||||
has_proc_macro_decls: false,
|
||||
outer_impl_trait: None,
|
||||
disallow_tilde_const: Some(TildeConstReason::Item),
|
||||
is_impl_trait_banned: false,
|
||||
extern_mod_safety: None,
|
||||
lint_buffer: lints,
|
||||
};
|
||||
|
@ -418,13 +418,6 @@ pub(crate) struct TraitObjectBound {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_impl_trait_path, code = E0667)]
|
||||
pub(crate) struct ImplTraitPath {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_nested_impl_trait, code = E0666)]
|
||||
pub(crate) struct NestedImplTrait {
|
||||
|
@ -1,8 +1,10 @@
|
||||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
`impl Trait` is not allowed in path parameters.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0667
|
||||
```ignore (removed error code)
|
||||
fn some_fn(mut x: impl Iterator) -> <impl Iterator>::Item { // error!
|
||||
x.next().unwrap()
|
||||
}
|
||||
@ -11,7 +13,7 @@ fn some_fn(mut x: impl Iterator) -> <impl Iterator>::Item { // error!
|
||||
You cannot use `impl Trait` in path parameters. If you want something
|
||||
equivalent, you can do this instead:
|
||||
|
||||
```
|
||||
```ignore (removed error code)
|
||||
fn some_fn<T: Iterator>(mut x: T) -> T::Item { // ok!
|
||||
x.next().unwrap()
|
||||
}
|
||||
|
@ -33,18 +33,12 @@ use crate::errors;
|
||||
|
||||
#[extension(trait RegionExt)]
|
||||
impl ResolvedArg {
|
||||
fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) {
|
||||
debug!("ResolvedArg::early: def_id={:?}", param.def_id);
|
||||
(param.def_id, ResolvedArg::EarlyBound(param.def_id))
|
||||
fn early(param: &GenericParam<'_>) -> ResolvedArg {
|
||||
ResolvedArg::EarlyBound(param.def_id)
|
||||
}
|
||||
|
||||
fn late(idx: u32, param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) {
|
||||
let depth = ty::INNERMOST;
|
||||
debug!(
|
||||
"ResolvedArg::late: idx={:?}, param={:?} depth={:?} def_id={:?}",
|
||||
idx, param, depth, param.def_id,
|
||||
);
|
||||
(param.def_id, ResolvedArg::LateBound(depth, idx, param.def_id))
|
||||
fn late(idx: u32, param: &GenericParam<'_>) -> ResolvedArg {
|
||||
ResolvedArg::LateBound(ty::INNERMOST, idx, param.def_id)
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<LocalDefId> {
|
||||
@ -282,24 +276,33 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou
|
||||
|
||||
fn late_arg_as_bound_arg<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
arg: &ResolvedArg,
|
||||
param: &GenericParam<'tcx>,
|
||||
) -> ty::BoundVariableKind {
|
||||
match arg {
|
||||
ResolvedArg::LateBound(_, _, def_id) => {
|
||||
let def_id = def_id.to_def_id();
|
||||
let name = tcx.item_name(def_id);
|
||||
match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
|
||||
}
|
||||
GenericParamKind::Type { .. } => {
|
||||
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name))
|
||||
}
|
||||
GenericParamKind::Const { .. } => ty::BoundVariableKind::Const,
|
||||
}
|
||||
let def_id = param.def_id.to_def_id();
|
||||
let name = tcx.item_name(def_id);
|
||||
match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
|
||||
}
|
||||
_ => bug!("{:?} is not a late argument", arg),
|
||||
GenericParamKind::Type { .. } => {
|
||||
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name))
|
||||
}
|
||||
GenericParamKind::Const { .. } => ty::BoundVariableKind::Const,
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn a [`ty::GenericParamDef`] into a bound arg. Generally, this should only
|
||||
/// be used when turning early-bound vars into late-bound vars when lowering
|
||||
/// return type notation.
|
||||
fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVariableKind {
|
||||
match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => {
|
||||
ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(param.def_id, param.name))
|
||||
}
|
||||
ty::GenericParamDefKind::Type { .. } => {
|
||||
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name))
|
||||
}
|
||||
ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,10 +363,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
let mut bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = FxIndexMap::default();
|
||||
let binders_iter =
|
||||
trait_ref.bound_generic_params.iter().enumerate().map(|(late_bound_idx, param)| {
|
||||
let pair = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param);
|
||||
let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
|
||||
bound_vars.insert(pair.0, pair.1);
|
||||
r
|
||||
let arg = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param);
|
||||
bound_vars.insert(param.def_id, arg);
|
||||
late_arg_as_bound_arg(self.tcx, param)
|
||||
});
|
||||
binders.extend(binders_iter);
|
||||
|
||||
@ -458,9 +460,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(late_bound_idx, param)| {
|
||||
let pair = ResolvedArg::late(late_bound_idx as u32, param);
|
||||
let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
|
||||
(pair, r)
|
||||
(
|
||||
(param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
|
||||
late_arg_as_bound_arg(self.tcx, param),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
@ -492,8 +495,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
||||
let mut bound_vars = FxIndexMap::default();
|
||||
debug!(?opaque.generics.params);
|
||||
for param in opaque.generics.params {
|
||||
let (def_id, reg) = ResolvedArg::early(param);
|
||||
bound_vars.insert(def_id, reg);
|
||||
let arg = ResolvedArg::early(param);
|
||||
bound_vars.insert(param.def_id, arg);
|
||||
}
|
||||
|
||||
let hir_id = self.tcx.local_def_id_to_hir_id(opaque.def_id);
|
||||
@ -618,9 +621,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(late_bound_idx, param)| {
|
||||
let pair = ResolvedArg::late(late_bound_idx as u32, param);
|
||||
let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
|
||||
(pair, r)
|
||||
(
|
||||
(param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
|
||||
late_arg_as_bound_arg(self.tcx, param),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
@ -870,9 +874,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(late_bound_idx, param)| {
|
||||
let pair = ResolvedArg::late(late_bound_idx as u32, param);
|
||||
let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
|
||||
(pair, r)
|
||||
(
|
||||
(param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
|
||||
late_arg_as_bound_arg(self.tcx, param),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
@ -1052,19 +1057,21 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = generics
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
if self.tcx.is_late_bound(param.hir_id) {
|
||||
let late_bound_idx = named_late_bound_vars;
|
||||
named_late_bound_vars += 1;
|
||||
ResolvedArg::late(late_bound_idx, param)
|
||||
} else {
|
||||
.map(|param| {
|
||||
(param.def_id, match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
if self.tcx.is_late_bound(param.hir_id) {
|
||||
let late_bound_idx = named_late_bound_vars;
|
||||
named_late_bound_vars += 1;
|
||||
ResolvedArg::late(late_bound_idx, param)
|
||||
} else {
|
||||
ResolvedArg::early(param)
|
||||
}
|
||||
}
|
||||
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
|
||||
ResolvedArg::early(param)
|
||||
}
|
||||
}
|
||||
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
|
||||
ResolvedArg::early(param)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -1075,11 +1082,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
matches!(param.kind, GenericParamKind::Lifetime { .. })
|
||||
&& self.tcx.is_late_bound(param.hir_id)
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(late_bound_idx, param)| {
|
||||
let pair = ResolvedArg::late(late_bound_idx as u32, param);
|
||||
late_arg_as_bound_arg(self.tcx, &pair.1, param)
|
||||
})
|
||||
.map(|param| late_arg_as_bound_arg(self.tcx, param))
|
||||
.collect();
|
||||
self.record_late_bound_vars(hir_id, binders);
|
||||
let scope = Scope::Binder {
|
||||
@ -1096,7 +1099,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
where
|
||||
F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>),
|
||||
{
|
||||
let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
|
||||
let bound_vars =
|
||||
generics.params.iter().map(|param| (param.def_id, ResolvedArg::early(param))).collect();
|
||||
self.record_late_bound_vars(hir_id, vec![]);
|
||||
let scope = Scope::Binder {
|
||||
hir_id,
|
||||
@ -1639,17 +1643,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
constraint.ident,
|
||||
ty::AssocKind::Fn,
|
||||
) {
|
||||
bound_vars.extend(self.tcx.generics_of(assoc_fn.def_id).own_params.iter().map(
|
||||
|param| match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
|
||||
ty::BoundRegionKind::BrNamed(param.def_id, param.name),
|
||||
),
|
||||
ty::GenericParamDefKind::Type { .. } => ty::BoundVariableKind::Ty(
|
||||
ty::BoundTyKind::Param(param.def_id, param.name),
|
||||
),
|
||||
ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
|
||||
},
|
||||
));
|
||||
bound_vars.extend(
|
||||
self.tcx
|
||||
.generics_of(assoc_fn.def_id)
|
||||
.own_params
|
||||
.iter()
|
||||
.map(|param| generic_param_def_as_bound_arg(param)),
|
||||
);
|
||||
bound_vars.extend(
|
||||
self.tcx.fn_sig(assoc_fn.def_id).instantiate_identity().bound_vars(),
|
||||
);
|
||||
@ -1968,17 +1968,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
// Append the early-bound vars on the function, and then the late-bound ones.
|
||||
// We actually turn type parameters into higher-ranked types here, but we
|
||||
// deny them later in HIR lowering.
|
||||
bound_vars.extend(self.tcx.generics_of(item_def_id).own_params.iter().map(|param| {
|
||||
match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
|
||||
ty::BoundRegionKind::BrNamed(param.def_id, param.name),
|
||||
),
|
||||
ty::GenericParamDefKind::Type { .. } => {
|
||||
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name))
|
||||
}
|
||||
ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
|
||||
}
|
||||
}));
|
||||
bound_vars.extend(
|
||||
self.tcx
|
||||
.generics_of(item_def_id)
|
||||
.own_params
|
||||
.iter()
|
||||
.map(|param| generic_param_def_as_bound_arg(param)),
|
||||
);
|
||||
bound_vars.extend(self.tcx.fn_sig(item_def_id).instantiate_identity().bound_vars());
|
||||
|
||||
// SUBTLE: Stash the old bound vars onto the *item segment* before appending
|
||||
|
@ -1203,15 +1203,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||
err.emit()
|
||||
} else if let Err(reported) = qself_ty.error_reported() {
|
||||
reported
|
||||
} else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
|
||||
// `<impl Trait as OtherTrait>::Assoc` makes no sense.
|
||||
struct_span_code_err!(
|
||||
self.dcx(),
|
||||
tcx.def_span(alias_ty.def_id),
|
||||
E0667,
|
||||
"`impl Trait` is not allowed in path parameters"
|
||||
)
|
||||
.emit() // Already reported in an earlier stage.
|
||||
} else {
|
||||
self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?;
|
||||
|
||||
|
@ -4,21 +4,14 @@
|
||||
//! AST visitor. Also see `rustc_session::lint::builtin`, which contains the
|
||||
//! definitions of lints that are emitted directly inside the main compiler.
|
||||
//!
|
||||
//! To add a new lint to rustc, declare it here using `declare_lint!()`.
|
||||
//! To add a new lint to rustc, declare it here using [`declare_lint!`].
|
||||
//! Then add code to emit the new lint in the appropriate circumstances.
|
||||
//! You can do that in an existing `LintPass` if it makes sense, or in a
|
||||
//! new `LintPass`, or using `Session::add_lint` elsewhere in the
|
||||
//! compiler. Only do the latter if the check can't be written cleanly as a
|
||||
//! `LintPass` (also, note that such lints will need to be defined in
|
||||
//! `rustc_session::lint::builtin`, not here).
|
||||
//!
|
||||
//! If you define a new `EarlyLintPass`, you will also need to add it to the
|
||||
//! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in
|
||||
//! `lib.rs`. Use the former for unit-like structs and the latter for structs
|
||||
//! with a `pub fn new()`.
|
||||
//! If you define a new [`EarlyLintPass`], you will also need to add it to the
|
||||
//! [`crate::early_lint_methods!`] invocation in `lib.rs`.
|
||||
//!
|
||||
//! If you define a new `LateLintPass`, you will also need to add it to the
|
||||
//! `late_lint_methods!` invocation in `lib.rs`.
|
||||
//! If you define a new [`LateLintPass`], you will also need to add it to the
|
||||
//! [`crate::late_lint_methods!`] invocation in `lib.rs`.
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
|
@ -1,18 +1,7 @@
|
||||
//! Implementation of lint checking.
|
||||
//! Basic types for managing and implementing lints.
|
||||
//!
|
||||
//! The lint checking is mostly consolidated into one pass which runs
|
||||
//! after all other analyses. Throughout compilation, lint warnings
|
||||
//! can be added via the `add_lint` method on the Session structure. This
|
||||
//! requires a span and an ID of the node that the lint is being added to. The
|
||||
//! lint isn't actually emitted at that time because it is unknown what the
|
||||
//! actual lint level at that location is.
|
||||
//!
|
||||
//! To actually emit lint warnings/errors, a separate pass is used.
|
||||
//! A context keeps track of the current state of all lint levels.
|
||||
//! Upon entering a node of the ast which can modify the lint settings, the
|
||||
//! previous lint state is pushed onto a stack and the ast is then recursed
|
||||
//! upon. As the ast is traversed, this keeps track of the current lint level
|
||||
//! for all lint attributes.
|
||||
//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an
|
||||
//! overview of how lints are implemented.
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::{iter, slice};
|
||||
@ -52,9 +41,6 @@ type LateLintPassFactory =
|
||||
dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
|
||||
|
||||
/// Information about the registered lints.
|
||||
///
|
||||
/// This is basically the subset of `Context` that we can
|
||||
/// build early in the compile pipeline.
|
||||
pub struct LintStore {
|
||||
/// Registered lints.
|
||||
lints: Vec<&'static Lint>,
|
||||
|
@ -1,18 +1,8 @@
|
||||
//! Implementation of lint checking.
|
||||
//! Implementation of the early lint pass.
|
||||
//!
|
||||
//! The lint checking is mostly consolidated into one pass which runs
|
||||
//! after all other analyses. Throughout compilation, lint warnings
|
||||
//! can be added via the `add_lint` method on the Session structure. This
|
||||
//! requires a span and an ID of the node that the lint is being added to. The
|
||||
//! lint isn't actually emitted at that time because it is unknown what the
|
||||
//! actual lint level at that location is.
|
||||
//!
|
||||
//! To actually emit lint warnings/errors, a separate pass is used.
|
||||
//! A context keeps track of the current state of all lint levels.
|
||||
//! Upon entering a node of the ast which can modify the lint settings, the
|
||||
//! previous lint state is pushed onto a stack and the ast is then recursed
|
||||
//! upon. As the ast is traversed, this keeps track of the current lint level
|
||||
//! for all lint attributes.
|
||||
//! The early lint pass works on AST nodes after macro expansion and name
|
||||
//! resolution, just before AST lowering. These lints are for purely
|
||||
//! syntactical lints.
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{self as ast_visit, Visitor, walk_list};
|
||||
|
@ -1,18 +1,7 @@
|
||||
//! Implementation of lint checking.
|
||||
//! Implementation of the late lint pass.
|
||||
//!
|
||||
//! The lint checking is mostly consolidated into one pass which runs
|
||||
//! after all other analyses. Throughout compilation, lint warnings
|
||||
//! can be added via the `add_lint` method on the Session structure. This
|
||||
//! requires a span and an ID of the node that the lint is being added to. The
|
||||
//! lint isn't actually emitted at that time because it is unknown what the
|
||||
//! actual lint level at that location is.
|
||||
//!
|
||||
//! To actually emit lint warnings/errors, a separate pass is used.
|
||||
//! A context keeps track of the current state of all lint levels.
|
||||
//! Upon entering a node of the ast which can modify the lint settings, the
|
||||
//! previous lint state is pushed onto a stack and the ast is then recursed
|
||||
//! upon. As the ast is traversed, this keeps track of the current lint level
|
||||
//! for all lint attributes.
|
||||
//! The late lint pass Works on HIR nodes, towards the end of analysis (after
|
||||
//! borrow checking, etc.). These lints have full type information available.
|
||||
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
|
@ -6,20 +6,14 @@
|
||||
//! other phases of the compiler, which are generally required to hold in order
|
||||
//! to compile the program at all.
|
||||
//!
|
||||
//! Most lints can be written as [LintPass] instances. These run after
|
||||
//! Most lints can be written as [`LintPass`] instances. These run after
|
||||
//! all other analyses. The `LintPass`es built into rustc are defined
|
||||
//! within [rustc_session::lint::builtin],
|
||||
//! which has further comments on how to add such a lint.
|
||||
//! rustc can also load external lint plugins, as is done for Clippy.
|
||||
//!
|
||||
//! Some of rustc's lints are defined elsewhere in the compiler and work by
|
||||
//! calling `add_lint()` on the overall `Session` object. This works when
|
||||
//! it happens before the main lint pass, which emits the lints stored by
|
||||
//! `add_lint()`. To emit lints after the main lint pass (from codegen, for
|
||||
//! example) requires more effort. See `emit_lint` and `GatherNodeLevels`
|
||||
//! in `context.rs`.
|
||||
//!
|
||||
//! Some code also exists in [rustc_session::lint], [rustc_middle::lint].
|
||||
//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an
|
||||
//! overview of how lints are implemented.
|
||||
//!
|
||||
//! ## Note
|
||||
//!
|
||||
|
@ -56,9 +56,9 @@ ENV \
|
||||
CFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
|
||||
CXX_x86_64_fortanix_unknown_sgx=clang++-11 \
|
||||
CXXFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
|
||||
AR_i686_unknown_freebsd=i686-unknown-freebsd13-ar \
|
||||
CC_i686_unknown_freebsd=i686-unknown-freebsd13-clang \
|
||||
CXX_i686_unknown_freebsd=i686-unknown-freebsd13-clang++ \
|
||||
AR_i686_unknown_freebsd=i686-unknown-freebsd12-ar \
|
||||
CC_i686_unknown_freebsd=i686-unknown-freebsd12-clang \
|
||||
CXX_i686_unknown_freebsd=i686-unknown-freebsd12-clang++ \
|
||||
CC_aarch64_unknown_uefi=clang-11 \
|
||||
CXX_aarch64_unknown_uefi=clang++-11 \
|
||||
CC_i686_unknown_uefi=clang-11 \
|
||||
|
@ -29,9 +29,9 @@ COPY scripts/cmake.sh /scripts/
|
||||
RUN /scripts/cmake.sh
|
||||
|
||||
ENV \
|
||||
AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-ar \
|
||||
CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang \
|
||||
CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang++
|
||||
AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar \
|
||||
CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang \
|
||||
CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang++
|
||||
|
||||
ENV HOSTS=x86_64-unknown-freebsd
|
||||
|
||||
|
@ -5,8 +5,8 @@ set -eux
|
||||
|
||||
arch=$1
|
||||
binutils_version=2.40
|
||||
freebsd_version=13.2
|
||||
triple=$arch-unknown-freebsd13
|
||||
freebsd_version=12.3
|
||||
triple=$arch-unknown-freebsd12
|
||||
sysroot=/usr/local/$triple
|
||||
|
||||
hide_output() {
|
||||
@ -59,7 +59,7 @@ done
|
||||
|
||||
# Originally downloaded from:
|
||||
# URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz
|
||||
URL=https://ci-mirrors.rust-lang.org/rustc/2024-02-18-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
|
||||
URL=https://ci-mirrors.rust-lang.org/rustc/2022-05-06-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
|
||||
curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
|
||||
|
||||
# Clang can do cross-builds out of the box, if we give it the right
|
||||
@ -68,7 +68,7 @@ curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
|
||||
# there might be other problems.)
|
||||
#
|
||||
# The --target option is last because the cross-build of LLVM uses
|
||||
# --target without an OS version ("-freebsd" vs. "-freebsd13"). This
|
||||
# --target without an OS version ("-freebsd" vs. "-freebsd12"). This
|
||||
# makes Clang default to libstdc++ (which no longer exists), and also
|
||||
# controls other features, like GNU-style symbol table hashing and
|
||||
# anything predicated on the version number in the __FreeBSD__
|
||||
|
@ -100,7 +100,7 @@ target | notes
|
||||
[`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29)
|
||||
[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3)
|
||||
`s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17)
|
||||
`x86_64-unknown-freebsd` | 64-bit FreeBSD (version 13.2)
|
||||
`x86_64-unknown-freebsd` | 64-bit FreeBSD
|
||||
`x86_64-unknown-illumos` | illumos
|
||||
`x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3
|
||||
[`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64
|
||||
@ -166,7 +166,7 @@ target | std | notes
|
||||
`i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87]
|
||||
[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI]
|
||||
[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI]
|
||||
`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD (version 13.2) [^x86_32-floats-return-ABI]
|
||||
`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
|
||||
`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI]
|
||||
[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI
|
||||
[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
|
||||
@ -258,7 +258,7 @@ target | std | host | notes
|
||||
[`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS |
|
||||
[`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.0 RTOS |
|
||||
[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS |
|
||||
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD (version 13.2)
|
||||
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
|
||||
[`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit
|
||||
`aarch64-unknown-illumos` | ✓ | ✓ | ARM64 illumos
|
||||
`aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
|
||||
@ -277,14 +277,14 @@ target | std | host | notes
|
||||
`armv4t-unknown-linux-gnueabi` | ? | | Armv4T Linux
|
||||
[`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Bare Armv5TE
|
||||
`armv5te-unknown-linux-uclibceabi` | ? | | Armv5TE Linux with uClibc
|
||||
`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD (version 13.2)
|
||||
`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD
|
||||
[`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float
|
||||
[`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain)
|
||||
[`armv7-rtems-eabihf`](platform-support/armv7-rtems-eabihf.md) | ? | | RTEMS OS for ARM BSPs
|
||||
[`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ | | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)
|
||||
[`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat
|
||||
[`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat
|
||||
`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD (version 13.2)
|
||||
`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD
|
||||
[`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float
|
||||
[`armv7-unknown-trusty`](platform-support/trusty.md) | ? | |
|
||||
[`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ | | Armv7-A for VxWorks
|
||||
@ -343,9 +343,9 @@ target | std | host | notes
|
||||
[`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | |
|
||||
[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | |
|
||||
[`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | |
|
||||
`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2, version 13.2)
|
||||
`powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD (version 13.2)
|
||||
`powerpc-unknown-freebsd` | | | PowerPC FreeBSD (version 13.2)
|
||||
`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2)
|
||||
`powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD
|
||||
`powerpc-unknown-freebsd` | | | PowerPC FreeBSD
|
||||
`powerpc64-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3
|
||||
[`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | |
|
||||
`powerpc64le-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3, Little Endian
|
||||
@ -361,7 +361,7 @@ target | std | host | notes
|
||||
[`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF
|
||||
[`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ | |
|
||||
[`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit
|
||||
`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD (version 13.2)
|
||||
`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD
|
||||
`riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia
|
||||
[`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD
|
||||
[`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
|
||||
|
44
src/tools/miri/.github/workflows/ci.yml
vendored
44
src/tools/miri/.github/workflows/ci.yml
vendored
@ -1,11 +1,7 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
# Run in PRs and for bors, but not on master.
|
||||
branches:
|
||||
- 'auto'
|
||||
- 'try'
|
||||
merge_group:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
@ -38,7 +34,7 @@ jobs:
|
||||
# The `style` job only runs on Linux; this makes sure the Windows-host-specific
|
||||
# code is also covered by clippy.
|
||||
- name: Check clippy
|
||||
if: matrix.os == 'windows-latest'
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: ./miri clippy -- -D warnings
|
||||
|
||||
- name: Test Miri
|
||||
@ -62,27 +58,25 @@ jobs:
|
||||
- name: rustdoc
|
||||
run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items
|
||||
|
||||
# These jobs doesn't actually test anything, but they're only used to tell
|
||||
# bors the build completed, as there is no practical way to detect when a
|
||||
# workflow is successful listening to webhooks only.
|
||||
#
|
||||
# Summary job for the merge queue.
|
||||
# ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
|
||||
end-success:
|
||||
name: bors build finished
|
||||
runs-on: ubuntu-latest
|
||||
# And they should be added below in `cron-fail-notify` as well.
|
||||
conclusion:
|
||||
needs: [build, style]
|
||||
if: github.event.pusher.name == 'bors' && success()
|
||||
steps:
|
||||
- name: mark the job as a success
|
||||
run: exit 0
|
||||
end-failure:
|
||||
name: bors build finished
|
||||
# We need to ensure this job does *not* get skipped if its dependencies fail,
|
||||
# because a skipped job is considered a success by GitHub. So we have to
|
||||
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
|
||||
# when the workflow is canceled manually.
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, style]
|
||||
if: github.event.pusher.name == 'bors' && (failure() || cancelled())
|
||||
steps:
|
||||
- name: mark the job as a failure
|
||||
run: exit 1
|
||||
# Manually check the status of all dependencies. `if: failure()` does not work.
|
||||
- name: Conclusion
|
||||
run: |
|
||||
# Print the dependent jobs to see them in the CI log
|
||||
jq -C <<< '${{ toJson(needs) }}'
|
||||
# Check if all jobs that we depend on (in the needs array) were successful.
|
||||
jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
|
||||
|
||||
cron-fail-notify:
|
||||
name: cronjob failure notification
|
||||
@ -93,7 +87,7 @@ jobs:
|
||||
# ... and create a PR.
|
||||
pull-requests: write
|
||||
needs: [build, style]
|
||||
if: github.event_name == 'schedule' && failure()
|
||||
if: ${{ github.event_name == 'schedule' && failure() }}
|
||||
steps:
|
||||
# Send a Zulip notification
|
||||
- name: Install zulip-send
|
||||
@ -145,7 +139,7 @@ jobs:
|
||||
git push -u origin $BRANCH
|
||||
- name: Create Pull Request
|
||||
run: |
|
||||
PR=$(gh pr create -B master --title 'Automatic Rustup' --body '')
|
||||
PR=$(gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.')
|
||||
~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
|
||||
--stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \
|
||||
--message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience."
|
||||
|
@ -154,7 +154,7 @@ case $HOST_TARGET in
|
||||
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
|
||||
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
|
||||
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
|
||||
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap pthread --skip threadname
|
||||
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap threadname pthread
|
||||
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm
|
||||
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
|
||||
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
|
||||
|
@ -1 +1 @@
|
||||
17a19e684cdf3ca088af8b4da6a6209d128913f4
|
||||
814df6e50eaf89b90793e7d9618bb60f1f18377a
|
||||
|
@ -424,7 +424,11 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> {
|
||||
}
|
||||
|
||||
#[inline(never)] // This is only called on fatal code paths
|
||||
pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpErrorKind<'tcx> {
|
||||
pub(super) fn protector_error(
|
||||
&self,
|
||||
item: &Item,
|
||||
kind: ProtectorKind,
|
||||
) -> InterpErrorKind<'tcx> {
|
||||
let protected = match kind {
|
||||
ProtectorKind::WeakProtector => "weakly protected",
|
||||
ProtectorKind::StrongProtector => "strongly protected",
|
||||
|
@ -145,6 +145,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(Scalar::from_bool(branch), dest)?;
|
||||
}
|
||||
|
||||
"floorf16" | "ceilf16" | "truncf16" | "roundf16" | "rintf16" => {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f16()?;
|
||||
let mode = match intrinsic_name {
|
||||
"floorf16" => Round::TowardNegative,
|
||||
"ceilf16" => Round::TowardPositive,
|
||||
"truncf16" => Round::TowardZero,
|
||||
"roundf16" => Round::NearestTiesToAway,
|
||||
"rintf16" => Round::NearestTiesToEven,
|
||||
_ => bug!(),
|
||||
};
|
||||
let res = f.round_to_integral(mode).value;
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
@ -175,6 +190,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"floorf128" | "ceilf128" | "truncf128" | "roundf128" | "rintf128" => {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f128()?;
|
||||
let mode = match intrinsic_name {
|
||||
"floorf128" => Round::TowardNegative,
|
||||
"ceilf128" => Round::TowardPositive,
|
||||
"truncf128" => Round::TowardZero,
|
||||
"roundf128" => Round::NearestTiesToAway,
|
||||
"rintf128" => Round::NearestTiesToEven,
|
||||
_ => bug!(),
|
||||
};
|
||||
let res = f.round_to_integral(mode).value;
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
| "sinf32"
|
||||
|
@ -1,7 +1,8 @@
|
||||
use either::Either;
|
||||
use rustc_apfloat::{Float, Round};
|
||||
use rustc_middle::ty::FloatTy;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::{mir, ty, ty::FloatTy};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::{Symbol, sym};
|
||||
use rustc_target::abi::{Endian, HasDataLayout};
|
||||
|
||||
@ -630,12 +631,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
||||
let index = generic_args[2]
|
||||
.expect_const()
|
||||
.try_to_valtree()
|
||||
.unwrap()
|
||||
.0
|
||||
.unwrap_branch();
|
||||
let index =
|
||||
generic_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
|
||||
let index_len = index.len();
|
||||
|
||||
assert_eq!(left_len, right_len);
|
||||
|
@ -147,7 +147,7 @@ pub use crate::range_map::RangeMap;
|
||||
pub use crate::shims::EmulateItemResult;
|
||||
pub use crate::shims::env::{EnvVars, EvalContextExt as _};
|
||||
pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
|
||||
pub use crate::shims::io_error::{EvalContextExt as _, LibcError};
|
||||
pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError};
|
||||
pub use crate::shims::os_str::EvalContextExt as _;
|
||||
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
|
||||
pub use crate::shims::time::EvalContextExt as _;
|
||||
|
@ -447,8 +447,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
} else {
|
||||
// If this does not fit in an isize, return null and, on Unix, set errno.
|
||||
if this.target_os_is_unix() {
|
||||
let einval = this.eval_libc("ENOMEM");
|
||||
this.set_last_error(einval)?;
|
||||
this.set_last_error(LibcError("ENOMEM"))?;
|
||||
}
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
@ -464,8 +463,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
} else {
|
||||
// On size overflow, return null and, on Unix, set errno.
|
||||
if this.target_os_is_unix() {
|
||||
let einval = this.eval_libc("ENOMEM");
|
||||
this.set_last_error(einval)?;
|
||||
this.set_last_error(LibcError("ENOMEM"))?;
|
||||
}
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
@ -486,8 +484,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
} else {
|
||||
// If this does not fit in an isize, return null and, on Unix, set errno.
|
||||
if this.target_os_is_unix() {
|
||||
let einval = this.eval_libc("ENOMEM");
|
||||
this.set_last_error(einval)?;
|
||||
this.set_last_error(LibcError("ENOMEM"))?;
|
||||
}
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
@ -141,6 +141,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
interp_ok(Scalar::from_i32(-1))
|
||||
}
|
||||
|
||||
/// Sets the last OS error and return `-1` as a `i64`-typed Scalar
|
||||
fn set_last_error_and_return_i64(
|
||||
&mut self,
|
||||
err: impl Into<IoError>,
|
||||
) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
this.set_last_error(err)?;
|
||||
interp_ok(Scalar::from_i64(-1))
|
||||
}
|
||||
|
||||
/// Gets the last error variable.
|
||||
fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
|
@ -81,9 +81,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
} else if relative_clocks.contains(&clk_id) {
|
||||
this.machine.clock.now().duration_since(this.machine.clock.epoch())
|
||||
} else {
|
||||
let einval = this.eval_libc("EINVAL");
|
||||
this.set_last_error(einval)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
};
|
||||
|
||||
let tv_sec = duration.as_secs();
|
||||
@ -109,9 +107,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Using tz is obsolete and should always be null
|
||||
let tz = this.read_pointer(tz_op)?;
|
||||
if !this.ptr_is_null(tz)? {
|
||||
let einval = this.eval_libc("EINVAL");
|
||||
this.set_last_error(einval)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
}
|
||||
|
||||
let duration = system_time_to_duration(&SystemTime::now())?;
|
||||
@ -323,9 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let duration = match this.read_timespec(&req)? {
|
||||
Some(duration) => duration,
|
||||
None => {
|
||||
let einval = this.eval_libc("EINVAL");
|
||||
this.set_last_error(einval)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::shims::unix::android::thread::prctl;
|
||||
use crate::*;
|
||||
|
||||
pub fn is_dyn_sym(_name: &str) -> bool {
|
||||
@ -25,6 +26,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
|
||||
}
|
||||
|
||||
// Threading
|
||||
"prctl" => prctl(this, link_name, abi, args, dest)?,
|
||||
|
||||
_ => return interp_ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
interp_ok(EmulateItemResult::NeedsReturn)
|
||||
|
@ -1 +1,2 @@
|
||||
pub mod foreign_items;
|
||||
pub mod thread;
|
||||
|
57
src/tools/miri/src/shims/unix/android/thread.rs
Normal file
57
src/tools/miri/src/shims/unix/android/thread.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::helpers::check_min_arg_count;
|
||||
use crate::shims::unix::thread::EvalContextExt as _;
|
||||
use crate::*;
|
||||
|
||||
const TASK_COMM_LEN: usize = 16;
|
||||
|
||||
pub fn prctl<'tcx>(
|
||||
this: &mut MiriInterpCx<'tcx>,
|
||||
link_name: Symbol,
|
||||
abi: Abi,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// We do not use `check_shim` here because `prctl` is variadic. The argument
|
||||
// count is checked bellow.
|
||||
this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
|
||||
|
||||
// FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
|
||||
let pr_set_name = 15;
|
||||
let pr_get_name = 16;
|
||||
|
||||
let [op] = check_min_arg_count("prctl", args)?;
|
||||
let res = match this.read_scalar(op)?.to_i32()? {
|
||||
op if op == pr_set_name => {
|
||||
let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
|
||||
let name = this.read_scalar(name)?;
|
||||
let thread = this.pthread_self()?;
|
||||
// The Linux kernel silently truncates long names.
|
||||
// https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
|
||||
let res =
|
||||
this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
|
||||
assert!(res);
|
||||
Scalar::from_u32(0)
|
||||
}
|
||||
op if op == pr_get_name => {
|
||||
let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
|
||||
let name = this.read_scalar(name)?;
|
||||
let thread = this.pthread_self()?;
|
||||
let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this);
|
||||
this.check_ptr_access(
|
||||
name.to_pointer(this)?,
|
||||
Size::from_bytes(TASK_COMM_LEN),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
)?;
|
||||
let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
|
||||
assert!(res);
|
||||
Scalar::from_u32(0)
|
||||
}
|
||||
op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
|
||||
};
|
||||
this.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
@ -423,7 +423,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let Some(fd) = this.machine.fds.get(old_fd_num) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
interp_ok(Scalar::from_i32(this.machine.fds.insert(fd)))
|
||||
}
|
||||
@ -432,7 +432,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let Some(fd) = this.machine.fds.get(old_fd_num) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
if new_fd_num != old_fd_num {
|
||||
// Close new_fd if it is previously opened.
|
||||
@ -448,7 +448,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
|
||||
// We need to check that there aren't unsupported options in `op`.
|
||||
@ -498,11 +498,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
|
||||
// always sets this flag when opening a file. However we still need to check that the
|
||||
// file itself is open.
|
||||
interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) {
|
||||
this.eval_libc_i32("FD_CLOEXEC")
|
||||
if !this.machine.fds.is_fd_num(fd_num) {
|
||||
this.set_last_error_and_return_i32(LibcError("EBADF"))
|
||||
} else {
|
||||
this.fd_not_found()?
|
||||
}))
|
||||
interp_ok(this.eval_libc("FD_CLOEXEC"))
|
||||
}
|
||||
}
|
||||
cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => {
|
||||
// Note that we always assume the FD_CLOEXEC flag is set for every open file, in part
|
||||
@ -521,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if let Some(fd) = this.machine.fds.get(fd_num) {
|
||||
interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start)))
|
||||
} else {
|
||||
interp_ok(Scalar::from_i32(this.fd_not_found()?))
|
||||
this.set_last_error_and_return_i32(LibcError("EBADF"))
|
||||
}
|
||||
}
|
||||
cmd if this.tcx.sess.target.os == "macos"
|
||||
@ -547,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let fd_num = this.read_scalar(fd_op)?.to_i32()?;
|
||||
|
||||
let Some(fd) = this.machine.fds.remove(fd_num) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
let result = fd.close(this.machine.communicate(), this)?;
|
||||
// return `0` if close is successful
|
||||
@ -555,17 +555,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
|
||||
}
|
||||
|
||||
/// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets
|
||||
/// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
|
||||
/// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
|
||||
/// types (like `read`, that returns an `i64`).
|
||||
fn fd_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
|
||||
let this = self.eval_context_mut();
|
||||
let ebadf = this.eval_libc("EBADF");
|
||||
this.set_last_error(ebadf)?;
|
||||
interp_ok((-1).into())
|
||||
}
|
||||
|
||||
/// Read data from `fd` into buffer specified by `buf` and `count`.
|
||||
///
|
||||
/// If `offset` is `None`, reads data from current cursor position associated with `fd`
|
||||
@ -599,9 +588,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// We temporarily dup the FD to be able to retain mutable access to `this`.
|
||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||
trace!("read: FD not found");
|
||||
let res: i32 = this.fd_not_found()?;
|
||||
this.write_int(res, dest)?;
|
||||
return interp_ok(());
|
||||
return this.set_last_error_and_return(LibcError("EBADF"), dest);
|
||||
};
|
||||
|
||||
trace!("read: FD mapped to {fd:?}");
|
||||
@ -646,9 +633,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// We temporarily dup the FD to be able to retain mutable access to `this`.
|
||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||
let res: i32 = this.fd_not_found()?;
|
||||
this.write_int(res, dest)?;
|
||||
return interp_ok(());
|
||||
return this.set_last_error_and_return(LibcError("EBADF"), dest);
|
||||
};
|
||||
|
||||
match offset {
|
||||
|
@ -362,8 +362,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
|
||||
match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
|
||||
None => {
|
||||
let enmem = this.eval_libc("ENOMEM");
|
||||
this.set_last_error(enmem)?;
|
||||
this.set_last_error(LibcError("ENOMEM"))?;
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
Some(len) => {
|
||||
@ -653,13 +652,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let chunk_size = CpuAffinityMask::chunk_size(this);
|
||||
|
||||
if this.ptr_is_null(mask)? {
|
||||
let efault = this.eval_libc("EFAULT");
|
||||
this.set_last_error(efault)?;
|
||||
this.write_int(-1, dest)?;
|
||||
this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
|
||||
} else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
|
||||
// we only copy whole chunks of size_of::<c_ulong>()
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
this.write_int(-1, dest)?;
|
||||
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
|
||||
} else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
|
||||
let cpuset = cpuset.clone();
|
||||
// we only copy whole chunks of size_of::<c_ulong>()
|
||||
@ -668,9 +664,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.write_null(dest)?;
|
||||
} else {
|
||||
// The thread whose ID is pid could not be found
|
||||
let esrch = this.eval_libc("ESRCH");
|
||||
this.set_last_error(esrch)?;
|
||||
this.write_int(-1, dest)?;
|
||||
this.set_last_error_and_return(LibcError("ESRCH"), dest)?;
|
||||
}
|
||||
}
|
||||
"sched_setaffinity" => {
|
||||
@ -695,9 +689,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
};
|
||||
|
||||
if this.ptr_is_null(mask)? {
|
||||
let efault = this.eval_libc("EFAULT");
|
||||
this.set_last_error(efault)?;
|
||||
this.write_int(-1, dest)?;
|
||||
this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
|
||||
} else {
|
||||
// NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
|
||||
// Any unspecified bytes are treated as zero here (none of the CPUs are configured).
|
||||
@ -713,8 +705,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
None => {
|
||||
// The intersection between the mask and the available CPUs was empty.
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
this.write_int(-1, dest)?;
|
||||
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -770,9 +761,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html
|
||||
// Solaris/Illumos: https://illumos.org/man/3C/getentropy
|
||||
if bufsize > 256 {
|
||||
let err = this.eval_libc("EIO");
|
||||
this.set_last_error(err)?;
|
||||
this.write_int(-1, dest)?;
|
||||
this.set_last_error_and_return(LibcError("EIO"), dest)?;
|
||||
} else {
|
||||
this.gen_random(buf, bufsize)?;
|
||||
this.write_null(dest)?;
|
||||
|
@ -29,6 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
max_len,
|
||||
/* truncate */ false,
|
||||
)?;
|
||||
}
|
||||
"pthread_get_name_np" => {
|
||||
|
@ -149,6 +149,7 @@ impl FileDescription for FileHandle {
|
||||
// to handle possible errors correctly.
|
||||
let result = self.file.sync_all();
|
||||
// Now we actually close the file and return the result.
|
||||
drop(*self);
|
||||
interp_ok(result)
|
||||
} else {
|
||||
// We drop the file, this closes it but ignores any errors
|
||||
@ -157,6 +158,7 @@ impl FileDescription for FileHandle {
|
||||
// `/dev/urandom` which are read-only. Check
|
||||
// https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
|
||||
// for a deeper discussion.
|
||||
drop(*self);
|
||||
interp_ok(Ok(()))
|
||||
}
|
||||
}
|
||||
@ -229,6 +231,8 @@ impl FileDescription for FileHandle {
|
||||
TRUE => Ok(()),
|
||||
FALSE => {
|
||||
let mut err = io::Error::last_os_error();
|
||||
// This only runs on Windows hosts so we can use `raw_os_error`.
|
||||
// We have to be careful not to forward that error code to target code.
|
||||
let code: u32 = err.raw_os_error().unwrap().try_into().unwrap();
|
||||
if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) {
|
||||
if lock_nb {
|
||||
@ -337,15 +341,10 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
_ => interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()),
|
||||
}
|
||||
}
|
||||
Err(e) =>
|
||||
match e.raw_os_error() {
|
||||
Some(error) => interp_ok(error),
|
||||
None =>
|
||||
throw_unsup_format!(
|
||||
"the error {} couldn't be converted to a return value",
|
||||
e
|
||||
),
|
||||
},
|
||||
Err(_) => {
|
||||
// Fallback on error
|
||||
interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -528,8 +527,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let o_tmpfile = this.eval_libc_i32("O_TMPFILE");
|
||||
if flag & o_tmpfile == o_tmpfile {
|
||||
// if the flag contains `O_TMPFILE` then we return a graceful error
|
||||
this.set_last_error(LibcError("EOPNOTSUPP"))?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EOPNOTSUPP"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,9 +546,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// O_NOFOLLOW only fails when the trailing component is a symlink;
|
||||
// the entire rest of the path can still contain symlinks.
|
||||
if path.is_symlink() {
|
||||
let eloop = this.eval_libc("ELOOP");
|
||||
this.set_last_error(eloop)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("ELOOP"));
|
||||
}
|
||||
}
|
||||
mirror |= o_nofollow;
|
||||
@ -565,8 +561,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`open`", reject_with)?;
|
||||
this.set_last_error(ErrorKind::PermissionDenied)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||
}
|
||||
|
||||
let fd = options
|
||||
@ -584,8 +579,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
|
||||
if offset < 0 {
|
||||
// Negative offsets return `EINVAL`.
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i64(-1));
|
||||
return this.set_last_error_and_return_i64(LibcError("EINVAL"));
|
||||
} else {
|
||||
SeekFrom::Start(u64::try_from(offset).unwrap())
|
||||
}
|
||||
@ -594,14 +588,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
} else if whence == this.eval_libc_i32("SEEK_END") {
|
||||
SeekFrom::End(i64::try_from(offset).unwrap())
|
||||
} else {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i64(-1));
|
||||
return this.set_last_error_and_return_i64(LibcError("EINVAL"));
|
||||
};
|
||||
|
||||
let communicate = this.machine.communicate();
|
||||
|
||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||
return interp_ok(Scalar::from_i64(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i64(LibcError("EBADF"));
|
||||
};
|
||||
let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap());
|
||||
drop(fd);
|
||||
@ -618,8 +611,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`unlink`", reject_with)?;
|
||||
this.set_last_error(ErrorKind::PermissionDenied)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||
}
|
||||
|
||||
let result = remove_file(path).map(|_| 0);
|
||||
@ -649,8 +641,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`symlink`", reject_with)?;
|
||||
this.set_last_error(ErrorKind::PermissionDenied)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||
}
|
||||
|
||||
let result = create_link(&target, &linkpath).map(|_| 0);
|
||||
@ -674,15 +665,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`stat`", reject_with)?;
|
||||
let eacc = this.eval_libc("EACCES");
|
||||
this.set_last_error(eacc)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EACCES"));
|
||||
}
|
||||
|
||||
// `stat` always follows symlinks.
|
||||
let metadata = match FileMetadata::from_path(this, &path, true)? {
|
||||
Some(metadata) => metadata,
|
||||
None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => return this.set_last_error_and_return_i32(err),
|
||||
};
|
||||
|
||||
interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
|
||||
@ -706,14 +695,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`lstat`", reject_with)?;
|
||||
let eacc = this.eval_libc("EACCES");
|
||||
this.set_last_error(eacc)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EACCES"));
|
||||
}
|
||||
|
||||
let metadata = match FileMetadata::from_path(this, &path, false)? {
|
||||
Some(metadata) => metadata,
|
||||
None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => return this.set_last_error_and_return_i32(err),
|
||||
};
|
||||
|
||||
interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
|
||||
@ -736,12 +723,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`fstat`", reject_with)?;
|
||||
// Set error code as "EBADF" (bad fd)
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
}
|
||||
|
||||
let metadata = match FileMetadata::from_fd_num(this, fd)? {
|
||||
Some(metadata) => metadata,
|
||||
None => return interp_ok(Scalar::from_i32(-1)),
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => return this.set_last_error_and_return_i32(err),
|
||||
};
|
||||
interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
|
||||
}
|
||||
@ -766,9 +753,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// If the statxbuf or pathname pointers are null, the function fails with `EFAULT`.
|
||||
if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? {
|
||||
let efault = this.eval_libc("EFAULT");
|
||||
this.set_last_error(efault)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EFAULT"));
|
||||
}
|
||||
|
||||
let statxbuf = this.deref_pointer_as(statxbuf_op, this.libc_ty_layout("statx"))?;
|
||||
@ -801,16 +786,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD") {
|
||||
// since `path` is provided, either absolute or
|
||||
// relative to CWD, `EACCES` is the most relevant.
|
||||
this.eval_libc("EACCES")
|
||||
LibcError("EACCES")
|
||||
} else {
|
||||
// `dirfd` is set to target file, and `path` is empty
|
||||
// (or we would have hit the `throw_unsup_format`
|
||||
// above). `EACCES` would violate the spec.
|
||||
assert!(empty_path_flag);
|
||||
this.eval_libc("EBADF")
|
||||
LibcError("EBADF")
|
||||
};
|
||||
this.set_last_error(ecode)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(ecode);
|
||||
}
|
||||
|
||||
// the `_mask_op` parameter specifies the file information that the caller requested.
|
||||
@ -831,8 +815,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
FileMetadata::from_path(this, &path, follow_symlink)?
|
||||
};
|
||||
let metadata = match metadata {
|
||||
Some(metadata) => metadata,
|
||||
None => return interp_ok(Scalar::from_i32(-1)),
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => return this.set_last_error_and_return_i32(err),
|
||||
};
|
||||
|
||||
// The `mode` field specifies the type of the file and the permissions over the file for
|
||||
@ -939,9 +923,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let newpath_ptr = this.read_pointer(newpath_op)?;
|
||||
|
||||
if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? {
|
||||
let efault = this.eval_libc("EFAULT");
|
||||
this.set_last_error(efault)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EFAULT"));
|
||||
}
|
||||
|
||||
let oldpath = this.read_path_from_c_str(oldpath_ptr)?;
|
||||
@ -950,8 +932,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`rename`", reject_with)?;
|
||||
this.set_last_error(ErrorKind::PermissionDenied)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||
}
|
||||
|
||||
let result = rename(oldpath, newpath).map(|_| 0);
|
||||
@ -974,8 +955,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`mkdir`", reject_with)?;
|
||||
this.set_last_error(ErrorKind::PermissionDenied)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||
}
|
||||
|
||||
#[cfg_attr(not(unix), allow(unused_mut))]
|
||||
@ -1002,8 +982,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`rmdir`", reject_with)?;
|
||||
this.set_last_error(ErrorKind::PermissionDenied)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||
}
|
||||
|
||||
let result = remove_dir(path).map(|_| 0i32);
|
||||
@ -1019,8 +998,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`opendir`", reject_with)?;
|
||||
let eacc = this.eval_libc("EACCES");
|
||||
this.set_last_error(eacc)?;
|
||||
this.set_last_error(LibcError("EACCES"))?;
|
||||
return interp_ok(Scalar::null_ptr(this));
|
||||
}
|
||||
|
||||
@ -1052,8 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`readdir`", reject_with)?;
|
||||
let eacc = this.eval_libc("EBADF");
|
||||
this.set_last_error(eacc)?;
|
||||
this.set_last_error(LibcError("EBADF"))?;
|
||||
return interp_ok(Scalar::null_ptr(this));
|
||||
}
|
||||
|
||||
@ -1152,14 +1129,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`readdir_r`", reject_with)?;
|
||||
// Set error code as "EBADF" (bad fd)
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
// Return error code, do *not* set `errno`.
|
||||
return interp_ok(this.eval_libc("EBADF"));
|
||||
}
|
||||
|
||||
let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| {
|
||||
err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir")
|
||||
})?;
|
||||
interp_ok(Scalar::from_i32(match open_dir.read_dir.next() {
|
||||
interp_ok(match open_dir.read_dir.next() {
|
||||
Some(Ok(dir_entry)) => {
|
||||
// Write into entry, write pointer to result, return 0 on success.
|
||||
// The name is written with write_os_str_to_c_str, while the rest of the
|
||||
@ -1237,25 +1214,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let result_place = this.deref_pointer(result_op)?;
|
||||
this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;
|
||||
|
||||
0
|
||||
Scalar::from_i32(0)
|
||||
}
|
||||
None => {
|
||||
// end of stream: return 0, assign *result=NULL
|
||||
this.write_null(&this.deref_pointer(result_op)?)?;
|
||||
0
|
||||
Scalar::from_i32(0)
|
||||
}
|
||||
Some(Err(e)) =>
|
||||
match e.raw_os_error() {
|
||||
// return positive error number on error
|
||||
Some(error) => error,
|
||||
None => {
|
||||
throw_unsup_format!(
|
||||
"the error {} couldn't be converted to a return value",
|
||||
e
|
||||
)
|
||||
}
|
||||
},
|
||||
}))
|
||||
Some(Err(e)) => {
|
||||
// return positive error number on error (do *not* set last error)
|
||||
this.io_error_to_errnum(e)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||
@ -1264,20 +1234,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let dirp = this.read_target_usize(dirp_op)?;
|
||||
|
||||
// Reject if isolation is enabled.
|
||||
interp_ok(Scalar::from_i32(
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`closedir`", reject_with)?;
|
||||
this.fd_not_found()?
|
||||
} else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) {
|
||||
if let Some(entry) = open_dir.entry {
|
||||
this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?;
|
||||
}
|
||||
drop(open_dir);
|
||||
0
|
||||
} else {
|
||||
this.fd_not_found()?
|
||||
},
|
||||
))
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`closedir`", reject_with)?;
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
}
|
||||
|
||||
let Some(mut open_dir) = this.machine.dirs.streams.remove(&dirp) else {
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
if let Some(entry) = open_dir.entry.take() {
|
||||
this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?;
|
||||
}
|
||||
// We drop the `open_dir`, which will close the host dir handle.
|
||||
drop(open_dir);
|
||||
|
||||
interp_ok(Scalar::from_i32(0))
|
||||
}
|
||||
|
||||
fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> {
|
||||
@ -1287,11 +1258,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`ftruncate64`", reject_with)?;
|
||||
// Set error code as "EBADF" (bad fd)
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
}
|
||||
|
||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
|
||||
// FIXME: Support ftruncate64 for all FDs
|
||||
@ -1307,14 +1278,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
interp_ok(Scalar::from_i32(result))
|
||||
} else {
|
||||
drop(fd);
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
interp_ok(Scalar::from_i32(-1))
|
||||
this.set_last_error_and_return_i32(LibcError("EINVAL"))
|
||||
}
|
||||
} else {
|
||||
drop(fd);
|
||||
// The file is not writable
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
interp_ok(Scalar::from_i32(-1))
|
||||
this.set_last_error_and_return_i32(LibcError("EINVAL"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1332,7 +1301,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`fsync`", reject_with)?;
|
||||
// Set error code as "EBADF" (bad fd)
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
}
|
||||
|
||||
self.ffullsync_fd(fd)
|
||||
@ -1341,7 +1310,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
// Only regular files support synchronization.
|
||||
let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
|
||||
@ -1361,11 +1330,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`fdatasync`", reject_with)?;
|
||||
// Set error code as "EBADF" (bad fd)
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
}
|
||||
|
||||
let Some(fd) = this.machine.fds.get(fd) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
// Only regular files support synchronization.
|
||||
let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
|
||||
@ -1391,26 +1360,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let flags = this.read_scalar(flags_op)?.to_i32()?;
|
||||
|
||||
if offset < 0 || nbytes < 0 {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
}
|
||||
let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE")
|
||||
| this.eval_libc_i32("SYNC_FILE_RANGE_WRITE")
|
||||
| this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER");
|
||||
if flags & allowed_flags != flags {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
}
|
||||
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`sync_file_range`", reject_with)?;
|
||||
// Set error code as "EBADF" (bad fd)
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
}
|
||||
|
||||
let Some(fd) = this.machine.fds.get(fd) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
// Only regular files support synchronization.
|
||||
let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
|
||||
@ -1436,8 +1403,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`readlink`", reject_with)?;
|
||||
let eacc = this.eval_libc("EACCES");
|
||||
this.set_last_error(eacc)?;
|
||||
this.set_last_error(LibcError("EACCES"))?;
|
||||
return interp_ok(-1);
|
||||
}
|
||||
|
||||
@ -1475,11 +1441,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
if fd.is_tty(this.machine.communicate()) {
|
||||
return interp_ok(Scalar::from_i32(1));
|
||||
} else {
|
||||
this.eval_libc("ENOTTY")
|
||||
LibcError("ENOTTY")
|
||||
}
|
||||
} else {
|
||||
// FD does not exist
|
||||
this.eval_libc("EBADF")
|
||||
LibcError("EBADF")
|
||||
};
|
||||
this.set_last_error(error)?;
|
||||
interp_ok(Scalar::from_i32(0))
|
||||
@ -1499,8 +1465,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`realpath`", reject_with)?;
|
||||
let eacc = this.eval_libc("EACCES");
|
||||
this.set_last_error(eacc)?;
|
||||
this.set_last_error(LibcError("EACCES"))?;
|
||||
return interp_ok(Scalar::from_target_usize(0, this));
|
||||
}
|
||||
|
||||
@ -1530,8 +1495,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Note that we do not explicitly handle `FILENAME_MAX`
|
||||
// (different from `PATH_MAX` above) as it is Linux-specific and
|
||||
// seems like a bit of a mess anyway: <https://eklitzke.org/path-max-is-tricky>.
|
||||
let enametoolong = this.eval_libc("ENAMETOOLONG");
|
||||
this.set_last_error(enametoolong)?;
|
||||
this.set_last_error(LibcError("ENAMETOOLONG"))?;
|
||||
return interp_ok(Scalar::from_target_usize(0, this));
|
||||
}
|
||||
processed_ptr
|
||||
@ -1574,9 +1538,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`mkstemp`", reject_with)?;
|
||||
let eacc = this.eval_libc("EACCES");
|
||||
this.set_last_error(eacc)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EACCES"));
|
||||
}
|
||||
|
||||
// Get the bytes of the suffix we expect in _target_ encoding.
|
||||
@ -1592,8 +1554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// If we don't find the suffix, it is an error.
|
||||
if last_six_char_bytes != suffix_bytes {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
}
|
||||
|
||||
// At this point we know we have 6 ASCII 'X' characters as a suffix.
|
||||
@ -1658,17 +1619,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
_ => {
|
||||
// "On error, -1 is returned, and errno is set to
|
||||
// indicate the error"
|
||||
this.set_last_error(e)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// We ran out of attempts to create the file, return an error.
|
||||
let eexist = this.eval_libc("EEXIST");
|
||||
this.set_last_error(eexist)?;
|
||||
interp_ok(Scalar::from_i32(-1))
|
||||
this.set_last_error_and_return_i32(LibcError("EEXIST"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1702,7 +1660,7 @@ impl FileMetadata {
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
path: &Path,
|
||||
follow_symlink: bool,
|
||||
) -> InterpResult<'tcx, Option<FileMetadata>> {
|
||||
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
|
||||
let metadata =
|
||||
if follow_symlink { std::fs::metadata(path) } else { std::fs::symlink_metadata(path) };
|
||||
|
||||
@ -1712,9 +1670,9 @@ impl FileMetadata {
|
||||
fn from_fd_num<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
fd_num: i32,
|
||||
) -> InterpResult<'tcx, Option<FileMetadata>> {
|
||||
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
|
||||
let Some(fd) = ecx.machine.fds.get(fd_num) else {
|
||||
return ecx.fd_not_found().map(|_: i32| None);
|
||||
return interp_ok(Err(LibcError("EBADF")));
|
||||
};
|
||||
|
||||
let file = &fd
|
||||
@ -1734,12 +1692,11 @@ impl FileMetadata {
|
||||
fn from_meta<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
metadata: Result<std::fs::Metadata, std::io::Error>,
|
||||
) -> InterpResult<'tcx, Option<FileMetadata>> {
|
||||
) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
|
||||
let metadata = match metadata {
|
||||
Ok(metadata) => metadata,
|
||||
Err(e) => {
|
||||
ecx.set_last_error(e)?;
|
||||
return interp_ok(None);
|
||||
return interp_ok(Err(e.into()));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1762,6 +1719,6 @@ impl FileMetadata {
|
||||
let modified = extract_sec_and_nsec(metadata.modified())?;
|
||||
|
||||
// FIXME: Provide more fields using platform specific methods.
|
||||
interp_ok(Some(FileMetadata { mode, size, created, accessed, modified }))
|
||||
interp_ok(Ok(FileMetadata { mode, size, created, accessed, modified }))
|
||||
}
|
||||
}
|
||||
|
@ -256,23 +256,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let epollhup = this.eval_libc_u32("EPOLLHUP");
|
||||
let epollerr = this.eval_libc_u32("EPOLLERR");
|
||||
|
||||
// Fail on unsupported operations.
|
||||
if op & epoll_ctl_add != epoll_ctl_add
|
||||
&& op & epoll_ctl_mod != epoll_ctl_mod
|
||||
&& op & epoll_ctl_del != epoll_ctl_del
|
||||
{
|
||||
throw_unsup_format!("epoll_ctl: encountered unknown unsupported operation {:#x}", op);
|
||||
}
|
||||
|
||||
// Throw EINVAL if epfd and fd have the same value.
|
||||
if epfd_value == fd {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
}
|
||||
|
||||
// Check if epfd is a valid epoll file descriptor.
|
||||
let Some(epfd) = this.machine.fds.get(epfd_value) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
let epoll_file_description = epfd
|
||||
.downcast::<Epoll>()
|
||||
@ -282,7 +273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let ready_list = &epoll_file_description.ready_list;
|
||||
|
||||
let Some(fd_ref) = this.machine.fds.get(fd) else {
|
||||
return interp_ok(Scalar::from_i32(this.fd_not_found()?));
|
||||
return this.set_last_error_and_return_i32(LibcError("EBADF"));
|
||||
};
|
||||
let id = fd_ref.get_id();
|
||||
|
||||
@ -332,15 +323,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Check the existence of fd in the interest list.
|
||||
if op == epoll_ctl_add {
|
||||
if interest_list.contains_key(&epoll_key) {
|
||||
let eexist = this.eval_libc("EEXIST");
|
||||
this.set_last_error(eexist)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EEXIST"));
|
||||
}
|
||||
} else {
|
||||
if !interest_list.contains_key(&epoll_key) {
|
||||
let enoent = this.eval_libc("ENOENT");
|
||||
this.set_last_error(enoent)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("ENOENT"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,15 +355,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Notification will be returned for current epfd if there is event in the file
|
||||
// descriptor we registered.
|
||||
check_and_update_one_event_interest(&fd_ref, interest, id, this)?;
|
||||
return interp_ok(Scalar::from_i32(0));
|
||||
interp_ok(Scalar::from_i32(0))
|
||||
} else if op == epoll_ctl_del {
|
||||
let epoll_key = (id, fd);
|
||||
|
||||
// Remove epoll_event_interest from interest_list.
|
||||
let Some(epoll_interest) = interest_list.remove(&epoll_key) else {
|
||||
let enoent = this.eval_libc("ENOENT");
|
||||
this.set_last_error(enoent)?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("ENOENT"));
|
||||
};
|
||||
// All related Weak<EpollEventInterest> will fail to upgrade after the drop.
|
||||
drop(epoll_interest);
|
||||
@ -394,9 +379,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
.unwrap()
|
||||
.retain(|event| event.upgrade().is_some());
|
||||
|
||||
return interp_ok(Scalar::from_i32(0));
|
||||
interp_ok(Scalar::from_i32(0))
|
||||
} else {
|
||||
throw_unsup_format!("unsupported epoll_ctl operation: {op}");
|
||||
}
|
||||
interp_ok(Scalar::from_i32(-1))
|
||||
}
|
||||
|
||||
/// The `epoll_wait()` system call waits for events on the `Epoll`
|
||||
@ -447,9 +433,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let timeout = this.read_scalar(timeout)?.to_i32()?;
|
||||
|
||||
if epfd_value <= 0 || maxevents <= 0 {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
this.write_int(-1, dest)?;
|
||||
return interp_ok(());
|
||||
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
|
||||
}
|
||||
|
||||
// This needs to come after the maxevents value check, or else maxevents.try_into().unwrap()
|
||||
@ -460,9 +444,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
)?;
|
||||
|
||||
let Some(epfd) = this.machine.fds.get(epfd_value) else {
|
||||
let result_value: i32 = this.fd_not_found()?;
|
||||
this.write_int(result_value, dest)?;
|
||||
return interp_ok(());
|
||||
return this.set_last_error_and_return(LibcError("EBADF"), dest);
|
||||
};
|
||||
// Create a weak ref of epfd and pass it to callback so we will make sure that epfd
|
||||
// is not close after the thread unblocks.
|
||||
|
@ -84,6 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
TASK_COMM_LEN,
|
||||
/* truncate */ false,
|
||||
)?;
|
||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
||||
this.write_scalar(res, dest)?;
|
||||
|
@ -24,7 +24,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// old_address must be a multiple of the page size
|
||||
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
|
||||
if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
|
||||
// We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
|
@ -63,8 +63,7 @@ pub fn futex<'tcx>(
|
||||
};
|
||||
|
||||
if bitset == 0 {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
||||
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
|
||||
return interp_ok(());
|
||||
}
|
||||
|
||||
@ -75,9 +74,7 @@ pub fn futex<'tcx>(
|
||||
let duration = match this.read_timespec(&timeout)? {
|
||||
Some(duration) => duration,
|
||||
None => {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
||||
return interp_ok(());
|
||||
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
|
||||
}
|
||||
};
|
||||
let timeout_clock = if op & futex_realtime == futex_realtime {
|
||||
@ -153,14 +150,12 @@ pub fn futex<'tcx>(
|
||||
Scalar::from_target_isize(0, this), // retval_succ
|
||||
Scalar::from_target_isize(-1, this), // retval_timeout
|
||||
dest.clone(),
|
||||
this.eval_libc("ETIMEDOUT"),
|
||||
this.eval_libc("ETIMEDOUT"), // errno_timeout
|
||||
);
|
||||
} else {
|
||||
// The futex value doesn't match the expected value, so we return failure
|
||||
// right away without sleeping: -1 and errno set to EAGAIN.
|
||||
let eagain = this.eval_libc("EAGAIN");
|
||||
this.set_last_error(eagain)?;
|
||||
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
||||
return this.set_last_error_and_return(LibcError("EAGAIN"), dest);
|
||||
}
|
||||
}
|
||||
// FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
|
||||
@ -180,9 +175,7 @@ pub fn futex<'tcx>(
|
||||
u32::MAX
|
||||
};
|
||||
if bitset == 0 {
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
||||
return interp_ok(());
|
||||
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
|
||||
}
|
||||
// Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
|
||||
// will see the latest value on addr which could be changed by our caller
|
||||
|
@ -181,6 +181,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
thread,
|
||||
this.read_scalar(name)?,
|
||||
this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
|
||||
/* truncate */ false,
|
||||
)? {
|
||||
Scalar::from_u32(0)
|
||||
} else {
|
||||
|
@ -57,11 +57,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// First, we do some basic argument validation as required by mmap
|
||||
if (flags & (map_private | map_shared)).count_ones() != 1 {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
if length == 0 {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
@ -103,11 +103,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
let align = this.machine.page_align();
|
||||
let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
};
|
||||
if map_length > this.target_usize_max() {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
@ -134,16 +134,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// as a dealloc.
|
||||
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
|
||||
if addr.addr().bytes() % this.machine.page_size != 0 {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
}
|
||||
|
||||
let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
return interp_ok(Scalar::from_i32(-1));
|
||||
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
|
||||
};
|
||||
if length > this.target_usize_max() {
|
||||
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||
this.set_last_error(LibcError("EINVAL"))?;
|
||||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
max_len,
|
||||
/* truncate */ false,
|
||||
)?;
|
||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
||||
this.write_scalar(res, dest)?;
|
||||
|
@ -64,23 +64,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
/// Set the name of the specified thread. If the name including the null terminator
|
||||
/// is longer than `name_max_len`, then `false` is returned.
|
||||
/// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
|
||||
/// is used as the thread name, otherwise `false` is returned.
|
||||
fn pthread_setname_np(
|
||||
&mut self,
|
||||
thread: Scalar,
|
||||
name: Scalar,
|
||||
name_max_len: usize,
|
||||
truncate: bool,
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
|
||||
let thread = ThreadId::try_from(thread).unwrap();
|
||||
let name = name.to_pointer(this)?;
|
||||
let name = this.read_c_str(name)?.to_owned();
|
||||
let mut name = this.read_c_str(name)?.to_owned();
|
||||
|
||||
// Comparing with `>=` to account for null terminator.
|
||||
if name.len() >= name_max_len {
|
||||
return interp_ok(false);
|
||||
if truncate {
|
||||
name.truncate(name_max_len.saturating_sub(1));
|
||||
} else {
|
||||
return interp_ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
this.set_thread_name(thread, name);
|
||||
|
@ -202,7 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
Scalar::from_i32(1), // retval_succ
|
||||
Scalar::from_i32(0), // retval_timeout
|
||||
dest.clone(),
|
||||
this.eval_windows("c", "ERROR_TIMEOUT"),
|
||||
this.eval_windows("c", "ERROR_TIMEOUT"), // errno_timeout
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -128,9 +128,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
@ -0,0 +1,10 @@
|
||||
//! Ensure we report UB when the buffer is smaller than 16 bytes (even if the thread
|
||||
//! name would fit in the smaller buffer).
|
||||
//@only-target: android # Miri supports prctl for Android only
|
||||
|
||||
fn main() {
|
||||
let mut buf = vec![0u8; 15];
|
||||
unsafe {
|
||||
libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::<libc::c_char>()); //~ ERROR: memory access failed: expected a pointer to 16 bytes of memory, but got alloc952 which is only 15 bytes from the end of the allocation
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
error: Undefined Behavior: memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation
|
||||
--> tests/fail-dep/libc/prctl-threadname.rs:LL:CC
|
||||
|
|
||||
LL | libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::<libc::c_char>());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> tests/fail-dep/libc/prctl-threadname.rs:LL:CC
|
||||
|
|
||||
LL | let mut buf = vec![0u8; 15];
|
||||
| ^^^^^^^^^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `main` at tests/fail-dep/libc/prctl-threadname.rs:LL:CC
|
||||
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! This is a regression test for <https://github.com/rust-lang/miri/issues/3947>: we had some
|
||||
//! faulty logic around `release_clock` that led to this code not reporting a data race.
|
||||
//@ignore-target: windows # no libc socketpair on Windows
|
||||
//@compile-flags: -Zmiri-preemption-rate=0
|
||||
//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-rate=0
|
||||
use std::thread;
|
||||
|
||||
fn main() {
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
// Ensure that a `ptr::without_provenance` ptr is truly invalid.
|
||||
fn main() {
|
||||
let x = 42;
|
||||
|
16
src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs
Normal file
16
src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs
Normal file
@ -0,0 +1,16 @@
|
||||
#![feature(explicit_tail_calls)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
fn g(x: *const i32) {
|
||||
let _val = unsafe { *x }; //~ERROR: has been freed, so this pointer is dangling
|
||||
}
|
||||
|
||||
fn f(_x: *const i32) {
|
||||
let local = 0;
|
||||
let ptr = &local as *const i32;
|
||||
become g(ptr)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f(std::ptr::null());
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
|
|
||||
LL | let _val = unsafe { *x };
|
||||
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
|
|
||||
LL | let local = 0;
|
||||
| ^^^^^
|
||||
help: ALLOC was deallocated here:
|
||||
--> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
|
|
||||
LL | }
|
||||
| ^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
|
|
||||
LL | f(std::ptr::null());
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
74
src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs
Normal file
74
src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//@only-target: android # Miri supports prctl for Android only
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::thread;
|
||||
|
||||
// The Linux kernel all names 16 bytes long including the null terminator.
|
||||
const MAX_THREAD_NAME_LEN: usize = 16;
|
||||
|
||||
fn main() {
|
||||
// The short name should be shorter than 16 bytes which POSIX promises
|
||||
// for thread names. The length includes a null terminator.
|
||||
let short_name = "test_named".to_owned();
|
||||
let long_name = std::iter::once("test_named_thread_truncation")
|
||||
.chain(std::iter::repeat(" yada").take(100))
|
||||
.collect::<String>();
|
||||
|
||||
fn set_thread_name(name: &CStr) -> i32 {
|
||||
unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr().cast::<libc::c_char>()) }
|
||||
}
|
||||
|
||||
fn get_thread_name(name: &mut [u8]) -> i32 {
|
||||
assert!(name.len() >= MAX_THREAD_NAME_LEN);
|
||||
unsafe { libc::prctl(libc::PR_GET_NAME, name.as_mut_ptr().cast::<libc::c_char>()) }
|
||||
}
|
||||
|
||||
// Set name via Rust API, get it via prctl.
|
||||
let long_name2 = long_name.clone();
|
||||
thread::Builder::new()
|
||||
.name(long_name.clone())
|
||||
.spawn(move || {
|
||||
let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
|
||||
assert_eq!(get_thread_name(&mut buf), 0);
|
||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||
let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)];
|
||||
assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
.unwrap();
|
||||
|
||||
// Set name via prctl and get it again (short name).
|
||||
thread::Builder::new()
|
||||
.spawn(move || {
|
||||
// Set short thread name.
|
||||
let cstr = CString::new(short_name.clone()).unwrap();
|
||||
assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit
|
||||
assert_eq!(set_thread_name(&cstr), 0);
|
||||
|
||||
let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
|
||||
assert_eq!(get_thread_name(&mut buf), 0);
|
||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||
assert_eq!(cstr.to_bytes(), short_name.as_bytes());
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
.unwrap();
|
||||
|
||||
// Set name via prctl and get it again (long name).
|
||||
thread::Builder::new()
|
||||
.spawn(move || {
|
||||
// Set full thread name.
|
||||
let cstr = CString::new(long_name.clone()).unwrap();
|
||||
assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
|
||||
// Names are truncated by the Linux kernel.
|
||||
assert_eq!(set_thread_name(&cstr), 0);
|
||||
|
||||
let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
|
||||
assert_eq!(get_thread_name(&mut buf), 0);
|
||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||
assert_eq!(cstr.to_bytes(), &long_name.as_bytes()[..(MAX_THREAD_NAME_LEN - 1)]);
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
.unwrap();
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
//@ignore-target: windows # No pthreads on Windows
|
||||
//@ignore-target: android # No pthread_{get,set}_name on Android
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::thread;
|
||||
|
||||
@ -65,6 +66,22 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Set name via Rust API, get it via pthreads.
|
||||
let long_name2 = long_name.clone();
|
||||
thread::Builder::new()
|
||||
.name(long_name.clone())
|
||||
.spawn(move || {
|
||||
let mut buf = vec![0u8; long_name2.len() + 1];
|
||||
assert_eq!(get_thread_name(&mut buf), 0);
|
||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||
let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)];
|
||||
assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
.unwrap();
|
||||
|
||||
// Set name via pthread and get it again (short name).
|
||||
thread::Builder::new()
|
||||
.spawn(move || {
|
||||
// Set short thread name.
|
||||
@ -130,6 +147,7 @@ fn main() {
|
||||
.join()
|
||||
.unwrap();
|
||||
|
||||
// Set name via pthread and get it again (long name).
|
||||
thread::Builder::new()
|
||||
.spawn(move || {
|
||||
// Set full thread name.
|
||||
|
@ -433,7 +433,8 @@ fn replace_vptr() {
|
||||
}
|
||||
|
||||
fn drop_principal() {
|
||||
use std::{alloc::Layout, any::Any};
|
||||
use std::alloc::Layout;
|
||||
use std::any::Any;
|
||||
|
||||
const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> {
|
||||
x
|
||||
|
@ -157,13 +157,18 @@ fn basic() {
|
||||
assert_eq(-{ 5.0_f128 }, -5.0_f128);
|
||||
|
||||
// infinities, NaN
|
||||
// FIXME(f16_f128): add when constants and `is_infinite` are available
|
||||
assert!((5.0_f16 / 0.0).is_infinite());
|
||||
assert_ne!({ 5.0_f16 / 0.0 }, { -5.0_f16 / 0.0 });
|
||||
assert!((5.0_f32 / 0.0).is_infinite());
|
||||
assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 });
|
||||
assert!((5.0_f64 / 0.0).is_infinite());
|
||||
assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 });
|
||||
assert!((5.0_f128 / 0.0).is_infinite());
|
||||
assert_ne!({ 5.0_f128 / 0.0 }, { 5.0_f128 / -0.0 });
|
||||
assert_ne!(f16::NAN, f16::NAN);
|
||||
assert_ne!(f32::NAN, f32::NAN);
|
||||
assert_ne!(f64::NAN, f64::NAN);
|
||||
assert_ne!(f128::NAN, f128::NAN);
|
||||
|
||||
// negative zero
|
||||
let posz = 0.0f16;
|
||||
@ -215,9 +220,14 @@ fn basic() {
|
||||
assert!((black_box(-1.0f128) % 1.0).is_sign_negative());
|
||||
assert!((black_box(-1.0f128) % -1.0).is_sign_negative());
|
||||
|
||||
// FIXME(f16_f128): add when `abs` is available
|
||||
assert_eq!((-1.0f16).abs(), 1.0f16);
|
||||
assert_eq!(34.2f16.abs(), 34.2f16);
|
||||
assert_eq!((-1.0f32).abs(), 1.0f32);
|
||||
assert_eq!(34.2f32.abs(), 34.2f32);
|
||||
assert_eq!((-1.0f64).abs(), 1.0f64);
|
||||
assert_eq!(34.2f64.abs(), 34.2f64);
|
||||
assert_eq!((-1.0f128).abs(), 1.0f128);
|
||||
assert_eq!(34.2f128.abs(), 34.2f128);
|
||||
}
|
||||
|
||||
/// Test casts from floats to ints and back
|
||||
@ -654,6 +664,14 @@ fn casts() {
|
||||
}
|
||||
|
||||
fn ops() {
|
||||
// f16 min/max
|
||||
assert_eq((1.0_f16).max(-1.0), 1.0);
|
||||
assert_eq((1.0_f16).min(-1.0), -1.0);
|
||||
assert_eq(f16::NAN.min(9.0), 9.0);
|
||||
assert_eq(f16::NAN.max(-9.0), -9.0);
|
||||
assert_eq((9.0_f16).min(f16::NAN), 9.0);
|
||||
assert_eq((-9.0_f16).max(f16::NAN), -9.0);
|
||||
|
||||
// f32 min/max
|
||||
assert_eq((1.0 as f32).max(-1.0), 1.0);
|
||||
assert_eq((1.0 as f32).min(-1.0), -1.0);
|
||||
@ -670,6 +688,21 @@ fn ops() {
|
||||
assert_eq((9.0 as f64).min(f64::NAN), 9.0);
|
||||
assert_eq((-9.0 as f64).max(f64::NAN), -9.0);
|
||||
|
||||
// f128 min/max
|
||||
assert_eq((1.0_f128).max(-1.0), 1.0);
|
||||
assert_eq((1.0_f128).min(-1.0), -1.0);
|
||||
assert_eq(f128::NAN.min(9.0), 9.0);
|
||||
assert_eq(f128::NAN.max(-9.0), -9.0);
|
||||
assert_eq((9.0_f128).min(f128::NAN), 9.0);
|
||||
assert_eq((-9.0_f128).max(f128::NAN), -9.0);
|
||||
|
||||
// f16 copysign
|
||||
assert_eq(3.5_f16.copysign(0.42), 3.5_f16);
|
||||
assert_eq(3.5_f16.copysign(-0.42), -3.5_f16);
|
||||
assert_eq((-3.5_f16).copysign(0.42), 3.5_f16);
|
||||
assert_eq((-3.5_f16).copysign(-0.42), -3.5_f16);
|
||||
assert!(f16::NAN.copysign(1.0).is_nan());
|
||||
|
||||
// f32 copysign
|
||||
assert_eq(3.5_f32.copysign(0.42), 3.5_f32);
|
||||
assert_eq(3.5_f32.copysign(-0.42), -3.5_f32);
|
||||
@ -683,6 +716,13 @@ fn ops() {
|
||||
assert_eq((-3.5_f64).copysign(0.42), 3.5_f64);
|
||||
assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64);
|
||||
assert!(f64::NAN.copysign(1.0).is_nan());
|
||||
|
||||
// f128 copysign
|
||||
assert_eq(3.5_f128.copysign(0.42), 3.5_f128);
|
||||
assert_eq(3.5_f128.copysign(-0.42), -3.5_f128);
|
||||
assert_eq((-3.5_f128).copysign(0.42), 3.5_f128);
|
||||
assert_eq((-3.5_f128).copysign(-0.42), -3.5_f128);
|
||||
assert!(f128::NAN.copysign(1.0).is_nan());
|
||||
}
|
||||
|
||||
/// Tests taken from rustc test suite.
|
||||
@ -807,6 +847,18 @@ fn nan_casts() {
|
||||
|
||||
fn rounding() {
|
||||
// Test cases taken from the library's tests for this feature
|
||||
// f16
|
||||
assert_eq(2.5f16.round_ties_even(), 2.0f16);
|
||||
assert_eq(1.0f16.round_ties_even(), 1.0f16);
|
||||
assert_eq(1.3f16.round_ties_even(), 1.0f16);
|
||||
assert_eq(1.5f16.round_ties_even(), 2.0f16);
|
||||
assert_eq(1.7f16.round_ties_even(), 2.0f16);
|
||||
assert_eq(0.0f16.round_ties_even(), 0.0f16);
|
||||
assert_eq((-0.0f16).round_ties_even(), -0.0f16);
|
||||
assert_eq((-1.0f16).round_ties_even(), -1.0f16);
|
||||
assert_eq((-1.3f16).round_ties_even(), -1.0f16);
|
||||
assert_eq((-1.5f16).round_ties_even(), -2.0f16);
|
||||
assert_eq((-1.7f16).round_ties_even(), -2.0f16);
|
||||
// f32
|
||||
assert_eq(2.5f32.round_ties_even(), 2.0f32);
|
||||
assert_eq(1.0f32.round_ties_even(), 1.0f32);
|
||||
@ -831,23 +883,59 @@ fn rounding() {
|
||||
assert_eq((-1.3f64).round_ties_even(), -1.0f64);
|
||||
assert_eq((-1.5f64).round_ties_even(), -2.0f64);
|
||||
assert_eq((-1.7f64).round_ties_even(), -2.0f64);
|
||||
// f128
|
||||
assert_eq(2.5f128.round_ties_even(), 2.0f128);
|
||||
assert_eq(1.0f128.round_ties_even(), 1.0f128);
|
||||
assert_eq(1.3f128.round_ties_even(), 1.0f128);
|
||||
assert_eq(1.5f128.round_ties_even(), 2.0f128);
|
||||
assert_eq(1.7f128.round_ties_even(), 2.0f128);
|
||||
assert_eq(0.0f128.round_ties_even(), 0.0f128);
|
||||
assert_eq((-0.0f128).round_ties_even(), -0.0f128);
|
||||
assert_eq((-1.0f128).round_ties_even(), -1.0f128);
|
||||
assert_eq((-1.3f128).round_ties_even(), -1.0f128);
|
||||
assert_eq((-1.5f128).round_ties_even(), -2.0f128);
|
||||
assert_eq((-1.7f128).round_ties_even(), -2.0f128);
|
||||
|
||||
assert_eq!(3.8f16.floor(), 3.0f16);
|
||||
assert_eq!((-1.1f16).floor(), -2.0f16);
|
||||
assert_eq!(3.8f32.floor(), 3.0f32);
|
||||
assert_eq!((-1.1f32).floor(), -2.0f32);
|
||||
assert_eq!(3.8f64.floor(), 3.0f64);
|
||||
assert_eq!((-1.1f64).floor(), -2.0f64);
|
||||
assert_eq!(3.8f128.floor(), 3.0f128);
|
||||
assert_eq!((-1.1f128).floor(), -2.0f128);
|
||||
|
||||
assert_eq!(3.8f16.ceil(), 4.0f16);
|
||||
assert_eq!((-2.3f16).ceil(), -2.0f16);
|
||||
assert_eq!(3.8f32.ceil(), 4.0f32);
|
||||
assert_eq!((-2.3f32).ceil(), -2.0f32);
|
||||
assert_eq!(3.8f64.ceil(), 4.0f64);
|
||||
assert_eq!((-2.3f64).ceil(), -2.0f64);
|
||||
assert_eq!(3.8f128.ceil(), 4.0f128);
|
||||
assert_eq!((-2.3f128).ceil(), -2.0f128);
|
||||
|
||||
assert_eq!(0.1f16.trunc(), 0.0f16);
|
||||
assert_eq!((-0.1f16).trunc(), 0.0f16);
|
||||
assert_eq!(0.1f32.trunc(), 0.0f32);
|
||||
assert_eq!((-0.1f32).trunc(), 0.0f32);
|
||||
assert_eq!(0.1f64.trunc(), 0.0f64);
|
||||
assert_eq!((-0.1f64).trunc(), 0.0f64);
|
||||
assert_eq!(0.1f128.trunc(), 0.0f128);
|
||||
assert_eq!((-0.1f128).trunc(), 0.0f128);
|
||||
|
||||
assert_eq!(3.3_f16.round(), 3.0);
|
||||
assert_eq!(2.5_f16.round(), 3.0);
|
||||
assert_eq!(3.3_f32.round(), 3.0);
|
||||
assert_eq!(2.5_f32.round(), 3.0);
|
||||
assert_eq!(3.9_f64.round(), 4.0);
|
||||
assert_eq!(2.5_f64.round(), 3.0);
|
||||
assert_eq!(3.9_f128.round(), 4.0);
|
||||
assert_eq!(2.5_f128.round(), 3.0);
|
||||
}
|
||||
|
||||
fn mul_add() {
|
||||
// FIXME(f16_f128): add when supported
|
||||
|
||||
assert_eq!(3.0f32.mul_add(2.0f32, 5.0f32), 11.0);
|
||||
assert_eq!(0.0f32.mul_add(-2.0, f32::consts::E), f32::consts::E);
|
||||
assert_eq!(3.0f64.mul_add(2.0, 5.0), 11.0);
|
||||
@ -983,7 +1071,7 @@ fn test_fast() {
|
||||
use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
pub fn test_operations_f16(a: f16, b: f16) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
@ -1006,10 +1094,38 @@ fn test_fast() {
|
||||
}
|
||||
}
|
||||
|
||||
test_operations_f64(1., 2.);
|
||||
test_operations_f64(10., 5.);
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f128(a: f128, b: f128) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
test_operations_f16(11., 2.);
|
||||
test_operations_f16(10., 15.);
|
||||
test_operations_f32(11., 2.);
|
||||
test_operations_f32(10., 15.);
|
||||
test_operations_f64(1., 2.);
|
||||
test_operations_f64(10., 5.);
|
||||
test_operations_f128(1., 2.);
|
||||
test_operations_f128(10., 5.);
|
||||
}
|
||||
|
||||
fn test_algebraic() {
|
||||
@ -1018,7 +1134,7 @@ fn test_algebraic() {
|
||||
};
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
pub fn test_operations_f16(a: f16, b: f16) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
@ -1037,15 +1153,41 @@ fn test_algebraic() {
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
test_operations_f64(1., 2.);
|
||||
test_operations_f64(10., 5.);
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f128(a: f128, b: f128) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
test_operations_f16(11., 2.);
|
||||
test_operations_f16(10., 15.);
|
||||
test_operations_f32(11., 2.);
|
||||
test_operations_f32(10., 15.);
|
||||
test_operations_f64(1., 2.);
|
||||
test_operations_f64(10., 5.);
|
||||
test_operations_f128(1., 2.);
|
||||
test_operations_f128(10., 5.);
|
||||
}
|
||||
|
||||
fn test_fmuladd() {
|
||||
use std::intrinsics::{fmuladdf32, fmuladdf64};
|
||||
|
||||
// FIXME(f16_f128): add when supported
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f32(a: f32, b: f32, c: f32) {
|
||||
assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c);
|
||||
|
@ -1,20 +0,0 @@
|
||||
//@ known-bug: rust-lang/rust#126725
|
||||
trait Foo {
|
||||
fn foo<'a>(&'a self) -> <&'a impl Sized as Bar>::Output;
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
type Output;
|
||||
}
|
||||
|
||||
struct X(i32);
|
||||
|
||||
impl<'a> Bar for &'a X {
|
||||
type Output = &'a i32;
|
||||
}
|
||||
|
||||
impl Foo for X {
|
||||
fn foo<'a>(&'a self) -> <&'a Self as Bar>::Output {
|
||||
&self.0
|
||||
}
|
||||
}
|
@ -10,30 +10,27 @@ fn path_parametrized_type_is_allowed() -> option::Option<impl Debug> {
|
||||
}
|
||||
|
||||
fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
|
||||
//~^ ERROR `impl Trait` is not allowed in path parameters
|
||||
//~| ERROR `impl Trait` is not allowed in path parameters
|
||||
//~^ ERROR `impl Trait` is not allowed in paths
|
||||
x.next().unwrap()
|
||||
}
|
||||
|
||||
fn projection_with_named_trait_is_disallowed(mut x: impl Iterator)
|
||||
-> <impl Iterator as Iterator>::Item
|
||||
//~^ ERROR `impl Trait` is not allowed in path parameters
|
||||
//~^ ERROR `impl Trait` is not allowed in paths
|
||||
{
|
||||
x.next().unwrap()
|
||||
}
|
||||
|
||||
fn projection_with_named_trait_inside_path_is_disallowed()
|
||||
-> <::std::ops::Range<impl Debug> as Iterator>::Item
|
||||
//~^ ERROR `impl Trait` is not allowed in path parameters
|
||||
//~| ERROR `impl Debug: Step` is not satisfied
|
||||
//~^ ERROR `impl Trait` is not allowed in paths
|
||||
{
|
||||
//~^ ERROR `impl Debug: Step` is not satisfied
|
||||
(1i32..100).next().unwrap()
|
||||
}
|
||||
|
||||
fn projection_from_impl_trait_inside_dyn_trait_is_disallowed()
|
||||
-> <dyn Iterator<Item = impl Debug> as Iterator>::Item
|
||||
//~^ ERROR `impl Trait` is not allowed in path parameters
|
||||
//~^ ERROR `impl Trait` is not allowed in paths
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
|
@ -1,73 +1,35 @@
|
||||
error[E0667]: `impl Trait` is not allowed in path parameters
|
||||
error[E0562]: `impl Trait` is not allowed in paths
|
||||
--> $DIR/impl_trait_projections.rs:12:51
|
||||
|
|
||||
LL | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
|
||||
|
||||
error[E0667]: `impl Trait` is not allowed in path parameters
|
||||
--> $DIR/impl_trait_projections.rs:19:9
|
||||
error[E0562]: `impl Trait` is not allowed in paths
|
||||
--> $DIR/impl_trait_projections.rs:18:9
|
||||
|
|
||||
LL | -> <impl Iterator as Iterator>::Item
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
|
||||
|
||||
error[E0667]: `impl Trait` is not allowed in path parameters
|
||||
--> $DIR/impl_trait_projections.rs:26:27
|
||||
error[E0562]: `impl Trait` is not allowed in paths
|
||||
--> $DIR/impl_trait_projections.rs:25:27
|
||||
|
|
||||
LL | -> <::std::ops::Range<impl Debug> as Iterator>::Item
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
|
||||
|
||||
error[E0667]: `impl Trait` is not allowed in path parameters
|
||||
--> $DIR/impl_trait_projections.rs:35:29
|
||||
error[E0562]: `impl Trait` is not allowed in paths
|
||||
--> $DIR/impl_trait_projections.rs:32:29
|
||||
|
|
||||
LL | -> <dyn Iterator<Item = impl Debug> as Iterator>::Item
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error[E0667]: `impl Trait` is not allowed in path parameters
|
||||
--> $DIR/impl_trait_projections.rs:12:51
|
||||
|
|
||||
LL | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
|
||||
| ^^^^^^^^^^^^^
|
||||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
|
||||
|
||||
error[E0277]: the trait bound `impl Debug: Step` is not satisfied
|
||||
--> $DIR/impl_trait_projections.rs:26:8
|
||||
|
|
||||
LL | -> <::std::ops::Range<impl Debug> as Iterator>::Item
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Step` is not implemented for `impl Debug`, which is required by `std::ops::Range<impl Debug>: Iterator`
|
||||
|
|
||||
= help: the following other types implement trait `Step`:
|
||||
Char
|
||||
Ipv4Addr
|
||||
Ipv6Addr
|
||||
char
|
||||
i128
|
||||
i16
|
||||
i32
|
||||
i64
|
||||
and 8 others
|
||||
= note: required for `std::ops::Range<impl Debug>` to implement `Iterator`
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
error[E0277]: the trait bound `impl Debug: Step` is not satisfied
|
||||
--> $DIR/impl_trait_projections.rs:29:1
|
||||
|
|
||||
LL | / {
|
||||
LL | |
|
||||
LL | | (1i32..100).next().unwrap()
|
||||
LL | | }
|
||||
| |_^ the trait `Step` is not implemented for `impl Debug`, which is required by `std::ops::Range<impl Debug>: Iterator`
|
||||
|
|
||||
= help: the following other types implement trait `Step`:
|
||||
Char
|
||||
Ipv4Addr
|
||||
Ipv6Addr
|
||||
char
|
||||
i128
|
||||
i16
|
||||
i32
|
||||
i64
|
||||
and 8 others
|
||||
= note: required for `std::ops::Range<impl Debug>` to implement `Iterator`
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0667.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
||||
For more information about this error, try `rustc --explain E0562`.
|
||||
|
22
tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs
Normal file
22
tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// issue: rust-lang/rust#126725
|
||||
|
||||
trait Foo {
|
||||
fn foo<'a>() -> <&'a impl Sized as Bar>::Output;
|
||||
//~^ ERROR `impl Trait` is not allowed in paths
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
type Output;
|
||||
}
|
||||
|
||||
impl<'a> Bar for &'a () {
|
||||
type Output = &'a i32;
|
||||
}
|
||||
|
||||
impl Foo for () {
|
||||
fn foo<'a>() -> <&'a Self as Bar>::Output {
|
||||
&0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,11 @@
|
||||
error[E0562]: `impl Trait` is not allowed in paths
|
||||
--> $DIR/bad-projection-from-opaque.rs:4:26
|
||||
|
|
||||
LL | fn foo<'a>() -> <&'a impl Sized as Bar>::Output;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0562`.
|
@ -6,7 +6,7 @@
|
||||
pub trait Bar { }
|
||||
pub trait Quux<T> { type Assoc; }
|
||||
pub fn demo(_: impl Quux<(), Assoc=<() as Quux<impl Bar>>::Assoc>) { }
|
||||
//~^ ERROR `impl Trait` is not allowed in path parameters
|
||||
//~^ ERROR `impl Trait` is not allowed in paths
|
||||
impl<T> Quux<T> for () { type Assoc = u32; }
|
||||
|
||||
fn main() { }
|
||||
|
@ -1,9 +1,11 @@
|
||||
error[E0667]: `impl Trait` is not allowed in path parameters
|
||||
error[E0562]: `impl Trait` is not allowed in paths
|
||||
--> $DIR/issue-57979-impl-trait-in-path.rs:8:48
|
||||
|
|
||||
LL | pub fn demo(_: impl Quux<(), Assoc=<() as Quux<impl Bar>>::Assoc>) { }
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0667`.
|
||||
For more information about this error, try `rustc --explain E0562`.
|
||||
|
Loading…
Reference in New Issue
Block a user