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:
bors 2024-10-27 20:00:19 +00:00
commit df4ca44d3f
60 changed files with 801 additions and 615 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +1 @@
17a19e684cdf3ca088af8b4da6a6209d128913f4
814df6e50eaf89b90793e7d9618bb60f1f18377a

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +1,2 @@
pub mod foreign_items;
pub mod thread;

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

View File

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

View File

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

View File

@ -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" => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
// Ensure that a `ptr::without_provenance` ptr is truly invalid.
fn main() {
let x = 42;

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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!()
}

View File

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

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

View File

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

View File

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

View File

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