Auto merge of #130755 - workingjubilee:rollup-zpja9b3, r=workingjubilee
Rollup of 7 pull requests Successful merges: - #129201 (std: implement the `random` feature (alternative version)) - #130536 (bootstrap: Set the dylib path when building books with rustdoc) - #130551 (Fix `break_last_token`.) - #130657 (Remove x86_64-fuchsia and aarch64-fuchsia target aliases) - #130721 (Add more test cases for block-no-opening-brace) - #130736 (Add rustfmt 2024 reformatting to git blame ignore) - #130746 (readd `@tgross35` and `@joboet` to the review rotation) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
9d6039ccae
@ -27,3 +27,5 @@ ec2cc761bc7067712ecc7734502f703fe3b024c8
|
||||
84ac80f1921afc243d71fd0caaa4f2838c294102
|
||||
# bless mir-opt tests to add `copy`
|
||||
99cb0c6bc399fb94a0ddde7e9b38e9c00d523bad
|
||||
# reformat with rustfmt edition 2024
|
||||
c682aa162b0d41e21cc6748f4fecfe01efb69d1f
|
||||
|
@ -385,35 +385,41 @@ pub fn lit(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> TokenKind {
|
||||
Literal(Lit::new(kind, symbol, suffix))
|
||||
}
|
||||
|
||||
/// An approximation to proc-macro-style single-character operators used by rustc parser.
|
||||
/// If the operator token can be broken into two tokens, the first of which is single-character,
|
||||
/// then this function performs that operation, otherwise it returns `None`.
|
||||
pub fn break_two_token_op(&self) -> Option<(TokenKind, TokenKind)> {
|
||||
Some(match *self {
|
||||
Le => (Lt, Eq),
|
||||
EqEq => (Eq, Eq),
|
||||
Ne => (Not, Eq),
|
||||
Ge => (Gt, Eq),
|
||||
AndAnd => (BinOp(And), BinOp(And)),
|
||||
OrOr => (BinOp(Or), BinOp(Or)),
|
||||
BinOp(Shl) => (Lt, Lt),
|
||||
BinOp(Shr) => (Gt, Gt),
|
||||
BinOpEq(Plus) => (BinOp(Plus), Eq),
|
||||
BinOpEq(Minus) => (BinOp(Minus), Eq),
|
||||
BinOpEq(Star) => (BinOp(Star), Eq),
|
||||
BinOpEq(Slash) => (BinOp(Slash), Eq),
|
||||
BinOpEq(Percent) => (BinOp(Percent), Eq),
|
||||
BinOpEq(Caret) => (BinOp(Caret), Eq),
|
||||
BinOpEq(And) => (BinOp(And), Eq),
|
||||
BinOpEq(Or) => (BinOp(Or), Eq),
|
||||
BinOpEq(Shl) => (Lt, Le),
|
||||
BinOpEq(Shr) => (Gt, Ge),
|
||||
DotDot => (Dot, Dot),
|
||||
DotDotDot => (Dot, DotDot),
|
||||
PathSep => (Colon, Colon),
|
||||
RArrow => (BinOp(Minus), Gt),
|
||||
LArrow => (Lt, BinOp(Minus)),
|
||||
FatArrow => (Eq, Gt),
|
||||
/// An approximation to proc-macro-style single-character operators used by
|
||||
/// rustc parser. If the operator token can be broken into two tokens, the
|
||||
/// first of which has `n` (1 or 2) chars, then this function performs that
|
||||
/// operation, otherwise it returns `None`.
|
||||
pub fn break_two_token_op(&self, n: u32) -> Option<(TokenKind, TokenKind)> {
|
||||
assert!(n == 1 || n == 2);
|
||||
Some(match (self, n) {
|
||||
(Le, 1) => (Lt, Eq),
|
||||
(EqEq, 1) => (Eq, Eq),
|
||||
(Ne, 1) => (Not, Eq),
|
||||
(Ge, 1) => (Gt, Eq),
|
||||
(AndAnd, 1) => (BinOp(And), BinOp(And)),
|
||||
(OrOr, 1) => (BinOp(Or), BinOp(Or)),
|
||||
(BinOp(Shl), 1) => (Lt, Lt),
|
||||
(BinOp(Shr), 1) => (Gt, Gt),
|
||||
(BinOpEq(Plus), 1) => (BinOp(Plus), Eq),
|
||||
(BinOpEq(Minus), 1) => (BinOp(Minus), Eq),
|
||||
(BinOpEq(Star), 1) => (BinOp(Star), Eq),
|
||||
(BinOpEq(Slash), 1) => (BinOp(Slash), Eq),
|
||||
(BinOpEq(Percent), 1) => (BinOp(Percent), Eq),
|
||||
(BinOpEq(Caret), 1) => (BinOp(Caret), Eq),
|
||||
(BinOpEq(And), 1) => (BinOp(And), Eq),
|
||||
(BinOpEq(Or), 1) => (BinOp(Or), Eq),
|
||||
(BinOpEq(Shl), 1) => (Lt, Le), // `<` + `<=`
|
||||
(BinOpEq(Shl), 2) => (BinOp(Shl), Eq), // `<<` + `=`
|
||||
(BinOpEq(Shr), 1) => (Gt, Ge), // `>` + `>=`
|
||||
(BinOpEq(Shr), 2) => (BinOp(Shr), Eq), // `>>` + `=`
|
||||
(DotDot, 1) => (Dot, Dot),
|
||||
(DotDotDot, 1) => (Dot, DotDot), // `.` + `..`
|
||||
(DotDotDot, 2) => (DotDot, Dot), // `..` + `.`
|
||||
(DotDotEq, 2) => (DotDot, Eq),
|
||||
(PathSep, 1) => (Colon, Colon),
|
||||
(RArrow, 1) => (BinOp(Minus), Gt),
|
||||
(LArrow, 1) => (Lt, BinOp(Minus)),
|
||||
(FatArrow, 1) => (Eq, Gt),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ struct LazyAttrTokenStreamImpl {
|
||||
start_token: (Token, Spacing),
|
||||
cursor_snapshot: TokenCursor,
|
||||
num_calls: u32,
|
||||
break_last_token: bool,
|
||||
break_last_token: u32,
|
||||
node_replacements: Box<[NodeReplacement]>,
|
||||
}
|
||||
|
||||
@ -339,17 +339,20 @@ pub(super) fn collect_tokens<R: HasAttrs + HasTokens>(
|
||||
let parser_replacements_end = self.capture_state.parser_replacements.len();
|
||||
|
||||
assert!(
|
||||
!(self.break_last_token && matches!(capture_trailing, Trailing::Yes)),
|
||||
"Cannot set break_last_token and have trailing token"
|
||||
!(self.break_last_token > 0 && matches!(capture_trailing, Trailing::Yes)),
|
||||
"Cannot have break_last_token > 0 and have trailing token"
|
||||
);
|
||||
assert!(self.break_last_token <= 2, "cannot break token more than twice");
|
||||
|
||||
let end_pos = self.num_bump_calls
|
||||
+ capture_trailing as u32
|
||||
// If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), then
|
||||
// extend the range of captured tokens to include it, since the parser was not actually
|
||||
// bumped past it. When the `LazyAttrTokenStream` gets converted into an
|
||||
// `AttrTokenStream`, we will create the proper token.
|
||||
+ self.break_last_token as u32;
|
||||
// If we "broke" the last token (e.g. breaking a `>>` token once into `>` + `>`, or
|
||||
// breaking a `>>=` token twice into `>` + `>` + `=`), then extend the range of
|
||||
// captured tokens to include it, because the parser was not actually bumped past it.
|
||||
// (Even if we broke twice, it was still just one token originally, hence the `1`.)
|
||||
// When the `LazyAttrTokenStream` gets converted into an `AttrTokenStream`, we will
|
||||
// rebreak that final token once or twice.
|
||||
+ if self.break_last_token == 0 { 0 } else { 1 };
|
||||
|
||||
let num_calls = end_pos - collect_pos.start_pos;
|
||||
|
||||
@ -425,7 +428,7 @@ pub(super) fn collect_tokens<R: HasAttrs + HasTokens>(
|
||||
// for the `#[cfg]` and/or `#[cfg_attr]` attrs. This allows us to run
|
||||
// eager cfg-expansion on the captured token stream.
|
||||
if definite_capture_mode {
|
||||
assert!(!self.break_last_token, "Should not have unglued last token with cfg attr");
|
||||
assert!(self.break_last_token == 0, "Should not have unglued last token with cfg attr");
|
||||
|
||||
// What is the status here when parsing the example code at the top of this method?
|
||||
//
|
||||
@ -471,7 +474,7 @@ pub(super) fn collect_tokens<R: HasAttrs + HasTokens>(
|
||||
/// close delims.
|
||||
fn make_attr_token_stream(
|
||||
iter: impl Iterator<Item = FlatToken>,
|
||||
break_last_token: bool,
|
||||
break_last_token: u32,
|
||||
) -> AttrTokenStream {
|
||||
#[derive(Debug)]
|
||||
struct FrameData {
|
||||
@ -513,18 +516,17 @@ struct FrameData {
|
||||
}
|
||||
}
|
||||
|
||||
if break_last_token {
|
||||
if break_last_token > 0 {
|
||||
let last_token = stack_top.inner.pop().unwrap();
|
||||
if let AttrTokenTree::Token(last_token, spacing) = last_token {
|
||||
let unglued_first = last_token.kind.break_two_token_op().unwrap().0;
|
||||
let (unglued, _) = last_token.kind.break_two_token_op(break_last_token).unwrap();
|
||||
|
||||
// An 'unglued' token is always two ASCII characters
|
||||
// Tokens are always ASCII chars, so we can use byte arithmetic here.
|
||||
let mut first_span = last_token.span.shrink_to_lo();
|
||||
first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1));
|
||||
first_span =
|
||||
first_span.with_hi(first_span.lo() + rustc_span::BytePos(break_last_token));
|
||||
|
||||
stack_top
|
||||
.inner
|
||||
.push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing));
|
||||
stack_top.inner.push(AttrTokenTree::Token(Token::new(unglued, first_span), spacing));
|
||||
} else {
|
||||
panic!("Unexpected last token {last_token:?}")
|
||||
}
|
||||
|
@ -146,21 +146,25 @@ pub struct Parser<'a> {
|
||||
token_cursor: TokenCursor,
|
||||
// The number of calls to `bump`, i.e. the position in the token stream.
|
||||
num_bump_calls: u32,
|
||||
// During parsing we may sometimes need to 'unglue' a glued token into two
|
||||
// component tokens (e.g. '>>' into '>' and '>), so the parser can consume
|
||||
// them one at a time. This process bypasses the normal capturing mechanism
|
||||
// (e.g. `num_bump_calls` will not be incremented), since the 'unglued'
|
||||
// tokens due not exist in the original `TokenStream`.
|
||||
// During parsing we may sometimes need to "unglue" a glued token into two
|
||||
// or three component tokens (e.g. `>>` into `>` and `>`, or `>>=` into `>`
|
||||
// and `>` and `=`), so the parser can consume them one at a time. This
|
||||
// process bypasses the normal capturing mechanism (e.g. `num_bump_calls`
|
||||
// will not be incremented), since the "unglued" tokens due not exist in
|
||||
// the original `TokenStream`.
|
||||
//
|
||||
// If we end up consuming both unglued tokens, this is not an issue. We'll
|
||||
// end up capturing the single 'glued' token.
|
||||
// If we end up consuming all the component tokens, this is not an issue,
|
||||
// because we'll end up capturing the single "glued" token.
|
||||
//
|
||||
// However, sometimes we may want to capture just the first 'unglued'
|
||||
// However, sometimes we may want to capture not all of the original
|
||||
// token. For example, capturing the `Vec<u8>` in `Option<Vec<u8>>`
|
||||
// requires us to unglue the trailing `>>` token. The `break_last_token`
|
||||
// field is used to track this token. It gets appended to the captured
|
||||
// field is used to track these tokens. They get appended to the captured
|
||||
// stream when we evaluate a `LazyAttrTokenStream`.
|
||||
break_last_token: bool,
|
||||
//
|
||||
// This value is always 0, 1, or 2. It can only reach 2 when splitting
|
||||
// `>>=` or `<<=`.
|
||||
break_last_token: u32,
|
||||
/// This field is used to keep track of how many left angle brackets we have seen. This is
|
||||
/// required in order to detect extra leading left angle brackets (`<` characters) and error
|
||||
/// appropriately.
|
||||
@ -453,7 +457,7 @@ pub fn new(
|
||||
expected_tokens: Vec::new(),
|
||||
token_cursor: TokenCursor { tree_cursor: stream.into_trees(), stack: Vec::new() },
|
||||
num_bump_calls: 0,
|
||||
break_last_token: false,
|
||||
break_last_token: 0,
|
||||
unmatched_angle_bracket_count: 0,
|
||||
angle_bracket_nesting: 0,
|
||||
last_unexpected_token_span: None,
|
||||
@ -773,7 +777,7 @@ fn break_and_eat(&mut self, expected: TokenKind) -> bool {
|
||||
self.bump();
|
||||
return true;
|
||||
}
|
||||
match self.token.kind.break_two_token_op() {
|
||||
match self.token.kind.break_two_token_op(1) {
|
||||
Some((first, second)) if first == expected => {
|
||||
let first_span = self.psess.source_map().start_point(self.token.span);
|
||||
let second_span = self.token.span.with_lo(first_span.hi());
|
||||
@ -783,8 +787,8 @@ fn break_and_eat(&mut self, expected: TokenKind) -> bool {
|
||||
//
|
||||
// If we consume any additional tokens, then this token
|
||||
// is not needed (we'll capture the entire 'glued' token),
|
||||
// and `bump` will set this field to `None`
|
||||
self.break_last_token = true;
|
||||
// and `bump` will set this field to 0.
|
||||
self.break_last_token += 1;
|
||||
// Use the spacing of the glued token as the spacing of the
|
||||
// unglued second token.
|
||||
self.bump_with((Token::new(second, second_span), self.token_spacing));
|
||||
@ -1148,10 +1152,9 @@ pub fn bump(&mut self) {
|
||||
// than `.0`/`.1` access.
|
||||
let mut next = self.token_cursor.inlined_next();
|
||||
self.num_bump_calls += 1;
|
||||
// We've retrieved an token from the underlying
|
||||
// cursor, so we no longer need to worry about
|
||||
// an unglued token. See `break_and_eat` for more details
|
||||
self.break_last_token = false;
|
||||
// We got a token from the underlying cursor and no longer need to
|
||||
// worry about an unglued token. See `break_and_eat` for more details.
|
||||
self.break_last_token = 0;
|
||||
if next.0.span.is_dummy() {
|
||||
// Tweak the location for better diagnostics, but keep syntactic context intact.
|
||||
let fallback_span = self.token.span;
|
||||
|
@ -1690,12 +1690,8 @@ fn $module() {
|
||||
("x86_64h-apple-darwin", x86_64h_apple_darwin),
|
||||
("i686-apple-darwin", i686_apple_darwin),
|
||||
|
||||
// FIXME(#106649): Remove aarch64-fuchsia in favor of aarch64-unknown-fuchsia
|
||||
("aarch64-fuchsia", aarch64_fuchsia),
|
||||
("aarch64-unknown-fuchsia", aarch64_unknown_fuchsia),
|
||||
("riscv64gc-unknown-fuchsia", riscv64gc_unknown_fuchsia),
|
||||
// FIXME(#106649): Remove x86_64-fuchsia in favor of x86_64-unknown-fuchsia
|
||||
("x86_64-fuchsia", x86_64_fuchsia),
|
||||
("x86_64-unknown-fuchsia", x86_64_unknown_fuchsia),
|
||||
|
||||
("avr-unknown-gnu-atmega328", avr_unknown_gnu_atmega328),
|
||||
|
@ -1 +0,0 @@
|
||||
pub(crate) use crate::spec::targets::aarch64_unknown_fuchsia::target;
|
@ -1 +0,0 @@
|
||||
pub(crate) use crate::spec::targets::x86_64_unknown_fuchsia::target;
|
@ -394,6 +394,8 @@ pub mod assert_matches {
|
||||
#[unstable(feature = "core_pattern_types", issue = "123646")]
|
||||
pub mod pat;
|
||||
pub mod pin;
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
pub mod random;
|
||||
#[unstable(feature = "new_range_api", issue = "125687")]
|
||||
pub mod range;
|
||||
pub mod result;
|
||||
|
62
library/core/src/random.rs
Normal file
62
library/core/src/random.rs
Normal file
@ -0,0 +1,62 @@
|
||||
//! Random value generation.
|
||||
//!
|
||||
//! The [`Random`] trait allows generating a random value for a type using a
|
||||
//! given [`RandomSource`].
|
||||
|
||||
/// A source of randomness.
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
pub trait RandomSource {
|
||||
/// Fills `bytes` with random bytes.
|
||||
fn fill_bytes(&mut self, bytes: &mut [u8]);
|
||||
}
|
||||
|
||||
/// A trait for getting a random value for a type.
|
||||
///
|
||||
/// **Warning:** Be careful when manipulating random values! The
|
||||
/// [`random`](Random::random) method on integers samples them with a uniform
|
||||
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
|
||||
/// modulo operations, some of the resulting values can become more likely than
|
||||
/// others. Use audited crates when in doubt.
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
pub trait Random: Sized {
|
||||
/// Generates a random value.
|
||||
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self;
|
||||
}
|
||||
|
||||
impl Random for bool {
|
||||
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
|
||||
u8::random(source) & 1 == 1
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_primitive {
|
||||
($t:ty) => {
|
||||
impl Random for $t {
|
||||
/// Generates a random value.
|
||||
///
|
||||
/// **Warning:** Be careful when manipulating the resulting value! This
|
||||
/// method samples according to a uniform distribution, so a value of 1 is
|
||||
/// just as likely as [`MAX`](Self::MAX). By using modulo operations, some
|
||||
/// values can become more likely than others. Use audited crates when in
|
||||
/// doubt.
|
||||
fn random(source: &mut (impl RandomSource + ?Sized)) -> Self {
|
||||
let mut bytes = (0 as Self).to_ne_bytes();
|
||||
source.fill_bytes(&mut bytes);
|
||||
Self::from_ne_bytes(bytes)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_primitive!(u8);
|
||||
impl_primitive!(i8);
|
||||
impl_primitive!(u16);
|
||||
impl_primitive!(i16);
|
||||
impl_primitive!(u32);
|
||||
impl_primitive!(i32);
|
||||
impl_primitive!(u64);
|
||||
impl_primitive!(i64);
|
||||
impl_primitive!(u128);
|
||||
impl_primitive!(i128);
|
||||
impl_primitive!(usize);
|
||||
impl_primitive!(isize);
|
@ -10,7 +10,8 @@
|
||||
#[allow(deprecated)]
|
||||
use super::{BuildHasher, Hasher, SipHasher13};
|
||||
use crate::cell::Cell;
|
||||
use crate::{fmt, sys};
|
||||
use crate::fmt;
|
||||
use crate::sys::random::hashmap_random_keys;
|
||||
|
||||
/// `RandomState` is the default state for [`HashMap`] types.
|
||||
///
|
||||
@ -65,7 +66,7 @@ pub fn new() -> RandomState {
|
||||
// increment one of the seeds on every RandomState creation, giving
|
||||
// every corresponding HashMap a different iteration order.
|
||||
thread_local!(static KEYS: Cell<(u64, u64)> = {
|
||||
Cell::new(sys::hashmap_random_keys())
|
||||
Cell::new(hashmap_random_keys())
|
||||
});
|
||||
|
||||
KEYS.with(|keys| {
|
||||
|
@ -318,6 +318,7 @@
|
||||
//
|
||||
// Library features (core):
|
||||
// tidy-alphabetical-start
|
||||
#![feature(array_chunks)]
|
||||
#![feature(c_str_module)]
|
||||
#![feature(char_internals)]
|
||||
#![feature(clone_to_uninit)]
|
||||
@ -348,6 +349,7 @@
|
||||
#![feature(prelude_2024)]
|
||||
#![feature(ptr_as_uninit)]
|
||||
#![feature(ptr_mask)]
|
||||
#![feature(random)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(slice_range)]
|
||||
@ -595,6 +597,8 @@
|
||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
pub mod random;
|
||||
pub mod sync;
|
||||
pub mod time;
|
||||
|
||||
|
104
library/std/src/random.rs
Normal file
104
library/std/src/random.rs
Normal file
@ -0,0 +1,104 @@
|
||||
//! Random value generation.
|
||||
//!
|
||||
//! The [`Random`] trait allows generating a random value for a type using a
|
||||
//! given [`RandomSource`].
|
||||
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
pub use core::random::*;
|
||||
|
||||
use crate::sys::random as sys;
|
||||
|
||||
/// The default random source.
|
||||
///
|
||||
/// This asks the system for random data suitable for cryptographic purposes
|
||||
/// such as key generation. If security is a concern, consult the platform
|
||||
/// documentation below for the specific guarantees your target provides.
|
||||
///
|
||||
/// The high quality of randomness provided by this source means it can be quite
|
||||
/// slow on some targets. If you need a large quantity of random numbers and
|
||||
/// security is not a concern, consider using an alternative random number
|
||||
/// generator (potentially seeded from this one).
|
||||
///
|
||||
/// # Underlying sources
|
||||
///
|
||||
/// Platform | Source
|
||||
/// -----------------------|---------------------------------------------------------------
|
||||
/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random`
|
||||
/// Windows | [`ProcessPrng`](https://learn.microsoft.com/en-us/windows/win32/seccng/processprng)
|
||||
/// Apple | `CCRandomGenerateBytes`
|
||||
/// DragonFly | [`arc4random_buf`](https://man.dragonflybsd.org/?command=arc4random)
|
||||
/// ESP-IDF | [`esp_fill_random`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t)
|
||||
/// FreeBSD | [`arc4random_buf`](https://man.freebsd.org/cgi/man.cgi?query=arc4random)
|
||||
/// Fuchsia | [`cprng_draw`](https://fuchsia.dev/reference/syscalls/cprng_draw)
|
||||
/// Haiku | `arc4random_buf`
|
||||
/// Illumos | [`arc4random_buf`](https://www.illumos.org/man/3C/arc4random)
|
||||
/// NetBSD | [`arc4random_buf`](https://man.netbsd.org/arc4random.3)
|
||||
/// OpenBSD | [`arc4random_buf`](https://man.openbsd.org/arc4random.3)
|
||||
/// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html)
|
||||
/// Vita | `arc4random_buf`
|
||||
/// Hermit | `read_entropy`
|
||||
/// Horizon | `getrandom` shim
|
||||
/// Hurd, L4Re, QNX | `/dev/urandom`
|
||||
/// Redox | `/scheme/rand`
|
||||
/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND)
|
||||
/// SOLID | `SOLID_RNG_SampleRandomBytes`
|
||||
/// TEEOS | `TEE_GenerateRandom`
|
||||
/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol)
|
||||
/// VxWorks | `randABytes` after waiting for `randSecure` to become ready
|
||||
/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno)
|
||||
/// ZKVM | `sys_rand`
|
||||
///
|
||||
/// Note that the sources used might change over time.
|
||||
///
|
||||
/// Consult the documentation for the underlying operations on your supported
|
||||
/// targets to determine whether they provide any particular desired properties,
|
||||
/// such as support for reseeding on VM fork operations.
|
||||
///
|
||||
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
|
||||
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
pub struct DefaultRandomSource;
|
||||
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
impl RandomSource for DefaultRandomSource {
|
||||
fn fill_bytes(&mut self, bytes: &mut [u8]) {
|
||||
sys::fill_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a random value with the default random source.
|
||||
///
|
||||
/// This is a convenience function for `T::random(&mut DefaultRandomSource)` and
|
||||
/// will sample according to the same distribution as the underlying [`Random`]
|
||||
/// trait implementation.
|
||||
///
|
||||
/// **Warning:** Be careful when manipulating random values! The
|
||||
/// [`random`](Random::random) method on integers samples them with a uniform
|
||||
/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using
|
||||
/// modulo operations, some of the resulting values can become more likely than
|
||||
/// others. Use audited crates when in doubt.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Generating a [version 4/variant 1 UUID] represented as text:
|
||||
/// ```
|
||||
/// #![feature(random)]
|
||||
///
|
||||
/// use std::random::random;
|
||||
///
|
||||
/// let bits: u128 = random();
|
||||
/// let g1 = (bits >> 96) as u32;
|
||||
/// let g2 = (bits >> 80) as u16;
|
||||
/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16;
|
||||
/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16;
|
||||
/// let g5 = (bits & 0xffffffffffff) as u64;
|
||||
/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}");
|
||||
/// println!("{uuid}");
|
||||
/// ```
|
||||
///
|
||||
/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
pub fn random<T: Random>() -> T {
|
||||
T::random(&mut DefaultRandomSource)
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
pub mod exit_guard;
|
||||
pub mod os_str;
|
||||
pub mod path;
|
||||
pub mod random;
|
||||
pub mod sync;
|
||||
pub mod thread_local;
|
||||
|
||||
|
@ -52,20 +52,6 @@ pub fn abort_internal() -> ! {
|
||||
unsafe { hermit_abi::abort() }
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
let mut buf = [0; 16];
|
||||
let mut slice = &mut buf[..];
|
||||
while !slice.is_empty() {
|
||||
let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) })
|
||||
.expect("failed to generate random hashmap keys");
|
||||
slice = &mut slice[res as usize..];
|
||||
}
|
||||
|
||||
let key1 = buf[..8].try_into().unwrap();
|
||||
let key2 = buf[8..].try_into().unwrap();
|
||||
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
|
||||
}
|
||||
|
||||
// This function is needed by the panic runtime. The symbol is named in
|
||||
// pre-link args for the target specification, so keep that in sync.
|
||||
#[cfg(not(test))]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::cmp;
|
||||
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
|
||||
use crate::sys::rand::rdrand64;
|
||||
use crate::random::{DefaultRandomSource, Random};
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
pub(crate) mod alloc;
|
||||
@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
|
||||
// trusted to ensure accurate timeouts.
|
||||
if let Ok(timeout_signed) = i64::try_from(timeout) {
|
||||
let tenth = timeout_signed / 10;
|
||||
let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
|
||||
let deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0);
|
||||
timeout = timeout_signed.saturating_add(deviation) as _;
|
||||
}
|
||||
}
|
||||
|
@ -132,24 +132,6 @@ pub extern "C" fn __rust_abort() {
|
||||
abort_internal();
|
||||
}
|
||||
|
||||
pub mod rand {
|
||||
pub fn rdrand64() -> u64 {
|
||||
unsafe {
|
||||
let mut ret: u64 = 0;
|
||||
for _ in 0..10 {
|
||||
if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
rtabort!("Failed to obtain random data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
(self::rand::rdrand64(), self::rand::rdrand64())
|
||||
}
|
||||
|
||||
pub use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
|
||||
pub trait TryIntoInner<Inner>: Sized {
|
||||
|
@ -62,13 +62,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
|
||||
pub fn abort_internal() -> ! {
|
||||
unsafe { libc::abort() }
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
unsafe {
|
||||
let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
|
||||
let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
|
||||
assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}");
|
||||
let [x1, x2] = out.assume_init();
|
||||
(x1, x2)
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,6 @@
|
||||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub use self::rand::hashmap_random_keys;
|
||||
|
||||
#[path = "../unsupported/args.rs"]
|
||||
pub mod args;
|
||||
#[path = "../unsupported/env.rs"]
|
||||
@ -23,7 +21,6 @@
|
||||
pub mod pipe;
|
||||
#[path = "../unsupported/process.rs"]
|
||||
pub mod process;
|
||||
mod rand;
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
#[allow(non_upper_case_globals)]
|
||||
|
@ -1,21 +0,0 @@
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
const KEY_LEN: usize = core::mem::size_of::<u64>();
|
||||
|
||||
let mut v = [0u8; KEY_LEN * 2];
|
||||
imp::fill_bytes(&mut v);
|
||||
|
||||
let key1 = v[0..KEY_LEN].try_into().unwrap();
|
||||
let key2 = v[KEY_LEN..].try_into().unwrap();
|
||||
|
||||
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
|
||||
}
|
||||
|
||||
mod imp {
|
||||
extern "C" {
|
||||
fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t);
|
||||
}
|
||||
|
||||
pub fn fill_bytes(v: &mut [u8]) {
|
||||
unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::<u8>()) }
|
||||
}
|
||||
}
|
@ -179,39 +179,6 @@ pub extern "C" fn __rust_abort() {
|
||||
abort_internal();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
get_random().unwrap()
|
||||
}
|
||||
|
||||
fn get_random() -> Option<(u64, u64)> {
|
||||
use r_efi::protocols::rng;
|
||||
|
||||
let mut buf = [0u8; 16];
|
||||
let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?;
|
||||
for handle in handles {
|
||||
if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
|
||||
let r = unsafe {
|
||||
((*protocol.as_ptr()).get_rng)(
|
||||
protocol.as_ptr(),
|
||||
crate::ptr::null_mut(),
|
||||
buf.len(),
|
||||
buf.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
if r.is_error() {
|
||||
continue;
|
||||
} else {
|
||||
return Some((
|
||||
u64::from_le_bytes(buf[..8].try_into().ok()?),
|
||||
u64::from_le_bytes(buf[8..].try_into().ok()?),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled
|
||||
extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) {
|
||||
uefi::env::disable_boot_services();
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![allow(missing_docs, nonstandard_style)]
|
||||
|
||||
pub use self::rand::hashmap_random_keys;
|
||||
use crate::io::ErrorKind;
|
||||
|
||||
#[cfg(not(target_os = "espidf"))]
|
||||
@ -26,7 +25,6 @@
|
||||
pub mod os;
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
pub mod rand;
|
||||
pub mod stack_overflow;
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
|
@ -1,302 +0,0 @@
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
const KEY_LEN: usize = core::mem::size_of::<u64>();
|
||||
|
||||
let mut v = [0u8; KEY_LEN * 2];
|
||||
if let Err(err) = read(&mut v) {
|
||||
panic!("failed to retrieve random hash map seed: {err}");
|
||||
}
|
||||
|
||||
let key1 = v[0..KEY_LEN].try_into().unwrap();
|
||||
let key2 = v[KEY_LEN..].try_into().unwrap();
|
||||
|
||||
(u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(
|
||||
target_vendor = "apple",
|
||||
target_os = "openbsd",
|
||||
target_os = "emscripten",
|
||||
target_os = "vita",
|
||||
all(target_os = "netbsd", not(netbsd10)),
|
||||
target_os = "fuchsia",
|
||||
target_os = "vxworks",
|
||||
))] {
|
||||
// Some systems have a syscall that directly retrieves random data.
|
||||
// If that is guaranteed to be available, use it.
|
||||
use imp::syscall as read;
|
||||
} else {
|
||||
// Otherwise, try the syscall to see if it exists only on some systems
|
||||
// and fall back to reading from the random device otherwise.
|
||||
fn read(bytes: &mut [u8]) -> crate::io::Result<()> {
|
||||
use crate::fs::File;
|
||||
use crate::io::Read;
|
||||
use crate::sync::OnceLock;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "espidf",
|
||||
target_os = "horizon",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
netbsd10,
|
||||
))]
|
||||
if let Some(res) = imp::syscall(bytes) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const PATH: &'static str = if cfg!(target_os = "redox") {
|
||||
"/scheme/rand"
|
||||
} else {
|
||||
"/dev/urandom"
|
||||
};
|
||||
|
||||
static FILE: OnceLock<File> = OnceLock::new();
|
||||
|
||||
FILE.get_or_try_init(|| File::open(PATH))?.read_exact(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All these systems a `getrandom` syscall.
|
||||
//
|
||||
// It is not guaranteed to be available, so return None to fallback to the file
|
||||
// implementation.
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "espidf",
|
||||
target_os = "horizon",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
netbsd10,
|
||||
))]
|
||||
mod imp {
|
||||
use crate::io::{Error, Result};
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::sys::os::errno;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
|
||||
use crate::sys::weak::syscall;
|
||||
|
||||
// A weak symbol allows interposition, e.g. for perf measurements that want to
|
||||
// disable randomness for consistency. Otherwise, we'll try a raw syscall.
|
||||
// (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28)
|
||||
syscall! {
|
||||
fn getrandom(
|
||||
buffer: *mut libc::c_void,
|
||||
length: libc::size_t,
|
||||
flags: libc::c_uint
|
||||
) -> libc::ssize_t
|
||||
}
|
||||
|
||||
// This provides the best quality random numbers available at the given moment
|
||||
// without ever blocking, and is preferable to falling back to /dev/urandom.
|
||||
static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true);
|
||||
if GRND_INSECURE_AVAILABLE.load(Ordering::Relaxed) {
|
||||
let ret = unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_INSECURE) };
|
||||
if ret == -1 && errno() as libc::c_int == libc::EINVAL {
|
||||
GRND_INSECURE_AVAILABLE.store(false, Ordering::Relaxed);
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "espidf",
|
||||
target_os = "horizon",
|
||||
target_os = "freebsd",
|
||||
netbsd10,
|
||||
target_os = "illumos",
|
||||
target_os = "solaris"
|
||||
))]
|
||||
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
|
||||
unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
|
||||
}
|
||||
|
||||
pub fn syscall(v: &mut [u8]) -> Option<Result<()>> {
|
||||
static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut read = 0;
|
||||
while read < v.len() {
|
||||
let result = getrandom(&mut v[read..]);
|
||||
if result == -1 {
|
||||
let err = errno() as libc::c_int;
|
||||
if err == libc::EINTR {
|
||||
continue;
|
||||
} else if err == libc::ENOSYS || err == libc::EPERM {
|
||||
// `getrandom` is not supported on the current system.
|
||||
//
|
||||
// Also fall back in case it is disabled by something like
|
||||
// seccomp or inside of docker.
|
||||
//
|
||||
// If the `getrandom` syscall is not implemented in the current kernel version it should return an
|
||||
// `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and
|
||||
// returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of
|
||||
// that we need to check for *both* `ENOSYS` and `EPERM`.
|
||||
//
|
||||
// Note that Docker's behavior is breaking other projects (notably glibc), so they're planning
|
||||
// to update their filtering to return `ENOSYS` in a future release:
|
||||
//
|
||||
// https://github.com/moby/moby/issues/42680
|
||||
//
|
||||
GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
|
||||
return None;
|
||||
} else if err == libc::EAGAIN {
|
||||
// getrandom has failed because it would have blocked as the
|
||||
// non-blocking pool (urandom) has not been initialized in
|
||||
// the kernel yet due to a lack of entropy. Fallback to
|
||||
// reading from `/dev/urandom` which will return potentially
|
||||
// insecure random data to avoid blocking applications which
|
||||
// could depend on this call without ever knowing they do and
|
||||
// don't have a work around.
|
||||
return None;
|
||||
} else {
|
||||
return Some(Err(Error::from_raw_os_error(err)));
|
||||
}
|
||||
} else {
|
||||
read += result as usize;
|
||||
}
|
||||
}
|
||||
|
||||
Some(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "macos", // Supported since macOS 10.12+.
|
||||
target_os = "openbsd",
|
||||
target_os = "emscripten",
|
||||
target_os = "vita",
|
||||
))]
|
||||
mod imp {
|
||||
use crate::io::{Error, Result};
|
||||
|
||||
pub fn syscall(v: &mut [u8]) -> Result<()> {
|
||||
// getentropy(2) permits a maximum buffer size of 256 bytes
|
||||
for s in v.chunks_mut(256) {
|
||||
let ret = unsafe { libc::getentropy(s.as_mut_ptr().cast(), s.len()) };
|
||||
if ret == -1 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply
|
||||
// call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes`
|
||||
// manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on
|
||||
// its own thread accessed via GCD. This seems needlessly heavyweight for our purposes
|
||||
// so we only use it when `getentropy` is blocked, which appears to be the case
|
||||
// on all platforms except macOS (see #102643).
|
||||
//
|
||||
// `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible
|
||||
// via `libSystem` (libc) while the other needs to link to `Security.framework`.
|
||||
#[cfg(all(target_vendor = "apple", not(target_os = "macos")))]
|
||||
mod imp {
|
||||
use libc::size_t;
|
||||
|
||||
use crate::ffi::{c_int, c_void};
|
||||
use crate::io::{Error, Result};
|
||||
|
||||
pub fn syscall(v: &mut [u8]) -> Result<()> {
|
||||
extern "C" {
|
||||
fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int;
|
||||
}
|
||||
|
||||
let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) };
|
||||
if ret != -1 { Ok(()) } else { Err(Error::last_os_error()) }
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification.
|
||||
#[cfg(all(target_os = "netbsd", not(netbsd10)))]
|
||||
mod imp {
|
||||
use crate::io::{Error, Result};
|
||||
use crate::ptr;
|
||||
|
||||
pub fn syscall(v: &mut [u8]) -> Result<()> {
|
||||
let mib = [libc::CTL_KERN, libc::KERN_ARND];
|
||||
// kern.arandom permits a maximum buffer size of 256 bytes
|
||||
for s in v.chunks_mut(256) {
|
||||
let mut s_len = s.len();
|
||||
let ret = unsafe {
|
||||
libc::sysctl(
|
||||
mib.as_ptr(),
|
||||
mib.len() as libc::c_uint,
|
||||
s.as_mut_ptr() as *mut _,
|
||||
&mut s_len,
|
||||
ptr::null(),
|
||||
0,
|
||||
)
|
||||
};
|
||||
if ret == -1 {
|
||||
return Err(Error::last_os_error());
|
||||
} else if s_len != s.len() {
|
||||
// FIXME(joboet): this can't actually happen, can it?
|
||||
panic!("read less bytes than requested from kern.arandom");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
mod imp {
|
||||
use crate::io::Result;
|
||||
|
||||
#[link(name = "zircon")]
|
||||
extern "C" {
|
||||
fn zx_cprng_draw(buffer: *mut u8, len: usize);
|
||||
}
|
||||
|
||||
pub fn syscall(v: &mut [u8]) -> Result<()> {
|
||||
unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) };
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
mod imp {
|
||||
use core::sync::atomic::AtomicBool;
|
||||
use core::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
use crate::io::{Error, Result};
|
||||
|
||||
pub fn syscall(v: &mut [u8]) -> Result<()> {
|
||||
static RNG_INIT: AtomicBool = AtomicBool::new(false);
|
||||
while !RNG_INIT.load(Relaxed) {
|
||||
let ret = unsafe { libc::randSecure() };
|
||||
if ret < 0 {
|
||||
return Err(Error::last_os_error());
|
||||
} else if ret > 0 {
|
||||
RNG_INIT.store(true, Relaxed);
|
||||
break;
|
||||
}
|
||||
|
||||
unsafe { libc::usleep(10) };
|
||||
}
|
||||
|
||||
let ret = unsafe {
|
||||
libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
|
||||
};
|
||||
if ret >= 0 { Ok(()) } else { Err(Error::last_os_error()) }
|
||||
}
|
||||
}
|
@ -27,7 +27,3 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
|
||||
pub fn abort_internal() -> ! {
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
(1, 2)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use crate::{io as std_io, mem};
|
||||
use crate::io as std_io;
|
||||
|
||||
#[inline]
|
||||
pub fn is_interrupted(errno: i32) -> bool {
|
||||
@ -108,16 +108,6 @@ pub fn abort_internal() -> ! {
|
||||
unsafe { libc::abort() }
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
let mut ret = (0u64, 0u64);
|
||||
unsafe {
|
||||
let base = &mut ret as *mut (u64, u64) as *mut u8;
|
||||
let len = mem::size_of_val(&ret);
|
||||
wasi::random_get(base, len).expect("random_get failure");
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn err2io(err: wasi::Errno) -> std_io::Error {
|
||||
std_io::Error::from_raw_os_error(err.raw().into())
|
||||
|
@ -47,4 +47,4 @@
|
||||
// then the compiler complains about conflicts.
|
||||
|
||||
use helpers::err2io;
|
||||
pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted};
|
||||
pub use helpers::{abort_internal, decode_error_kind, is_interrupted};
|
||||
|
@ -50,6 +50,6 @@
|
||||
// then the compiler complains about conflicts.
|
||||
|
||||
use helpers::err2io;
|
||||
pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted};
|
||||
pub use helpers::{abort_internal, decode_error_kind, is_interrupted};
|
||||
|
||||
mod cabi_realloc;
|
||||
|
@ -1,7 +1,6 @@
|
||||
#![allow(missing_docs, nonstandard_style)]
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub use self::rand::hashmap_random_keys;
|
||||
use crate::ffi::{OsStr, OsString};
|
||||
use crate::io::ErrorKind;
|
||||
use crate::mem::MaybeUninit;
|
||||
@ -27,7 +26,6 @@
|
||||
pub mod os;
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
pub mod rand;
|
||||
pub mod stdio;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
|
@ -2,12 +2,13 @@
|
||||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
|
||||
use crate::os::windows::prelude::*;
|
||||
use crate::path::Path;
|
||||
use crate::random::{DefaultRandomSource, Random};
|
||||
use crate::sync::atomic::AtomicUsize;
|
||||
use crate::sync::atomic::Ordering::Relaxed;
|
||||
use crate::sys::c;
|
||||
use crate::sys::fs::{File, OpenOptions};
|
||||
use crate::sys::handle::Handle;
|
||||
use crate::sys::pal::windows::api::{self, WinError};
|
||||
use crate::sys::{c, hashmap_random_keys};
|
||||
use crate::sys_common::{FromInner, IntoInner};
|
||||
use crate::{mem, ptr};
|
||||
|
||||
@ -79,7 +80,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
|
||||
name = format!(
|
||||
r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}",
|
||||
c::GetCurrentProcessId(),
|
||||
random_number()
|
||||
random_number(),
|
||||
);
|
||||
let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::<Vec<_>>();
|
||||
let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED;
|
||||
@ -214,7 +215,7 @@ fn random_number() -> usize {
|
||||
return N.fetch_add(1, Relaxed);
|
||||
}
|
||||
|
||||
N.store(hashmap_random_keys().0 as usize, Relaxed);
|
||||
N.store(usize::random(&mut DefaultRandomSource), Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
use core::{mem, ptr};
|
||||
|
||||
use crate::sys::c;
|
||||
|
||||
#[cfg(not(target_vendor = "win7"))]
|
||||
#[inline]
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
let mut v = (0, 0);
|
||||
let ret = unsafe { c::ProcessPrng(ptr::addr_of_mut!(v).cast::<u8>(), mem::size_of_val(&v)) };
|
||||
// ProcessPrng is documented as always returning `TRUE`.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value
|
||||
debug_assert_eq!(ret, c::TRUE);
|
||||
v
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "win7")]
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
use crate::ffi::c_void;
|
||||
use crate::io;
|
||||
|
||||
let mut v = (0, 0);
|
||||
let ret = unsafe {
|
||||
c::RtlGenRandom(ptr::addr_of_mut!(v).cast::<c_void>(), mem::size_of_val(&v) as u32)
|
||||
};
|
||||
|
||||
if ret != 0 { v } else { panic!("RNG broken: {}", io::Error::last_os_error()) }
|
||||
}
|
@ -60,11 +60,3 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
|
||||
pub fn abort_internal() -> ! {
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
let mut buf = [0u32; 4];
|
||||
unsafe {
|
||||
abi::sys_rand(buf.as_mut_ptr(), 4);
|
||||
};
|
||||
((buf[0] as u64) << 32 + buf[1] as u64, (buf[2] as u64) << 32 + buf[3] as u64)
|
||||
}
|
||||
|
15
library/std/src/sys/random/apple.rs
Normal file
15
library/std/src/sys/random/apple.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//! Random data on Apple platforms.
|
||||
//!
|
||||
//! `CCRandomGenerateBytes` calls into `CCRandomCopyBytes` with `kCCRandomDefault`.
|
||||
//! `CCRandomCopyBytes` manages a CSPRNG which is seeded from the kernel's CSPRNG.
|
||||
//! We use `CCRandomGenerateBytes` instead of `SecCopyBytes` because it is accessible via
|
||||
//! `libSystem` (libc) while the other needs to link to `Security.framework`.
|
||||
//!
|
||||
//! Note that technically, `arc4random_buf` is available as well, but that calls
|
||||
//! into the same system service anyway, and `CCRandomGenerateBytes` has been
|
||||
//! proven to be App Store-compatible.
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) };
|
||||
assert_eq!(ret, libc::kCCSuccess, "failed to generate random data");
|
||||
}
|
34
library/std/src/sys/random/arc4random.rs
Normal file
34
library/std/src/sys/random/arc4random.rs
Normal file
@ -0,0 +1,34 @@
|
||||
//! Random data generation with `arc4random_buf`.
|
||||
//!
|
||||
//! Contrary to its name, `arc4random` doesn't actually use the horribly-broken
|
||||
//! RC4 cypher anymore, at least not on modern systems, but rather something
|
||||
//! like ChaCha20 with continual reseeding from the OS. That makes it an ideal
|
||||
//! source of large quantities of cryptographically secure data, which is exactly
|
||||
//! what we need for `DefaultRandomSource`. Unfortunately, it's not available
|
||||
//! on all UNIX systems, most notably Linux (until recently, but it's just a
|
||||
//! wrapper for `getrandom`. Since we need to hook into `getrandom` directly
|
||||
//! for `HashMap` keys anyway, we just keep our version).
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "haiku",
|
||||
target_os = "illumos",
|
||||
target_os = "solaris",
|
||||
target_os = "vita",
|
||||
)))]
|
||||
use libc::arc4random_buf;
|
||||
|
||||
// FIXME: move these to libc (except Haiku, that one needs to link to libbsd.so).
|
||||
#[cfg(any(
|
||||
target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h
|
||||
target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random
|
||||
target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html
|
||||
target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
|
||||
))]
|
||||
#[cfg_attr(target_os = "haiku", link(name = "bsd"))]
|
||||
extern "C" {
|
||||
fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t);
|
||||
}
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) }
|
||||
}
|
9
library/std/src/sys/random/espidf.rs
Normal file
9
library/std/src/sys/random/espidf.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use crate::ffi::c_void;
|
||||
|
||||
extern "C" {
|
||||
fn esp_fill_random(buf: *mut c_void, len: usize);
|
||||
}
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
unsafe { esp_fill_random(bytes.as_mut_ptr().cast(), bytes.len()) }
|
||||
}
|
13
library/std/src/sys/random/fuchsia.rs
Normal file
13
library/std/src/sys/random/fuchsia.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! Random data generation using the Zircon kernel.
|
||||
//!
|
||||
//! Fuchsia, as always, is quite nice and provides exactly the API we need:
|
||||
//! <https://fuchsia.dev/reference/syscalls/cprng_draw>.
|
||||
|
||||
#[link(name = "zircon")]
|
||||
extern "C" {
|
||||
fn zx_cprng_draw(buffer: *mut u8, len: usize);
|
||||
}
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
unsafe { zx_cprng_draw(bytes.as_mut_ptr(), bytes.len()) }
|
||||
}
|
17
library/std/src/sys/random/getentropy.rs
Normal file
17
library/std/src/sys/random/getentropy.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! Random data generation through `getentropy`.
|
||||
//!
|
||||
//! Since issue 8 (2024), the POSIX specification mandates the existence of the
|
||||
//! `getentropy` function, which fills a slice of up to `GETENTROPY_MAX` bytes
|
||||
//! (256 on all known platforms) with random data. Unfortunately, it's only
|
||||
//! meant to be used to seed other CPRNGs, which we don't have, so we only use
|
||||
//! it where `arc4random_buf` and friends aren't available or secure (currently
|
||||
//! that's only the case on Emscripten).
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
// GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated
|
||||
// to be at least 256, so just use that as limit.
|
||||
for chunk in bytes.chunks_mut(256) {
|
||||
let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) };
|
||||
assert_ne!(r, -1, "failed to generate random data");
|
||||
}
|
||||
}
|
7
library/std/src/sys/random/hermit.rs
Normal file
7
library/std/src/sys/random/hermit.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub fn fill_bytes(mut bytes: &mut [u8]) {
|
||||
while !bytes.is_empty() {
|
||||
let res = unsafe { hermit_abi::read_entropy(bytes.as_mut_ptr(), bytes.len(), 0) };
|
||||
assert_ne!(res, -1, "failed to generate random data");
|
||||
bytes = &mut bytes[res as usize..];
|
||||
}
|
||||
}
|
7
library/std/src/sys/random/horizon.rs
Normal file
7
library/std/src/sys/random/horizon.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub fn fill_bytes(mut bytes: &mut [u8]) {
|
||||
while !bytes.is_empty() {
|
||||
let r = unsafe { libc::getrandom(bytes.as_mut_ptr().cast(), bytes.len(), 0) };
|
||||
assert_ne!(r, -1, "failed to generate random data");
|
||||
bytes = &mut bytes[r as usize..];
|
||||
}
|
||||
}
|
170
library/std/src/sys/random/linux.rs
Normal file
170
library/std/src/sys/random/linux.rs
Normal file
@ -0,0 +1,170 @@
|
||||
//! Random data generation with the Linux kernel.
|
||||
//!
|
||||
//! The first interface random data interface to be introduced on Linux were
|
||||
//! the `/dev/random` and `/dev/urandom` special files. As paths can become
|
||||
//! unreachable when inside a chroot and when the file descriptors are exhausted,
|
||||
//! this was not enough to provide userspace with a reliable source of randomness,
|
||||
//! so when the OpenBSD 5.6 introduced the `getentropy` syscall, Linux 3.17 got
|
||||
//! its very own `getrandom` syscall to match.[^1] Unfortunately, even if our
|
||||
//! minimum supported version were high enough, we still couldn't rely on the
|
||||
//! syscall being available, as it is blocked in `seccomp` by default.
|
||||
//!
|
||||
//! The question is therefore which of the random sources to use. Historically,
|
||||
//! the kernel contained two pools: the blocking and non-blocking pool. The
|
||||
//! blocking pool used entropy estimation to limit the amount of available
|
||||
//! bytes, while the non-blocking pool, once initialized using the blocking
|
||||
//! pool, uses a CPRNG to return an unlimited number of random bytes. With a
|
||||
//! strong enough CPRNG however, the entropy estimation didn't contribute that
|
||||
//! much towards security while being an excellent vector for DoS attacs. Thus,
|
||||
//! the blocking pool was removed in kernel version 5.6.[^2] That patch did not
|
||||
//! magically increase the quality of the non-blocking pool, however, so we can
|
||||
//! safely consider it strong enough even in older kernel versions and use it
|
||||
//! unconditionally.
|
||||
//!
|
||||
//! One additional consideration to make is that the non-blocking pool is not
|
||||
//! always initialized during early boot. We want the best quality of randomness
|
||||
//! for the output of `DefaultRandomSource` so we simply wait until it is
|
||||
//! initialized. When `HashMap` keys however, this represents a potential source
|
||||
//! of deadlocks, as the additional entropy may only be generated once the
|
||||
//! program makes forward progress. In that case, we just use the best random
|
||||
//! data the system has available at the time.
|
||||
//!
|
||||
//! So in conclusion, we always want the output of the non-blocking pool, but
|
||||
//! may need to wait until it is initalized. The default behaviour of `getrandom`
|
||||
//! is to wait until the non-blocking pool is initialized and then draw from there,
|
||||
//! so if `getrandom` is available, we use its default to generate the bytes. For
|
||||
//! `HashMap`, however, we need to specify the `GRND_INSECURE` flags, but that
|
||||
//! is only available starting with kernel version 5.6. Thus, if we detect that
|
||||
//! the flag is unsupported, we try `GRND_NONBLOCK` instead, which will only
|
||||
//! succeed if the pool is initialized. If it isn't, we fall back to the file
|
||||
//! access method.
|
||||
//!
|
||||
//! The behaviour of `/dev/urandom` is inverse to that of `getrandom`: it always
|
||||
//! yields data, even when the pool is not initialized. For generating `HashMap`
|
||||
//! keys, this is not important, so we can use it directly. For secure data
|
||||
//! however, we need to wait until initialization, which we can do by `poll`ing
|
||||
//! `/dev/random`.
|
||||
//!
|
||||
//! TLDR: our fallback strategies are:
|
||||
//!
|
||||
//! Secure data | `HashMap` keys
|
||||
//! --------------------------------------------|------------------
|
||||
//! getrandom(0) | getrandom(GRND_INSECURE)
|
||||
//! poll("/dev/random") && read("/dev/urandom") | getrandom(GRND_NONBLOCK)
|
||||
//! | read("/dev/urandom")
|
||||
//!
|
||||
//! [^1]: <https://lwn.net/Articles/606141/>
|
||||
//! [^2]: <https://lwn.net/Articles/808575/>
|
||||
//!
|
||||
// FIXME(in 2040 or so): once the minimum kernel version is 5.6, remove the
|
||||
// `GRND_NONBLOCK` fallback and use `/dev/random` instead of `/dev/urandom`
|
||||
// when secure data is required.
|
||||
|
||||
use crate::fs::File;
|
||||
use crate::io::Read;
|
||||
use crate::os::fd::AsRawFd;
|
||||
use crate::sync::OnceLock;
|
||||
use crate::sync::atomic::AtomicBool;
|
||||
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
|
||||
use crate::sys::pal::os::errno;
|
||||
use crate::sys::pal::weak::syscall;
|
||||
|
||||
fn getrandom(mut bytes: &mut [u8], insecure: bool) {
|
||||
// A weak symbol allows interposition, e.g. for perf measurements that want to
|
||||
// disable randomness for consistency. Otherwise, we'll try a raw syscall.
|
||||
// (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28)
|
||||
syscall! {
|
||||
fn getrandom(
|
||||
buffer: *mut libc::c_void,
|
||||
length: libc::size_t,
|
||||
flags: libc::c_uint
|
||||
) -> libc::ssize_t
|
||||
}
|
||||
|
||||
static GETRANDOM_AVAILABLE: AtomicBool = AtomicBool::new(true);
|
||||
static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true);
|
||||
static URANDOM_READY: AtomicBool = AtomicBool::new(false);
|
||||
static DEVICE: OnceLock<File> = OnceLock::new();
|
||||
|
||||
if GETRANDOM_AVAILABLE.load(Relaxed) {
|
||||
loop {
|
||||
if bytes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let flags = if insecure {
|
||||
if GRND_INSECURE_AVAILABLE.load(Relaxed) {
|
||||
libc::GRND_INSECURE
|
||||
} else {
|
||||
libc::GRND_NONBLOCK
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let ret = unsafe { getrandom(bytes.as_mut_ptr().cast(), bytes.len(), flags) };
|
||||
if ret != -1 {
|
||||
bytes = &mut bytes[ret as usize..];
|
||||
} else {
|
||||
match errno() {
|
||||
libc::EINTR => continue,
|
||||
// `GRND_INSECURE` is not available, try
|
||||
// `GRND_NONBLOCK`.
|
||||
libc::EINVAL if flags == libc::GRND_INSECURE => {
|
||||
GRND_INSECURE_AVAILABLE.store(false, Relaxed);
|
||||
continue;
|
||||
}
|
||||
// The pool is not initialized yet, fall back to
|
||||
// /dev/urandom for now.
|
||||
libc::EAGAIN if flags == libc::GRND_NONBLOCK => break,
|
||||
// `getrandom` is unavailable or blocked by seccomp.
|
||||
// Don't try it again and fall back to /dev/urandom.
|
||||
libc::ENOSYS | libc::EPERM => {
|
||||
GETRANDOM_AVAILABLE.store(false, Relaxed);
|
||||
break;
|
||||
}
|
||||
_ => panic!("failed to generate random data"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When we want cryptographic strength, we need to wait for the CPRNG-pool
|
||||
// to become initialized. Do this by polling `/dev/random` until it is ready.
|
||||
if !insecure {
|
||||
if !URANDOM_READY.load(Acquire) {
|
||||
let random = File::open("/dev/random").expect("failed to open /dev/random");
|
||||
let mut fd = libc::pollfd { fd: random.as_raw_fd(), events: libc::POLLIN, revents: 0 };
|
||||
|
||||
while !URANDOM_READY.load(Acquire) {
|
||||
let ret = unsafe { libc::poll(&mut fd, 1, -1) };
|
||||
match ret {
|
||||
1 => {
|
||||
assert_eq!(fd.revents, libc::POLLIN);
|
||||
URANDOM_READY.store(true, Release);
|
||||
break;
|
||||
}
|
||||
-1 if errno() == libc::EINTR => continue,
|
||||
_ => panic!("poll(\"/dev/random\") failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEVICE
|
||||
.get_or_try_init(|| File::open("/dev/urandom"))
|
||||
.and_then(|mut dev| dev.read_exact(bytes))
|
||||
.expect("failed to generate random data");
|
||||
}
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
getrandom(bytes, false);
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
let mut bytes = [0; 16];
|
||||
getrandom(&mut bytes, true);
|
||||
let k1 = u64::from_ne_bytes(bytes[..8].try_into().unwrap());
|
||||
let k2 = u64::from_ne_bytes(bytes[8..].try_into().unwrap());
|
||||
(k1, k2)
|
||||
}
|
95
library/std/src/sys/random/mod.rs
Normal file
95
library/std/src/sys/random/mod.rs
Normal file
@ -0,0 +1,95 @@
|
||||
cfg_if::cfg_if! {
|
||||
// Tier 1
|
||||
if #[cfg(any(target_os = "linux", target_os = "android"))] {
|
||||
mod linux;
|
||||
pub use linux::{fill_bytes, hashmap_random_keys};
|
||||
} else if #[cfg(target_os = "windows")] {
|
||||
mod windows;
|
||||
pub use windows::fill_bytes;
|
||||
} else if #[cfg(target_vendor = "apple")] {
|
||||
mod apple;
|
||||
pub use apple::fill_bytes;
|
||||
// Others, in alphabetical ordering.
|
||||
} else if #[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "haiku",
|
||||
target_os = "illumos",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "solaris",
|
||||
target_os = "vita",
|
||||
))] {
|
||||
mod arc4random;
|
||||
pub use arc4random::fill_bytes;
|
||||
} else if #[cfg(target_os = "emscripten")] {
|
||||
mod getentropy;
|
||||
pub use getentropy::fill_bytes;
|
||||
} else if #[cfg(target_os = "espidf")] {
|
||||
mod espidf;
|
||||
pub use espidf::fill_bytes;
|
||||
} else if #[cfg(target_os = "fuchsia")] {
|
||||
mod fuchsia;
|
||||
pub use fuchsia::fill_bytes;
|
||||
} else if #[cfg(target_os = "hermit")] {
|
||||
mod hermit;
|
||||
pub use hermit::fill_bytes;
|
||||
} else if #[cfg(target_os = "horizon")] {
|
||||
// FIXME: add arc4random_buf to shim-3ds
|
||||
mod horizon;
|
||||
pub use horizon::fill_bytes;
|
||||
} else if #[cfg(any(
|
||||
target_os = "hurd",
|
||||
target_os = "l4re",
|
||||
target_os = "nto",
|
||||
))] {
|
||||
mod unix_legacy;
|
||||
pub use unix_legacy::fill_bytes;
|
||||
} else if #[cfg(target_os = "redox")] {
|
||||
mod redox;
|
||||
pub use redox::fill_bytes;
|
||||
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
|
||||
mod sgx;
|
||||
pub use sgx::fill_bytes;
|
||||
} else if #[cfg(target_os = "solid_asp3")] {
|
||||
mod solid;
|
||||
pub use solid::fill_bytes;
|
||||
} else if #[cfg(target_os = "teeos")] {
|
||||
mod teeos;
|
||||
pub use teeos::fill_bytes;
|
||||
} else if #[cfg(target_os = "uefi")] {
|
||||
mod uefi;
|
||||
pub use uefi::fill_bytes;
|
||||
} else if #[cfg(target_os = "vxworks")] {
|
||||
mod vxworks;
|
||||
pub use vxworks::fill_bytes;
|
||||
} else if #[cfg(target_os = "wasi")] {
|
||||
mod wasi;
|
||||
pub use wasi::fill_bytes;
|
||||
} else if #[cfg(target_os = "zkvm")] {
|
||||
mod zkvm;
|
||||
pub use zkvm::fill_bytes;
|
||||
} else if #[cfg(any(
|
||||
all(target_family = "wasm", target_os = "unknown"),
|
||||
target_os = "xous",
|
||||
))] {
|
||||
// FIXME: finally remove std support for wasm32-unknown-unknown
|
||||
// FIXME: add random data generation to xous
|
||||
mod unsupported;
|
||||
pub use unsupported::{fill_bytes, hashmap_random_keys};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
all(target_family = "wasm", target_os = "unknown"),
|
||||
target_os = "xous",
|
||||
)))]
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
let mut buf = [0; 16];
|
||||
fill_bytes(&mut buf);
|
||||
let k1 = u64::from_ne_bytes(buf[..8].try_into().unwrap());
|
||||
let k2 = u64::from_ne_bytes(buf[8..].try_into().unwrap());
|
||||
(k1, k2)
|
||||
}
|
12
library/std/src/sys/random/redox.rs
Normal file
12
library/std/src/sys/random/redox.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::fs::File;
|
||||
use crate::io::Read;
|
||||
use crate::sync::OnceLock;
|
||||
|
||||
static SCHEME: OnceLock<File> = OnceLock::new();
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
SCHEME
|
||||
.get_or_try_init(|| File::open("/scheme/rand"))
|
||||
.and_then(|mut scheme| scheme.read_exact(bytes))
|
||||
.expect("failed to generate random data");
|
||||
}
|
67
library/std/src/sys/random/sgx.rs
Normal file
67
library/std/src/sys/random/sgx.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use crate::arch::x86_64::{_rdrand16_step, _rdrand32_step, _rdrand64_step};
|
||||
|
||||
const RETRIES: u32 = 10;
|
||||
|
||||
fn fail() -> ! {
|
||||
panic!("failed to generate random data");
|
||||
}
|
||||
|
||||
fn rdrand64() -> u64 {
|
||||
unsafe {
|
||||
let mut ret: u64 = 0;
|
||||
for _ in 0..RETRIES {
|
||||
if _rdrand64_step(&mut ret) == 1 {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
fn rdrand32() -> u32 {
|
||||
unsafe {
|
||||
let mut ret: u32 = 0;
|
||||
for _ in 0..RETRIES {
|
||||
if _rdrand32_step(&mut ret) == 1 {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
fn rdrand16() -> u16 {
|
||||
unsafe {
|
||||
let mut ret: u16 = 0;
|
||||
for _ in 0..RETRIES {
|
||||
if _rdrand16_step(&mut ret) == 1 {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
let mut chunks = bytes.array_chunks_mut();
|
||||
for chunk in &mut chunks {
|
||||
*chunk = rdrand64().to_ne_bytes();
|
||||
}
|
||||
|
||||
let mut chunks = chunks.into_remainder().array_chunks_mut();
|
||||
for chunk in &mut chunks {
|
||||
*chunk = rdrand32().to_ne_bytes();
|
||||
}
|
||||
|
||||
let mut chunks = chunks.into_remainder().array_chunks_mut();
|
||||
for chunk in &mut chunks {
|
||||
*chunk = rdrand16().to_ne_bytes();
|
||||
}
|
||||
|
||||
if let [byte] = chunks.into_remainder() {
|
||||
*byte = rdrand16() as u8;
|
||||
}
|
||||
}
|
8
library/std/src/sys/random/solid.rs
Normal file
8
library/std/src/sys/random/solid.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::sys::pal::abi;
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
unsafe {
|
||||
let result = abi::SOLID_RNG_SampleRandomBytes(bytes.as_mut_ptr(), bytes.len());
|
||||
assert_eq!(result, 0, "failed to generate random data");
|
||||
}
|
||||
}
|
7
library/std/src/sys/random/teeos.rs
Normal file
7
library/std/src/sys/random/teeos.rs
Normal file
@ -0,0 +1,7 @@
|
||||
extern "C" {
|
||||
fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t);
|
||||
}
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
unsafe { TEE_GenerateRandom(bytes.as_mut_ptr().cast(), bytes.len()) }
|
||||
}
|
27
library/std/src/sys/random/uefi.rs
Normal file
27
library/std/src/sys/random/uefi.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use r_efi::protocols::rng;
|
||||
|
||||
use crate::sys::pal::helpers;
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
let handles =
|
||||
helpers::locate_handles(rng::PROTOCOL_GUID).expect("failed to generate random data");
|
||||
for handle in handles {
|
||||
if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
|
||||
let r = unsafe {
|
||||
((*protocol.as_ptr()).get_rng)(
|
||||
protocol.as_ptr(),
|
||||
crate::ptr::null_mut(),
|
||||
bytes.len(),
|
||||
bytes.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
if r.is_error() {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic!("failed to generate random data");
|
||||
}
|
20
library/std/src/sys/random/unix_legacy.rs
Normal file
20
library/std/src/sys/random/unix_legacy.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Random data from `/dev/urandom`
|
||||
//!
|
||||
//! Before `getentropy` was standardized in 2024, UNIX didn't have a standardized
|
||||
//! way of getting random data, so systems just followed the precedent set by
|
||||
//! Linux and exposed random devices at `/dev/random` and `/dev/urandom`. Thus,
|
||||
//! for the few systems that support neither `arc4random_buf` nor `getentropy`
|
||||
//! yet, we just read from the file.
|
||||
|
||||
use crate::fs::File;
|
||||
use crate::io::Read;
|
||||
use crate::sync::OnceLock;
|
||||
|
||||
static DEVICE: OnceLock<File> = OnceLock::new();
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
DEVICE
|
||||
.get_or_try_init(|| File::open("/dev/urandom"))
|
||||
.and_then(|mut dev| dev.read_exact(bytes))
|
||||
.expect("failed to generate random data");
|
||||
}
|
15
library/std/src/sys/random/unsupported.rs
Normal file
15
library/std/src/sys/random/unsupported.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use crate::ptr;
|
||||
|
||||
pub fn fill_bytes(_: &mut [u8]) {
|
||||
panic!("this target does not support random data generation");
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
// Use allocation addresses for a bit of randomness. This isn't
|
||||
// particularily secure, but there isn't really an alternative.
|
||||
let stack = 0u8;
|
||||
let heap = Box::new(0u8);
|
||||
let k1 = ptr::from_ref(&stack).addr() as u64;
|
||||
let k2 = ptr::from_ref(&*heap).addr() as u64;
|
||||
(k1, k2)
|
||||
}
|
25
library/std/src/sys/random/vxworks.rs
Normal file
25
library/std/src/sys/random/vxworks.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::sync::atomic::AtomicBool;
|
||||
use crate::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
static RNG_INIT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub fn fill_bytes(mut bytes: &mut [u8]) {
|
||||
while !RNG_INIT.load(Relaxed) {
|
||||
let ret = unsafe { libc::randSecure() };
|
||||
if ret < 0 {
|
||||
panic!("failed to generate random data");
|
||||
} else if ret > 0 {
|
||||
RNG_INIT.store(true, Relaxed);
|
||||
break;
|
||||
}
|
||||
|
||||
unsafe { libc::usleep(10) };
|
||||
}
|
||||
|
||||
while !bytes.is_empty() {
|
||||
let len = bytes.len().try_into().unwrap_or(libc::c_int::MAX);
|
||||
let ret = unsafe { libc::randABytes(bytes.as_mut_ptr(), len) };
|
||||
assert!(ret >= 0, "failed to generate random data");
|
||||
bytes = &mut bytes[len as usize..];
|
||||
}
|
||||
}
|
5
library/std/src/sys/random/wasi.rs
Normal file
5
library/std/src/sys/random/wasi.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
unsafe {
|
||||
wasi::random_get(bytes.as_mut_ptr(), bytes.len()).expect("failed to generate random data")
|
||||
}
|
||||
}
|
20
library/std/src/sys/random/windows.rs
Normal file
20
library/std/src/sys/random/windows.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use crate::sys::c;
|
||||
|
||||
#[cfg(not(target_vendor = "win7"))]
|
||||
#[inline]
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
let ret = unsafe { c::ProcessPrng(bytes.as_mut_ptr(), bytes.len()) };
|
||||
// ProcessPrng is documented as always returning `TRUE`.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value
|
||||
debug_assert_eq!(ret, c::TRUE);
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "win7")]
|
||||
pub fn fill_bytes(mut bytes: &mut [u8]) {
|
||||
while !bytes.is_empty() {
|
||||
let len = bytes.len().try_into().unwrap_or(u32::MAX);
|
||||
let ret = unsafe { c::RtlGenRandom(bytes.as_mut_ptr().cast(), len) };
|
||||
assert_ne!(ret, 0, "failed to generate random data");
|
||||
bytes = &mut bytes[len as usize..];
|
||||
}
|
||||
}
|
21
library/std/src/sys/random/zkvm.rs
Normal file
21
library/std/src/sys/random/zkvm.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::sys::pal::abi;
|
||||
|
||||
pub fn fill_bytes(bytes: &mut [u8]) {
|
||||
let (pre, words, post) = unsafe { bytes.align_to_mut::<u32>() };
|
||||
if !words.is_empty() {
|
||||
unsafe {
|
||||
abi::sys_rand(words.as_mut_ptr(), words.len());
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = [0u32; 2];
|
||||
let len = (pre.len() + post.len() + size_of::<u32>() - 1) / size_of::<u32>();
|
||||
if len != 0 {
|
||||
unsafe { abi::sys_rand(buf.as_mut_ptr(), len) };
|
||||
}
|
||||
|
||||
let buf = buf.map(u32::to_ne_bytes);
|
||||
let buf = buf.as_flattened();
|
||||
pre.copy_from_slice(&buf[..pre.len()]);
|
||||
post.copy_from_slice(&buf[pre.len()..pre.len() + post.len()]);
|
||||
}
|
@ -63,7 +63,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
src: builder.src.join($path),
|
||||
parent: Some(self),
|
||||
languages: $lang.into(),
|
||||
rustdoc: None,
|
||||
rustdoc_compiler: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -113,7 +113,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
src: builder.md_doc_out(self.target).join("unstable-book"),
|
||||
parent: Some(self),
|
||||
languages: vec![],
|
||||
rustdoc: None,
|
||||
rustdoc_compiler: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -125,7 +125,7 @@ struct RustbookSrc<P: Step> {
|
||||
src: PathBuf,
|
||||
parent: Option<P>,
|
||||
languages: Vec<&'static str>,
|
||||
rustdoc: Option<PathBuf>,
|
||||
rustdoc_compiler: Option<Compiler>,
|
||||
}
|
||||
|
||||
impl<P: Step> Step for RustbookSrc<P> {
|
||||
@ -157,7 +157,9 @@ fn run(self, builder: &Builder<'_>) {
|
||||
let _ = fs::remove_dir_all(&out);
|
||||
|
||||
let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
|
||||
if let Some(mut rustdoc) = self.rustdoc {
|
||||
|
||||
if let Some(compiler) = self.rustdoc_compiler {
|
||||
let mut rustdoc = builder.rustdoc(compiler);
|
||||
rustdoc.pop();
|
||||
let old_path = env::var_os("PATH").unwrap_or_default();
|
||||
let new_path =
|
||||
@ -165,6 +167,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
.expect("could not add rustdoc to PATH");
|
||||
|
||||
rustbook_cmd.env("PATH", new_path);
|
||||
builder.add_rustc_lib_path(compiler, &mut rustbook_cmd);
|
||||
}
|
||||
|
||||
rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).run(builder);
|
||||
@ -240,7 +243,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
src: absolute_path.clone(),
|
||||
parent: Some(self),
|
||||
languages: vec![],
|
||||
rustdoc: None,
|
||||
rustdoc_compiler: None,
|
||||
});
|
||||
|
||||
// building older edition redirects
|
||||
@ -253,7 +256,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
// treat the other editions as not having a parent.
|
||||
parent: Option::<Self>::None,
|
||||
languages: vec![],
|
||||
rustdoc: None,
|
||||
rustdoc_compiler: None,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1236,7 +1239,7 @@ fn run(self, builder: &Builder<'_>) {
|
||||
src: out_base,
|
||||
parent: Some(self),
|
||||
languages: vec![],
|
||||
rustdoc: None,
|
||||
rustdoc_compiler: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1270,16 +1273,15 @@ fn run(self, builder: &Builder<'_>) {
|
||||
// This is needed for generating links to the standard library using
|
||||
// the mdbook-spec plugin.
|
||||
builder.ensure(compile::Std::new(self.compiler, builder.config.build));
|
||||
let rustdoc = builder.rustdoc(self.compiler);
|
||||
|
||||
// Run rustbook/mdbook to generate the HTML pages.
|
||||
builder.ensure(RustbookSrc {
|
||||
target: self.target,
|
||||
name: "reference".to_owned(),
|
||||
src: builder.src.join("src/doc/reference"),
|
||||
rustdoc_compiler: Some(self.compiler),
|
||||
parent: Some(self),
|
||||
languages: vec![],
|
||||
rustdoc: Some(rustdoc),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,6 @@ target | std | notes
|
||||
[`aarch64-apple-ios`](platform-support/apple-ios.md) | ✓ | ARM64 iOS
|
||||
[`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on ARM64
|
||||
[`aarch64-apple-ios-sim`](platform-support/apple-ios.md) | ✓ | Apple iOS Simulator on ARM64
|
||||
`aarch64-fuchsia` | ✓ | Alias for `aarch64-unknown-fuchsia`
|
||||
[`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia
|
||||
[`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android
|
||||
[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI
|
||||
@ -199,7 +198,6 @@ target | std | notes
|
||||
[`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS
|
||||
[`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64
|
||||
[`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX
|
||||
`x86_64-fuchsia` | ✓ | Alias for `x86_64-unknown-fuchsia`
|
||||
[`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia
|
||||
[`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android
|
||||
`x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos
|
||||
|
@ -78,6 +78,16 @@ fn emulate_foreign_item_inner(
|
||||
this.write_pointer(environ, dest)?;
|
||||
}
|
||||
|
||||
// Random data generation
|
||||
"CCRandomGenerateBytes" => {
|
||||
let [bytes, count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let bytes = this.read_pointer(bytes)?;
|
||||
let count = this.read_target_usize(count)?;
|
||||
let success = this.eval_libc_i32("kCCSuccess");
|
||||
this.gen_random(bytes, count)?;
|
||||
this.write_int(success, dest)?;
|
||||
}
|
||||
|
||||
// Time related shims
|
||||
"mach_absolute_time" => {
|
||||
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
|
@ -0,0 +1,7 @@
|
||||
//@only-target: apple # This directly tests apple-only functions
|
||||
|
||||
fn main() {
|
||||
let mut bytes = [0u8; 24];
|
||||
let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) };
|
||||
assert_eq!(ret, libc::kCCSuccess);
|
||||
}
|
@ -9,9 +9,6 @@
|
||||
//@ revisions: aarch64_be_unknown_netbsd
|
||||
//@ [aarch64_be_unknown_netbsd] compile-flags: --target aarch64_be-unknown-netbsd
|
||||
//@ [aarch64_be_unknown_netbsd] needs-llvm-components: aarch64
|
||||
//@ revisions: aarch64_fuchsia
|
||||
//@ [aarch64_fuchsia] compile-flags: --target aarch64-fuchsia
|
||||
//@ [aarch64_fuchsia] needs-llvm-components: aarch64
|
||||
//@ revisions: aarch64_kmc_solid_asp3
|
||||
//@ [aarch64_kmc_solid_asp3] compile-flags: --target aarch64-kmc-solid_asp3
|
||||
//@ [aarch64_kmc_solid_asp3] needs-llvm-components: aarch64
|
||||
@ -525,9 +522,6 @@
|
||||
//@ revisions: x86_64_fortanix_unknown_sgx
|
||||
//@ [x86_64_fortanix_unknown_sgx] compile-flags: --target x86_64-fortanix-unknown-sgx
|
||||
//@ [x86_64_fortanix_unknown_sgx] needs-llvm-components: x86
|
||||
//@ revisions: x86_64_fuchsia
|
||||
//@ [x86_64_fuchsia] compile-flags: --target x86_64-fuchsia
|
||||
//@ [x86_64_fuchsia] needs-llvm-components: x86
|
||||
//@ revisions: x86_64_linux_android
|
||||
//@ [x86_64_linux_android] compile-flags: --target x86_64-linux-android
|
||||
//@ [x86_64_linux_android] needs-llvm-components: x86
|
||||
|
16
tests/ui/macros/break-last-token-twice.rs
Normal file
16
tests/ui/macros/break-last-token-twice.rs
Normal file
@ -0,0 +1,16 @@
|
||||
//@ check-pass
|
||||
|
||||
macro_rules! m {
|
||||
(static $name:ident: $t:ty = $e:expr) => {
|
||||
let $name: $t = $e;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
m! {
|
||||
// Tricky: the trailing `>>=` token here is broken twice:
|
||||
// - into `>` and `>=`
|
||||
// - then the `>=` is broken into `>` and `=`
|
||||
static _x: Vec<Vec<u32>>= vec![]
|
||||
}
|
||||
}
|
@ -4,28 +4,46 @@
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn f1() {
|
||||
fn in_loop() {
|
||||
loop
|
||||
let x = 0; //~ ERROR expected `{`, found keyword `let`
|
||||
drop(0);
|
||||
}
|
||||
|
||||
fn f2() {
|
||||
fn in_while() {
|
||||
while true
|
||||
let x = 0; //~ ERROR expected `{`, found keyword `let`
|
||||
}
|
||||
|
||||
fn f3() {
|
||||
fn in_for() {
|
||||
for x in 0..1
|
||||
let x = 0; //~ ERROR expected `{`, found keyword `let`
|
||||
}
|
||||
|
||||
fn f4() {
|
||||
|
||||
// FIXME
|
||||
fn in_try() {
|
||||
try //~ ERROR expected expression, found reserved keyword `try`
|
||||
let x = 0;
|
||||
}
|
||||
|
||||
fn f5() {
|
||||
// FIXME(#80931)
|
||||
fn in_async() {
|
||||
async
|
||||
let x = 0; //~ ERROR expected one of `move`, `|`, or `||`, found keyword `let`
|
||||
}
|
||||
|
||||
// FIXME(#78168)
|
||||
fn in_const() {
|
||||
let x = const 2; //~ ERROR expected expression, found keyword `const`
|
||||
}
|
||||
|
||||
// FIXME(#78168)
|
||||
fn in_const_in_match() {
|
||||
let x = 2;
|
||||
match x {
|
||||
const 2 => {}
|
||||
//~^ ERROR expected identifier, found keyword `const`
|
||||
//~| ERROR expected one of `=>`, `if`, or `|`, found `2`
|
||||
}
|
||||
}
|
||||
|
@ -38,18 +38,36 @@ LL | { let x = 0; }
|
||||
| + +
|
||||
|
||||
error: expected expression, found reserved keyword `try`
|
||||
--> $DIR/block-no-opening-brace.rs:24:5
|
||||
--> $DIR/block-no-opening-brace.rs:26:5
|
||||
|
|
||||
LL | try
|
||||
| ^^^ expected expression
|
||||
|
||||
error: expected one of `move`, `|`, or `||`, found keyword `let`
|
||||
--> $DIR/block-no-opening-brace.rs:30:9
|
||||
--> $DIR/block-no-opening-brace.rs:33:9
|
||||
|
|
||||
LL | async
|
||||
| - expected one of `move`, `|`, or `||`
|
||||
LL | let x = 0;
|
||||
| ^^^ unexpected token
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error: expected expression, found keyword `const`
|
||||
--> $DIR/block-no-opening-brace.rs:38:13
|
||||
|
|
||||
LL | let x = const 2;
|
||||
| ^^^^^ expected expression
|
||||
|
||||
error: expected identifier, found keyword `const`
|
||||
--> $DIR/block-no-opening-brace.rs:45:9
|
||||
|
|
||||
LL | const 2 => {}
|
||||
| ^^^^^ expected identifier, found keyword
|
||||
|
||||
error: expected one of `=>`, `if`, or `|`, found `2`
|
||||
--> $DIR/block-no-opening-brace.rs:45:15
|
||||
|
|
||||
LL | const 2 => {}
|
||||
| ^ expected one of `=>`, `if`, or `|`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
@ -923,10 +923,8 @@ contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
|
||||
users_on_vacation = [
|
||||
"fmease",
|
||||
"jhpratt",
|
||||
"joboet",
|
||||
"jyn514",
|
||||
"oli-obk",
|
||||
"tgross35",
|
||||
]
|
||||
|
||||
[assign.adhoc_groups]
|
||||
|
Loading…
Reference in New Issue
Block a user