diff --git a/Cargo.lock b/Cargo.lock index 018d03ed26c96..d135226326f91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1164,7 +1164,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2106,9 +2106,9 @@ dependencies = [ [[package]] name = "libffi" -version = "4.1.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7681c6fab541f799a829e44a445a0666cf8d8a6cfebf89419e6aed52c604e87" +checksum = "0444124f3ffd67e1b0b0c661a7f81a278a135eb54aaad4078e79fbc8be50c8a5" dependencies = [ "libc", "libffi-sys", @@ -2116,9 +2116,9 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "3.3.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0d828d367b4450ed08e7d510dc46636cd660055f50d67ac943bfe788767c29" +checksum = "3d722da8817ea580d0669da6babe2262d7b86a1af1103da24102b8bb9c101ce7" dependencies = [ "cc", ] @@ -2374,7 +2374,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/compiler/rustc_ast_lowering/src/contract.rs b/compiler/rustc_ast_lowering/src/contract.rs new file mode 100644 index 0000000000000..2f1c3d66d4e70 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/contract.rs @@ -0,0 +1,324 @@ +use thin_vec::thin_vec; + +use crate::LoweringContext; + +impl<'a, 'hir> LoweringContext<'a, 'hir> { + /// Lowered contracts are guarded with the `contract_checks` compiler flag, + /// i.e. the flag turns into a boolean guard in the lowered HIR. The reason + /// for not eliminating the contract code entirely when the `contract_checks` + /// flag is disabled is so that contracts can be type checked, even when + /// they are disabled, which avoids them becoming stale (i.e. out of sync + /// with the codebase) over time. + /// + /// The optimiser should be able to eliminate all contract code guarded + /// by `if false`, leaving the original body intact when runtime contract + /// checks are disabled. + pub(super) fn lower_contract( + &mut self, + body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>, + contract: &rustc_ast::FnContract, + ) -> rustc_hir::Expr<'hir> { + match (&contract.requires, &contract.ensures) { + (Some(req), Some(ens)) => { + // Lower the fn contract, which turns: + // + // { body } + // + // into: + // + // let __postcond = if contract_checks { + // contract_check_requires(PRECOND); + // Some(|ret_val| POSTCOND) + // } else { + // None + // }; + // { + // let ret = { body }; + // + // if contract_checks { + // contract_check_ensures(__postcond, ret) + // } else { + // ret + // } + // } + + let precond = self.lower_precond(req); + let postcond_checker = self.lower_postcond_checker(ens); + + let contract_check = + self.lower_contract_check_with_postcond(Some(precond), postcond_checker); + + let wrapped_body = + self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span); + self.expr_block(wrapped_body) + } + (None, Some(ens)) => { + // Lower the fn contract, which turns: + // + // { body } + // + // into: + // + // let __postcond = if contract_checks { + // Some(|ret_val| POSTCOND) + // } else { + // None + // }; + // { + // let ret = { body }; + // + // if contract_checks { + // contract_check_ensures(__postcond, ret) + // } else { + // ret + // } + // } + + let postcond_checker = self.lower_postcond_checker(ens); + let contract_check = + self.lower_contract_check_with_postcond(None, postcond_checker); + + let wrapped_body = + self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span); + self.expr_block(wrapped_body) + } + (Some(req), None) => { + // Lower the fn contract, which turns: + // + // { body } + // + // into: + // + // { + // if contracts_checks { + // contract_requires(PRECOND); + // } + // body + // } + let precond = self.lower_precond(req); + let precond_check = self.lower_contract_check_just_precond(precond); + + let body = self.arena.alloc(body(self)); + + // Flatten the body into precond check, then body. + let wrapped_body = self.block_all( + body.span, + self.arena.alloc_from_iter([precond_check].into_iter()), + Some(body), + ); + self.expr_block(wrapped_body) + } + (None, None) => body(self), + } + } + + /// Lower the precondition check intrinsic. + fn lower_precond(&mut self, req: &Box) -> rustc_hir::Stmt<'hir> { + let lowered_req = self.lower_expr_mut(&req); + let req_span = self.mark_span_with_reason( + rustc_span::DesugaringKind::Contract, + lowered_req.span, + None, + ); + let precond = self.expr_call_lang_item_fn_mut( + req_span, + rustc_hir::LangItem::ContractCheckRequires, + &*arena_vec![self; lowered_req], + ); + self.stmt_expr(req.span, precond) + } + + fn lower_postcond_checker( + &mut self, + ens: &Box, + ) -> &'hir rustc_hir::Expr<'hir> { + let ens_span = self.lower_span(ens.span); + let ens_span = + self.mark_span_with_reason(rustc_span::DesugaringKind::Contract, ens_span, None); + let lowered_ens = self.lower_expr_mut(&ens); + self.expr_call_lang_item_fn( + ens_span, + rustc_hir::LangItem::ContractBuildCheckEnsures, + &*arena_vec![self; lowered_ens], + ) + } + + fn lower_contract_check_just_precond( + &mut self, + precond: rustc_hir::Stmt<'hir>, + ) -> rustc_hir::Stmt<'hir> { + let stmts = self.arena.alloc_from_iter([precond].into_iter()); + + let then_block_stmts = self.block_all(precond.span, stmts, None); + let then_block = self.arena.alloc(self.expr_block(&then_block_stmts)); + + let precond_check = rustc_hir::ExprKind::If( + self.arena.alloc(self.expr_bool_literal(precond.span, self.tcx.sess.contract_checks())), + then_block, + None, + ); + + let precond_check = self.expr(precond.span, precond_check); + self.stmt_expr(precond.span, precond_check) + } + + fn lower_contract_check_with_postcond( + &mut self, + precond: Option>, + postcond_checker: &'hir rustc_hir::Expr<'hir>, + ) -> &'hir rustc_hir::Expr<'hir> { + let stmts = self.arena.alloc_from_iter(precond.into_iter()); + let span = match precond { + Some(precond) => precond.span, + None => postcond_checker.span, + }; + + let postcond_checker = self.arena.alloc(self.expr_enum_variant_lang_item( + postcond_checker.span, + rustc_hir::lang_items::LangItem::OptionSome, + &*arena_vec![self; *postcond_checker], + )); + let then_block_stmts = self.block_all(span, stmts, Some(postcond_checker)); + let then_block = self.arena.alloc(self.expr_block(&then_block_stmts)); + + let none_expr = self.arena.alloc(self.expr_enum_variant_lang_item( + postcond_checker.span, + rustc_hir::lang_items::LangItem::OptionNone, + Default::default(), + )); + let else_block = self.block_expr(none_expr); + let else_block = self.arena.alloc(self.expr_block(else_block)); + + let contract_check = rustc_hir::ExprKind::If( + self.arena.alloc(self.expr_bool_literal(span, self.tcx.sess.contract_checks())), + then_block, + Some(else_block), + ); + self.arena.alloc(self.expr(span, contract_check)) + } + + fn wrap_body_with_contract_check( + &mut self, + body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>, + contract_check: &'hir rustc_hir::Expr<'hir>, + postcond_span: rustc_span::Span, + ) -> &'hir rustc_hir::Block<'hir> { + let check_ident: rustc_span::Ident = + rustc_span::Ident::from_str_and_span("__ensures_checker", postcond_span); + let (check_hir_id, postcond_decl) = { + // Set up the postcondition `let` statement. + let (checker_pat, check_hir_id) = self.pat_ident_binding_mode_mut( + postcond_span, + check_ident, + rustc_hir::BindingMode::NONE, + ); + ( + check_hir_id, + self.stmt_let_pat( + None, + postcond_span, + Some(contract_check), + self.arena.alloc(checker_pat), + rustc_hir::LocalSource::Contract, + ), + ) + }; + + // Install contract_ensures so we will intercept `return` statements, + // then lower the body. + self.contract_ensures = Some((postcond_span, check_ident, check_hir_id)); + let body = self.arena.alloc(body(self)); + + // Finally, inject an ensures check on the implicit return of the body. + let body = self.inject_ensures_check(body, postcond_span, check_ident, check_hir_id); + + // Flatten the body into precond, then postcond, then wrapped body. + let wrapped_body = self.block_all( + body.span, + self.arena.alloc_from_iter([postcond_decl].into_iter()), + Some(body), + ); + wrapped_body + } + + /// Create an `ExprKind::Ret` that is optionally wrapped by a call to check + /// a contract ensures clause, if it exists. + pub(super) fn checked_return( + &mut self, + opt_expr: Option<&'hir rustc_hir::Expr<'hir>>, + ) -> rustc_hir::ExprKind<'hir> { + let checked_ret = + if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures { + let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span)); + Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id)) + } else { + opt_expr + }; + rustc_hir::ExprKind::Ret(checked_ret) + } + + /// Wraps an expression with a call to the ensures check before it gets returned. + pub(super) fn inject_ensures_check( + &mut self, + expr: &'hir rustc_hir::Expr<'hir>, + span: rustc_span::Span, + cond_ident: rustc_span::Ident, + cond_hir_id: rustc_hir::HirId, + ) -> &'hir rustc_hir::Expr<'hir> { + // { + // let ret = { body }; + // + // if contract_checks { + // contract_check_ensures(__postcond, ret) + // } else { + // ret + // } + // } + let ret_ident: rustc_span::Ident = rustc_span::Ident::from_str_and_span("__ret", span); + + // Set up the return `let` statement. + let (ret_pat, ret_hir_id) = + self.pat_ident_binding_mode_mut(span, ret_ident, rustc_hir::BindingMode::NONE); + + let ret_stmt = self.stmt_let_pat( + None, + span, + Some(expr), + self.arena.alloc(ret_pat), + rustc_hir::LocalSource::Contract, + ); + + let ret = self.expr_ident(span, ret_ident, ret_hir_id); + + let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id); + let contract_check = self.expr_call_lang_item_fn_mut( + span, + rustc_hir::LangItem::ContractCheckEnsures, + arena_vec![self; *cond_fn, *ret], + ); + let contract_check = self.arena.alloc(contract_check); + let call_expr = self.block_expr_block(contract_check); + + // same ident can't be used in 2 places, so we create a new one for the + // else branch + let ret = self.expr_ident(span, ret_ident, ret_hir_id); + let ret_block = self.block_expr_block(ret); + + let contracts_enabled: rustc_hir::Expr<'_> = + self.expr_bool_literal(span, self.tcx.sess.contract_checks()); + let contract_check = self.arena.alloc(self.expr( + span, + rustc_hir::ExprKind::If( + self.arena.alloc(contracts_enabled), + call_expr, + Some(ret_block), + ), + )); + + let attrs: rustc_ast::AttrVec = thin_vec![self.unreachable_code_attr(span)]; + self.lower_attrs(contract_check.hir_id, &attrs, span, rustc_hir::Target::Expression); + + let ret_block = self.block_all(span, arena_vec![self; ret_stmt], Some(contract_check)); + self.arena.alloc(self.expr_block(self.arena.alloc(ret_block))) + } +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index bb6b25baf013d..4a9b9f544b53d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -383,36 +383,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - /// Create an `ExprKind::Ret` that is optionally wrapped by a call to check - /// a contract ensures clause, if it exists. - fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> { - let checked_ret = - if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures { - let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span)); - Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id)) - } else { - opt_expr - }; - hir::ExprKind::Ret(checked_ret) - } - - /// Wraps an expression with a call to the ensures check before it gets returned. - pub(crate) fn inject_ensures_check( - &mut self, - expr: &'hir hir::Expr<'hir>, - span: Span, - cond_ident: Ident, - cond_hir_id: HirId, - ) -> &'hir hir::Expr<'hir> { - let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id); - let call_expr = self.expr_call_lang_item_fn_mut( - span, - hir::LangItem::ContractCheckEnsures, - arena_vec![self; *cond_fn, *expr], - ); - self.arena.alloc(call_expr) - } - pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); @@ -1971,16 +1941,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }; - // `#[allow(unreachable_code)]` - let attr = attr::mk_attr_nested_word( - &self.tcx.sess.psess.attr_id_generator, - AttrStyle::Outer, - Safety::Default, - sym::allow, - sym::unreachable_code, - try_span, - ); - let attrs: AttrVec = thin_vec![attr]; + let attrs: AttrVec = thin_vec![self.unreachable_code_attr(try_span)]; // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,` let continue_arm = { @@ -2120,7 +2081,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e)) } - fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> { + pub(super) fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> { self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]))) } @@ -2161,6 +2122,43 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::Call(e, args)) } + pub(super) fn expr_struct( + &mut self, + span: Span, + path: &'hir hir::QPath<'hir>, + fields: &'hir [hir::ExprField<'hir>], + ) -> hir::Expr<'hir> { + self.expr(span, hir::ExprKind::Struct(path, fields, rustc_hir::StructTailExpr::None)) + } + + pub(super) fn expr_enum_variant( + &mut self, + span: Span, + path: &'hir hir::QPath<'hir>, + fields: &'hir [hir::Expr<'hir>], + ) -> hir::Expr<'hir> { + let fields = self.arena.alloc_from_iter(fields.into_iter().enumerate().map(|(i, f)| { + hir::ExprField { + hir_id: self.next_id(), + ident: Ident::from_str(&i.to_string()), + expr: f, + span: f.span, + is_shorthand: false, + } + })); + self.expr_struct(span, path, fields) + } + + pub(super) fn expr_enum_variant_lang_item( + &mut self, + span: Span, + lang_item: hir::LangItem, + fields: &'hir [hir::Expr<'hir>], + ) -> hir::Expr<'hir> { + let path = self.arena.alloc(self.lang_item_path(span, lang_item)); + self.expr_enum_variant(span, path, fields) + } + pub(super) fn expr_call( &mut self, span: Span, @@ -2189,8 +2187,21 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args)) } - fn expr_lang_item_path(&mut self, span: Span, lang_item: hir::LangItem) -> hir::Expr<'hir> { - self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span)))) + pub(super) fn expr_lang_item_path( + &mut self, + span: Span, + lang_item: hir::LangItem, + ) -> hir::Expr<'hir> { + let path = self.lang_item_path(span, lang_item); + self.expr(span, hir::ExprKind::Path(path)) + } + + pub(super) fn lang_item_path( + &mut self, + span: Span, + lang_item: hir::LangItem, + ) -> hir::QPath<'hir> { + hir::QPath::LangItem(lang_item, self.lower_span(span)) } /// `::name` @@ -2270,6 +2281,17 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(b.span, hir::ExprKind::Block(b, None)) } + /// Wrap an expression in a block, and wrap that block in an expression again. + /// Useful for constructing if-expressions, which require expressions of + /// kind block. + pub(super) fn block_expr_block( + &mut self, + expr: &'hir hir::Expr<'hir>, + ) -> &'hir hir::Expr<'hir> { + let b = self.block_expr(expr); + self.arena.alloc(self.expr_block(b)) + } + pub(super) fn expr_array_ref( &mut self, span: Span, @@ -2283,6 +2305,10 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr)) } + pub(super) fn expr_bool_literal(&mut self, span: Span, val: bool) -> hir::Expr<'hir> { + self.expr(span, hir::ExprKind::Lit(Spanned { node: LitKind::Bool(val), span })) + } + pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> { let hir_id = self.next_id(); hir::Expr { hir_id, kind, span: self.lower_span(span) } @@ -2316,6 +2342,19 @@ impl<'hir> LoweringContext<'_, 'hir> { body: expr, } } + + /// `#[allow(unreachable_code)]` + pub(super) fn unreachable_code_attr(&mut self, span: Span) -> Attribute { + let attr = attr::mk_attr_nested_word( + &self.tcx.sess.psess.attr_id_generator, + AttrStyle::Outer, + Safety::Default, + sym::allow, + sym::unreachable_code, + span, + ); + attr + } } /// Used by [`LoweringContext::make_lowered_await`] to customize the desugaring based on what kind diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 53351f91c46bc..d11b7f798b6db 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1214,76 +1214,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let params = this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))); - // Optionally lower the fn contract, which turns: - // - // { body } - // - // into: - // - // { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) } + // Optionally lower the fn contract if let Some(contract) = contract { - let precond = if let Some(req) = &contract.requires { - // Lower the precondition check intrinsic. - let lowered_req = this.lower_expr_mut(&req); - let req_span = this.mark_span_with_reason( - DesugaringKind::Contract, - lowered_req.span, - None, - ); - let precond = this.expr_call_lang_item_fn_mut( - req_span, - hir::LangItem::ContractCheckRequires, - &*arena_vec![this; lowered_req], - ); - Some(this.stmt_expr(req.span, precond)) - } else { - None - }; - let (postcond, body) = if let Some(ens) = &contract.ensures { - let ens_span = this.lower_span(ens.span); - let ens_span = - this.mark_span_with_reason(DesugaringKind::Contract, ens_span, None); - // Set up the postcondition `let` statement. - let check_ident: Ident = - Ident::from_str_and_span("__ensures_checker", ens_span); - let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut( - ens_span, - check_ident, - hir::BindingMode::NONE, - ); - let lowered_ens = this.lower_expr_mut(&ens); - let postcond_checker = this.expr_call_lang_item_fn( - ens_span, - hir::LangItem::ContractBuildCheckEnsures, - &*arena_vec![this; lowered_ens], - ); - let postcond = this.stmt_let_pat( - None, - ens_span, - Some(postcond_checker), - this.arena.alloc(checker_pat), - hir::LocalSource::Contract, - ); - - // Install contract_ensures so we will intercept `return` statements, - // then lower the body. - this.contract_ensures = Some((ens_span, check_ident, check_hir_id)); - let body = this.arena.alloc(body(this)); - - // Finally, inject an ensures check on the implicit return of the body. - let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id); - (Some(postcond), body) - } else { - let body = &*this.arena.alloc(body(this)); - (None, body) - }; - // Flatten the body into precond, then postcond, then wrapped body. - let wrapped_body = this.block_all( - body.span, - this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()), - Some(body), - ); - (params, this.expr_block(wrapped_body)) + (params, this.lower_contract(body, contract)) } else { (params, body(this)) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4e2243e87873c..282a62ca44310 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -77,6 +77,7 @@ macro_rules! arena_vec { mod asm; mod block; +mod contract; mod delegation; mod errors; mod expr; diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index 13c63268e1e87..118efd15412e6 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -149,7 +149,7 @@ fn expand_requires_tts( new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), - token::Delimiter::Parenthesis, + token::Delimiter::Brace, annotation, )); Ok(()) @@ -171,7 +171,7 @@ fn expand_ensures_tts( new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), - token::Delimiter::Parenthesis, + token::Delimiter::Brace, annotation, )); Ok(()) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 293459cc11c2f..3243e12e69999 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -5,7 +5,9 @@ use std::cmp::Ordering; use cranelift_module::*; use rustc_data_structures::fx::FxHashSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::interpret::{AllocId, GlobalAlloc, Scalar, read_target_uint}; +use rustc_middle::mir::interpret::{ + AllocId, GlobalAlloc, PointerArithmetic, Scalar, read_target_uint, +}; use rustc_middle::ty::{ExistentialTraitRef, ScalarInt}; use crate::prelude::*; @@ -138,8 +140,11 @@ pub(crate) fn codegen_const_value<'tcx>( let base_addr = match fx.tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => { if alloc.inner().len() == 0 { - assert_eq!(offset, Size::ZERO); - fx.bcx.ins().iconst(fx.pointer_type, alloc.inner().align.bytes() as i64) + let val = alloc.inner().align.bytes().wrapping_add(offset.bytes()); + fx.bcx.ins().iconst( + fx.pointer_type, + fx.tcx.truncate_to_target_usize(val) as i64, + ) } else { let data_id = data_id_for_alloc_id( &mut fx.constants_cx, diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 28848ca61845c..7c2969e587186 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -5,7 +5,7 @@ use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; use rustc_middle::mir::Mutability; -use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; +use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::LayoutOf; use crate::context::CodegenCx; @@ -247,8 +247,8 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { // This avoids generating a zero-sized constant value and actually needing a // real address at runtime. if alloc.inner().len() == 0 { - assert_eq!(offset.bytes(), 0); - let val = self.const_usize(alloc.inner().align.bytes()); + let val = alloc.inner().align.bytes().wrapping_add(offset.bytes()); + let val = self.const_usize(self.tcx.truncate_to_target_usize(val)); return if matches!(layout.primitive(), Pointer(_)) { self.context.new_cast(None, val, ty) } else { diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 175fc8535ac3f..b0cf9925019d2 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -12,7 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hashes::Hash128; use rustc_hir::def_id::DefId; use rustc_middle::bug; -use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; +use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::DllImport; use tracing::debug; @@ -281,8 +281,8 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { // This avoids generating a zero-sized constant value and actually needing a // real address at runtime. if alloc.inner().len() == 0 { - assert_eq!(offset.bytes(), 0); - let llval = self.const_usize(alloc.inner().align.bytes()); + let val = alloc.inner().align.bytes().wrapping_add(offset.bytes()); + let llval = self.const_usize(self.tcx.truncate_to_target_usize(val)); return if matches!(layout.primitive(), Pointer(_)) { unsafe { llvm::LLVMConstIntToPtr(llval, llty) } } else { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bc3448be5823e..a6659912e3fb9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -647,11 +647,11 @@ pub(crate) fn check_intrinsic_type( sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))), - // contract_checks() -> bool - sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit), - sym::contract_check_ensures => (2, 0, vec![param(0), param(1)], param(1)), + sym::contract_check_ensures => { + (2, 0, vec![Ty::new_option(tcx, param(0)), param(1)], param(1)) + } sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => { (2, 0, vec![param(0), param(0)], param(1)) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index fb6ebe066a8f2..880b0ca2fb1fe 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -792,7 +792,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(_, lhs, rhs), .. }), Some(TypeError::Sorts(ExpectedFound { expected, .. })), ) if rhs.hir_id == expr.hir_id - && self.typeck_results.borrow().expr_ty_adjusted_opt(lhs) == Some(expected) => + && self.typeck_results.borrow().expr_ty_adjusted_opt(lhs) == Some(expected) + // let expressions being marked as `bool` is confusing (see issue #147665) + && !matches!(lhs.kind, hir::ExprKind::Let(..)) => { err.span_label(lhs.span, format!("expected because this is `{expected}`")); } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index f7728baa2638a..96e85fdb10a6e 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -872,7 +872,7 @@ impl BitRelations> for ChunkedBitSet { let mut self_chunk_words = **other_chunk_words; for word in self_chunk_words[0..num_words].iter_mut().rev() { *word = !*word & tail_mask; - tail_mask = u64::MAX; + tail_mask = Word::MAX; } let self_chunk_count = chunk_domain_size - *other_chunk_count; debug_assert_eq!( @@ -887,7 +887,7 @@ impl BitRelations> for ChunkedBitSet { ) => { // See `ChunkedBitSet::union` for details on what is happening here. let num_words = num_words(chunk_domain_size as usize); - let op = |a: u64, b: u64| a & !b; + let op = |a: Word, b: Word| a & !b; if !bitwise_changes( &self_chunk_words[0..num_words], &other_chunk_words[0..num_words], diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a3fdd4e35b6aa..ad8b1fd0693b0 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -919,6 +919,12 @@ impl<'tcx> Ty<'tcx> { Ty::new_generic_adt(tcx, def_id, ty) } + #[inline] + pub fn new_option(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = tcx.require_lang_item(LangItem::Option, DUMMY_SP); + Ty::new_generic_adt(tcx, def_id, ty) + } + #[inline] pub fn new_maybe_uninit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let def_id = tcx.require_lang_item(LangItem::MaybeUninit, DUMMY_SP); diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 8dadce0d448d3..fc8092fd5832b 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -34,17 +34,6 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { )); terminator.kind = TerminatorKind::Goto { target }; } - sym::contract_checks => { - let target = target.unwrap(); - block.statements.push(Statement::new( - terminator.source_info, - StatementKind::Assign(Box::new(( - *destination, - Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool), - ))), - )); - terminator.kind = TerminatorKind::Goto { target }; - } sym::forget => { let target = target.unwrap(); block.statements.push(Statement::new( diff --git a/library/Cargo.lock b/library/Cargo.lock index b5b534c986b99..50fb060f3ce86 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -166,6 +166,16 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "moto-rt" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058a2807a30527bee4c30df7ababe971cdde94372d4dbd1ff145bb403381436c" +dependencies = [ + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "object" version = "0.37.3" @@ -316,6 +326,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", + "moto-rt", "object", "panic_abort", "panic_unwind", diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 5927d03646928..c78f2c8a47e00 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -886,7 +886,6 @@ impl Arc { /// let five = Arc::try_new_in(5, System)?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` - #[inline] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn try_new_in(data: T, alloc: A) -> Result, AllocError> { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index cef700be9ea1f..c397e762d5589 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2631,23 +2631,6 @@ pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 { ptr } -/// Returns whether we should perform contract-checking at runtime. -/// -/// This is meant to be similar to the ub_checks intrinsic, in terms -/// of not prematurely committing at compile-time to whether contract -/// checking is turned on, so that we can specify contracts in libstd -/// and let an end user opt into turning them on. -#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] -#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] -#[inline(always)] -#[rustc_intrinsic] -pub const fn contract_checks() -> bool { - // FIXME: should this be `false` or `cfg!(contract_checks)`? - - // cfg!(contract_checks) - false -} - /// Check if the pre-condition `cond` has been met. /// /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition @@ -2668,7 +2651,7 @@ pub const fn contract_check_requires bool + Copy>(cond: C) { if const { // Do nothing } else { - if contract_checks() && !cond() { + if !cond() { // Emit no unwind panic in case this was a safety requirement. crate::panicking::panic_nounwind("failed requires check"); } @@ -2681,6 +2664,8 @@ pub const fn contract_check_requires bool + Copy>(cond: C) { /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. /// +/// If `cond` is `None`, then no postcondition checking is performed. +/// /// Note that this function is a no-op during constant evaluation. #[unstable(feature = "contracts_internals", issue = "128044")] // Similar to `contract_check_requires`, we need to use the user-facing @@ -2689,16 +2674,24 @@ pub const fn contract_check_requires bool + Copy>(cond: C) { #[rustc_const_unstable(feature = "contracts", issue = "128044")] #[lang = "contract_check_ensures"] #[rustc_intrinsic] -pub const fn contract_check_ensures bool + Copy, Ret>(cond: C, ret: Ret) -> Ret { +pub const fn contract_check_ensures bool + Copy, Ret>( + cond: Option, + ret: Ret, +) -> Ret { const_eval_select!( - @capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret : + @capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: Option, ret: Ret } -> Ret : if const { // Do nothing ret } else { - if contract_checks() && !cond(&ret) { - // Emit no unwind panic in case this was a safety requirement. - crate::panicking::panic_nounwind("failed ensures check"); + match cond { + crate::option::Option::Some(cond) => { + if !cond(&ret) { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed ensures check"); + } + }, + crate::option::Option::None => {}, } ret } diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index e17a2e03d2dc4..42916558b5fc4 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -9,6 +9,8 @@ use crate::{ascii, iter, ops}; impl [u8] { /// Checks if all bytes in this slice are within the ASCII range. + /// + /// An empty slice returns `true`. #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[rustc_const_stable(feature = "const_slice_is_ascii", since = "1.74.0")] #[must_use] diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 3a5efa7d83511..82019b9b3afe5 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2704,6 +2704,8 @@ impl str { /// Checks if all characters in this string are within the ASCII range. /// + /// An empty string returns `true`. + /// /// # Examples /// /// ``` diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index d4108a57acaf0..685c2cf162abd 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -70,6 +70,9 @@ fortanix-sgx-abi = { version = "0.6.1", features = [ 'rustc-dep-of-std', ], public = true } +[target.'cfg(target_os = "motor")'.dependencies] +moto-rt = { version = "0.15", features = ['rustc-dep-of-std'], public = true } + [target.'cfg(target_os = "hermit")'.dependencies] hermit-abi = { version = "0.5.0", features = [ 'rustc-dep-of-std', diff --git a/library/std/build.rs b/library/std/build.rs index 8a5a785060c85..bee28e88491d0 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -30,6 +30,7 @@ fn main() { || target_os == "windows" || target_os == "fuchsia" || (target_vendor == "fortanix" && target_env == "sgx") + || target_os == "motor" || target_os == "hermit" || target_os == "trusty" || target_os == "l4re" diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 6c098034eea3b..09bd911aa769a 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -1215,6 +1215,8 @@ impl OsStr { /// Checks if all characters in this string are within the ASCII range. /// + /// An empty string returns `true`. + /// /// # Examples /// /// ``` diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 10e1e73a115bd..6a0e7a640028b 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -3,6 +3,9 @@ #![stable(feature = "io_safety", since = "1.63.0")] #![deny(unsafe_op_in_unsafe_fn)] +#[cfg(target_os = "motor")] +use moto_rt::libc; + use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; #[cfg(not(target_os = "trusty"))] use crate::fs; @@ -12,7 +15,8 @@ use crate::mem::ManuallyDrop; target_arch = "wasm32", target_env = "sgx", target_os = "hermit", - target_os = "trusty" + target_os = "trusty", + target_os = "motor" )))] use crate::sys::cvt; #[cfg(not(target_os = "trusty"))] @@ -95,7 +99,12 @@ impl OwnedFd { impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - #[cfg(not(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty")))] + #[cfg(not(any( + target_arch = "wasm32", + target_os = "hermit", + target_os = "trusty", + target_os = "motor" + )))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result { // We want to atomically duplicate this file descriptor and set the @@ -123,6 +132,15 @@ impl BorrowedFd<'_> { pub fn try_clone_to_owned(&self) -> crate::io::Result { Err(crate::io::Error::UNSUPPORTED_PLATFORM) } + + /// Creates a new `OwnedFd` instance that shares the same underlying file + /// description as the existing `BorrowedFd` instance. + #[cfg(target_os = "motor")] + #[stable(feature = "io_safety", since = "1.63.0")] + pub fn try_clone_to_owned(&self) -> crate::io::Result { + let fd = moto_rt::fs::duplicate(self.as_raw_fd()).map_err(crate::sys::map_motor_error)?; + Ok(unsafe { OwnedFd::from_raw_fd(fd) }) + } } #[stable(feature = "io_safety", since = "1.63.0")] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 34a6cf1a8b84d..c01e6b83cd366 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -4,13 +4,17 @@ #[cfg(target_os = "hermit")] use hermit_abi as libc; +#[cfg(target_os = "motor")] +use moto_rt::libc; +#[cfg(target_os = "motor")] +use super::owned::OwnedFd; #[cfg(not(target_os = "trusty"))] use crate::fs; use crate::io; #[cfg(target_os = "hermit")] use crate::os::hermit::io::OwnedFd; -#[cfg(not(target_os = "hermit"))] +#[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))] use crate::os::raw; #[cfg(all(doc, not(target_arch = "wasm32")))] use crate::os::unix::io::AsFd; @@ -23,10 +27,10 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] -#[cfg(not(target_os = "hermit"))] +#[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))] pub type RawFd = raw::c_int; #[stable(feature = "rust1", since = "1.0.0")] -#[cfg(target_os = "hermit")] +#[cfg(any(target_os = "hermit", target_os = "motor"))] pub type RawFd = i32; /// A trait to extract the raw file descriptor from an underlying object. diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index fd7a11433af1b..76374402be4b3 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -155,6 +155,8 @@ pub mod ios; pub mod l4re; #[cfg(target_os = "macos")] pub mod macos; +#[cfg(target_os = "motor")] +pub mod motor; #[cfg(target_os = "netbsd")] pub mod netbsd; #[cfg(target_os = "nto")] @@ -182,7 +184,14 @@ pub mod vxworks; #[cfg(target_os = "xous")] pub mod xous; -#[cfg(any(unix, target_os = "hermit", target_os = "trusty", target_os = "wasi", doc))] +#[cfg(any( + unix, + target_os = "hermit", + target_os = "trusty", + target_os = "wasi", + target_os = "motor", + doc +))] pub mod fd; #[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] diff --git a/library/std/src/os/motor/ffi.rs b/library/std/src/os/motor/ffi.rs new file mode 100644 index 0000000000000..509fe641bb353 --- /dev/null +++ b/library/std/src/os/motor/ffi.rs @@ -0,0 +1,37 @@ +//! Motor OS-specific extensions to primitives in the [`std::ffi`] module. +#![unstable(feature = "motor_ext", issue = "147456")] + +use crate::ffi::{OsStr, OsString}; +use crate::sealed::Sealed; + +/// Motor OS-specific extensions to [`OsString`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. +pub trait OsStringExt: Sealed { + /// Motor OS strings are utf-8, and thus just strings. + fn as_str(&self) -> &str; +} + +impl OsStringExt for OsString { + #[inline] + fn as_str(&self) -> &str { + self.to_str().unwrap() + } +} + +/// Motor OS-specific extensions to [`OsString`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. +pub trait OsStrExt: Sealed { + /// Motor OS strings are utf-8, and thus just strings. + fn as_str(&self) -> &str; +} + +impl OsStrExt for OsStr { + #[inline] + fn as_str(&self) -> &str { + self.to_str().unwrap() + } +} diff --git a/library/std/src/os/motor/mod.rs b/library/std/src/os/motor/mod.rs new file mode 100644 index 0000000000000..18da079c74a15 --- /dev/null +++ b/library/std/src/os/motor/mod.rs @@ -0,0 +1,4 @@ +#![unstable(feature = "motor_ext", issue = "147456")] + +pub mod ffi; +pub mod process; diff --git a/library/std/src/os/motor/process.rs b/library/std/src/os/motor/process.rs new file mode 100644 index 0000000000000..015fbcb97f97d --- /dev/null +++ b/library/std/src/os/motor/process.rs @@ -0,0 +1,15 @@ +#![unstable(feature = "motor_ext", issue = "147456")] + +use crate::sealed::Sealed; +use crate::sys_common::AsInner; + +pub trait ChildExt: Sealed { + /// Extracts the main thread raw handle, without taking ownership + fn sys_handle(&self) -> u64; +} + +impl ChildExt for crate::process::Child { + fn sys_handle(&self) -> u64 { + self.as_inner().handle() + } +} diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index 2045b2fecc6ac..f2f1d1c7feceb 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -83,6 +83,9 @@ cfg_select! { target_os = "hermit" => { mod hermit; } + target_os = "motor" => { + mod motor; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; } diff --git a/library/std/src/sys/alloc/motor.rs b/library/std/src/sys/alloc/motor.rs new file mode 100644 index 0000000000000..271e3c40c26ae --- /dev/null +++ b/library/std/src/sys/alloc/motor.rs @@ -0,0 +1,28 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: same requirements as in GlobalAlloc::alloc. + moto_rt::alloc::alloc(layout) + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: same requirements as in GlobalAlloc::alloc_zeroed. + moto_rt::alloc::alloc_zeroed(layout) + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: same requirements as in GlobalAlloc::dealloc. + unsafe { moto_rt::alloc::dealloc(ptr, layout) } + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: same requirements as in GlobalAlloc::realloc. + unsafe { moto_rt::alloc::realloc(ptr, layout, new_size) } + } +} diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs index b6f464161ee2b..64b2c014b54fe 100644 --- a/library/std/src/sys/anonymous_pipe/mod.rs +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -9,6 +9,10 @@ cfg_select! { mod windows; pub use windows::{AnonPipe, pipe}; } + target_os = "motor" => { + mod motor; + pub use motor::{AnonPipe, pipe}; + } _ => { mod unsupported; pub use unsupported::{AnonPipe, pipe}; diff --git a/library/std/src/sys/anonymous_pipe/motor.rs b/library/std/src/sys/anonymous_pipe/motor.rs new file mode 100644 index 0000000000000..dfe10f7fafe49 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/motor.rs @@ -0,0 +1,11 @@ +use crate::io; +use crate::sys::fd::FileDesc; +use crate::sys::pipe::anon_pipe; +use crate::sys_common::IntoInner; + +pub type AnonPipe = FileDesc; + +#[inline] +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index 75c59da721e19..5424d40a15883 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -6,6 +6,7 @@ all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), target_family = "windows", target_os = "hermit", + target_os = "motor", target_os = "uefi", target_os = "wasi", target_os = "xous", @@ -28,6 +29,10 @@ cfg_select! { mod sgx; pub use sgx::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } target_os = "uefi" => { mod uefi; pub use uefi::*; diff --git a/library/std/src/sys/args/motor.rs b/library/std/src/sys/args/motor.rs new file mode 100644 index 0000000000000..c3dbe87cec411 --- /dev/null +++ b/library/std/src/sys/args/motor.rs @@ -0,0 +1,13 @@ +pub use super::common::Args; +use crate::ffi::OsString; + +pub fn args() -> Args { + let motor_args: Vec = moto_rt::process::args(); + let mut rust_args = Vec::new(); + + for arg in motor_args { + rust_args.push(OsString::from(arg)); + } + + Args::new(rust_args) +} diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs index f211a9fc86b3b..89856516b6dce 100644 --- a/library/std/src/sys/env/mod.rs +++ b/library/std/src/sys/env/mod.rs @@ -5,6 +5,7 @@ #[cfg(any( target_family = "unix", target_os = "hermit", + target_os = "motor", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "solid_asp3", target_os = "uefi", @@ -26,6 +27,10 @@ cfg_select! { mod hermit; pub use hermit::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; diff --git a/library/std/src/sys/env/motor.rs b/library/std/src/sys/env/motor.rs new file mode 100644 index 0000000000000..1f756ccd3ee85 --- /dev/null +++ b/library/std/src/sys/env/motor.rs @@ -0,0 +1,27 @@ +pub use super::common::Env; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::os::motor::ffi::OsStrExt; + +pub fn env() -> Env { + let motor_env: Vec<(String, String)> = moto_rt::process::env(); + let mut rust_env = vec![]; + + for (k, v) in motor_env { + rust_env.push((OsString::from(k), OsString::from(v))); + } + + Env::new(rust_env) +} + +pub fn getenv(key: &OsStr) -> Option { + moto_rt::process::getenv(key.as_str()).map(|s| OsString::from(s)) +} + +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + Ok(moto_rt::process::setenv(key.as_str(), val.as_str())) +} + +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + Ok(moto_rt::process::unsetenv(key.as_str())) +} diff --git a/library/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs index 7cb9dd1cba9d3..330499ecc18f6 100644 --- a/library/std/src/sys/fd/mod.rs +++ b/library/std/src/sys/fd/mod.rs @@ -11,6 +11,10 @@ cfg_select! { mod hermit; pub use hermit::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; diff --git a/library/std/src/sys/fd/motor.rs b/library/std/src/sys/fd/motor.rs new file mode 100644 index 0000000000000..4211fef8007ab --- /dev/null +++ b/library/std/src/sys/fd/motor.rs @@ -0,0 +1,124 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::map_motor_error; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct FileDesc(OwnedFd); + +impl FileDesc { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.as_raw_fd(), nonblocking).map_err(map_motor_error) + } + + #[inline] + pub fn duplicate(&self) -> io::Result { + let fd = moto_rt::fs::duplicate(self.as_raw_fd()).map_err(map_motor_error)?; + // SAFETY: safe because we just got it from the OS runtime. + unsafe { Ok(Self::from_raw_fd(fd)) } + } + + #[inline] + pub fn try_clone(&self) -> io::Result { + self.duplicate() + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } +} + +impl AsInner for FileDesc { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(owned_fd) + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for FileDesc { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 64f5a6b36d3db..403bfb2d9b929 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -27,6 +27,10 @@ cfg_select! { mod hermit; use hermit as imp; } + target_os = "motor" => { + mod motor; + use motor as imp; + } target_os = "solid_asp3" => { mod solid; use solid as imp; diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs new file mode 100644 index 0000000000000..656b6e81b9518 --- /dev/null +++ b/library/std/src/sys/fs/motor.rs @@ -0,0 +1,478 @@ +use crate::ffi::OsString; +use crate::hash::Hash; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::{Path, PathBuf}; +use crate::sys::fd::FileDesc; +pub use crate::sys::fs::common::exists; +use crate::sys::time::SystemTime; +use crate::sys::{map_motor_error, unsupported}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct FileType { + rt_filetype: u8, +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.rt_filetype == moto_rt::fs::FILETYPE_DIRECTORY + } + + pub fn is_file(&self) -> bool { + self.rt_filetype == moto_rt::fs::FILETYPE_FILE + } + + pub fn is_symlink(&self) -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct FilePermissions { + rt_perm: u64, +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + (self.rt_perm & moto_rt::fs::PERM_WRITE == 0) + && (self.rt_perm & moto_rt::fs::PERM_READ != 0) + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.rt_perm = moto_rt::fs::PERM_READ; + } else { + self.rt_perm = moto_rt::fs::PERM_READ | moto_rt::fs::PERM_WRITE; + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + modified: u128, + accessed: u128, +} + +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = t.as_u128(); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = t.as_u128(); + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct FileAttr { + inner: moto_rt::fs::FileAttr, +} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.inner.size + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions { rt_perm: self.inner.perm } + } + + pub fn file_type(&self) -> FileType { + FileType { rt_filetype: self.inner.file_type } + } + + pub fn modified(&self) -> io::Result { + match self.inner.modified { + 0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)), + x => Ok(SystemTime::from_u128(x)), + } + } + + pub fn accessed(&self) -> io::Result { + match self.inner.accessed { + 0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)), + x => Ok(SystemTime::from_u128(x)), + } + } + + pub fn created(&self) -> io::Result { + match self.inner.created { + 0 => Err(crate::io::Error::from(crate::io::ErrorKind::Other)), + x => Ok(SystemTime::from_u128(x)), + } + } +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + rt_open_options: u32, +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { rt_open_options: 0 } + } + + pub fn read(&mut self, read: bool) { + if read { + self.rt_open_options |= moto_rt::fs::O_READ; + } else { + self.rt_open_options &= !moto_rt::fs::O_READ; + } + } + + pub fn write(&mut self, write: bool) { + if write { + self.rt_open_options |= moto_rt::fs::O_WRITE; + } else { + self.rt_open_options &= !moto_rt::fs::O_WRITE; + } + } + + pub fn append(&mut self, append: bool) { + if append { + self.rt_open_options |= moto_rt::fs::O_APPEND; + } else { + self.rt_open_options &= !moto_rt::fs::O_APPEND; + } + } + + pub fn truncate(&mut self, truncate: bool) { + if truncate { + self.rt_open_options |= moto_rt::fs::O_TRUNCATE; + } else { + self.rt_open_options &= !moto_rt::fs::O_TRUNCATE; + } + } + + pub fn create(&mut self, create: bool) { + if create { + self.rt_open_options |= moto_rt::fs::O_CREATE; + } else { + self.rt_open_options &= !moto_rt::fs::O_CREATE; + } + } + + pub fn create_new(&mut self, create_new: bool) { + if create_new { + self.rt_open_options |= moto_rt::fs::O_CREATE_NEW; + } else { + self.rt_open_options &= !moto_rt::fs::O_CREATE_NEW; + } + } +} + +#[derive(Debug)] +pub struct File(FileDesc); + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::open(path, opts.rt_open_options) + .map(|fd| unsafe { Self::from_raw_fd(fd) }) + .map_err(map_motor_error) + } + + pub fn file_attr(&self) -> io::Result { + moto_rt::fs::get_file_attr(self.as_raw_fd()) + .map(|inner| -> FileAttr { FileAttr { inner } }) + .map_err(map_motor_error) + } + + pub fn fsync(&self) -> io::Result<()> { + moto_rt::fs::fsync(self.as_raw_fd()).map_err(map_motor_error) + } + + pub fn datasync(&self) -> io::Result<()> { + moto_rt::fs::datasync(self.as_raw_fd()).map_err(map_motor_error) + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + moto_rt::fs::truncate(self.as_raw_fd(), size).map_err(map_motor_error) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn flush(&self) -> io::Result<()> { + moto_rt::fs::flush(self.as_raw_fd()).map_err(map_motor_error) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + match pos { + SeekFrom::Start(offset) => { + moto_rt::fs::seek(self.as_raw_fd(), offset as i64, moto_rt::fs::SEEK_SET) + .map_err(map_motor_error) + } + SeekFrom::End(offset) => { + moto_rt::fs::seek(self.as_raw_fd(), offset, moto_rt::fs::SEEK_END) + .map_err(map_motor_error) + } + SeekFrom::Current(offset) => { + moto_rt::fs::seek(self.as_raw_fd(), offset, moto_rt::fs::SEEK_CUR) + .map_err(map_motor_error) + } + } + } + + pub fn tell(&self) -> io::Result { + self.seek(SeekFrom::Current(0)) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.as_raw_fd()) + .map(|fd| unsafe { Self::from_raw_fd(fd) }) + .map_err(map_motor_error) + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + moto_rt::fs::set_file_perm(self.as_raw_fd(), perm.rt_perm).map_err(map_motor_error) + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + unsupported() // Let's not do that. + } + + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> Result<(), crate::fs::TryLockError> { + Err(crate::fs::TryLockError::Error(io::Error::from(io::ErrorKind::Unsupported))) + } + + pub fn try_lock_shared(&self) -> Result<(), crate::fs::TryLockError> { + Err(crate::fs::TryLockError::Error(io::Error::from(io::ErrorKind::Unsupported))) + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + + pub fn size(&self) -> Option> { + None + } +} + +#[derive(Debug)] +pub struct DirBuilder {} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::mkdir(path).map_err(map_motor_error) + } +} + +pub fn unlink(path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::unlink(path).map_err(map_motor_error) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = old.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let new = new.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::rename(old, new).map_err(map_motor_error) +} + +pub fn rmdir(path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::rmdir(path).map_err(map_motor_error) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::rmdir_all(path).map_err(map_motor_error) +} + +pub fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::set_perm(path, perm.rt_perm).map_err(map_motor_error) +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(path: &Path) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let inner = moto_rt::fs::stat(path).map_err(map_motor_error)?; + Ok(FileAttr { inner }) +} + +pub fn lstat(path: &Path) -> io::Result { + stat(path) +} + +pub fn canonicalize(path: &Path) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let path = moto_rt::fs::canonicalize(path).map_err(map_motor_error)?; + Ok(path.into()) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + let from = from.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + let to = to.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + moto_rt::fs::copy(from, to).map_err(map_motor_error) +} + +#[derive(Debug)] +pub struct ReadDir { + rt_fd: moto_rt::RtFd, + path: String, +} + +impl Drop for ReadDir { + fn drop(&mut self) { + moto_rt::fs::closedir(self.rt_fd).unwrap(); + } +} + +pub fn readdir(path: &Path) -> io::Result { + let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; + Ok(ReadDir { + rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?, + path: path.to_owned(), + }) +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + match moto_rt::fs::readdir(self.rt_fd).map_err(map_motor_error) { + Ok(maybe_item) => match maybe_item { + Some(inner) => Some(Ok(DirEntry { inner, parent_path: self.path.clone() })), + None => None, + }, + Err(err) => Some(Err(err)), + } + } +} + +pub struct DirEntry { + parent_path: String, + inner: moto_rt::fs::DirEntry, +} + +impl DirEntry { + fn filename(&self) -> &str { + core::str::from_utf8(unsafe { + core::slice::from_raw_parts(self.inner.fname.as_ptr(), self.inner.fname_size as usize) + }) + .unwrap() + } + + pub fn path(&self) -> PathBuf { + let mut path = self.parent_path.clone(); + path.push_str("/"); + path.push_str(self.filename()); + path.into() + } + + pub fn file_name(&self) -> OsString { + self.filename().to_owned().into() + } + + pub fn metadata(&self) -> io::Result { + Ok(FileAttr { inner: self.inner.attr }) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType { rt_filetype: self.inner.attr.file_type }) + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut for File { + #[inline] + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} diff --git a/library/std/src/sys/io/is_terminal/motor.rs b/library/std/src/sys/io/is_terminal/motor.rs new file mode 100644 index 0000000000000..0b70299adaaa2 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/motor.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + moto_rt::fs::is_terminal(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index fe8ec1dbb7325..0916eda1c06a5 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -39,6 +39,10 @@ mod is_terminal { mod hermit; pub use hermit::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } _ => { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs index 41e7159f909ae..2f064914a8317 100644 --- a/library/std/src/sys/net/connection/mod.rs +++ b/library/std/src/sys/net/connection/mod.rs @@ -17,6 +17,10 @@ cfg_select! { mod wasip1; pub use wasip1::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } target_os = "xous" => { mod xous; pub use xous::*; diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs new file mode 100644 index 0000000000000..e9bf29e34f90c --- /dev/null +++ b/library/std/src/sys/net/connection/motor.rs @@ -0,0 +1,521 @@ +pub use moto_rt::netc; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::SocketAddr::{V4, V6}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::map_motor_error; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +// We want to re-use as much of Rust's stdlib code as possible, +// and most of it is unixy, but with a lot of nesting. +#[derive(Debug)] +pub struct Socket(FileDesc); + +#[derive(Debug)] +pub struct TcpStream { + inner: Socket, +} + +impl TcpStream { + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn connect(addr: A) -> io::Result { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::tcp_connect(&addr, Duration::MAX, false) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { + let addr = into_netc(addr); + moto_rt::net::tcp_connect(&addr, timeout, false) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_read_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_write_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn read_timeout(&self) -> io::Result> { + moto_rt::net::read_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn write_timeout(&self) -> io::Result> { + moto_rt::net::write_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + moto_rt::net::peek(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let bufs: &mut [&mut [u8]] = unsafe { core::mem::transmute(bufs) }; + moto_rt::fs::read_vectored(self.inner.as_raw_fd(), bufs).map_err(map_motor_error) + } + + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let bufs: &[&[u8]] = unsafe { core::mem::transmute(bufs) }; + moto_rt::fs::write_vectored(self.inner.as_raw_fd(), bufs).map_err(map_motor_error) + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn peer_addr(&self) -> io::Result { + moto_rt::net::peer_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn socket_addr(&self) -> io::Result { + moto_rt::net::socket_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn shutdown(&self, shutdown: Shutdown) -> io::Result<()> { + let shutdown = match shutdown { + Shutdown::Read => moto_rt::net::SHUTDOWN_READ, + Shutdown::Write => moto_rt::net::SHUTDOWN_WRITE, + Shutdown::Both => moto_rt::net::SHUTDOWN_READ | moto_rt::net::SHUTDOWN_WRITE, + }; + + moto_rt::net::shutdown(self.inner.as_raw_fd(), shutdown).map_err(map_motor_error) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.inner.as_raw_fd()) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_linger(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_linger(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn linger(&self) -> io::Result> { + moto_rt::net::linger(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + moto_rt::net::set_nodelay(self.inner.as_raw_fd(), nodelay).map_err(map_motor_error) + } + + pub fn nodelay(&self) -> io::Result { + moto_rt::net::nodelay(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error) + } + + pub fn ttl(&self) -> io::Result { + moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn take_error(&self) -> io::Result> { + let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?; + if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error) + } +} + +#[derive(Debug)] +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn bind(addr: A) -> io::Result { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::bind(moto_rt::net::PROTO_TCP, &addr) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn socket_addr(&self) -> io::Result { + moto_rt::net::socket_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + moto_rt::net::accept(self.inner.as_raw_fd()) + .map(|(fd, addr)| { + (TcpStream { inner: unsafe { Socket::from_raw_fd(fd) } }, from_netc(&addr)) + }) + .map_err(map_motor_error) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.inner.as_raw_fd()) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error) + } + + pub fn ttl(&self) -> io::Result { + moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + moto_rt::net::set_only_v6(self.inner.as_raw_fd(), only_v6).map_err(map_motor_error) + } + + pub fn only_v6(&self) -> io::Result { + moto_rt::net::only_v6(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn take_error(&self) -> io::Result> { + let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?; + if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error) + } +} + +#[derive(Debug)] +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn bind(addr: A) -> io::Result { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::bind(moto_rt::net::PROTO_UDP, &addr) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn peer_addr(&self) -> io::Result { + moto_rt::net::peer_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn socket_addr(&self) -> io::Result { + moto_rt::net::socket_addr(self.inner.as_raw_fd()) + .map(|addr| from_netc(&addr)) + .map_err(map_motor_error) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + moto_rt::net::udp_recv_from(self.inner.as_raw_fd(), buf) + .map(|(sz, addr)| (sz, from_netc(&addr))) + .map_err(map_motor_error) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + moto_rt::net::udp_peek_from(self.inner.as_raw_fd(), buf) + .map(|(sz, addr)| (sz, from_netc(&addr))) + .map_err(map_motor_error) + } + + pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> io::Result { + let addr = into_netc(addr); + moto_rt::net::udp_send_to(self.inner.as_raw_fd(), buf, &addr).map_err(map_motor_error) + } + + pub fn duplicate(&self) -> io::Result { + moto_rt::fs::duplicate(self.inner.as_raw_fd()) + .map(|fd| Self { inner: unsafe { Socket::from_raw_fd(fd) } }) + .map_err(map_motor_error) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_read_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + moto_rt::net::set_write_timeout(self.inner.as_raw_fd(), timeout).map_err(map_motor_error) + } + + pub fn read_timeout(&self) -> io::Result> { + moto_rt::net::read_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn write_timeout(&self) -> io::Result> { + moto_rt::net::write_timeout(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + moto_rt::net::set_udp_broadcast(self.inner.as_raw_fd(), broadcast).map_err(map_motor_error) + } + + pub fn broadcast(&self) -> io::Result { + moto_rt::net::udp_broadcast(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_multicast_loop_v4(&self, val: bool) -> io::Result<()> { + moto_rt::net::set_udp_multicast_loop_v4(self.inner.as_raw_fd(), val) + .map_err(map_motor_error) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + moto_rt::net::udp_multicast_loop_v4(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_multicast_ttl_v4(&self, val: u32) -> io::Result<()> { + moto_rt::net::set_udp_multicast_ttl_v4(self.inner.as_raw_fd(), val).map_err(map_motor_error) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + moto_rt::net::udp_multicast_ttl_v4(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn set_multicast_loop_v6(&self, val: bool) -> io::Result<()> { + moto_rt::net::set_udp_multicast_loop_v6(self.inner.as_raw_fd(), val) + .map_err(map_motor_error) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + moto_rt::net::udp_multicast_loop_v6(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn join_multicast_v4(&self, addr: &Ipv4Addr, iface: &Ipv4Addr) -> io::Result<()> { + let addr = (*addr).into(); + let iface = (*iface).into(); + moto_rt::net::join_udp_multicast_v4(self.inner.as_raw_fd(), &addr, &iface) + .map_err(map_motor_error) + } + + pub fn join_multicast_v6(&self, addr: &Ipv6Addr, iface: u32) -> io::Result<()> { + let addr = (*addr).into(); + moto_rt::net::join_udp_multicast_v6(self.inner.as_raw_fd(), &addr, iface) + .map_err(map_motor_error) + } + + pub fn leave_multicast_v4(&self, addr: &Ipv4Addr, iface: &Ipv4Addr) -> io::Result<()> { + let addr = (*addr).into(); + let iface = (*iface).into(); + moto_rt::net::leave_udp_multicast_v4(self.inner.as_raw_fd(), &addr, &iface) + .map_err(map_motor_error) + } + + pub fn leave_multicast_v6(&self, addr: &Ipv6Addr, iface: u32) -> io::Result<()> { + let addr = (*addr).into(); + moto_rt::net::leave_udp_multicast_v6(self.inner.as_raw_fd(), &addr, iface) + .map_err(map_motor_error) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + moto_rt::net::set_ttl(self.inner.as_raw_fd(), ttl).map_err(map_motor_error) + } + + pub fn ttl(&self) -> io::Result { + moto_rt::net::ttl(self.inner.as_raw_fd()).map_err(map_motor_error) + } + + pub fn take_error(&self) -> io::Result> { + moto_rt::net::take_error(self.inner.as_raw_fd()) + .map(|e| match e { + moto_rt::E_OK => None, + e => Some(map_motor_error(e)), + }) + .map_err(map_motor_error) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + moto_rt::net::set_nonblocking(self.inner.as_raw_fd(), nonblocking).map_err(map_motor_error) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + moto_rt::net::peek(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.inner.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn connect(&self, addr: A) -> io::Result<()> { + let addr = into_netc(&addr.to_socket_addrs()?.next().unwrap()); + moto_rt::net::udp_connect(self.inner.as_raw_fd(), &addr).map_err(map_motor_error) + } +} + +pub struct LookupHost { + addresses: alloc::collections::VecDeque, +} + +pub fn lookup_host(host: &str, port: u16) -> io::Result { + let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?; + Ok(LookupHost { addresses }) +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.addresses.pop_front().map(|addr| from_netc(&addr)) + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(host_port: &str) -> io::Result { + let (host, port_str) = host_port + .rsplit_once(':') + .ok_or(moto_rt::E_INVALID_ARGUMENT) + .map_err(map_motor_error)?; + let port: u16 = + port_str.parse().map_err(|_| moto_rt::E_INVALID_ARGUMENT).map_err(map_motor_error)?; + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(host_port: (&'a str, u16)) -> io::Result { + let (host, port) = host_port; + + let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?; + Ok(LookupHost { addresses }) + } +} + +fn into_netc(addr: &SocketAddr) -> netc::sockaddr { + match addr { + V4(addr4) => netc::sockaddr { v4: (*addr4).into() }, + V6(addr6) => netc::sockaddr { v6: (*addr6).into() }, + } +} + +fn from_netc(addr: &netc::sockaddr) -> SocketAddr { + // SAFETY: all variants of union netc::sockaddr have `sin_family` at the same offset. + let family = unsafe { addr.v4.sin_family }; + match family { + netc::AF_INET => SocketAddr::V4(crate::net::SocketAddrV4::from(unsafe { addr.v4 })), + netc::AF_INET6 => SocketAddr::V6(crate::net::SocketAddrV6::from(unsafe { addr.v6 })), + _ => panic!("bad sin_family {family}"), + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} + +impl AsInner for TcpStream { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl FromInner for TcpListener { + fn from_inner(socket: Socket) -> TcpListener { + TcpListener { inner: socket } + } +} + +impl FromInner for UdpSocket { + fn from_inner(socket: Socket) -> UdpSocket { + UdpSocket { inner: socket } + } +} diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 9e964540a87c1..e11df38a8ee68 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -41,6 +41,10 @@ cfg_select! { mod hermit; pub use self::hermit::*; } + target_os = "motor" => { + mod motor; + pub use self::motor::*; + } target_os = "trusty" => { mod trusty; pub use self::trusty::*; diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs new file mode 100644 index 0000000000000..c64f8ff7a8a83 --- /dev/null +++ b/library/std/src/sys/pal/motor/mod.rs @@ -0,0 +1,77 @@ +#![allow(unsafe_op_in_unsafe_fn)] + +pub mod os; +pub mod pipe; +pub mod time; + +pub use moto_rt::futex; + +use crate::io as std_io; +use crate::sys::RawOsError; + +pub(crate) fn map_motor_error(err: moto_rt::ErrorCode) -> crate::io::Error { + crate::io::Error::from_raw_os_error(err.into()) +} + +#[cfg(not(test))] +#[unsafe(no_mangle)] +pub extern "C" fn motor_start() -> ! { + // Initialize the runtime. + moto_rt::start(); + + // Call main. + unsafe extern "C" { + fn main(_: isize, _: *const *const u8, _: u8) -> i32; + } + let result = unsafe { main(0, core::ptr::null(), 0) }; + + // Terminate the process. + moto_rt::process::exit(result) +} + +// SAFETY: must be called only once during runtime initialization. +// NOTE: Motor OS uses moto_rt::start() to initialize runtime (see above). +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +pub fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> std_io::Error { + std_io::Error::UNSUPPORTED_PLATFORM +} + +pub fn is_interrupted(_code: RawOsError) -> bool { + false // Motor OS doesn't have signals. +} + +pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { + use moto_rt::error::*; + use std_io::ErrorKind; + + if code < 0 || code > u16::MAX.into() { + return std_io::ErrorKind::Uncategorized; + } + + match code as moto_rt::ErrorCode /* u16 */ { + E_ALREADY_IN_USE => ErrorKind::AlreadyExists, + E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_NOT_FOUND => ErrorKind::NotFound, + E_TIMED_OUT => ErrorKind::TimedOut, + E_NOT_IMPLEMENTED => ErrorKind::Unsupported, + E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, + E_UNEXPECTED_EOF => ErrorKind::UnexpectedEof, + E_INVALID_ARGUMENT => ErrorKind::InvalidInput, + E_NOT_READY => ErrorKind::WouldBlock, + E_NOT_CONNECTED => ErrorKind::NotConnected, + _ => crate::io::ErrorKind::Uncategorized, + } +} + +pub fn abort_internal() -> ! { + core::intrinsics::abort(); +} diff --git a/library/std/src/sys/pal/motor/os.rs b/library/std/src/sys/pal/motor/os.rs new file mode 100644 index 0000000000000..052e3b238b6af --- /dev/null +++ b/library/std/src/sys/pal/motor/os.rs @@ -0,0 +1,100 @@ +use super::map_motor_error; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; +use crate::os::motor::ffi::OsStrExt; +use crate::path::{self, PathBuf}; +use crate::sys::RawOsError; +use crate::{fmt, io}; + +pub fn errno() -> RawOsError { + // Not used in Motor OS because it is ambiguous: Motor OS + // is micro-kernel-based, and I/O happens via a shared-memory + // ring buffer, so an I/O operation that on a unix is a syscall + // may involve no sycalls on Motor OS at all, or a syscall + // that e.g. waits for a notification from the I/O driver + // (sys-io); and the wait syscall may succeed, but the + // driver may report an I/O error; or a bunch of results + // for several I/O operations, some successful and some + // not. + // + // Also I/O operations in a Motor OS process are handled by a + // separate runtime background/I/O thread, so it is really hard + // to define what "last system error in the current thread" + // actually means. + moto_rt::E_UNKNOWN.into() +} + +pub fn error_string(errno: RawOsError) -> String { + let error_code: moto_rt::ErrorCode = match errno { + x if x < 0 => moto_rt::E_UNKNOWN, + x if x > u16::MAX.into() => moto_rt::E_UNKNOWN, + x => x as moto_rt::ErrorCode, /* u16 */ + }; + format!("{}", moto_rt::Error::from(error_code)) +} + +pub fn getcwd() -> io::Result { + moto_rt::fs::getcwd().map(PathBuf::from).map_err(map_motor_error) +} + +pub fn chdir(path: &path::Path) -> io::Result<()> { + moto_rt::fs::chdir(path.as_os_str().as_str()).map_err(map_motor_error) +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on this platform yet" + } +} + +pub fn current_exe() -> io::Result { + moto_rt::process::current_exe().map(PathBuf::from).map_err(map_motor_error) +} + +pub fn temp_dir() -> PathBuf { + PathBuf::from(moto_rt::fs::TEMP_DIR) +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(code: i32) -> ! { + moto_rt::process::exit(code) +} + +pub fn getpid() -> u32 { + panic!("Pids on Motor OS are u64.") +} diff --git a/library/std/src/sys/pal/motor/pipe.rs b/library/std/src/sys/pal/motor/pipe.rs new file mode 100644 index 0000000000000..d3be6ddf1573e --- /dev/null +++ b/library/std/src/sys/pal/motor/pipe.rs @@ -0,0 +1,121 @@ +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::map_motor_error; +use crate::sys_common::{FromInner, IntoInner}; + +#[derive(Debug)] +pub struct AnonPipe(FileDesc); + +impl From for AnonPipe { + fn from(rt_fd: moto_rt::RtFd) -> AnonPipe { + unsafe { AnonPipe::from_raw_fd(rt_fd) } + } +} + +impl AnonPipe { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut temp_vec = Vec::new(); + let mut size = 0_usize; + loop { + temp_vec.resize(256, 0_u8); + match self.read(&mut temp_vec[..]) { + Ok(sz) => { + if sz == 0 { + return Ok(size); + } + size += sz; + temp_vec.truncate(sz); + buf.append(&mut temp_vec); + } + Err(err) => { + if size != 0 { + return Ok(size); + } else { + return Err(err); + } + } + } + } + } +} + +impl AsRawFd for AnonPipe { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl FromRawFd for AnonPipe { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let desc = FileDesc::from_raw_fd(fd); + Self(desc) + } +} + +impl IntoRawFd for AnonPipe { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl AsFd for AnonPipe { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl IntoInner for AnonPipe { + fn into_inner(self) -> OwnedFd { + self.0.into_inner() + } +} + +impl IntoInner for AnonPipe { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for AnonPipe { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} + +pub fn read2(_p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { + Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) +} + +#[inline] +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} diff --git a/library/std/src/sys/pal/motor/time.rs b/library/std/src/sys/pal/motor/time.rs new file mode 100644 index 0000000000000..e917fd466c2e4 --- /dev/null +++ b/library/std/src/sys/pal/motor/time.rs @@ -0,0 +1 @@ +pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH}; diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index faa2616a6320d..15530323a198d 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -62,7 +62,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { } pub(crate) fn is_absolute(path: &Path) -> bool { - if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { + if cfg!(any(unix, target_os = "hermit", target_os = "wasi", target_os = "motor")) { path.has_root() } else { path.has_root() && path.prefix().is_some() diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 158e44e1764a3..eabef92244d01 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -17,7 +17,7 @@ cfg_select! { target_os = "emscripten" => { mod emcc; } - any(target_env = "msvc", target_family = "wasm") => { + any(target_env = "msvc", target_family = "wasm", target_os = "motor") => { // This is required by the compiler to exist (e.g., it's a lang item), // but it's never actually called by the compiler because // __CxxFrameHandler3 (msvc) / __gxx_wasm_personality_v0 (wasm) is the diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index a1ed0cd2cdd2d..92e459298fc4c 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -11,6 +11,10 @@ cfg_select! { mod uefi; use uefi as imp; } + target_os = "motor" => { + mod motor; + use motor as imp; + } _ => { mod unsupported; use unsupported as imp; @@ -38,6 +42,7 @@ pub use imp::{ )) ), target_os = "windows", + target_os = "motor" ))] pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec)> { use crate::sys::pipe::read2; @@ -77,5 +82,6 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec< )) ), target_os = "windows", + target_os = "motor" )))] pub use imp::output; diff --git a/library/std/src/sys/process/motor.rs b/library/std/src/sys/process/motor.rs new file mode 100644 index 0000000000000..9060902bc3d20 --- /dev/null +++ b/library/std/src/sys/process/motor.rs @@ -0,0 +1,313 @@ +use super::CommandEnvs; +use super::env::CommandEnv; +use crate::ffi::OsStr; +pub use crate::ffi::OsString as EnvKey; +use crate::num::NonZeroI32; +use crate::os::fd::{FromRawFd, IntoRawFd}; +use crate::os::motor::ffi::OsStrExt; +use crate::path::Path; +use crate::process::StdioPipes; +use crate::sys::fs::File; +use crate::sys::map_motor_error; +use crate::sys::pipe::AnonPipe; +use crate::sys_common::{AsInner, FromInner}; +use crate::{fmt, io}; + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(crate::sys::fd::FileDesc), +} + +impl Stdio { + fn into_rt(self) -> moto_rt::RtFd { + match self { + Stdio::Inherit => moto_rt::process::STDIO_INHERIT, + Stdio::Null => moto_rt::process::STDIO_NULL, + Stdio::MakePipe => moto_rt::process::STDIO_MAKE_PIPE, + Stdio::Fd(fd) => fd.into_raw_fd(), + } + } + + fn try_clone(&self) -> io::Result { + match self { + Self::Fd(fd) => { + Ok(Self::Fd(crate::sys::fd::FileDesc::from_inner(fd.as_inner().try_clone()?))) + } + Self::Inherit => Ok(Self::Inherit), + Self::Null => Ok(Self::Null), + Self::MakePipe => Ok(Self::MakePipe), + } + } +} + +#[derive(Default)] +pub struct Command { + program: String, + args: Vec, + cwd: Option, + stdin: Option, + stdout: Option, + stderr: Option, + env: CommandEnv, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + let mut env = CommandEnv::default(); + env.remove(OsStr::new(moto_rt::process::STDIO_IS_TERMINAL_ENV_KEY)); + + Command { program: program.as_str().to_owned(), env, ..Default::default() } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.as_str().to_owned()) + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.as_str().to_owned()) + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn get_program(&self) -> &OsStr { + OsStr::new(self.program.as_str()) + } + + pub fn get_args(&self) -> CommandArgs<'_> { + let iter = self.args.iter(); + CommandArgs { iter } + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + self.cwd.as_ref().map(Path::new) + } + + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + let stdin = if let Some(stdin) = self.stdin.as_ref() { + stdin.try_clone()?.into_rt() + } else if needs_stdin { + default.try_clone()?.into_rt() + } else { + Stdio::Null.into_rt() + }; + let stdout = if let Some(stdout) = self.stdout.as_ref() { + stdout.try_clone()?.into_rt() + } else { + default.try_clone()?.into_rt() + }; + let stderr = if let Some(stderr) = self.stdout.as_ref() { + stderr.try_clone()?.into_rt() + } else { + default.try_clone()?.into_rt() + }; + + let mut env = Vec::<(String, String)>::new(); + for (k, v) in self.env.capture() { + env.push((k.as_str().to_owned(), v.as_str().to_owned())); + } + + let args = moto_rt::process::SpawnArgs { + program: self.program.clone(), + args: self.args.clone(), + env, + cwd: self.cwd.clone(), + stdin, + stdout, + stderr, + }; + + let (handle, stdin, stdout, stderr) = + moto_rt::process::spawn(args).map_err(map_motor_error)?; + + Ok(( + Process { handle }, + StdioPipes { + stdin: if stdin >= 0 { Some(stdin.into()) } else { None }, + stdout: if stdout >= 0 { Some(stdout.into()) } else { None }, + stderr: if stderr >= 0 { Some(stderr.into()) } else { None }, + }, + )) + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + unsafe { Stdio::Fd(crate::sys::fd::FileDesc::from_raw_fd(pipe.into_raw_fd())) } + } +} + +impl From for Stdio { + fn from(fd: crate::sys::fd::FileDesc) -> Stdio { + Stdio::Fd(fd) + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + panic!("Not implemented") + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + panic!("Not implemented") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + panic!("Not implemented") + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub struct ExitStatus(i32); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + if self.0 == 0 { Ok(()) } else { Err(ExitStatusError(*self)) } + } + + pub fn code(&self) -> Option { + Some(self.0) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "exit code: {}", self.0) + } +} +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(ExitStatus); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + self.0 + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + NonZeroI32::new(self.0.0) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(i32); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(0); + pub const FAILURE: ExitCode = ExitCode(1); + + pub fn as_i32(&self) -> i32 { + self.0 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + Self(code as i32) + } +} + +pub struct Process { + handle: u64, +} + +impl Drop for Process { + fn drop(&mut self) { + moto_rt::alloc::release_handle(self.handle).unwrap(); + } +} + +impl Process { + pub fn id(&self) -> u32 { + 0 + } + + pub fn kill(&mut self) -> io::Result<()> { + match moto_rt::process::kill(self.handle) { + moto_rt::E_OK => Ok(()), + err => Err(map_motor_error(err)), + } + } + + pub fn wait(&mut self) -> io::Result { + moto_rt::process::wait(self.handle).map(|c| ExitStatus(c)).map_err(map_motor_error) + } + + pub fn try_wait(&mut self) -> io::Result> { + match moto_rt::process::try_wait(self.handle) { + Ok(s) => Ok(Some(ExitStatus(s))), + Err(err) => match err { + moto_rt::E_NOT_READY => Ok(None), + err => Err(map_motor_error(err)), + }, + } + } + + #[allow(unused)] + pub fn handle(&self) -> u64 { + self.handle + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, String>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|arg| OsStr::new(arg)) + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index ec81d89a0f3af..91f72d0738790 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -62,6 +62,10 @@ cfg_select! { mod redox; pub use redox::fill_bytes; } + target_os = "motor" => { + mod motor; + pub use motor::fill_bytes; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::fill_bytes; diff --git a/library/std/src/sys/random/motor.rs b/library/std/src/sys/random/motor.rs new file mode 100644 index 0000000000000..386b3704a91ea --- /dev/null +++ b/library/std/src/sys/random/motor.rs @@ -0,0 +1,3 @@ +pub fn fill_bytes(bytes: &mut [u8]) { + moto_rt::fill_random_bytes(bytes) +} diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 660317e3ea844..d51ea9ad726b5 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -13,6 +13,10 @@ cfg_select! { mod sgx; pub use sgx::*; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } target_os = "solid_asp3" => { mod solid; pub use solid::*; diff --git a/library/std/src/sys/stdio/motor.rs b/library/std/src/sys/stdio/motor.rs new file mode 100644 index 0000000000000..0a44feab723d0 --- /dev/null +++ b/library/std/src/sys/stdio/motor.rs @@ -0,0 +1,232 @@ +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::map_motor_error; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{io, process, sys}; + +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; + +pub struct Stdin {} + +impl Stdin { + pub const fn new() -> Self { + Self {} + } +} + +pub struct Stdout {} + +impl Stdout { + pub const fn new() -> Self { + Self {} + } +} + +pub struct Stderr {} + +impl Stderr { + pub const fn new() -> Self { + Self {} + } +} + +impl crate::sealed::Sealed for Stdin {} + +impl crate::io::IsTerminal for Stdin { + fn is_terminal(&self) -> bool { + moto_rt::fs::is_terminal(moto_rt::FD_STDIN) + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + moto_rt::fs::read(moto_rt::FD_STDIN, buf).map_err(map_motor_error) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(moto_rt::FD_STDOUT, buf).map_err(map_motor_error) + } + + fn flush(&mut self) -> io::Result<()> { + moto_rt::fs::flush(moto_rt::FD_STDOUT).map_err(map_motor_error) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + moto_rt::fs::write(moto_rt::FD_STDERR, buf).map_err(map_motor_error) + } + + fn flush(&mut self) -> io::Result<()> { + moto_rt::fs::flush(moto_rt::FD_STDERR).map_err(map_motor_error) + } +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl FromRawFd for process::Stdio { + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { + let fd = unsafe { sys::fd::FileDesc::from_raw_fd(fd) }; + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for process::Stdio { + /// Takes ownership of a file descriptor and returns a [`Stdio`](process::Stdio) + /// that can attach a stream to it. + #[inline] + fn from(fd: OwnedFd) -> process::Stdio { + let fd = sys::fd::FileDesc::from_inner(fd); + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdin { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdout { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStderr { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdin { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_raw_fd() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdout { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_raw_fd() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStderr { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_raw_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for crate::process::ChildStdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for OwnedFd { + /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor. + #[inline] + fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { + child_stdin.into_inner().into_inner() + } +} + +/// Creates a `ChildStdin` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From for process::ChildStdin { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStdin { + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStdin::from_inner(pipe) + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for crate::process::ChildStdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for OwnedFd { + /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor. + #[inline] + fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { + child_stdout.into_inner().into_inner() + } +} + +/// Creates a `ChildStdout` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From for process::ChildStdout { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStdout { + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStdout::from_inner(pipe) + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for crate::process::ChildStderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for OwnedFd { + /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor. + #[inline] + fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { + child_stderr.into_inner().into_inner() + } +} + +/// Creates a `ChildStderr` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From for process::ChildStderr { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStderr { + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStderr::from_inner(pipe) + } +} diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs index cb67d273759dd..83cf0ae629851 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -6,6 +6,7 @@ cfg_select! { target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", + target_os = "motor", target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs index c885b0eabae2d..e3d6ad1129c83 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -5,6 +5,7 @@ cfg_select! { target_os = "android", target_os = "freebsd", target_os = "openbsd", + target_os = "motor", target_os = "dragonfly", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs index 8adeb1f259d72..aeea884b9f617 100644 --- a/library/std/src/sys/sync/once/mod.rs +++ b/library/std/src/sys/sync/once/mod.rs @@ -14,6 +14,7 @@ cfg_select! { target_os = "android", all(target_arch = "wasm32", target_feature = "atomics"), target_os = "freebsd", + target_os = "motor", target_os = "openbsd", target_os = "dragonfly", target_os = "fuchsia", diff --git a/library/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs index 82f1dd18dee49..ab5715bf2de33 100644 --- a/library/std/src/sys/sync/rwlock/mod.rs +++ b/library/std/src/sys/sync/rwlock/mod.rs @@ -9,6 +9,7 @@ cfg_select! { target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", + target_os = "motor", ) => { mod futex; pub use futex::RwLock; diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index b9fb27b4eef2d..e8a9dc884f816 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -8,6 +8,7 @@ cfg_select! { target_os = "openbsd", target_os = "dragonfly", target_os = "fuchsia", + target_os = "motor", target_os = "hermit", ) => { mod futex; diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index a20b2a3ddd8ce..b98be62be0ad2 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -6,6 +6,10 @@ cfg_select! { mod unsupported; pub use unsupported::{current_os_id, set_name}; } + target_os = "motor" => { + mod motor; + pub use motor::*; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; diff --git a/library/std/src/sys/thread/motor.rs b/library/std/src/sys/thread/motor.rs new file mode 100644 index 0000000000000..0457d8818f326 --- /dev/null +++ b/library/std/src/sys/thread/motor.rs @@ -0,0 +1,63 @@ +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZeroUsize; +use crate::sys::map_motor_error; +use crate::time::Duration; + +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 256; + +pub struct Thread { + sys_thread: moto_rt::thread::ThreadHandle, +} + +unsafe impl Send for Thread {} +unsafe impl Sync for Thread {} + +impl Thread { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { + extern "C" fn __moto_rt_thread_fn(thread_arg: u64) { + unsafe { + Box::from_raw( + core::ptr::with_exposed_provenance::>(thread_arg as usize) + .cast_mut(), + )(); + } + } + + let thread_arg = Box::into_raw(Box::new(p)).expose_provenance() as u64; + let sys_thread = moto_rt::thread::spawn(__moto_rt_thread_fn, stack, thread_arg) + .map_err(map_motor_error)?; + Ok(Self { sys_thread }) + } + + pub fn join(self) { + assert!(moto_rt::thread::join(self.sys_thread) == moto_rt::E_OK) + } +} + +pub fn set_name(name: &CStr) { + let bytes = name.to_bytes(); + if let Ok(s) = core::str::from_utf8(bytes) { + let _ = moto_rt::thread::set_name(s); + } +} + +pub fn current_os_id() -> Option { + None +} + +pub fn available_parallelism() -> io::Result { + Ok(unsafe { NonZeroUsize::new_unchecked(moto_rt::num_cpus()) }) +} + +pub fn yield_now() { + moto_rt::thread::yield_now() +} + +pub fn sleep(dur: Duration) { + moto_rt::thread::sleep_until(moto_rt::time::Instant::now() + dur) +} diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index f7f051b1addc4..e88011aa22dad 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -187,6 +187,14 @@ pub(crate) mod key { pub(super) use xous::{Key, get, set}; use xous::{create, destroy}; } + target_os = "motor" => { + mod racy; + #[cfg(test)] + mod tests; + pub(super) use racy::LazyKey; + pub(super) use moto_rt::tls::{Key, get, set}; + use moto_rt::tls::{create, destroy}; + } _ => {} } } diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md index be361eee330ac..090dc00844802 100644 --- a/src/doc/style-guide/src/items.md +++ b/src/doc/style-guide/src/items.md @@ -123,7 +123,7 @@ struct Foo { Prefer using a unit struct (e.g., `struct Foo;`) to an empty struct (e.g., `struct Foo();` or `struct Foo {}`, these only exist to simplify code generation), but if you must use an empty struct, keep it on one line with no -space between the braces: `struct Foo;` or `struct Foo {}`. +space between the braces: `struct Foo();` or `struct Foo {}`. The same guidelines are used for untagged union declarations. diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 8792d90ac5366..d66529a464b69 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -794,9 +794,9 @@ checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libffi" -version = "4.1.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7681c6fab541f799a829e44a445a0666cf8d8a6cfebf89419e6aed52c604e87" +checksum = "0444124f3ffd67e1b0b0c661a7f81a278a135eb54aaad4078e79fbc8be50c8a5" dependencies = [ "libc", "libffi-sys", @@ -804,9 +804,9 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "3.3.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0d828d367b4450ed08e7d510dc46636cd660055f50d67ac943bfe788767c29" +checksum = "3d722da8817ea580d0669da6babe2262d7b86a1af1103da24102b8bb9c101ce7" dependencies = [ "cc", ] diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 12123c260da0a..8bb4f1c160934 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -39,7 +39,7 @@ features = ['unprefixed_malloc_on_supported_platforms'] [target.'cfg(unix)'.dependencies] libc = "0.2" # native-lib dependencies -libffi = { version = "4.1.1", optional = true } +libffi = { version = "5.0.0", optional = true } libloading = { version = "0.8", optional = true } serde = { version = "1.0.219", features = ["derive"], optional = true } diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f877706520ea1..1160b1b97285d 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -36e4f5d1fe1d63953a5bf1758ce2b64172623e2e +28d0a4a205f9e511ad2f51ee79a4aa19a704a455 diff --git a/src/tools/miri/src/concurrency/genmc/global_allocations.rs b/src/tools/miri/src/concurrency/genmc/global_allocations.rs index 272f8d2484038..7f34c60dcdaff 100644 --- a/src/tools/miri/src/concurrency/genmc/global_allocations.rs +++ b/src/tools/miri/src/concurrency/genmc/global_allocations.rs @@ -6,7 +6,7 @@ use rand::SeedableRng; use rand::rngs::StdRng; use rustc_const_eval::interpret::{AllocId, AllocInfo, InterpResult, interp_ok}; use rustc_data_structures::fx::FxHashMap; -use tracing::debug; +use rustc_log::tracing::debug; use crate::alloc_addresses::AddressGenerator; diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 6721a38c683fb..6eb51e1b50cb9 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use std::time::Instant; use genmc_sys::EstimationResult; +use rustc_log::tracing; use rustc_middle::ty::TyCtxt; use super::GlobalState; diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs index 6ccbaf4f2482b..0703e0595b338 100644 --- a/src/tools/miri/src/concurrency/genmc/scheduling.rs +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -1,4 +1,5 @@ use genmc_sys::{ActionKind, ExecutionState}; +use rustc_data_structures::either::Either; use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::{self, Ty}; @@ -38,7 +39,7 @@ fn get_next_instruction_kind<'tcx>( let Some(frame) = thread_manager.active_thread_stack().last() else { return interp_ok(NonAtomic); }; - let either::Either::Left(loc) = frame.current_loc() else { + let Either::Left(loc) = frame.current_loc() else { // We are unwinding, so the next step is definitely not atomic. return interp_ok(NonAtomic); }; diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index d3486dcbb19c9..1c3de9035cf86 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -139,6 +139,7 @@ pub enum NonHaltingDiagnostic { NativeCallSharedMem { tracing: bool, }, + NativeCallFnPtr, WeakMemoryOutdatedLoad { ptr: Pointer, }, @@ -644,6 +645,11 @@ impl<'tcx> MiriMachine<'tcx> { Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning), NativeCallSharedMem { .. } => ("sharing memory with a native function".to_string(), DiagLevel::Warning), + NativeCallFnPtr => + ( + "sharing a function pointer with a native function".to_string(), + DiagLevel::Warning, + ), ExternTypeReborrow => ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning), GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } => @@ -682,6 +688,8 @@ impl<'tcx> MiriMachine<'tcx> { Int2Ptr { .. } => format!("integer-to-pointer cast"), NativeCallSharedMem { .. } => format!("sharing memory with a native function called via FFI"), + NativeCallFnPtr => + format!("sharing a function pointer with a native function called via FFI"), WeakMemoryOutdatedLoad { ptr } => format!("weak memory emulation: outdated value returned from load at {ptr}"), ExternTypeReborrow => @@ -779,6 +787,11 @@ impl<'tcx> MiriMachine<'tcx> { ), ] }, + NativeCallFnPtr => { + vec![note!( + "calling Rust functions from C is not supported and will, in the best case, crash the program" + )] + } ExternTypeReborrow => { assert!(self.borrow_tracker.as_ref().is_some_and(|b| { matches!( diff --git a/src/tools/miri/src/shims/native_lib/ffi.rs b/src/tools/miri/src/shims/native_lib/ffi.rs index 0badf22bb7651..196f43c6f6a6c 100644 --- a/src/tools/miri/src/shims/native_lib/ffi.rs +++ b/src/tools/miri/src/shims/native_lib/ffi.rs @@ -9,11 +9,9 @@ use libffi::middle::{Arg as ArgPtr, Cif, Type as FfiType}; /// /// The safety invariants of the foreign function being called must be upheld (if any). pub unsafe fn call(fun: CodePtr, args: &mut [OwnedArg]) -> R { - let arg_ptrs: Vec<_> = args.iter().map(|arg| arg.ptr()).collect(); let cif = Cif::new(args.iter_mut().map(|arg| arg.ty.take().unwrap()), R::reify().into_middle()); - // SAFETY: Caller upholds that the function is safe to call, and since we - // were passed a slice reference we know the `OwnedArg`s won't have been - // dropped by this point. + let arg_ptrs: Vec<_> = args.iter().map(|arg| ArgPtr::new(&*arg.bytes)).collect(); + // SAFETY: Caller upholds that the function is safe to call. unsafe { cif.call(fun, &arg_ptrs) } } @@ -31,16 +29,4 @@ impl OwnedArg { pub fn new(ty: FfiType, bytes: Box<[u8]>) -> Self { Self { ty: Some(ty), bytes } } - - /// Creates a libffi argument pointer pointing to this argument's bytes. - /// NB: Since `libffi::middle::Arg` ignores the lifetime of the reference - /// it's derived from, it is up to the caller to ensure the `OwnedArg` is - /// not dropped before unsafely calling `libffi::middle::Cif::call()`! - fn ptr(&self) -> ArgPtr { - // FIXME: Using `&self.bytes[0]` to reference the whole array is - // definitely unsound under SB, but we're waiting on - // https://github.com/libffi-rs/libffi-rs/commit/112a37b3b6ffb35bd75241fbcc580de40ba74a73 - // to land in a release so that we don't need to do this. - ArgPtr::new(&self.bytes[0]) - } } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index d111d8d32d6a8..0a0d1bcb0a85a 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -7,7 +7,7 @@ use libffi::low::CodePtr; use libffi::middle::Type as FfiType; use rustc_abi::{HasDataLayout, Size}; use rustc_data_structures::either; -use rustc_middle::ty::layout::HasTypingEnv; +use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_span::Symbol; use serde::{Deserialize, Serialize}; @@ -278,7 +278,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // This should go first so that we emit unsupported before doing a bunch // of extra work for types that aren't supported yet. - let ty = this.ty_to_ffitype(v.layout.ty)?; + let ty = this.ty_to_ffitype(v.layout)?; // Helper to print a warning when a pointer is shared with the native code. let expose = |prov: Provenance| -> InterpResult<'tcx> { @@ -387,34 +387,44 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let mut fields = vec![]; for field in &adt_def.non_enum_variant().fields { - fields.push(this.ty_to_ffitype(field.ty(*this.tcx, args))?); + let layout = this.layout_of(field.ty(*this.tcx, args))?; + fields.push(this.ty_to_ffitype(layout)?); } interp_ok(FfiType::structure(fields)) } /// Gets the matching libffi type for a given Ty. - fn ty_to_ffitype(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, FfiType> { - let this = self.eval_context_ref(); - interp_ok(match ty.kind() { - ty::Int(IntTy::I8) => FfiType::i8(), - ty::Int(IntTy::I16) => FfiType::i16(), - ty::Int(IntTy::I32) => FfiType::i32(), - ty::Int(IntTy::I64) => FfiType::i64(), - ty::Int(IntTy::Isize) => FfiType::isize(), - ty::Uint(UintTy::U8) => FfiType::u8(), - ty::Uint(UintTy::U16) => FfiType::u16(), - ty::Uint(UintTy::U32) => FfiType::u32(), - ty::Uint(UintTy::U64) => FfiType::u64(), - ty::Uint(UintTy::Usize) => FfiType::usize(), - ty::RawPtr(pointee_ty, _mut) => { - if !pointee_ty.is_sized(*this.tcx, this.typing_env()) { - throw_unsup_format!("passing a pointer to an unsized type over FFI: {}", ty); - } - FfiType::pointer() - } - ty::Adt(adt_def, args) => self.adt_to_ffitype(ty, *adt_def, args)?, - _ => throw_unsup_format!("unsupported argument type for native call: {}", ty), + fn ty_to_ffitype(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, FfiType> { + use rustc_abi::{AddressSpace, BackendRepr, Integer, Primitive}; + + // `BackendRepr::Scalar` is also a signal to pass this type as a scalar in the ABI. This + // matches what codegen does. This does mean that we support some types whose ABI is not + // stable, but that's fine -- we are anyway quite conservative in native-lib mode. + if let BackendRepr::Scalar(s) = layout.backend_repr { + // Simple sanity-check: this cannot be `repr(C)`. + assert!(!layout.ty.ty_adt_def().is_some_and(|adt| adt.repr().c())); + return interp_ok(match s.primitive() { + Primitive::Int(Integer::I8, /* signed */ true) => FfiType::i8(), + Primitive::Int(Integer::I16, /* signed */ true) => FfiType::i16(), + Primitive::Int(Integer::I32, /* signed */ true) => FfiType::i32(), + Primitive::Int(Integer::I64, /* signed */ true) => FfiType::i64(), + Primitive::Int(Integer::I8, /* signed */ false) => FfiType::u8(), + Primitive::Int(Integer::I16, /* signed */ false) => FfiType::u16(), + Primitive::Int(Integer::I32, /* signed */ false) => FfiType::u32(), + Primitive::Int(Integer::I64, /* signed */ false) => FfiType::u64(), + Primitive::Pointer(AddressSpace::ZERO) => FfiType::pointer(), + _ => + throw_unsup_format!( + "unsupported scalar argument type for native call: {}", + layout.ty + ), + }); + } + interp_ok(match layout.ty.kind() { + // Scalar types have already been handled above. + ty::Adt(adt_def, args) => self.adt_to_ffitype(layout.ty, *adt_def, args)?, + _ => throw_unsup_format!("unsupported argument type for native call: {}", layout.ty), }) } } @@ -455,6 +465,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // pointer was passed as argument). Uninitialised memory is left as-is, but any data // exposed this way is garbage anyway. this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| { + if matches!(info.kind, AllocKind::Function) { + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { + // Newly set, so first time we get here. + this.emit_diagnostic(NonHaltingDiagnostic::NativeCallFnPtr); + } + } // If there is no data behind this pointer, skip this. if !matches!(info.kind, AllocKind::LiveData) { return interp_ok(()); diff --git a/src/tools/miri/src/shims/native_lib/trace/child.rs b/src/tools/miri/src/shims/native_lib/trace/child.rs index 95b0617a0261b..795ad4a32076d 100644 --- a/src/tools/miri/src/shims/native_lib/trace/child.rs +++ b/src/tools/miri/src/shims/native_lib/trace/child.rs @@ -228,9 +228,10 @@ pub unsafe fn init_sv() -> Result<(), SvInitError> { match init { // The "Ok" case means that we couldn't ptrace. Ok(e) => return Err(e), - Err(p) => { + Err(_p) => { eprintln!( - "Supervisor process panicked!\n{p:?}\n\nTry running again without using the native-lib tracer." + "Supervisor process panicked!\n\" + Try running again without `-Zmiri-native-lib-enable-tracing`." ); std::process::exit(1); } diff --git a/src/tools/miri/src/shims/native_lib/trace/parent.rs b/src/tools/miri/src/shims/native_lib/trace/parent.rs index 3ae98259ab35b..335188b331838 100644 --- a/src/tools/miri/src/shims/native_lib/trace/parent.rs +++ b/src/tools/miri/src/shims/native_lib/trace/parent.rs @@ -530,7 +530,18 @@ fn handle_segfault( // Don't use wait_for_signal here since 1 instruction doesn't give room // for any uncertainty + we don't want it `cont()`ing randomly by accident // Also, don't let it continue with unprotected memory if something errors! - let _ = wait::waitid(wait::Id::Pid(pid), WAIT_FLAGS).map_err(|_| ExecEnd(None))?; + let stat = wait::waitid(wait::Id::Pid(pid), WAIT_FLAGS).map_err(|_| ExecEnd(None))?; + match stat { + wait::WaitStatus::Signaled(_, s, _) + | wait::WaitStatus::Stopped(_, s) + | wait::WaitStatus::PtraceEvent(_, s, _) => + assert!( + !matches!(s, signal::SIGSEGV), + "native code segfaulted when re-trying memory access\n\ + is the native code trying to call a Rust function?" + ), + _ => (), + } // Zero out again to be safe for a in (ch_stack..ch_stack.strict_add(CALLBACK_STACK_SIZE)).step_by(ARCH_WORD_SIZE) { diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index d04ef5eac541b..1f8e60484edaa 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -815,7 +815,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_cond_timedwait" => { let [cond, mutex, abstime] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - this.pthread_cond_timedwait(cond, mutex, abstime, dest, /* macos_relative_np */ false)?; + this.pthread_cond_timedwait( + cond, mutex, abstime, dest, /* macos_relative_np */ false, + )?; } "pthread_cond_destroy" => { let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 0754eb45a2d29..1b273593de63e 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -310,7 +310,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_cond_timedwait_relative_np" => { let [cond, mutex, reltime] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - this.pthread_cond_timedwait(cond, mutex, reltime, dest, /* macos_relative_np */ true)?; + this.pthread_cond_timedwait( + cond, mutex, reltime, dest, /* macos_relative_np */ true, + )?; } _ => return interp_ok(EmulateItemResult::NotSupported), diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr index 04a3025baefa9..200eba8050bb5 100644 --- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr +++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr @@ -16,3 +16,18 @@ note: inside `main` LL | test_access_pointer(); | ^^^^^^^^^^^^^^^^^^^^^ +warning: sharing a function pointer with a native function called via FFI + --> tests/native-lib/pass/ptr_read_access.rs:LL:CC + | +LL | pass_fn_ptr(Some(nop)); // this one is not + | ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function + | + = help: calling Rust functions from C is not supported and will, in the best case, crash the program + = note: BACKTRACE: + = note: inside `pass_fn_ptr` at tests/native-lib/pass/ptr_read_access.rs:LL:CC +note: inside `main` + --> tests/native-lib/pass/ptr_read_access.rs:LL:CC + | +LL | pass_fn_ptr(); + | ^^^^^^^^^^^^^ + diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs index bab73f7cf1782..36eff04a03c05 100644 --- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs +++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs @@ -3,11 +3,14 @@ //@[trace] compile-flags: -Zmiri-native-lib-enable-tracing //@compile-flags: -Zmiri-permissive-provenance +use std::ptr::NonNull; + fn main() { test_access_pointer(); test_access_simple(); test_access_nested(); test_access_static(); + pass_fn_ptr(); } /// Test function that dereferences an int pointer and prints its contents from C. @@ -30,11 +33,15 @@ fn test_access_simple() { extern "C" { fn access_simple(s_ptr: *const Simple) -> i32; + fn access_simple2(s_ptr: NonNull) -> i32; + fn access_simple3(s_ptr: Option>) -> i32; } let simple = Simple { field: -42 }; assert_eq!(unsafe { access_simple(&simple) }, -42); + assert_eq!(unsafe { access_simple2(NonNull::from(&simple)) }, -42); + assert_eq!(unsafe { access_simple3(Some(NonNull::from(&simple))) }, -42); } /// Test function that dereferences nested struct pointers and accesses fields. @@ -75,3 +82,16 @@ fn test_access_static() { assert_eq!(unsafe { access_static(&STATIC) }, 9001); } + +fn pass_fn_ptr() { + extern "C" { + fn pass_fn_ptr(s: Option); + } + + extern "C" fn nop() {} + + unsafe { + pass_fn_ptr(None); // this one is fine + pass_fn_ptr(Some(nop)); // this one is not + } +} diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr index c2a4508b7fcc7..5c0e954deb9b6 100644 --- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr +++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr @@ -17,3 +17,18 @@ note: inside `main` LL | test_access_pointer(); | ^^^^^^^^^^^^^^^^^^^^^ +warning: sharing a function pointer with a native function called via FFI + --> tests/native-lib/pass/ptr_read_access.rs:LL:CC + | +LL | pass_fn_ptr(Some(nop)); // this one is not + | ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function + | + = help: calling Rust functions from C is not supported and will, in the best case, crash the program + = note: BACKTRACE: + = note: inside `pass_fn_ptr` at tests/native-lib/pass/ptr_read_access.rs:LL:CC +note: inside `main` + --> tests/native-lib/pass/ptr_read_access.rs:LL:CC + | +LL | pass_fn_ptr(); + | ^^^^^^^^^^^^^ + diff --git a/src/tools/miri/tests/native-lib/ptr_read_access.c b/src/tools/miri/tests/native-lib/ptr_read_access.c index 021eb6adca4f6..5f071ca3d424d 100644 --- a/src/tools/miri/tests/native-lib/ptr_read_access.c +++ b/src/tools/miri/tests/native-lib/ptr_read_access.c @@ -19,6 +19,13 @@ typedef struct Simple { EXPORT int32_t access_simple(const Simple *s_ptr) { return s_ptr->field; } +// Some copies so Rust can import them at different types. +EXPORT int32_t access_simple2(const Simple *s_ptr) { + return s_ptr->field; +} +EXPORT int32_t access_simple3(const Simple *s_ptr) { + return s_ptr->field; +} /* Test: test_access_nested */ @@ -55,3 +62,9 @@ EXPORT int32_t access_static(const Static *s_ptr) { EXPORT uintptr_t do_one_deref(const int32_t ***ptr) { return (uintptr_t)*ptr; } + +/* Test: pass_fn_ptr */ + +EXPORT void pass_fn_ptr(void f(void)) { + (void)f; // suppress unused warning +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 531d637d1f24d..7105b076d652d 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -1,6 +1,10 @@ #![feature(pointer_is_aligned_to)] use std::{mem, ptr, slice}; +#[path = "../../utils/mod.rs"] +mod utils; +use utils::check_nondet; + fn test_memcpy() { unsafe { let src = [1i8, 2, 3]; @@ -120,13 +124,12 @@ fn test_memset() { } fn test_malloc() { - // Test that small allocations sometimes *are* not very aligned. - let saw_unaligned = (0..64).any(|_| unsafe { + // Test that small allocations sometimes are *not* very aligned (and sometimes they are). + check_nondet(|| unsafe { let p = libc::malloc(3); libc::free(p); - (p as usize) % 4 != 0 // find any that this is *not* 4-aligned + (p as usize) % 4 == 0 }); - assert!(saw_unaligned); unsafe { let p1 = libc::malloc(20); diff --git a/src/tools/miri/tests/pass/dyn-traits.rs b/src/tools/miri/tests/pass/dyn-traits.rs index f6220120968f7..47f913adae053 100644 --- a/src/tools/miri/tests/pass/dyn-traits.rs +++ b/src/tools/miri/tests/pass/dyn-traits.rs @@ -1,3 +1,7 @@ +#[path = "../utils/mod.rs"] +mod utils; +use utils::check_nondet; + fn ref_box_dyn() { struct Struct(i32); @@ -147,7 +151,7 @@ fn vtable_ptr_eq() { // We don't always get the same vtable when casting this to a wide pointer. let x = &2; let x_wide = x as &dyn fmt::Display; - assert!((0..256).any(|_| !ptr::eq(x as &dyn fmt::Display, x_wide))); + check_nondet(|| ptr::eq(x as &dyn fmt::Display, x_wide)); } fn main() { diff --git a/src/tools/miri/tests/pass/function_pointers.rs b/src/tools/miri/tests/pass/function_pointers.rs index 7145be334a13e..96f6f2568e98e 100644 --- a/src/tools/miri/tests/pass/function_pointers.rs +++ b/src/tools/miri/tests/pass/function_pointers.rs @@ -2,6 +2,10 @@ use std::mem; +#[path = "../utils/mod.rs"] +mod utils; +use utils::check_nondet; + trait Answer { fn answer() -> Self; } @@ -30,6 +34,7 @@ fn i() -> i32 { 73 } +#[inline(never)] // optimizations mask the non-determinism we test for below fn return_fn_ptr(f: fn() -> i32) -> fn() -> i32 { f } @@ -85,7 +90,7 @@ fn main() { assert!(return_fn_ptr(i) as unsafe fn() -> i32 == i as fn() -> i32 as unsafe fn() -> i32); // Miri gives different addresses to different reifications of a generic function. // at least if we try often enough. - assert!((0..256).any(|_| return_fn_ptr(f) != f)); + check_nondet(|| return_fn_ptr(f) == f); // However, if we only turn `f` into a function pointer and use that pointer, // it is equal to itself. let f2 = f as fn() -> i32; diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 022dcc5dcbafa..43fbf6b085f75 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -13,6 +13,7 @@ use std::path::Path; #[path = "../../utils/mod.rs"] mod utils; +use utils::check_nondet; fn main() { test_path_conversion(); @@ -81,25 +82,25 @@ fn test_file() { } fn test_file_partial_reads_writes() { - let path = utils::prepare_with_content("miri_test_fs_file.txt", b"abcdefg"); + let path1 = utils::prepare_with_content("miri_test_fs_file1.txt", b"abcdefg"); + let path2 = utils::prepare_with_content("miri_test_fs_file2.txt", b"abcdefg"); // Ensure we sometimes do incomplete writes. - let got_short_write = (0..16).any(|_| { - let _ = remove_file(&path); // FIXME(win, issue #4483): errors if the file already exists - let mut file = File::create(&path).unwrap(); - file.write(&[0; 4]).unwrap() != 4 + check_nondet(|| { + let _ = remove_file(&path1); // FIXME(win, issue #4483): errors if the file already exists + let mut file = File::create(&path1).unwrap(); + file.write(&[0; 4]).unwrap() == 4 }); - assert!(got_short_write); // Ensure we sometimes do incomplete reads. - let got_short_read = (0..16).any(|_| { - let mut file = File::open(&path).unwrap(); + check_nondet(|| { + let mut file = File::open(&path2).unwrap(); let mut buf = [0; 4]; - file.read(&mut buf).unwrap() != 4 + file.read(&mut buf).unwrap() == 4 }); - assert!(got_short_read); // Clean up - remove_file(&path).unwrap(); + remove_file(&path1).unwrap(); + remove_file(&path2).unwrap(); } fn test_file_clone() { diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index 37f9996216343..f855f115e43dc 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -55,7 +55,10 @@ pub fn check_all_outcomes( /// Check that the operation is non-deterministic #[track_caller] pub fn check_nondet(f: impl Fn() -> T) { - let rounds = 50; + // We test some rather unlikely events with this, such as two global allocations getting the + // same "salt" (1/32 chance). So give this *many* shots before we consider the test to have + // failed. + let rounds = 500; let first = f(); for _ in 1..rounds { if f() != first { diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 3f40bef821c9d..3164dcc77be1d 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -569,6 +569,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ "libc", "memchr", "miniz_oxide", + "moto-rt", "object", "r-efi", "r-efi-alloc", diff --git a/tests/ui/binop/let-chain-type-issue-147665.rs b/tests/ui/binop/let-chain-type-issue-147665.rs new file mode 100644 index 0000000000000..4a6b40c6f9877 --- /dev/null +++ b/tests/ui/binop/let-chain-type-issue-147665.rs @@ -0,0 +1,7 @@ +// Shouldn't highlight `let x = 1` as having type bool. +//@ edition:2024 + +fn main() { + if let x = 1 && 2 {} + //~^ ERROR mismatched types +} diff --git a/tests/ui/binop/let-chain-type-issue-147665.stderr b/tests/ui/binop/let-chain-type-issue-147665.stderr new file mode 100644 index 0000000000000..b2b82228eaebf --- /dev/null +++ b/tests/ui/binop/let-chain-type-issue-147665.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/let-chain-type-issue-147665.rs:5:21 + | +LL | if let x = 1 && 2 {} + | ^ expected `bool`, found integer + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/zst_no_llvm_alloc.rs b/tests/ui/consts/zst_no_llvm_alloc.rs index 1e92e3bbd4c1b..ec5600ade16f1 100644 --- a/tests/ui/consts/zst_no_llvm_alloc.rs +++ b/tests/ui/consts/zst_no_llvm_alloc.rs @@ -1,10 +1,21 @@ //@ run-pass +// We need some non-1 alignment to test we use the alignment of the type in the compiler. #[repr(align(4))] struct Foo; static FOO: Foo = Foo; +// This tests for regression of https://github.com/rust-lang/rust/issues/147516 +// +// The compiler will codegen `&Zst` without creating a real allocation, just a properly aligned +// `usize` (i.e., ptr::dangling). However, code can add an arbitrary offset from that base +// allocation. We confirm here that we correctly codegen that offset combined with the necessary +// alignment of the base &() as a 1-ZST and &Foo as a 4-ZST. +const A: *const () = (&() as *const ()).wrapping_byte_add(2); +const B: *const () = (&Foo as *const _ as *const ()).wrapping_byte_add(usize::MAX); +const C: *const () = (&Foo as *const _ as *const ()).wrapping_byte_add(2); + fn main() { // There's no stable guarantee that these are true. // However, we want them to be true so that our LLVM IR and runtime are a bit faster: @@ -15,6 +26,13 @@ fn main() { let x: &'static Foo = &Foo; assert_eq!(x as *const Foo as usize, 4); + // * A 1-aligned ZST (1-ZST) is placed at 0x1. Then offsetting that by 2 results in 3. + // * Foo is a 4-aligned ZST, so is placed at 0x4. +2 = 6 + // * Foo is a 4-aligned ZST, so is placed at 0x4. +usize::MAX = -1 (same bit pattern) = 3 + assert_eq!(A.addr(), 3); + assert_eq!(B.addr(), 3); + assert_eq!(C.addr(), 6); + // The exact addresses returned by these library functions are not necessarily stable guarantees // but for now we assert that we're still matching. #[allow(dangling_pointers_from_temporaries)] diff --git a/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs b/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs new file mode 100644 index 0000000000000..d234acb8268a1 --- /dev/null +++ b/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs @@ -0,0 +1,17 @@ +//@ run-pass +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::ensures; + +#[ensures({*x = 0; |_ret| true})] +fn buggy_add(x: &mut u32, y: u32) { + *x = *x + y; +} + +fn main() { + let mut x = 10; + buggy_add(&mut x, 100); + assert_eq!(x, 110); +} diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr b/tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr similarity index 77% rename from tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr rename to tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr index a60ce1602659b..dd9ebe9bd3556 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr +++ b/tests/ui/contracts/contracts-disabled-side-effect-ensures.stderr @@ -1,7 +1,7 @@ warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 + --> $DIR/contracts-disabled-side-effect-ensures.rs:2:12 | -LL | #![feature(contracts)] // to access core::contracts +LL | #![feature(contracts)] | ^^^^^^^^^ | = note: see issue #128044 for more information diff --git a/tests/ui/contracts/cross-crate-checks/auxiliary/id.rs b/tests/ui/contracts/cross-crate-checks/auxiliary/id.rs new file mode 100644 index 0000000000000..c6210375a8482 --- /dev/null +++ b/tests/ui/contracts/cross-crate-checks/auxiliary/id.rs @@ -0,0 +1,16 @@ +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail] compile-flags: -Zcontract-checks=yes +//@ [chk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_fail] compile-flags: -Zcontract-checks=yes + +#![crate_type = "lib"] +#![feature(contracts)] + +extern crate core; +use core::contracts::requires; + +/// Example function with a spec to be called across a crate boundary. +#[requires(x > 0)] +pub fn id_if_positive(x: u32) -> u32 { + x +} diff --git a/tests/ui/contracts/cross-crate-checks/cross-crate.rs b/tests/ui/contracts/cross-crate-checks/cross-crate.rs new file mode 100644 index 0000000000000..43f81fe0d807a --- /dev/null +++ b/tests/ui/contracts/cross-crate-checks/cross-crate.rs @@ -0,0 +1,26 @@ +//@ aux-build:id.rs +//@ revisions: unchk_pass unchk_fail chk_pass chk_fail +// +// The dependency crate `id` can be compiled with runtime contract checking +// enabled independently of whether this crate is compiled with contract checks +// or not. +// +// chk/unchk indicates whether this crate is compiled with contracts or not +// and pass/fail indicates whether the `id` crate is compiled with contract checks. +// +//@ [unchk_pass] run-pass +//@ [unchk_fail] run-crash +//@ [chk_pass] run-pass +//@ [chk_fail] run-crash +// +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail] compile-flags: -Zcontract-checks=yes + +extern crate id; + +fn main() { + id::id_if_positive(0); +} diff --git a/tests/ui/contracts/empty-ensures.rs b/tests/ui/contracts/empty-ensures.rs new file mode 100644 index 0000000000000..d897f27bf6c94 --- /dev/null +++ b/tests/ui/contracts/empty-ensures.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::ensures; + +#[ensures()] +//~^ ERROR expected a `Fn(&_)` closure, found `()` [E0277] +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/empty-ensures.stderr b/tests/ui/contracts/empty-ensures.stderr new file mode 100644 index 0000000000000..407a253bd8565 --- /dev/null +++ b/tests/ui/contracts/empty-ensures.stderr @@ -0,0 +1,25 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/empty-ensures.rs:2:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: expected a `Fn(&_)` closure, found `()` + --> $DIR/empty-ensures.rs:8:1 + | +LL | #[ensures()] + | ^^^^^^^^^^^^ + | | + | expected an `Fn(&_)` closure, found `()` + | required by a bound introduced by this call + | + = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/contracts/empty-requires.rs b/tests/ui/contracts/empty-requires.rs new file mode 100644 index 0000000000000..e3c72dcd66a17 --- /dev/null +++ b/tests/ui/contracts/empty-requires.rs @@ -0,0 +1,18 @@ +//@ dont-require-annotations: NOTE +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::requires; + +#[requires()] +//~^ ERROR mismatched types [E0308] +//~| NOTE expected `bool`, found `()` +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/empty-requires.stderr b/tests/ui/contracts/empty-requires.stderr new file mode 100644 index 0000000000000..b48e547b8cda7 --- /dev/null +++ b/tests/ui/contracts/empty-requires.stderr @@ -0,0 +1,18 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/empty-requires.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/empty-requires.rs:9:1 + | +LL | #[requires()] + | ^^^^^^^^^^^^^ expected `bool`, found `()` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 6e613b53fc9b1..9dd5167c9e117 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -1,36 +1,23 @@ -//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires +//@ revisions: default chk_fail_ensures chk_fail_requires // //@ [default] run-pass -//@ [unchk_pass] run-pass -//@ [chk_pass] run-pass //@ [chk_fail_requires] run-crash //@ [chk_fail_ensures] run-crash -// -//@ [unchk_pass] compile-flags: -Zcontract-checks=no -//@ [chk_pass] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes -#![feature(cfg_contract_checks, contracts_internals, core_intrinsics)] +#![feature(contracts_internals, core_intrinsics)] fn main() { - #[cfg(any(default, unchk_pass))] // default: disabled - assert_eq!(core::intrinsics::contract_checks(), false); - - #[cfg(chk_pass)] // explicitly enabled - assert_eq!(core::intrinsics::contract_checks(), true); - // always pass core::intrinsics::contract_check_requires(|| true); - // fail if enabled - #[cfg(any(default, unchk_pass, chk_fail_requires))] + // always fail + #[cfg(chk_fail_requires)] core::intrinsics::contract_check_requires(|| false); let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old }; // Always pass - core::intrinsics::contract_check_ensures(doubles_to_two, 1); + core::intrinsics::contract_check_ensures(Some(doubles_to_two), 1); - // Fail if enabled - #[cfg(any(default, unchk_pass, chk_fail_ensures))] - core::intrinsics::contract_check_ensures(doubles_to_two, 2); + // always fail + #[cfg(chk_fail_ensures)] + core::intrinsics::contract_check_ensures(Some(doubles_to_two), 2); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr index a60ce1602659b..acce6b1fbc729 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr @@ -1,5 +1,5 @@ warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 + --> $DIR/contract-lang-items.rs:8:12 | LL | #![feature(contracts)] // to access core::contracts | ^^^^^^^^^ diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr index a60ce1602659b..acce6b1fbc729 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr @@ -1,5 +1,5 @@ warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 + --> $DIR/contract-lang-items.rs:8:12 | LL | #![feature(contracts)] // to access core::contracts | ^^^^^^^^^ diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index ac72d233bf6b4..ad88ebfe22e3b 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -1,25 +1,16 @@ -//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post +//@ revisions: unchk_pass chk_pass chk_fail_post // //@ [unchk_pass] run-pass -//@ [unchk_fail_post] run-pass //@ [chk_pass] run-pass // //@ [chk_fail_post] run-crash -// -//@ [unchk_pass] compile-flags: -Zcontract-checks=no -//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no -// -//@ [chk_pass] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes #![feature(contracts)] // to access core::contracts //~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![feature(contracts_internals)] // to access check_requires lang item #![feature(core_intrinsics)] fn foo(x: Baz) -> i32 { - let injected_checker = { - core::contracts::build_check_ensures(|ret| *ret > 100) - }; + let injected_checker = Some(core::contracts::build_check_ensures(|ret| *ret > 100)); let ret = x.baz + 50; core::intrinsics::contract_check_ensures(injected_checker, ret) @@ -29,11 +20,11 @@ struct Baz { baz: i32 } const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; -#[cfg(any(unchk_fail_post, chk_fail_post))] +#[cfg(chk_fail_post)] const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; fn main() { assert_eq!(foo(BAZ_PASS_PRE_POST), 150); - #[cfg(any(unchk_fail_post, chk_fail_post))] + #[cfg(chk_fail_post)] foo(BAZ_FAIL_POST); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr index a60ce1602659b..acce6b1fbc729 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr @@ -1,5 +1,5 @@ warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/contract-lang-items.rs:15:12 + --> $DIR/contract-lang-items.rs:8:12 | LL | #![feature(contracts)] // to access core::contracts | ^^^^^^^^^ diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index 6e5a7a3f95005..48bd376594f22 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -2,11 +2,9 @@ fn main() { // intrinsics are guarded by contracts_internals feature gate. - core::intrinsics::contract_checks(); - //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_requires(|| true); //~^ ERROR use of unstable library feature `contracts_internals` - core::intrinsics::contract_check_ensures( |_|true, &1); + core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1); //~^ ERROR use of unstable library feature `contracts_internals` core::contracts::build_check_ensures(|_: &()| true); diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 1e39bd62e245b..c1318fc15a78d 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -1,5 +1,5 @@ error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:16:28 + --> $DIR/internal-feature-gating.rs:14:28 | LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } | ^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:18:28 + --> $DIR/internal-feature-gating.rs:16:28 | LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } | ^^^^^^^^^^^^^^^^ @@ -21,16 +21,6 @@ LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:5:5 | -LL | core::intrinsics::contract_checks(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #128044 for more information - = help: add `#![feature(contracts_internals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `contracts_internals` - --> $DIR/internal-feature-gating.rs:7:5 - | LL | core::intrinsics::contract_check_requires(|| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -39,9 +29,9 @@ LL | core::intrinsics::contract_check_requires(|| true); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `contracts_internals` - --> $DIR/internal-feature-gating.rs:9:5 + --> $DIR/internal-feature-gating.rs:7:5 | -LL | core::intrinsics::contract_check_ensures( |_|true, &1); +LL | core::intrinsics::contract_check_ensures(Some(|_: &&u32| true), &1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #128044 for more information @@ -49,7 +39,7 @@ LL | core::intrinsics::contract_check_ensures( |_|true, &1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `contracts_internals` - --> $DIR/internal-feature-gating.rs:12:5 + --> $DIR/internal-feature-gating.rs:10:5 | LL | core::contracts::build_check_ensures(|_: &()| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,6 +48,6 @@ LL | core::contracts::build_check_ensures(|_: &()| true); = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0658`.