Merge from rustc
This commit is contained in:
commit
8a3b7463ed
128 changed files with 1918 additions and 1748 deletions
|
|
@ -49,14 +49,6 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Which niches (beyond the `null` niche) are available on references.
|
||||
#[derive(Default, Copy, Clone, Hash, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
|
||||
pub struct ReferenceNichePolicy {
|
||||
pub size: bool,
|
||||
pub align: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
|
||||
pub enum IntegerType {
|
||||
|
|
@ -354,33 +346,6 @@ impl TargetDataLayout {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn target_usize_max(&self) -> u64 {
|
||||
self.pointer_size.unsigned_int_max().try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn target_isize_min(&self) -> i64 {
|
||||
self.pointer_size.signed_int_min().try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn target_isize_max(&self) -> i64 {
|
||||
self.pointer_size.signed_int_max().try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the (inclusive) range of possible addresses for an allocation with
|
||||
/// the given size and alignment.
|
||||
///
|
||||
/// Note that this doesn't take into account target-specific limitations.
|
||||
#[inline]
|
||||
pub fn address_range_for(&self, size: Size, align: Align) -> (u64, u64) {
|
||||
let end = Size::from_bytes(self.target_usize_max());
|
||||
let min = align.bytes();
|
||||
let max = (end - size).align_down_to(align).bytes();
|
||||
(min, max)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
|
||||
for &(size, align) in &self.vector_align {
|
||||
|
|
@ -508,12 +473,6 @@ impl Size {
|
|||
Size::from_bytes((self.bytes() + mask) & !mask)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn align_down_to(self, align: Align) -> Size {
|
||||
let mask = align.bytes() - 1;
|
||||
Size::from_bytes(self.bytes() & !mask)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_aligned(self, align: Align) -> bool {
|
||||
let mask = align.bytes() - 1;
|
||||
|
|
@ -1008,43 +967,6 @@ impl WrappingRange {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `range` is contained in `self`.
|
||||
#[inline(always)]
|
||||
pub fn contains_range<I: Into<u128> + Ord>(&self, range: RangeInclusive<I>) -> bool {
|
||||
if range.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let (vmin, vmax) = range.into_inner();
|
||||
let (vmin, vmax) = (vmin.into(), vmax.into());
|
||||
|
||||
if self.start <= self.end {
|
||||
self.start <= vmin && vmax <= self.end
|
||||
} else {
|
||||
// The last check is needed to cover the following case:
|
||||
// `vmin ... start, end ... vmax`. In this special case there is no gap
|
||||
// between `start` and `end` so we must return true.
|
||||
self.start <= vmin || vmax <= self.end || self.start == self.end + 1
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `range` has an overlap with `self`.
|
||||
#[inline(always)]
|
||||
pub fn overlaps_range<I: Into<u128> + Ord>(&self, range: RangeInclusive<I>) -> bool {
|
||||
if range.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let (vmin, vmax) = range.into_inner();
|
||||
let (vmin, vmax) = (vmin.into(), vmax.into());
|
||||
|
||||
if self.start <= self.end {
|
||||
self.start <= vmax && vmin <= self.end
|
||||
} else {
|
||||
self.start <= vmax || vmin <= self.end
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `self` with replaced `start`
|
||||
#[inline(always)]
|
||||
pub fn with_start(mut self, start: u128) -> Self {
|
||||
|
|
@ -1062,15 +984,9 @@ impl WrappingRange {
|
|||
/// Returns `true` if `size` completely fills the range.
|
||||
#[inline]
|
||||
pub fn is_full_for(&self, size: Size) -> bool {
|
||||
debug_assert!(self.is_in_range_for(size));
|
||||
self.start == (self.end.wrapping_add(1) & size.unsigned_int_max())
|
||||
}
|
||||
|
||||
/// Returns `true` if the range is valid for `size`.
|
||||
#[inline(always)]
|
||||
pub fn is_in_range_for(&self, size: Size) -> bool {
|
||||
let max_value = size.unsigned_int_max();
|
||||
self.start <= max_value && self.end <= max_value
|
||||
debug_assert!(self.start <= max_value && self.end <= max_value);
|
||||
self.start == (self.end.wrapping_add(1) & max_value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1511,21 +1427,16 @@ impl Niche {
|
|||
|
||||
pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
|
||||
assert!(count > 0);
|
||||
if count > self.available(cx) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Self { value, valid_range: v, .. } = *self;
|
||||
let max_value = value.size(cx).unsigned_int_max();
|
||||
let distance_end_zero = max_value - v.end;
|
||||
let size = value.size(cx);
|
||||
assert!(size.bits() <= 128);
|
||||
let max_value = size.unsigned_int_max();
|
||||
|
||||
// Null-pointer optimization. This is guaranteed by Rust (at least for `Option<_>`),
|
||||
// and offers better codegen opportunities.
|
||||
if count == 1 && matches!(value, Pointer(_)) && !v.contains(0) {
|
||||
// Select which bound to move to minimize the number of lost niches.
|
||||
let valid_range =
|
||||
if v.start - 1 > distance_end_zero { v.with_end(0) } else { v.with_start(0) };
|
||||
return Some((0, Scalar::Initialized { value, valid_range }));
|
||||
let niche = v.end.wrapping_add(1)..v.start;
|
||||
let available = niche.end.wrapping_sub(niche.start) & max_value;
|
||||
if count > available {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Extend the range of valid values being reserved by moving either `v.start` or `v.end` bound.
|
||||
|
|
@ -1548,6 +1459,7 @@ impl Niche {
|
|||
let end = v.end.wrapping_add(count) & max_value;
|
||||
Some((start, Scalar::Initialized { value, valid_range: v.with_end(end) }))
|
||||
};
|
||||
let distance_end_zero = max_value - v.end;
|
||||
if v.start > v.end {
|
||||
// zero is unavailable because wrapping occurs
|
||||
move_end(v)
|
||||
|
|
|
|||
|
|
@ -2353,7 +2353,12 @@ impl Param {
|
|||
/// Builds a `Param` object from `ExplicitSelf`.
|
||||
pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param {
|
||||
let span = eself.span.to(eself_ident.span);
|
||||
let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None });
|
||||
let infer_ty = P(Ty {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: TyKind::ImplicitSelf,
|
||||
span: eself_ident.span,
|
||||
tokens: None,
|
||||
});
|
||||
let (mutbl, ty) = match eself.node {
|
||||
SelfKind::Explicit(ty, mutbl) => (mutbl, ty),
|
||||
SelfKind::Value(mutbl) => (mutbl, infer_ty),
|
||||
|
|
|
|||
|
|
@ -12,9 +12,11 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install rustfmt
|
||||
- name: Avoid installing rustc-dev
|
||||
run: |
|
||||
rustup component add rustfmt
|
||||
sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain
|
||||
echo 'profile = "minimal"' >> rust-toolchain
|
||||
rustfmt -v
|
||||
|
||||
- name: Rustfmt
|
||||
run: |
|
||||
|
|
@ -127,7 +129,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare dependencies
|
||||
run: ./y.rs prepare
|
||||
run: ./y.sh prepare
|
||||
|
||||
- name: Disable JIT tests
|
||||
run: |
|
||||
|
|
@ -136,7 +138,7 @@ jobs:
|
|||
- name: Test
|
||||
env:
|
||||
TARGET_TRIPLE: x86_64-unknown-linux-gnu
|
||||
run: ./y.rs test --use-backend llvm
|
||||
run: ./y.sh test --use-backend llvm
|
||||
|
||||
bench:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -51,18 +51,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-bforest"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6160c0a96253993b79fb7e0983534a4515ecf666120ddf8f92068114997ebc"
|
||||
checksum = "ec27af72e56235eb326b5bf2de4e70ab7c5ac1fb683a1829595badaf821607fd"
|
||||
dependencies = [
|
||||
"cranelift-entity",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b38da5f63562e42f3c929d7c76871098e5ad12c8ab44b0659ffc529f22a5b3a"
|
||||
checksum = "2231e12925e6c5f4bc9c95b62a798eea6ed669a95bc3e00f8b2adb3b7b9b7a80"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"cranelift-bforest",
|
||||
|
|
@ -81,39 +81,39 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-meta"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "011371e213e163b55dd9e8404b3f2d9fa52cd14dc2f3dc5b83e61ffceff126db"
|
||||
checksum = "413b00b8dfb3aab85674a534677e7ca08854b503f164a70ec0634fce80996e2c"
|
||||
dependencies = [
|
||||
"cranelift-codegen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-shared"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf97dde7f5ad571161cdd203a2c9c88682ef669830aea3c14ea5d164ef8bb43"
|
||||
checksum = "cd0feb9ecc8193ef5cb04f494c5bd835e5bfec4bde726e7ac0444fc9dd76229e"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-control"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd9a9254aee733b0f2b68e5eaaf0337ad53cb23252a056c10a35370551be8d40"
|
||||
checksum = "72eedd2afcf5fee1e042eaaf18d3750e48ad0eca364a9f5971ecfdd5ef85bf71"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-entity"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf39a33ee39479d1337cd9333f3c09786c5a0ca1ec509edcaf9d1346d5de0e5"
|
||||
checksum = "7af19157be42671073cf8c2a52d6a4ae1e7b11f1dcb4131fede356d9f91c29dd"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-frontend"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e260b92a193a0a2dccc3938f133d9532e7dcfe8d03e36bf8b7d3518c1c1793"
|
||||
checksum = "c2dc7636c5fad156be7d9ae691cd1aaecd97326caf2ab534ba168056d56aa76c"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
|
|
@ -123,15 +123,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-isle"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9446c8e1aadfcdacee1a49592bc2c25d1d9bf5484782c163e7f5485c92cd3c1c"
|
||||
checksum = "c1111aea4fb6fade5779903f184249a3fc685a799fe4ec59126f9af59c7c2a74"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-jit"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "689a6df165d0f860c1e1a3d53c28944e2743c3e9ee4c678cf190fe60ad7a6ef5"
|
||||
checksum = "dadf88076317f6286ec77ebbe65978734fb43b6befdc96f52ff4c4c511841644"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -149,9 +149,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-module"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b1402d6ff1695b429536b2eaa126db560fc94c375ed0e9cfb15051fc07427f7"
|
||||
checksum = "c6bae8a82dbf82241b1083e57e06870d2c2bdc9852727be99d58477513816953"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -160,9 +160,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-native"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac916f3c5aff4b817e42fc2e682292b931495b3fe2603d5e3c3cf602d74e344"
|
||||
checksum = "1ecfc01a634448468a698beac433d98040033046678a0eed3ca39a3a9f63ae86"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"libc",
|
||||
|
|
@ -171,9 +171,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-object"
|
||||
version = "0.96.1"
|
||||
version = "0.98.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23860f4cd064017f2108e6bc5d25660a77cd6eea77f1ac0756870a00abb12e93"
|
||||
checksum = "0ee14a7276999f0dcaae2de84043e2c2de50820fb89b3db56fab586a4ad26734"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -193,6 +193,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
|
|
@ -206,7 +212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
|
||||
dependencies = [
|
||||
"fallible-iterator",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
|
|
@ -225,6 +231,12 @@ dependencies = [
|
|||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
|
|
@ -235,6 +247,16 @@ dependencies = [
|
|||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
|
|
@ -283,7 +305,7 @@ checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
|
|||
dependencies = [
|
||||
"crc32fast",
|
||||
"hashbrown 0.13.2",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
|
|
@ -295,9 +317,9 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
|||
|
||||
[[package]]
|
||||
name = "regalloc2"
|
||||
version = "0.8.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a52e724646c6c0800fc456ec43b4165d2f91fba88ceaca06d9e0b400023478"
|
||||
checksum = "5b4dcbd3a2ae7fb94b5813fa0e957c6ab51bf5d0a8ee1b69e0c2d0f1e6eb8485"
|
||||
dependencies = [
|
||||
"hashbrown 0.13.2",
|
||||
"log",
|
||||
|
|
@ -335,7 +357,7 @@ dependencies = [
|
|||
"cranelift-native",
|
||||
"cranelift-object",
|
||||
"gimli",
|
||||
"indexmap",
|
||||
"indexmap 2.0.0",
|
||||
"libloading",
|
||||
"object",
|
||||
"smallvec",
|
||||
|
|
@ -374,9 +396,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||
|
||||
[[package]]
|
||||
name = "wasmtime-jit-icache-coherence"
|
||||
version = "9.0.1"
|
||||
version = "11.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d90933b781e1cef7656baed671c7a90bdba0c1c694e04fdd4124419308f5cbb"
|
||||
checksum = "e34eb67f0829a5614ec54716c8e0c9fe68fab7b9df3686c85f719c9d247f7169"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
|
|
|||
|
|
@ -8,17 +8,17 @@ crate-type = ["dylib"]
|
|||
|
||||
[dependencies]
|
||||
# These have to be in sync with each other
|
||||
cranelift-codegen = { version = "0.96.1", features = ["unwind", "all-arch"] }
|
||||
cranelift-frontend = { version = "0.96.1" }
|
||||
cranelift-module = { version = "0.96.1" }
|
||||
cranelift-native = { version = "0.96.1" }
|
||||
cranelift-jit = { version = "0.96.1", optional = true }
|
||||
cranelift-object = { version = "0.96.1" }
|
||||
cranelift-codegen = { version = "0.98", features = ["unwind", "all-arch"] }
|
||||
cranelift-frontend = { version = "0.98" }
|
||||
cranelift-module = { version = "0.98" }
|
||||
cranelift-native = { version = "0.98" }
|
||||
cranelift-jit = { version = "0.98", optional = true }
|
||||
cranelift-object = { version = "0.98" }
|
||||
target-lexicon = "0.12.0"
|
||||
gimli = { version = "0.27.2", default-features = false, features = ["write"]}
|
||||
object = { version = "0.30.3", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
|
||||
|
||||
indexmap = "1.9.3"
|
||||
indexmap = "2.0.0"
|
||||
libloading = { version = "0.7.3", optional = true }
|
||||
smallvec = "1.8.1"
|
||||
|
||||
|
|
|
|||
|
|
@ -65,12 +65,12 @@ to `./build/host/stage2/bin/`. Note that you would need to do this every time yo
|
|||
5. Copy cargo from another toolchain: `cp $(rustup which cargo) .build/<your hostname triple>/stage2/bin/cargo`
|
||||
* Another option is to build it at step 3 and copy with other executables at step 4.
|
||||
6. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`.
|
||||
7. (Windows only) compile y.rs: `rustc +stage2 -O y.rs`.
|
||||
8. You need to prefix every `./y.rs` (or `y` if you built `y.rs`) command by `rustup run stage2` to make cg_clif use your local changes in rustc.
|
||||
7. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`.
|
||||
8. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc.
|
||||
|
||||
* `rustup run stage2 ./y.rs prepare`
|
||||
* `rustup run stage2 ./y.rs build`
|
||||
* (Optional) run tests: `rustup run stage2 ./y.rs test`
|
||||
* `rustup run stage2 ./y.sh prepare`
|
||||
* `rustup run stage2 ./y.sh build`
|
||||
* (Optional) run tests: `rustup run stage2 ./y.sh test`
|
||||
9. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`.
|
||||
|
||||
## Configuration
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use super::path::{Dirs, RelPath};
|
||||
|
|
@ -30,6 +31,12 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
|
|||
|
||||
let bench_runs = env::var("BENCH_RUNS").unwrap_or_else(|_| "10".to_string()).parse().unwrap();
|
||||
|
||||
let mut gha_step_summary = if let Ok(file) = std::env::var("GITHUB_STEP_SUMMARY") {
|
||||
Some(std::fs::OpenOptions::new().append(true).open(file).unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
eprintln!("[BENCH COMPILE] ebobby/simple-raytracer");
|
||||
let cargo_clif = RelPath::DIST
|
||||
.to_path(dirs)
|
||||
|
|
@ -60,36 +67,64 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
|
|||
target_dir = target_dir.display(),
|
||||
);
|
||||
|
||||
let bench_compile_markdown = RelPath::DIST.to_path(dirs).join("bench_compile.md");
|
||||
|
||||
let bench_compile = hyperfine_command(
|
||||
1,
|
||||
bench_runs,
|
||||
Some(&clean_cmd),
|
||||
&[&llvm_build_cmd, &clif_build_cmd, &clif_build_opt_cmd],
|
||||
&[
|
||||
("cargo build", &llvm_build_cmd),
|
||||
("cargo-clif build", &clif_build_cmd),
|
||||
("cargo-clif build --release", &clif_build_opt_cmd),
|
||||
],
|
||||
&bench_compile_markdown,
|
||||
);
|
||||
|
||||
spawn_and_wait(bench_compile);
|
||||
|
||||
if let Some(gha_step_summary) = gha_step_summary.as_mut() {
|
||||
gha_step_summary.write_all(b"## Compile ebobby/simple-raytracer\n\n").unwrap();
|
||||
gha_step_summary.write_all(&std::fs::read(bench_compile_markdown).unwrap()).unwrap();
|
||||
gha_step_summary.write_all(b"\n").unwrap();
|
||||
}
|
||||
|
||||
eprintln!("[BENCH RUN] ebobby/simple-raytracer");
|
||||
|
||||
let bench_run_markdown = RelPath::DIST.to_path(dirs).join("bench_run.md");
|
||||
|
||||
let raytracer_cg_llvm = Path::new(".").join(get_file_name(
|
||||
&bootstrap_host_compiler.rustc,
|
||||
"raytracer_cg_llvm",
|
||||
"bin",
|
||||
));
|
||||
let raytracer_cg_clif = Path::new(".").join(get_file_name(
|
||||
&bootstrap_host_compiler.rustc,
|
||||
"raytracer_cg_clif",
|
||||
"bin",
|
||||
));
|
||||
let raytracer_cg_clif_opt = Path::new(".").join(get_file_name(
|
||||
&bootstrap_host_compiler.rustc,
|
||||
"raytracer_cg_clif_opt",
|
||||
"bin",
|
||||
));
|
||||
let mut bench_run = hyperfine_command(
|
||||
0,
|
||||
bench_runs,
|
||||
None,
|
||||
&[
|
||||
Path::new(".")
|
||||
.join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_llvm", "bin"))
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
Path::new(".")
|
||||
.join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif", "bin"))
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
Path::new(".")
|
||||
.join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif_opt", "bin"))
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
("", raytracer_cg_llvm.to_str().unwrap()),
|
||||
("", raytracer_cg_clif.to_str().unwrap()),
|
||||
("", raytracer_cg_clif_opt.to_str().unwrap()),
|
||||
],
|
||||
&bench_run_markdown,
|
||||
);
|
||||
bench_run.current_dir(RelPath::BUILD.to_path(dirs));
|
||||
spawn_and_wait(bench_run);
|
||||
|
||||
if let Some(gha_step_summary) = gha_step_summary.as_mut() {
|
||||
gha_step_summary.write_all(b"## Run ebobby/simple-raytracer\n\n").unwrap();
|
||||
gha_step_summary.write_all(&std::fs::read(bench_run_markdown).unwrap()).unwrap();
|
||||
gha_step_summary.write_all(b"\n").unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
|||
|
||||
use super::path::{Dirs, RelPath};
|
||||
use super::rustc_info::get_file_name;
|
||||
use super::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler};
|
||||
use super::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler, LogGroup};
|
||||
|
||||
pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif");
|
||||
|
||||
|
|
@ -13,6 +13,8 @@ pub(crate) fn build_backend(
|
|||
bootstrap_host_compiler: &Compiler,
|
||||
use_unstable_features: bool,
|
||||
) -> PathBuf {
|
||||
let _group = LogGroup::guard("Build backend");
|
||||
|
||||
let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs);
|
||||
maybe_incremental(&mut cmd);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use super::path::{Dirs, RelPath};
|
|||
use super::rustc_info::get_file_name;
|
||||
use super::utils::{
|
||||
maybe_incremental, remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler,
|
||||
LogGroup,
|
||||
};
|
||||
use super::{CodegenBackend, SysrootKind};
|
||||
|
||||
|
|
@ -22,6 +23,8 @@ pub(crate) fn build_sysroot(
|
|||
rustup_toolchain_name: Option<&str>,
|
||||
target_triple: String,
|
||||
) -> Compiler {
|
||||
let _guard = LogGroup::guard("Build sysroot");
|
||||
|
||||
eprintln!("[BUILD] sysroot {:?}", sysroot_kind);
|
||||
|
||||
DIST_DIR.ensure_fresh(dirs);
|
||||
|
|
@ -251,7 +254,10 @@ fn build_clif_sysroot_for_triple(
|
|||
rustflags
|
||||
.push_str(&format!(" --sysroot {}", RTSTARTUP_SYSROOT.to_path(dirs).to_str().unwrap()));
|
||||
if channel == "release" {
|
||||
rustflags.push_str(" -Zmir-opt-level=3");
|
||||
// Incremental compilation by default disables mir inlining. This leads to both a decent
|
||||
// compile perf and a significant runtime perf regression. As such forcefully enable mir
|
||||
// inlining.
|
||||
rustflags.push_str(" -Zinline-mir");
|
||||
}
|
||||
compiler.rustflags += &rustflags;
|
||||
let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) {
|
|||
STDLIB_SRC.to_path(dirs).join("Cargo.toml"),
|
||||
r#"
|
||||
[workspace]
|
||||
resolver = "1"
|
||||
members = ["./library/sysroot"]
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use super::config;
|
|||
use super::path::{Dirs, RelPath};
|
||||
use super::prepare::{apply_patches, GitRepo};
|
||||
use super::rustc_info::get_default_sysroot;
|
||||
use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler};
|
||||
use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler, LogGroup};
|
||||
use super::{CodegenBackend, SysrootKind};
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
|
|
@ -21,6 +21,7 @@ struct TestCase {
|
|||
enum TestCaseCmd {
|
||||
Custom { func: &'static dyn Fn(&TestRunner<'_>) },
|
||||
BuildLib { source: &'static str, crate_types: &'static str },
|
||||
BuildBin { source: &'static str },
|
||||
BuildBinAndRun { source: &'static str, args: &'static [&'static str] },
|
||||
JitBin { source: &'static str, args: &'static str },
|
||||
}
|
||||
|
|
@ -39,6 +40,10 @@ impl TestCase {
|
|||
Self { config, cmd: TestCaseCmd::BuildLib { source, crate_types } }
|
||||
}
|
||||
|
||||
const fn build_bin(config: &'static str, source: &'static str) -> Self {
|
||||
Self { config, cmd: TestCaseCmd::BuildBin { source } }
|
||||
}
|
||||
|
||||
const fn build_bin_and_run(
|
||||
config: &'static str,
|
||||
source: &'static str,
|
||||
|
|
@ -92,6 +97,7 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[
|
|||
TestCase::build_bin_and_run("aot.float-minmax-pass", "example/float-minmax-pass.rs", &[]),
|
||||
TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]),
|
||||
TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]),
|
||||
TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"),
|
||||
];
|
||||
|
||||
// FIXME(rust-random/rand#1293): Newer rand versions fail to test on Windows. Update once this is
|
||||
|
|
@ -119,8 +125,8 @@ pub(crate) static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir
|
|||
pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github(
|
||||
"rust-lang",
|
||||
"portable-simd",
|
||||
"ad8afa8c81273b3b49acbea38cd3bcf17a34cf2b",
|
||||
"800548f8000e31bd",
|
||||
"7c7dbe0c505ccbc02ff30c1e37381ab1d47bf46f",
|
||||
"5bcc9c544f6fa7bd",
|
||||
"portable-simd",
|
||||
);
|
||||
|
||||
|
|
@ -380,15 +386,17 @@ impl<'a> TestRunner<'a> {
|
|||
let tag = tag.to_uppercase();
|
||||
let is_jit_test = tag == "JIT";
|
||||
|
||||
if !config::get_bool(config)
|
||||
let _guard = if !config::get_bool(config)
|
||||
|| (is_jit_test && !self.jit_supported)
|
||||
|| self.skip_tests.contains(&config)
|
||||
{
|
||||
eprintln!("[{tag}] {testname} (skipped)");
|
||||
continue;
|
||||
} else {
|
||||
let guard = LogGroup::guard(&format!("[{tag}] {testname}"));
|
||||
eprintln!("[{tag}] {testname}");
|
||||
}
|
||||
guard
|
||||
};
|
||||
|
||||
match *cmd {
|
||||
TestCaseCmd::Custom { func } => func(self),
|
||||
|
|
@ -405,6 +413,13 @@ impl<'a> TestRunner<'a> {
|
|||
]);
|
||||
}
|
||||
}
|
||||
TestCaseCmd::BuildBin { source } => {
|
||||
if self.use_unstable_features {
|
||||
self.run_rustc([source]);
|
||||
} else {
|
||||
self.run_rustc([source, "--cfg", "no_unstable_features"]);
|
||||
}
|
||||
}
|
||||
TestCaseCmd::BuildBinAndRun { source, args } => {
|
||||
if self.use_unstable_features {
|
||||
self.run_rustc([source]);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::fs;
|
|||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{self, Command, Stdio};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use super::path::{Dirs, RelPath};
|
||||
|
||||
|
|
@ -136,10 +137,13 @@ pub(crate) fn hyperfine_command(
|
|||
warmup: u64,
|
||||
runs: u64,
|
||||
prepare: Option<&str>,
|
||||
cmds: &[&str],
|
||||
cmds: &[(&str, &str)],
|
||||
markdown_export: &Path,
|
||||
) -> Command {
|
||||
let mut bench = Command::new("hyperfine");
|
||||
|
||||
bench.arg("--export-markdown").arg(markdown_export);
|
||||
|
||||
if warmup != 0 {
|
||||
bench.arg("--warmup").arg(warmup.to_string());
|
||||
}
|
||||
|
|
@ -152,7 +156,12 @@ pub(crate) fn hyperfine_command(
|
|||
bench.arg("--prepare").arg(prepare);
|
||||
}
|
||||
|
||||
bench.args(cmds);
|
||||
for &(name, cmd) in cmds {
|
||||
if name != "" {
|
||||
bench.arg("-n").arg(name);
|
||||
}
|
||||
bench.arg(cmd);
|
||||
}
|
||||
|
||||
bench
|
||||
}
|
||||
|
|
@ -167,6 +176,8 @@ pub(crate) fn git_command<'a>(repo_dir: impl Into<Option<&'a Path>>, cmd: &str)
|
|||
.arg("user.email=dummy@example.com")
|
||||
.arg("-c")
|
||||
.arg("core.autocrlf=false")
|
||||
.arg("-c")
|
||||
.arg("commit.gpgSign=false")
|
||||
.arg(cmd);
|
||||
if let Some(repo_dir) = repo_dir.into() {
|
||||
git_cmd.current_dir(repo_dir);
|
||||
|
|
@ -259,6 +270,33 @@ pub(crate) fn is_ci_opt() -> bool {
|
|||
env::var("CI_OPT").is_ok()
|
||||
}
|
||||
|
||||
static IN_GROUP: AtomicBool = AtomicBool::new(false);
|
||||
pub(crate) struct LogGroup {
|
||||
is_gha: bool,
|
||||
}
|
||||
|
||||
impl LogGroup {
|
||||
pub(crate) fn guard(name: &str) -> LogGroup {
|
||||
let is_gha = env::var("GITHUB_ACTIONS").is_ok();
|
||||
|
||||
assert!(!IN_GROUP.swap(true, Ordering::SeqCst));
|
||||
if is_gha {
|
||||
eprintln!("::group::{name}");
|
||||
}
|
||||
|
||||
LogGroup { is_gha }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LogGroup {
|
||||
fn drop(&mut self) {
|
||||
if self.is_gha {
|
||||
eprintln!("::endgroup::");
|
||||
}
|
||||
IN_GROUP.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_incremental(cmd: &mut Command) {
|
||||
if is_ci() || std::env::var("CARGO_BUILD_INCREMENTAL").map_or(false, |val| val == "false") {
|
||||
// Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ aot.track-caller-attribute
|
|||
aot.float-minmax-pass
|
||||
aot.mod_bench
|
||||
aot.issue-72793
|
||||
aot.issue-59326
|
||||
|
||||
testsuite.extended_sysroot
|
||||
test.rust-random/rand
|
||||
|
|
|
|||
27
compiler/rustc_codegen_cranelift/example/issue-59326.rs
Normal file
27
compiler/rustc_codegen_cranelift/example/issue-59326.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Based on https://github.com/rust-lang/rust/blob/689511047a75a30825e367d4fd45c74604d0b15e/tests/ui/issues/issue-59326.rs#L1
|
||||
// check-pass
|
||||
trait Service {
|
||||
type S;
|
||||
}
|
||||
|
||||
trait Framing {
|
||||
type F;
|
||||
}
|
||||
|
||||
impl Framing for () {
|
||||
type F = ();
|
||||
}
|
||||
|
||||
trait HttpService<F: Framing>: Service<S = F::F> {}
|
||||
|
||||
type BoxService = Box<dyn HttpService<(), S = ()>>;
|
||||
|
||||
fn build_server<F: FnOnce() -> BoxService>(_: F) {}
|
||||
|
||||
fn make_server<F: Framing>() -> Box<dyn HttpService<F, S = F::F>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
build_server(|| make_server())
|
||||
}
|
||||
|
|
@ -547,7 +547,9 @@ impl<T> Box<T> {
|
|||
impl<T: ?Sized, A> Drop for Box<T, A> {
|
||||
fn drop(&mut self) {
|
||||
// inner value is dropped by compiler
|
||||
libc::free(self.0.pointer.0 as *mut u8);
|
||||
unsafe {
|
||||
libc::free(self.0.pointer.0 as *mut u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
#![feature(core_intrinsics, generators, generator_trait, is_sorted, repr_simd)]
|
||||
#![feature(
|
||||
core_intrinsics,
|
||||
generators,
|
||||
generator_trait,
|
||||
is_sorted,
|
||||
repr_simd,
|
||||
tuple_trait,
|
||||
unboxed_closures
|
||||
)]
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::arch::x86_64::*;
|
||||
|
|
@ -155,12 +163,34 @@ fn main() {
|
|||
}
|
||||
|
||||
foo(I64X2(0, 0));
|
||||
|
||||
transmute_fat_pointer();
|
||||
|
||||
rust_call_abi();
|
||||
}
|
||||
|
||||
fn panic(_: u128) {
|
||||
panic!();
|
||||
}
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
type TwoPtrs = i64;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
type TwoPtrs = i128;
|
||||
|
||||
fn transmute_fat_pointer() -> TwoPtrs {
|
||||
unsafe { transmute::<_, TwoPtrs>("true !") }
|
||||
}
|
||||
|
||||
extern "rust-call" fn rust_call_abi_callee<T: std::marker::Tuple>(_: T) {}
|
||||
|
||||
fn rust_call_abi() {
|
||||
rust_call_abi_callee(());
|
||||
rust_call_abi_callee((1, 2));
|
||||
}
|
||||
|
||||
#[repr(simd)]
|
||||
struct I64X2(i64, i64);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,18 @@ Cranelift doesn't support them yet
|
|||
library/core/tests/atomic.rs | 4 ---
|
||||
4 files changed, 4 insertions(+), 50 deletions(-)
|
||||
|
||||
diff --git a/lib.rs b/lib.rs
|
||||
index 897a5e9..331f66f 100644
|
||||
--- a/lib.rs
|
||||
+++ b/lib.rs
|
||||
@@ -93,7 +93,6 @@
|
||||
#![feature(const_option)]
|
||||
#![feature(const_option_ext)]
|
||||
#![feature(const_result)]
|
||||
-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
|
||||
#![feature(int_roundings)]
|
||||
#![feature(slice_group_by)]
|
||||
#![feature(split_array)]
|
||||
diff --git a/atomic.rs b/atomic.rs
|
||||
index b735957..ea728b6 100644
|
||||
--- a/atomic.rs
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
|
|||
index d9de37e..8293fce 100644
|
||||
--- a/library/core/src/sync/atomic.rs
|
||||
+++ b/library/core/src/sync/atomic.rs
|
||||
@@ -2234,46 +2234,6 @@ atomic_int! {
|
||||
"AtomicU64::new(0)",
|
||||
u64 AtomicU64 ATOMIC_U64_INIT
|
||||
@@ -2996,42 +2996,6 @@ atomic_int! {
|
||||
8,
|
||||
u64 AtomicU64
|
||||
}
|
||||
-#[cfg(target_has_atomic_load_store = "128")]
|
||||
-atomic_int! {
|
||||
|
|
@ -53,14 +53,12 @@ index d9de37e..8293fce 100644
|
|||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"),
|
||||
- "i128",
|
||||
- "#![feature(integer_atomics)]\n\n",
|
||||
- atomic_min, atomic_max,
|
||||
- 16,
|
||||
- "AtomicI128::new(0)",
|
||||
- i128 AtomicI128 ATOMIC_I128_INIT
|
||||
- i128 AtomicI128
|
||||
-}
|
||||
-#[cfg(target_has_atomic_load_store = "128")]
|
||||
-atomic_int! {
|
||||
|
|
@ -73,16 +71,15 @@ index d9de37e..8293fce 100644
|
|||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"),
|
||||
- "u128",
|
||||
- "#![feature(integer_atomics)]\n\n",
|
||||
- atomic_umin, atomic_umax,
|
||||
- 16,
|
||||
- "AtomicU128::new(0)",
|
||||
- u128 AtomicU128 ATOMIC_U128_INIT
|
||||
- u128 AtomicU128
|
||||
-}
|
||||
|
||||
#[cfg(target_has_atomic_load_store = "ptr")]
|
||||
macro_rules! atomic_int_ptr_sized {
|
||||
( $($target_pointer_width:literal $align:literal)* ) => { $(
|
||||
--
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.19.0"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
|
||||
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"gimli",
|
||||
|
|
@ -34,6 +34,12 @@ dependencies = [
|
|||
"rand_xorshift",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9"
|
||||
|
||||
[[package]]
|
||||
name = "auxv"
|
||||
version = "0.3.3"
|
||||
|
|
@ -68,9 +74,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.93"
|
||||
version = "0.1.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76630810d973ecea3dbf611e1b7aecfb1012751ef1ff8de3998f89014a166781"
|
||||
checksum = "6866e0f3638013234db3c89ead7a14d278354338e7237257407500009012b23f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -145,10 +151,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.13.2"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -156,9 +163,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
|
|
@ -186,9 +193,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"compiler_builtins",
|
||||
|
|
@ -198,9 +205,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.30.4"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
|
||||
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"memchr",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2023-06-15"
|
||||
channel = "nightly-2023-07-22"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools"]
|
||||
|
|
|
|||
|
|
@ -40,14 +40,22 @@ fn main() {
|
|||
"cargo"
|
||||
};
|
||||
|
||||
let args: Vec<_> = match env::args().nth(1).as_deref() {
|
||||
let mut args = env::args().skip(1).collect::<Vec<_>>();
|
||||
if args.get(0).map(|arg| &**arg) == Some("clif") {
|
||||
// Avoid infinite recursion when invoking `cargo-clif` as cargo subcommand using
|
||||
// `cargo clif`.
|
||||
args.remove(0);
|
||||
}
|
||||
|
||||
let args: Vec<_> = match args.get(0).map(|arg| &**arg) {
|
||||
Some("jit") => {
|
||||
env::set_var(
|
||||
"RUSTFLAGS",
|
||||
env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
|
||||
);
|
||||
args.remove(0);
|
||||
IntoIterator::into_iter(["rustc".to_string()])
|
||||
.chain(env::args().skip(2))
|
||||
.chain(args)
|
||||
.chain([
|
||||
"--".to_string(),
|
||||
"-Zunstable-options".to_string(),
|
||||
|
|
@ -60,8 +68,9 @@ fn main() {
|
|||
"RUSTFLAGS",
|
||||
env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic",
|
||||
);
|
||||
args.remove(0);
|
||||
IntoIterator::into_iter(["rustc".to_string()])
|
||||
.chain(env::args().skip(2))
|
||||
.chain(args)
|
||||
.chain([
|
||||
"--".to_string(),
|
||||
"-Zunstable-options".to_string(),
|
||||
|
|
@ -69,7 +78,7 @@ fn main() {
|
|||
])
|
||||
.collect()
|
||||
}
|
||||
_ => env::args().skip(1).collect(),
|
||||
_ => args,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ git fetch
|
|||
git checkout -- .
|
||||
git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')"
|
||||
|
||||
git -c user.name=Dummy -c user.email=dummy@example.com am ../patches/*-stdlib-*.patch
|
||||
git -c user.name=Dummy -c user.email=dummy@example.com -c commit.gpgSign=false \
|
||||
am ../patches/*-stdlib-*.patch
|
||||
|
||||
git apply - <<EOF
|
||||
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
|
||||
|
|
@ -51,7 +52,7 @@ popd
|
|||
# FIXME remove once inline asm is fully supported
|
||||
export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc"
|
||||
|
||||
export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd download/sysroot/sysroot_src; pwd)"
|
||||
export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd build/stdlib; pwd)"
|
||||
|
||||
# Allow the testsuite to use llvm tools
|
||||
host_triple=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
|
|||
# missing features
|
||||
# ================
|
||||
|
||||
rm -r tests/run-make/comment-section # cg_clif doesn't yet write the .comment section
|
||||
|
||||
# requires stack unwinding
|
||||
# FIXME add needs-unwind to this test
|
||||
rm -r tests/run-make/libtest-junit
|
||||
|
|
@ -98,8 +100,11 @@ rm -r tests/run-make/sepcomp-inlining # same
|
|||
rm -r tests/run-make/sepcomp-separate # same
|
||||
rm -r tests/run-make/sepcomp-cci-copies # same
|
||||
rm -r tests/run-make/volatile-intrinsics # same
|
||||
rm -r tests/run-make/llvm-ident # same
|
||||
rm -r tests/run-make/no-builtins-attribute # same
|
||||
rm tests/ui/abi/stack-protector.rs # requires stack protector support
|
||||
rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes
|
||||
rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific
|
||||
|
||||
# giving different but possibly correct results
|
||||
# =============================================
|
||||
|
|
@ -118,6 +123,7 @@ rm tests/ui/suggestions/derive-trait-for-method-call.rs # same
|
|||
rm tests/ui/typeck/issue-46112.rs # same
|
||||
rm tests/ui/consts/const_cmp_type_id.rs # same
|
||||
rm tests/ui/consts/issue-73976-monomorphic.rs # same
|
||||
rm tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs # same
|
||||
|
||||
# rustdoc-clif passes extra args, suppressing the help message when no args are passed
|
||||
rm -r tests/run-make/issue-88756-default-output
|
||||
|
|
@ -143,6 +149,8 @@ rm -r tests/run-make/used # same
|
|||
rm -r tests/run-make/no-alloc-shim
|
||||
rm -r tests/run-make/emit-to-stdout
|
||||
|
||||
rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported
|
||||
|
||||
# bugs in the test suite
|
||||
# ======================
|
||||
rm tests/ui/backtrace.rs # TODO warning
|
||||
|
|
@ -150,6 +158,8 @@ rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue
|
|||
|
||||
rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd
|
||||
|
||||
rm tests/ui/panic-handler/weak-lang-item-2.rs # Will be fixed by #113568
|
||||
|
||||
cp ../dist/bin/rustdoc-clif ../dist/bin/rustdoc # some tests expect bin/rustdoc to exist
|
||||
|
||||
# prevent $(RUSTDOC) from picking up the sysroot built by x.py. It conflicts with the one used by
|
||||
|
|
|
|||
|
|
@ -445,9 +445,14 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
|
||||
// Unpack arguments tuple for closures
|
||||
let mut args = if fn_sig.abi() == Abi::RustCall {
|
||||
assert_eq!(args.len(), 2, "rust-call abi requires two arguments");
|
||||
let self_arg = codegen_call_argument_operand(fx, &args[0]);
|
||||
let pack_arg = codegen_call_argument_operand(fx, &args[1]);
|
||||
let (self_arg, pack_arg) = match args {
|
||||
[pack_arg] => (None, codegen_call_argument_operand(fx, pack_arg)),
|
||||
[self_arg, pack_arg] => (
|
||||
Some(codegen_call_argument_operand(fx, self_arg)),
|
||||
codegen_call_argument_operand(fx, pack_arg),
|
||||
),
|
||||
_ => panic!("rust-call abi requires one or two arguments"),
|
||||
};
|
||||
|
||||
let tupled_arguments = match pack_arg.value.layout().ty.kind() {
|
||||
ty::Tuple(ref tupled_arguments) => tupled_arguments,
|
||||
|
|
@ -455,7 +460,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
};
|
||||
|
||||
let mut args = Vec::with_capacity(1 + tupled_arguments.len());
|
||||
args.push(self_arg);
|
||||
args.extend(self_arg);
|
||||
for i in 0..tupled_arguments.len() {
|
||||
args.push(CallArgument {
|
||||
value: pack_arg.value.value_field(fx, FieldIdx::new(i)),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use rustc_middle::ty::layout::FnAbiOf;
|
|||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
||||
use cranelift_codegen::ir::UserFuncName;
|
||||
use cranelift_codegen::CodegenError;
|
||||
use cranelift_module::ModuleError;
|
||||
|
||||
use crate::constant::ConstantCx;
|
||||
use crate::debuginfo::FunctionDebugContext;
|
||||
|
|
@ -172,7 +174,21 @@ pub(crate) fn compile_fn(
|
|||
// Define function
|
||||
cx.profiler.generic_activity("define function").run(|| {
|
||||
context.want_disasm = cx.should_write_ir;
|
||||
module.define_function(codegened_func.func_id, context).unwrap();
|
||||
match module.define_function(codegened_func.func_id, context) {
|
||||
Ok(()) => {}
|
||||
Err(ModuleError::Compilation(CodegenError::ImplLimitExceeded)) => {
|
||||
let handler = rustc_session::EarlyErrorHandler::new(
|
||||
rustc_session::config::ErrorOutputType::default(),
|
||||
);
|
||||
handler.early_error(format!(
|
||||
"backend implementation limit exceeded while compiling {name}",
|
||||
name = codegened_func.symbol_name
|
||||
));
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Error while defining {name}: {err:?}", name = codegened_func.symbol_name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if cx.should_write_ir {
|
||||
|
|
@ -356,7 +372,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
|
||||
codegen_panic_inner(
|
||||
fx,
|
||||
rustc_hir::LangItem::PanicBoundsCheck,
|
||||
rustc_hir::LangItem::PanicMisalignedPointerDereference,
|
||||
&[required, found, location],
|
||||
source_info.span,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ impl FunctionDebugContext {
|
|||
for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() {
|
||||
debug_context.dwarf.unit.line_program.row().address_offset = u64::from(start);
|
||||
if !loc.is_default() {
|
||||
let source_loc = *self.source_loc_set.get_index(loc.bits() as usize).unwrap();
|
||||
let source_loc = self.source_loc_set[loc.bits() as usize];
|
||||
create_row_for_span(debug_context, source_loc);
|
||||
} else {
|
||||
create_row_for_span(debug_context, self.function_source_loc);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ pub(crate) struct DebugContext {
|
|||
pub(crate) struct FunctionDebugContext {
|
||||
entry_id: UnitEntryId,
|
||||
function_source_loc: (FileId, u64, u64),
|
||||
source_loc_set: indexmap::IndexSet<(FileId, u64, u64)>,
|
||||
source_loc_set: IndexSet<(FileId, u64, u64)>,
|
||||
}
|
||||
|
||||
impl DebugContext {
|
||||
|
|
|
|||
|
|
@ -114,9 +114,9 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
|
|||
.iter()
|
||||
.map(|cgu| cgu.items_in_deterministic_order(tcx).into_iter())
|
||||
.flatten()
|
||||
.collect::<FxHashMap<_, (_, _)>>()
|
||||
.collect::<FxHashMap<_, _>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<(_, (_, _))>>();
|
||||
.collect::<Vec<(_, _)>>();
|
||||
|
||||
tcx.sess.time("codegen mono items", || {
|
||||
super::predefine_mono_items(tcx, &mut jit_module, &mono_items);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,20 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
// Spin loop hint
|
||||
}
|
||||
|
||||
// Used by is_x86_feature_detected!();
|
||||
"llvm.x86.xgetbv" => {
|
||||
// FIXME use the actual xgetbv instruction
|
||||
intrinsic_args!(fx, args => (v); intrinsic);
|
||||
|
||||
let v = v.load_scalar(fx);
|
||||
|
||||
// As of writing on XCR0 exists
|
||||
fx.bcx.ins().trapnz(v, TrapCode::UnreachableCodeReached);
|
||||
|
||||
let res = fx.bcx.ins().iconst(types::I64, 1 /* bit 0 must be set */);
|
||||
ret.write_cvalue(fx, CValue::by_val(res, fx.layout_of(fx.tcx.types.i64)));
|
||||
}
|
||||
|
||||
// Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8`
|
||||
"llvm.x86.sse2.pmovmskb.128"
|
||||
| "llvm.x86.avx2.pmovmskb"
|
||||
|
|
@ -53,7 +67,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32));
|
||||
ret.write_cvalue(fx, res);
|
||||
}
|
||||
"llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd" => {
|
||||
"llvm.x86.sse.cmp.ps" | "llvm.x86.sse2.cmp.pd" => {
|
||||
let (x, y, kind) = match args {
|
||||
[x, y, kind] => (x, y, kind),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
|
|
@ -66,18 +80,95 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
let flt_cc = match kind
|
||||
.try_to_bits(Size::from_bytes(1))
|
||||
.unwrap_or_else(|| panic!("kind not scalar: {:?}", kind))
|
||||
.try_into()
|
||||
.unwrap()
|
||||
{
|
||||
0 => FloatCC::Equal,
|
||||
1 => FloatCC::LessThan,
|
||||
2 => FloatCC::LessThanOrEqual,
|
||||
7 => FloatCC::Ordered,
|
||||
3 => FloatCC::Unordered,
|
||||
4 => FloatCC::NotEqual,
|
||||
5 => FloatCC::UnorderedOrGreaterThanOrEqual,
|
||||
6 => FloatCC::UnorderedOrGreaterThan,
|
||||
_CMP_EQ_OQ | _CMP_EQ_OS => FloatCC::Equal,
|
||||
_CMP_LT_OS | _CMP_LT_OQ => FloatCC::LessThan,
|
||||
_CMP_LE_OS | _CMP_LE_OQ => FloatCC::LessThanOrEqual,
|
||||
_CMP_UNORD_Q | _CMP_UNORD_S => FloatCC::Unordered,
|
||||
_CMP_NEQ_UQ | _CMP_NEQ_US => FloatCC::NotEqual,
|
||||
_CMP_NLT_US | _CMP_NLT_UQ => FloatCC::UnorderedOrGreaterThanOrEqual,
|
||||
_CMP_NLE_US | _CMP_NLE_UQ => FloatCC::UnorderedOrGreaterThan,
|
||||
_CMP_ORD_Q | _CMP_ORD_S => FloatCC::Ordered,
|
||||
_CMP_EQ_UQ | _CMP_EQ_US => FloatCC::UnorderedOrEqual,
|
||||
_CMP_NGE_US | _CMP_NGE_UQ => FloatCC::UnorderedOrLessThan,
|
||||
_CMP_NGT_US | _CMP_NGT_UQ => FloatCC::UnorderedOrLessThanOrEqual,
|
||||
_CMP_FALSE_OQ | _CMP_FALSE_OS => todo!(),
|
||||
_CMP_NEQ_OQ | _CMP_NEQ_OS => FloatCC::OrderedNotEqual,
|
||||
_CMP_GE_OS | _CMP_GE_OQ => FloatCC::GreaterThanOrEqual,
|
||||
_CMP_GT_OS | _CMP_GT_OQ => FloatCC::GreaterThan,
|
||||
_CMP_TRUE_UQ | _CMP_TRUE_US => todo!(),
|
||||
|
||||
kind => unreachable!("kind {:?}", kind),
|
||||
};
|
||||
|
||||
// Copied from stdarch
|
||||
/// Equal (ordered, non-signaling)
|
||||
const _CMP_EQ_OQ: i32 = 0x00;
|
||||
/// Less-than (ordered, signaling)
|
||||
const _CMP_LT_OS: i32 = 0x01;
|
||||
/// Less-than-or-equal (ordered, signaling)
|
||||
const _CMP_LE_OS: i32 = 0x02;
|
||||
/// Unordered (non-signaling)
|
||||
const _CMP_UNORD_Q: i32 = 0x03;
|
||||
/// Not-equal (unordered, non-signaling)
|
||||
const _CMP_NEQ_UQ: i32 = 0x04;
|
||||
/// Not-less-than (unordered, signaling)
|
||||
const _CMP_NLT_US: i32 = 0x05;
|
||||
/// Not-less-than-or-equal (unordered, signaling)
|
||||
const _CMP_NLE_US: i32 = 0x06;
|
||||
/// Ordered (non-signaling)
|
||||
const _CMP_ORD_Q: i32 = 0x07;
|
||||
/// Equal (unordered, non-signaling)
|
||||
const _CMP_EQ_UQ: i32 = 0x08;
|
||||
/// Not-greater-than-or-equal (unordered, signaling)
|
||||
const _CMP_NGE_US: i32 = 0x09;
|
||||
/// Not-greater-than (unordered, signaling)
|
||||
const _CMP_NGT_US: i32 = 0x0a;
|
||||
/// False (ordered, non-signaling)
|
||||
const _CMP_FALSE_OQ: i32 = 0x0b;
|
||||
/// Not-equal (ordered, non-signaling)
|
||||
const _CMP_NEQ_OQ: i32 = 0x0c;
|
||||
/// Greater-than-or-equal (ordered, signaling)
|
||||
const _CMP_GE_OS: i32 = 0x0d;
|
||||
/// Greater-than (ordered, signaling)
|
||||
const _CMP_GT_OS: i32 = 0x0e;
|
||||
/// True (unordered, non-signaling)
|
||||
const _CMP_TRUE_UQ: i32 = 0x0f;
|
||||
/// Equal (ordered, signaling)
|
||||
const _CMP_EQ_OS: i32 = 0x10;
|
||||
/// Less-than (ordered, non-signaling)
|
||||
const _CMP_LT_OQ: i32 = 0x11;
|
||||
/// Less-than-or-equal (ordered, non-signaling)
|
||||
const _CMP_LE_OQ: i32 = 0x12;
|
||||
/// Unordered (signaling)
|
||||
const _CMP_UNORD_S: i32 = 0x13;
|
||||
/// Not-equal (unordered, signaling)
|
||||
const _CMP_NEQ_US: i32 = 0x14;
|
||||
/// Not-less-than (unordered, non-signaling)
|
||||
const _CMP_NLT_UQ: i32 = 0x15;
|
||||
/// Not-less-than-or-equal (unordered, non-signaling)
|
||||
const _CMP_NLE_UQ: i32 = 0x16;
|
||||
/// Ordered (signaling)
|
||||
const _CMP_ORD_S: i32 = 0x17;
|
||||
/// Equal (unordered, signaling)
|
||||
const _CMP_EQ_US: i32 = 0x18;
|
||||
/// Not-greater-than-or-equal (unordered, non-signaling)
|
||||
const _CMP_NGE_UQ: i32 = 0x19;
|
||||
/// Not-greater-than (unordered, non-signaling)
|
||||
const _CMP_NGT_UQ: i32 = 0x1a;
|
||||
/// False (ordered, signaling)
|
||||
const _CMP_FALSE_OS: i32 = 0x1b;
|
||||
/// Not-equal (ordered, signaling)
|
||||
const _CMP_NEQ_OS: i32 = 0x1c;
|
||||
/// Greater-than-or-equal (ordered, non-signaling)
|
||||
const _CMP_GE_OQ: i32 = 0x1d;
|
||||
/// Greater-than (ordered, non-signaling)
|
||||
const _CMP_GT_OQ: i32 = 0x1e;
|
||||
/// True (unordered, signaling)
|
||||
const _CMP_TRUE_US: i32 = 0x1f;
|
||||
|
||||
simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, res_lane_ty, x_lane, y_lane| {
|
||||
let res_lane = match lane_ty.kind() {
|
||||
ty::Float(_) => fx.bcx.ins().fcmp(flt_cc, x_lane, y_lane),
|
||||
|
|
@ -103,6 +194,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrai.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.psrai.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.pslli.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
|
|
@ -137,6 +245,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrai.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.sse2.psrai.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.pslli.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
|
|
@ -171,6 +296,57 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx.psrai.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrai.d imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.psrli.q" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrli.q imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 64 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.sse2.pslli.q" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.pslli.q imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 64 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx.pslli.d" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
|
|
@ -205,6 +381,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx2.psrai.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
|
||||
.expect("llvm.x86.avx.psrai.w imm8 not const");
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
|
||||
.try_to_bits(Size::from_bytes(4))
|
||||
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
|
||||
{
|
||||
imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)),
|
||||
_ => fx.bcx.ins().iconst(types::I32, 0),
|
||||
});
|
||||
}
|
||||
"llvm.x86.avx2.pslli.w" => {
|
||||
let (a, imm8) = match args {
|
||||
[a, imm8] => (a, imm8),
|
||||
|
|
@ -313,7 +506,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
ret.place_lane(fx, 2).to_ptr().store(fx, res_2, MemFlags::trusted());
|
||||
ret.place_lane(fx, 3).to_ptr().store(fx, res_3, MemFlags::trusted());
|
||||
}
|
||||
"llvm.x86.sse2.storeu.dq" => {
|
||||
"llvm.x86.sse2.storeu.dq" | "llvm.x86.sse2.storeu.pd" => {
|
||||
intrinsic_args!(fx, args => (mem_addr, a); intrinsic);
|
||||
let mem_addr = mem_addr.load_scalar(fx);
|
||||
|
||||
|
|
@ -321,17 +514,45 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout());
|
||||
dest.write_cvalue(fx, a);
|
||||
}
|
||||
"llvm.x86.addcarry.64" => {
|
||||
"llvm.x86.ssse3.pabs.b.128" | "llvm.x86.ssse3.pabs.w.128" | "llvm.x86.ssse3.pabs.d.128" => {
|
||||
let a = match args {
|
||||
[a] => a,
|
||||
_ => bug!("wrong number of args for intrinsic {intrinsic}"),
|
||||
};
|
||||
let a = codegen_operand(fx, a);
|
||||
|
||||
simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| {
|
||||
fx.bcx.ins().iabs(lane)
|
||||
});
|
||||
}
|
||||
"llvm.x86.addcarry.32" | "llvm.x86.addcarry.64" => {
|
||||
intrinsic_args!(fx, args => (c_in, a, b); intrinsic);
|
||||
let c_in = c_in.load_scalar(fx);
|
||||
|
||||
llvm_add_sub(fx, BinOp::Add, ret, c_in, a, b);
|
||||
let (cb_out, c) = llvm_add_sub(fx, BinOp::Add, c_in, a, b);
|
||||
|
||||
let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, a.layout().ty]));
|
||||
let val = CValue::by_val_pair(cb_out, c, layout);
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
"llvm.x86.subborrow.64" => {
|
||||
"llvm.x86.addcarryx.u32" | "llvm.x86.addcarryx.u64" => {
|
||||
intrinsic_args!(fx, args => (c_in, a, b, out); intrinsic);
|
||||
let c_in = c_in.load_scalar(fx);
|
||||
|
||||
let (cb_out, c) = llvm_add_sub(fx, BinOp::Add, c_in, a, b);
|
||||
|
||||
Pointer::new(out.load_scalar(fx)).store(fx, c, MemFlags::trusted());
|
||||
ret.write_cvalue(fx, CValue::by_val(cb_out, fx.layout_of(fx.tcx.types.u8)));
|
||||
}
|
||||
"llvm.x86.subborrow.32" | "llvm.x86.subborrow.64" => {
|
||||
intrinsic_args!(fx, args => (b_in, a, b); intrinsic);
|
||||
let b_in = b_in.load_scalar(fx);
|
||||
|
||||
llvm_add_sub(fx, BinOp::Sub, ret, b_in, a, b);
|
||||
let (cb_out, c) = llvm_add_sub(fx, BinOp::Sub, b_in, a, b);
|
||||
|
||||
let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, a.layout().ty]));
|
||||
let val = CValue::by_val_pair(cb_out, c, layout);
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
_ => {
|
||||
fx.tcx
|
||||
|
|
@ -356,21 +577,11 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
fn llvm_add_sub<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
bin_op: BinOp,
|
||||
ret: CPlace<'tcx>,
|
||||
cb_in: Value,
|
||||
a: CValue<'tcx>,
|
||||
b: CValue<'tcx>,
|
||||
) {
|
||||
assert_eq!(
|
||||
a.layout().ty,
|
||||
fx.tcx.types.u64,
|
||||
"llvm.x86.addcarry.64/llvm.x86.subborrow.64 second operand must be u64"
|
||||
);
|
||||
assert_eq!(
|
||||
b.layout().ty,
|
||||
fx.tcx.types.u64,
|
||||
"llvm.x86.addcarry.64/llvm.x86.subborrow.64 third operand must be u64"
|
||||
);
|
||||
) -> (Value, Value) {
|
||||
assert_eq!(a.layout().ty, b.layout().ty);
|
||||
|
||||
// c + carry -> c + first intermediate carry or borrow respectively
|
||||
let int0 = crate::num::codegen_checked_int_binop(fx, bin_op, a, b);
|
||||
|
|
@ -378,15 +589,14 @@ fn llvm_add_sub<'tcx>(
|
|||
let cb0 = int0.value_field(fx, FieldIdx::new(1)).load_scalar(fx);
|
||||
|
||||
// c + carry -> c + second intermediate carry or borrow respectively
|
||||
let cb_in_as_u64 = fx.bcx.ins().uextend(types::I64, cb_in);
|
||||
let cb_in_as_u64 = CValue::by_val(cb_in_as_u64, fx.layout_of(fx.tcx.types.u64));
|
||||
let int1 = crate::num::codegen_checked_int_binop(fx, bin_op, c, cb_in_as_u64);
|
||||
let clif_ty = fx.clif_type(a.layout().ty).unwrap();
|
||||
let cb_in_as_int = fx.bcx.ins().uextend(clif_ty, cb_in);
|
||||
let cb_in_as_int = CValue::by_val(cb_in_as_int, fx.layout_of(a.layout().ty));
|
||||
let int1 = crate::num::codegen_checked_int_binop(fx, bin_op, c, cb_in_as_int);
|
||||
let (c, cb1) = int1.load_scalar_pair(fx);
|
||||
|
||||
// carry0 | carry1 -> carry or borrow respectively
|
||||
let cb_out = fx.bcx.ins().bor(cb0, cb1);
|
||||
|
||||
let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, fx.tcx.types.u64]));
|
||||
let val = CValue::by_val_pair(cb_out, c, layout);
|
||||
ret.write_cvalue(fx, val);
|
||||
(cb_out, c)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -647,12 +647,13 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout);
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
sym::volatile_store | sym::unaligned_volatile_store => {
|
||||
sym::volatile_store | sym::unaligned_volatile_store | sym::nontemporal_store => {
|
||||
intrinsic_args!(fx, args => (ptr, val); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
// Cranelift treats stores as volatile by default
|
||||
// FIXME correctly handle unaligned_volatile_store
|
||||
// FIXME actually do nontemporal stores if requested
|
||||
let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout());
|
||||
dest.write_cvalue(fx, val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,8 +268,6 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn isa::Tar
|
|||
};
|
||||
flags_builder.set("tls_model", tls_model).unwrap();
|
||||
|
||||
flags_builder.set("enable_simd", "true").unwrap();
|
||||
|
||||
flags_builder.set("enable_llvm_abi_extensions", "true").unwrap();
|
||||
|
||||
use rustc_session::config::OptLevel;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use crate::prelude::*;
|
||||
|
||||
use rustc_middle::ty::FnSig;
|
||||
|
||||
use cranelift_codegen::entity::EntityRef;
|
||||
use cranelift_codegen::ir::immediates::Offset32;
|
||||
|
||||
|
|
@ -160,6 +162,7 @@ impl<'tcx> CValue<'tcx> {
|
|||
}
|
||||
|
||||
/// Load a value with layout.abi of scalar
|
||||
#[track_caller]
|
||||
pub(crate) fn load_scalar(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> Value {
|
||||
let layout = self.1;
|
||||
match self.0 {
|
||||
|
|
@ -182,6 +185,7 @@ impl<'tcx> CValue<'tcx> {
|
|||
}
|
||||
|
||||
/// Load a value pair with layout.abi of scalar pair
|
||||
#[track_caller]
|
||||
pub(crate) fn load_scalar_pair(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> (Value, Value) {
|
||||
let layout = self.1;
|
||||
match self.0 {
|
||||
|
|
@ -583,17 +587,25 @@ impl<'tcx> CPlace<'tcx> {
|
|||
let dst_layout = self.layout();
|
||||
match self.inner {
|
||||
CPlaceInner::Var(_local, var) => {
|
||||
let data = CValue(from.0, dst_layout).load_scalar(fx);
|
||||
let data = match from.1.abi {
|
||||
Abi::Scalar(_) => CValue(from.0, dst_layout).load_scalar(fx),
|
||||
_ => {
|
||||
let (ptr, meta) = from.force_stack(fx);
|
||||
assert!(meta.is_none());
|
||||
CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar(fx)
|
||||
}
|
||||
};
|
||||
let dst_ty = fx.clif_type(self.layout().ty).unwrap();
|
||||
transmute_scalar(fx, var, data, dst_ty);
|
||||
}
|
||||
CPlaceInner::VarPair(_local, var1, var2) => {
|
||||
let (data1, data2) = if from.layout().ty == dst_layout.ty {
|
||||
CValue(from.0, dst_layout).load_scalar_pair(fx)
|
||||
} else {
|
||||
let (ptr, meta) = from.force_stack(fx);
|
||||
assert!(meta.is_none());
|
||||
CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx)
|
||||
let (data1, data2) = match from.1.abi {
|
||||
Abi::ScalarPair(_, _) => CValue(from.0, dst_layout).load_scalar_pair(fx),
|
||||
_ => {
|
||||
let (ptr, meta) = from.force_stack(fx);
|
||||
assert!(meta.is_none());
|
||||
CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx)
|
||||
}
|
||||
};
|
||||
let (dst_ty1, dst_ty2) = fx.clif_pair_type(self.layout().ty).unwrap();
|
||||
transmute_scalar(fx, var1, data1, dst_ty1);
|
||||
|
|
@ -607,30 +619,38 @@ impl<'tcx> CPlace<'tcx> {
|
|||
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_notrap();
|
||||
match from.layout().abi {
|
||||
Abi::Scalar(_) => {
|
||||
let val = from.load_scalar(fx);
|
||||
to_ptr.store(fx, val, flags);
|
||||
return;
|
||||
}
|
||||
Abi::ScalarPair(a_scalar, b_scalar) => {
|
||||
let (value, extra) = from.load_scalar_pair(fx);
|
||||
let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
|
||||
to_ptr.store(fx, value, flags);
|
||||
to_ptr.offset(fx, b_offset).store(fx, extra, flags);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match from.0 {
|
||||
CValueInner::ByVal(val) => {
|
||||
to_ptr.store(fx, val, flags);
|
||||
}
|
||||
CValueInner::ByValPair(_, _) => {
|
||||
bug!("Non ScalarPair abi {:?} for ByValPair CValue", dst_layout.abi);
|
||||
}
|
||||
CValueInner::ByValPair(val1, val2) => match from.layout().abi {
|
||||
Abi::ScalarPair(a_scalar, b_scalar) => {
|
||||
let b_offset =
|
||||
scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
|
||||
to_ptr.store(fx, val1, flags);
|
||||
to_ptr.offset(fx, b_offset).store(fx, val2, flags);
|
||||
}
|
||||
_ => bug!("Non ScalarPair abi {:?} for ByValPair CValue", dst_layout.abi),
|
||||
},
|
||||
CValueInner::ByRef(from_ptr, None) => {
|
||||
match from.layout().abi {
|
||||
Abi::Scalar(_) => {
|
||||
let val = from.load_scalar(fx);
|
||||
to_ptr.store(fx, val, flags);
|
||||
return;
|
||||
}
|
||||
Abi::ScalarPair(a_scalar, b_scalar) => {
|
||||
let b_offset =
|
||||
scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
|
||||
let (val1, val2) = from.load_scalar_pair(fx);
|
||||
to_ptr.store(fx, val1, flags);
|
||||
to_ptr.offset(fx, b_offset).store(fx, val2, flags);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let from_addr = from_ptr.get_addr(fx);
|
||||
let to_addr = to_ptr.get_addr(fx);
|
||||
let src_layout = from.1;
|
||||
|
|
@ -815,11 +835,42 @@ pub(crate) fn assert_assignable<'tcx>(
|
|||
ParamEnv::reveal_all(),
|
||||
from_ty.fn_sig(fx.tcx),
|
||||
);
|
||||
let FnSig {
|
||||
inputs_and_output: types_from,
|
||||
c_variadic: c_variadic_from,
|
||||
unsafety: unsafety_from,
|
||||
abi: abi_from,
|
||||
} = from_sig;
|
||||
let to_sig = fx
|
||||
.tcx
|
||||
.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to_ty.fn_sig(fx.tcx));
|
||||
let FnSig {
|
||||
inputs_and_output: types_to,
|
||||
c_variadic: c_variadic_to,
|
||||
unsafety: unsafety_to,
|
||||
abi: abi_to,
|
||||
} = to_sig;
|
||||
let mut types_from = types_from.iter();
|
||||
let mut types_to = types_to.iter();
|
||||
loop {
|
||||
match (types_from.next(), types_to.next()) {
|
||||
(Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1),
|
||||
(None, None) => break,
|
||||
(Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
from_sig, to_sig,
|
||||
c_variadic_from, c_variadic_to,
|
||||
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
|
||||
from_sig, to_sig, fx,
|
||||
);
|
||||
assert_eq!(
|
||||
unsafety_from, unsafety_to,
|
||||
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
|
||||
from_sig, to_sig, fx,
|
||||
);
|
||||
assert_eq!(
|
||||
abi_from, abi_to,
|
||||
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
|
||||
from_sig, to_sig, fx,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -339,8 +339,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
|||
return pointee;
|
||||
}
|
||||
|
||||
let assume_valid_ptr = true;
|
||||
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
|
||||
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
|
||||
|
||||
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
||||
result
|
||||
|
|
|
|||
|
|
@ -411,8 +411,8 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
|||
if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) {
|
||||
return pointee;
|
||||
}
|
||||
let assume_valid_ptr = true;
|
||||
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
|
||||
|
||||
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
|
||||
|
||||
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
||||
result
|
||||
|
|
|
|||
|
|
@ -501,7 +501,22 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
});
|
||||
|
||||
// #73631: closures inherit `#[target_feature]` annotations
|
||||
if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) {
|
||||
//
|
||||
// If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
|
||||
//
|
||||
// At this point, `unsafe` has already been checked and `#[target_feature]` only affects codegen.
|
||||
// Emitting both `#[inline(always)]` and `#[target_feature]` can potentially result in an
|
||||
// ICE, because LLVM errors when the function fails to be inlined due to a target feature
|
||||
// mismatch.
|
||||
//
|
||||
// Using `#[inline(always)]` implies that this closure will most likely be inlined into
|
||||
// its parent function, which effectively inherits the features anyway. Boxing this closure
|
||||
// would result in this closure being compiled without the inherited target features, but this
|
||||
// is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute.
|
||||
if tcx.features().target_feature_11
|
||||
&& tcx.is_closure(did.to_def_id())
|
||||
&& codegen_fn_attrs.inline != InlineAttr::Always
|
||||
{
|
||||
let owner_id = tcx.parent(did.to_def_id());
|
||||
if tcx.def_kind(owner_id).has_codegen_attrs() {
|
||||
codegen_fn_attrs
|
||||
|
|
|
|||
|
|
@ -244,6 +244,7 @@ const_eval_not_enough_caller_args =
|
|||
const_eval_null_box = {$front_matter}: encountered a null box
|
||||
const_eval_null_fn_ptr = {$front_matter}: encountered a null function pointer
|
||||
const_eval_null_ref = {$front_matter}: encountered a null reference
|
||||
const_eval_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range}
|
||||
const_eval_nullary_intrinsic_fail =
|
||||
could not evaluate nullary intrinsic
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{LangItem, CRATE_HIR_ID};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::PointerArithmetic;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::lint::builtin::INVALID_ALIGNMENT;
|
||||
|
|
@ -16,7 +17,7 @@ use rustc_ast::Mutability;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{Align, HasDataLayout as _, Size};
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use crate::errors::{LongRunning, LongRunningWarn};
|
||||
|
|
@ -303,8 +304,8 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
|
|||
Ok(ControlFlow::Break(()))
|
||||
} else {
|
||||
// Not alignable in const, return `usize::MAX`.
|
||||
let usize_max = self.data_layout().target_usize_max();
|
||||
self.write_scalar(Scalar::from_target_usize(usize_max, self), dest)?;
|
||||
let usize_max = Scalar::from_target_usize(self.target_usize_max(), self);
|
||||
self.write_scalar(usize_max, dest)?;
|
||||
self.return_to_block(ret)?;
|
||||
Ok(ControlFlow::Break(()))
|
||||
}
|
||||
|
|
@ -332,7 +333,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
|
|||
// Inequality with integers other than null can never be known for sure.
|
||||
(Scalar::Int(int), ptr @ Scalar::Ptr(..))
|
||||
| (ptr @ Scalar::Ptr(..), Scalar::Int(int))
|
||||
if int.is_null() && !self.ptr_scalar_range(ptr)?.contains(&0) =>
|
||||
if int.is_null() && !self.scalar_may_be_null(ptr)? =>
|
||||
{
|
||||
0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -617,6 +617,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
|
|||
MutableRefInConst => const_eval_mutable_ref_in_const,
|
||||
NullFnPtr => const_eval_null_fn_ptr,
|
||||
NeverVal => const_eval_never_val,
|
||||
NullablePtrOutOfRange { .. } => const_eval_nullable_ptr_out_of_range,
|
||||
PtrOutOfRange { .. } => const_eval_ptr_out_of_range,
|
||||
OutOfRange { .. } => const_eval_out_of_range,
|
||||
UnsafeCell => const_eval_unsafe_cell,
|
||||
|
|
@ -731,7 +732,9 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
|
|||
| InvalidFnPtr { value } => {
|
||||
err.set_arg("value", value);
|
||||
}
|
||||
PtrOutOfRange { range, max_value } => add_range_arg(range, max_value, handler, err),
|
||||
NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => {
|
||||
add_range_arg(range, max_value, handler, err)
|
||||
}
|
||||
OutOfRange { range, max_value, value } => {
|
||||
err.set_arg("value", value);
|
||||
add_range_arg(range, max_value, handler, err);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants, WrappingRange};
|
||||
use rustc_target::abi::{self, TagEncoding};
|
||||
use rustc_target::abi::{VariantIdx, Variants};
|
||||
|
||||
use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar};
|
||||
|
||||
|
|
@ -179,24 +180,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// discriminant (encoded in niche/tag) and variant index are the same.
|
||||
let variants_start = niche_variants.start().as_u32();
|
||||
let variants_end = niche_variants.end().as_u32();
|
||||
let variants_len = u128::from(variants_end - variants_start);
|
||||
let variant = match tag_val.try_to_int() {
|
||||
Err(dbg_val) => {
|
||||
// So this is a pointer then, and casting to an int failed.
|
||||
// Can only happen during CTFE.
|
||||
// The pointer and niches ranges must be disjoint, then we know
|
||||
// this is the untagged variant (as the value is not in the niche).
|
||||
// Everything else, we conservatively reject.
|
||||
let range = self.ptr_scalar_range(tag_val)?;
|
||||
let niches = WrappingRange {
|
||||
start: niche_start,
|
||||
end: niche_start.wrapping_add(variants_len),
|
||||
};
|
||||
if niches.overlaps_range(range) {
|
||||
// The niche must be just 0, and the ptr not null, then we know this is
|
||||
// okay. Everything else, we conservatively reject.
|
||||
let ptr_valid = niche_start == 0
|
||||
&& variants_start == variants_end
|
||||
&& !self.scalar_may_be_null(tag_val)?;
|
||||
if !ptr_valid {
|
||||
throw_ub!(InvalidTag(dbg_val))
|
||||
} else {
|
||||
untagged_variant
|
||||
}
|
||||
untagged_variant
|
||||
}
|
||||
Ok(tag_bits) => {
|
||||
let tag_bits = tag_bits.assert_bits(tag_layout.size);
|
||||
|
|
@ -209,7 +205,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let variant_index_relative =
|
||||
variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
|
||||
// Check if this is in the range that indicates an actual discriminant.
|
||||
if variant_index_relative <= variants_len {
|
||||
if variant_index_relative <= u128::from(variants_end - variants_start) {
|
||||
let variant_index_relative = u32::try_from(variant_index_relative)
|
||||
.expect("we checked that this fits into a u32");
|
||||
// Then computing the absolute variant idx should not overflow any more.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::{
|
||||
self,
|
||||
interpret::{Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, Scalar},
|
||||
interpret::{
|
||||
Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar,
|
||||
},
|
||||
BinOp, NonDivergingIntrinsic,
|
||||
};
|
||||
use rustc_middle::ty;
|
||||
|
|
@ -13,7 +15,7 @@ use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
|
|||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{Abi, Align, HasDataLayout as _, Primitive, Size};
|
||||
use rustc_target::abi::{Abi, Align, Primitive, Size};
|
||||
|
||||
use super::{
|
||||
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
|
||||
|
|
@ -359,12 +361,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
)?;
|
||||
|
||||
// Perform division by size to compute return value.
|
||||
let dl = self.data_layout();
|
||||
let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned {
|
||||
assert!(0 <= dist && dist <= dl.target_isize_max());
|
||||
assert!(0 <= dist && dist <= self.target_isize_max());
|
||||
usize_layout
|
||||
} else {
|
||||
assert!(dl.target_isize_min() <= dist && dist <= dl.target_isize_max());
|
||||
assert!(self.target_isize_min() <= dist && dist <= self.target_isize_max());
|
||||
isize_layout
|
||||
};
|
||||
let pointee_layout = self.layout_of(instance_args.type_at(0))?;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use std::assert_matches::assert_matches;
|
|||
use std::borrow::Cow;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::ops::RangeInclusive;
|
||||
use std::ptr;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
|
|
@ -1223,34 +1222,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
|
||||
/// Machine pointer introspection.
|
||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
/// Turn a pointer-sized scalar into a (non-empty) range of possible values.
|
||||
/// Test if this value might be null.
|
||||
/// If the machine does not support ptr-to-int casts, this is conservative.
|
||||
pub fn ptr_scalar_range(
|
||||
&self,
|
||||
scalar: Scalar<M::Provenance>,
|
||||
) -> InterpResult<'tcx, RangeInclusive<u64>> {
|
||||
if let Ok(int) = scalar.to_target_usize(self) {
|
||||
return Ok(int..=int);
|
||||
}
|
||||
|
||||
let ptr = scalar.to_pointer(self)?;
|
||||
|
||||
// Can only happen during CTFE.
|
||||
Ok(match self.ptr_try_get_alloc_id(ptr) {
|
||||
Ok((alloc_id, offset, _)) => {
|
||||
let offset = offset.bytes();
|
||||
let (size, align, _) = self.get_alloc_info(alloc_id);
|
||||
let dl = self.data_layout();
|
||||
if offset > size.bytes() {
|
||||
// If the pointer is out-of-bounds, we do not have a
|
||||
// meaningful range to return.
|
||||
0..=dl.target_usize_max()
|
||||
} else {
|
||||
let (min, max) = dl.address_range_for(size, align);
|
||||
(min + offset)..=(max + offset)
|
||||
pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> {
|
||||
Ok(match scalar.try_to_int() {
|
||||
Ok(int) => int.is_null(),
|
||||
Err(_) => {
|
||||
// Can only happen during CTFE.
|
||||
let ptr = scalar.to_pointer(self)?;
|
||||
match self.ptr_try_get_alloc_id(ptr) {
|
||||
Ok((alloc_id, offset, _)) => {
|
||||
let (size, _align, _kind) = self.get_alloc_info(alloc_id);
|
||||
// If the pointer is out-of-bounds, it may be null.
|
||||
// Note that one-past-the-end (offset == size) is still inbounds, and never null.
|
||||
offset > size
|
||||
}
|
||||
Err(_offset) => bug!("a non-int scalar is always a pointer"),
|
||||
}
|
||||
}
|
||||
Err(_offset) => bug!("a non-int scalar is always a pointer"),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ use rustc_middle::mir::interpret::{
|
|||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants};
|
||||
use rustc_target::abi::{
|
||||
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
||||
};
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
|
|
@ -552,7 +554,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
// FIXME: Check if the signature matches
|
||||
} else {
|
||||
// Otherwise (for standalone Miri), we have to still check it to be non-null.
|
||||
if self.ecx.ptr_scalar_range(value)?.contains(&0) {
|
||||
if self.ecx.scalar_may_be_null(value)? {
|
||||
throw_validation_failure!(self.path, NullFnPtr);
|
||||
}
|
||||
}
|
||||
|
|
@ -593,36 +595,46 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
) -> InterpResult<'tcx> {
|
||||
let size = scalar_layout.size(self.ecx);
|
||||
let valid_range = scalar_layout.valid_range(self.ecx);
|
||||
let WrappingRange { start, end } = valid_range;
|
||||
let max_value = size.unsigned_int_max();
|
||||
assert!(valid_range.end <= max_value);
|
||||
match scalar.try_to_int() {
|
||||
Ok(int) => {
|
||||
// We have an explicit int: check it against the valid range.
|
||||
let bits = int.assert_bits(size);
|
||||
if valid_range.contains(bits) {
|
||||
Ok(())
|
||||
} else {
|
||||
throw_validation_failure!(
|
||||
self.path,
|
||||
OutOfRange { value: format!("{bits}"), range: valid_range, max_value }
|
||||
)
|
||||
}
|
||||
}
|
||||
assert!(end <= max_value);
|
||||
let bits = match scalar.try_to_int() {
|
||||
Ok(int) => int.assert_bits(size),
|
||||
Err(_) => {
|
||||
// So this is a pointer then, and casting to an int failed.
|
||||
// Can only happen during CTFE.
|
||||
// We check if the possible addresses are compatible with the valid range.
|
||||
let range = self.ecx.ptr_scalar_range(scalar)?;
|
||||
if valid_range.contains_range(range) {
|
||||
Ok(())
|
||||
// We support 2 kinds of ranges here: full range, and excluding zero.
|
||||
if start == 1 && end == max_value {
|
||||
// Only null is the niche. So make sure the ptr is NOT null.
|
||||
if self.ecx.scalar_may_be_null(scalar)? {
|
||||
throw_validation_failure!(
|
||||
self.path,
|
||||
NullablePtrOutOfRange { range: valid_range, max_value }
|
||||
)
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
} else if scalar_layout.is_always_valid(self.ecx) {
|
||||
// Easy. (This is reachable if `enforce_number_validity` is set.)
|
||||
return Ok(());
|
||||
} else {
|
||||
// Reject conservatively, because the pointer *could* have a bad value.
|
||||
// Conservatively, we reject, because the pointer *could* have a bad
|
||||
// value.
|
||||
throw_validation_failure!(
|
||||
self.path,
|
||||
PtrOutOfRange { range: valid_range, max_value }
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
// Now compare.
|
||||
if valid_range.contains(bits) {
|
||||
Ok(())
|
||||
} else {
|
||||
throw_validation_failure!(
|
||||
self.path,
|
||||
OutOfRange { value: format!("{bits}"), range: valid_range, max_value }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,11 +58,10 @@ impl<'tcx> MirPass<'tcx> for Validator {
|
|||
.iterate_to_fixpoint()
|
||||
.into_results_cursor(body);
|
||||
|
||||
let mut checker = TypeChecker {
|
||||
let mut cfg_checker = CfgChecker {
|
||||
when: &self.when,
|
||||
body,
|
||||
tcx,
|
||||
param_env,
|
||||
mir_phase,
|
||||
unwind_edge_count: 0,
|
||||
reachable_blocks: traversal::reachable_as_bitset(body),
|
||||
|
|
@ -70,13 +69,17 @@ impl<'tcx> MirPass<'tcx> for Validator {
|
|||
place_cache: FxHashSet::default(),
|
||||
value_cache: FxHashSet::default(),
|
||||
};
|
||||
checker.visit_body(body);
|
||||
checker.check_cleanup_control_flow();
|
||||
cfg_checker.visit_body(body);
|
||||
cfg_checker.check_cleanup_control_flow();
|
||||
|
||||
for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) {
|
||||
cfg_checker.fail(location, msg);
|
||||
}
|
||||
|
||||
if let MirPhase::Runtime(_) = body.phase {
|
||||
if let ty::InstanceDef::Item(_) = body.source.instance {
|
||||
if body.has_free_regions() {
|
||||
checker.fail(
|
||||
cfg_checker.fail(
|
||||
Location::START,
|
||||
format!("Free regions in optimized {} MIR", body.phase.name()),
|
||||
);
|
||||
|
|
@ -86,11 +89,10 @@ impl<'tcx> MirPass<'tcx> for Validator {
|
|||
}
|
||||
}
|
||||
|
||||
struct TypeChecker<'a, 'tcx> {
|
||||
struct CfgChecker<'a, 'tcx> {
|
||||
when: &'a str,
|
||||
body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
mir_phase: MirPhase,
|
||||
unwind_edge_count: usize,
|
||||
reachable_blocks: BitSet<BasicBlock>,
|
||||
|
|
@ -99,7 +101,7 @@ struct TypeChecker<'a, 'tcx> {
|
|||
value_cache: FxHashSet<u128>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
|
||||
#[track_caller]
|
||||
fn fail(&self, location: Location, msg: impl AsRef<str>) {
|
||||
let span = self.body.source_info(location).span;
|
||||
|
|
@ -248,30 +250,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
UnwindAction::Unreachable | UnwindAction::Terminate => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if src can be assigned into dest.
|
||||
/// This is not precise, it will accept some incorrect assignments.
|
||||
fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
|
||||
// Fast path before we normalize.
|
||||
if src == dest {
|
||||
// Equal types, all is good.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We sometimes have to use `defining_opaque_types` for subtyping
|
||||
// to succeed here and figuring out how exactly that should work
|
||||
// is annoying. It is harmless enough to just not validate anything
|
||||
// in that case. We still check this after analysis as all opaque
|
||||
// types have been revealed at this point.
|
||||
if (src, dest).has_opaque_types() {
|
||||
return true;
|
||||
}
|
||||
|
||||
crate::util::is_subtype(self.tcx, self.param_env, src, dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
|
||||
if self.body.local_decls.get(local).is_none() {
|
||||
self.fail(
|
||||
|
|
@ -296,6 +277,275 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
match &statement.kind {
|
||||
StatementKind::Assign(box (dest, rvalue)) => {
|
||||
// FIXME(JakobDegen): Check this for all rvalues, not just this one.
|
||||
if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
|
||||
// The sides of an assignment must not alias. Currently this just checks whether
|
||||
// the places are identical.
|
||||
if dest == src {
|
||||
self.fail(
|
||||
location,
|
||||
"encountered `Assign` statement with overlapping memory",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
StatementKind::AscribeUserType(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`AscribeUserType` should have been removed after drop lowering phase",
|
||||
);
|
||||
}
|
||||
}
|
||||
StatementKind::FakeRead(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FakeRead` should have been removed after drop lowering phase",
|
||||
);
|
||||
}
|
||||
}
|
||||
StatementKind::SetDiscriminant { .. } => {
|
||||
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
|
||||
}
|
||||
}
|
||||
StatementKind::Deinit(..) => {
|
||||
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`Deinit`is not allowed until deaggregation");
|
||||
}
|
||||
}
|
||||
StatementKind::Retag(kind, _) => {
|
||||
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
|
||||
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
|
||||
// seem to fail to set their `MirPhase` correctly.
|
||||
if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
|
||||
self.fail(location, format!("explicit `{:?}` is forbidden", kind));
|
||||
}
|
||||
}
|
||||
StatementKind::StorageLive(local) => {
|
||||
// We check that the local is not live when entering a `StorageLive` for it.
|
||||
// Technically, violating this restriction is only UB and not actually indicative
|
||||
// of not well-formed MIR. This means that an optimization which turns MIR that
|
||||
// already has UB into MIR that fails this check is not necessarily wrong. However,
|
||||
// we have no such optimizations at the moment, and so we include this check anyway
|
||||
// to help us catch bugs. If you happen to write an optimization that might cause
|
||||
// this to incorrectly fire, feel free to remove this check.
|
||||
if self.reachable_blocks.contains(location.block) {
|
||||
self.storage_liveness.seek_before_primary_effect(location);
|
||||
let locals_with_storage = self.storage_liveness.get();
|
||||
if locals_with_storage.contains(*local) {
|
||||
self.fail(
|
||||
location,
|
||||
format!("StorageLive({local:?}) which already has storage here"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
StatementKind::StorageDead(_)
|
||||
| StatementKind::Intrinsic(_)
|
||||
| StatementKind::Coverage(_)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Nop => {}
|
||||
}
|
||||
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
match &terminator.kind {
|
||||
TerminatorKind::Goto { target } => {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
}
|
||||
TerminatorKind::SwitchInt { targets, discr: _ } => {
|
||||
for (_, target) in targets.iter() {
|
||||
self.check_edge(location, target, EdgeKind::Normal);
|
||||
}
|
||||
self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
|
||||
|
||||
self.value_cache.clear();
|
||||
self.value_cache.extend(targets.iter().map(|(value, _)| value));
|
||||
let has_duplicates = targets.iter().len() != self.value_cache.len();
|
||||
if has_duplicates {
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"duplicated values in `SwitchInt` terminator: {:?}",
|
||||
terminator.kind,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Drop { target, unwind, .. } => {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::Call { args, destination, target, unwind, .. } => {
|
||||
if let Some(target) = target {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
}
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
|
||||
// The call destination place and Operand::Move place used as an argument might be
|
||||
// passed by a reference to the callee. Consequently they must be non-overlapping.
|
||||
// Currently this simply checks for duplicate places.
|
||||
self.place_cache.clear();
|
||||
self.place_cache.insert(destination.as_ref());
|
||||
let mut has_duplicates = false;
|
||||
for arg in args {
|
||||
if let Operand::Move(place) = arg {
|
||||
has_duplicates |= !self.place_cache.insert(place.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
if has_duplicates {
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"encountered overlapping memory in `Call` terminator: {:?}",
|
||||
terminator.kind,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Assert { target, unwind, .. } => {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::Yield { resume, drop, .. } => {
|
||||
if self.body.generator.is_none() {
|
||||
self.fail(location, "`Yield` cannot appear outside generator bodies");
|
||||
}
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`Yield` should have been replaced by generator lowering");
|
||||
}
|
||||
self.check_edge(location, *resume, EdgeKind::Normal);
|
||||
if let Some(drop) = drop {
|
||||
self.check_edge(location, *drop, EdgeKind::Normal);
|
||||
}
|
||||
}
|
||||
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FalseEdge` should have been removed after drop elaboration",
|
||||
);
|
||||
}
|
||||
self.check_edge(location, *real_target, EdgeKind::Normal);
|
||||
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
|
||||
}
|
||||
TerminatorKind::FalseUnwind { real_target, unwind } => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FalseUnwind` should have been removed after drop elaboration",
|
||||
);
|
||||
}
|
||||
self.check_edge(location, *real_target, EdgeKind::Normal);
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::InlineAsm { destination, unwind, .. } => {
|
||||
if let Some(destination) = destination {
|
||||
self.check_edge(location, *destination, EdgeKind::Normal);
|
||||
}
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::GeneratorDrop => {
|
||||
if self.body.generator.is_none() {
|
||||
self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
|
||||
}
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`GeneratorDrop` should have been replaced by generator lowering",
|
||||
);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Resume | TerminatorKind::Terminate => {
|
||||
let bb = location.block;
|
||||
if !self.body.basic_blocks[bb].is_cleanup {
|
||||
self.fail(
|
||||
location,
|
||||
"Cannot `Resume` or `Terminate` from non-cleanup basic block",
|
||||
)
|
||||
}
|
||||
}
|
||||
TerminatorKind::Return => {
|
||||
let bb = location.block;
|
||||
if self.body.basic_blocks[bb].is_cleanup {
|
||||
self.fail(location, "Cannot `Return` from cleanup basic block")
|
||||
}
|
||||
}
|
||||
TerminatorKind::Unreachable => {}
|
||||
}
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
|
||||
fn visit_source_scope(&mut self, scope: SourceScope) {
|
||||
if self.body.source_scopes.get(scope).is_none() {
|
||||
self.tcx.sess.diagnostic().delay_span_bug(
|
||||
self.body.span,
|
||||
format!(
|
||||
"broken MIR in {:?} ({}):\ninvalid source scope {:?}",
|
||||
self.body.source.instance, self.when, scope,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_types<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mir_phase: MirPhase,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) -> Vec<(Location, String)> {
|
||||
let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() };
|
||||
type_checker.visit_body(body);
|
||||
type_checker.failures
|
||||
}
|
||||
|
||||
struct TypeChecker<'a, 'tcx> {
|
||||
body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
mir_phase: MirPhase,
|
||||
failures: Vec<(Location, String)>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
fn fail(&mut self, location: Location, msg: impl Into<String>) {
|
||||
self.failures.push((location, msg.into()));
|
||||
}
|
||||
|
||||
/// Check if src can be assigned into dest.
|
||||
/// This is not precise, it will accept some incorrect assignments.
|
||||
fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
|
||||
// Fast path before we normalize.
|
||||
if src == dest {
|
||||
// Equal types, all is good.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We sometimes have to use `defining_opaque_types` for subtyping
|
||||
// to succeed here and figuring out how exactly that should work
|
||||
// is annoying. It is harmless enough to just not validate anything
|
||||
// in that case. We still check this after analysis as all opaque
|
||||
// types have been revealed at this point.
|
||||
if (src, dest).has_opaque_types() {
|
||||
return true;
|
||||
}
|
||||
|
||||
crate::util::is_subtype(self.tcx, self.param_env, src, dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
|
||||
if self.tcx.sess.opts.unstable_opts.validate_mir
|
||||
|
|
@ -342,10 +592,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
ProjectionElem::Field(f, ty) => {
|
||||
let parent_ty = place_ref.ty(&self.body.local_decls, self.tcx);
|
||||
let fail_out_of_bounds = |this: &Self, location| {
|
||||
let fail_out_of_bounds = |this: &mut Self, location| {
|
||||
this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
|
||||
};
|
||||
let check_equal = |this: &Self, location, f_ty| {
|
||||
let check_equal = |this: &mut Self, location, f_ty| {
|
||||
if !this.mir_assign_valid_types(ty, f_ty) {
|
||||
this.fail(
|
||||
location,
|
||||
|
|
@ -440,9 +690,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
|
||||
let check_place = |place: Place<'_>| {
|
||||
let check_place = |this: &mut Self, place: Place<'_>| {
|
||||
if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
|
||||
self.fail(
|
||||
this.fail(
|
||||
START_BLOCK.start_location(),
|
||||
format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
|
||||
);
|
||||
|
|
@ -451,7 +701,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
match debuginfo.value {
|
||||
VarDebugInfoContents::Const(_) => {}
|
||||
VarDebugInfoContents::Place(place) => {
|
||||
check_place(place);
|
||||
check_place(self, place);
|
||||
if debuginfo.references != 0 && place.projection.last() == Some(&PlaceElem::Deref) {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
|
|
@ -461,7 +711,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
VarDebugInfoContents::Composite { ty, ref fragments } => {
|
||||
for f in fragments {
|
||||
check_place(f.contents);
|
||||
check_place(self, f.contents);
|
||||
if ty.is_union() || ty.is_enum() {
|
||||
self.fail(
|
||||
START_BLOCK.start_location(),
|
||||
|
|
@ -718,7 +968,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => {
|
||||
let fail_out_of_bounds = |this: &Self, location, field, ty| {
|
||||
let fail_out_of_bounds = |this: &mut Self, location, field, ty| {
|
||||
this.fail(location, format!("Out of bounds field {field:?} for {ty:?}"));
|
||||
};
|
||||
|
||||
|
|
@ -894,26 +1144,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
self.fail(location, format!("explicit `{:?}` is forbidden", kind));
|
||||
}
|
||||
}
|
||||
StatementKind::StorageLive(local) => {
|
||||
// We check that the local is not live when entering a `StorageLive` for it.
|
||||
// Technically, violating this restriction is only UB and not actually indicative
|
||||
// of not well-formed MIR. This means that an optimization which turns MIR that
|
||||
// already has UB into MIR that fails this check is not necessarily wrong. However,
|
||||
// we have no such optimizations at the moment, and so we include this check anyway
|
||||
// to help us catch bugs. If you happen to write an optimization that might cause
|
||||
// this to incorrectly fire, feel free to remove this check.
|
||||
if self.reachable_blocks.contains(location.block) {
|
||||
self.storage_liveness.seek_before_primary_effect(location);
|
||||
let locals_with_storage = self.storage_liveness.get();
|
||||
if locals_with_storage.contains(*local) {
|
||||
self.fail(
|
||||
location,
|
||||
format!("StorageLive({local:?}) which already has storage here"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
StatementKind::StorageDead(_)
|
||||
StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Coverage(_)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::PlaceMention(..)
|
||||
|
|
@ -925,9 +1157,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
match &terminator.kind {
|
||||
TerminatorKind::Goto { target } => {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
}
|
||||
TerminatorKind::SwitchInt { targets, discr } => {
|
||||
let switch_ty = discr.ty(&self.body.local_decls, self.tcx);
|
||||
|
||||
|
|
@ -941,36 +1170,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
other => bug!("unhandled type: {:?}", other),
|
||||
});
|
||||
|
||||
for (value, target) in targets.iter() {
|
||||
for (value, _) in targets.iter() {
|
||||
if Scalar::<()>::try_from_uint(value, size).is_none() {
|
||||
self.fail(
|
||||
location,
|
||||
format!("the value {:#x} is not a proper {:?}", value, switch_ty),
|
||||
)
|
||||
}
|
||||
|
||||
self.check_edge(location, target, EdgeKind::Normal);
|
||||
}
|
||||
self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
|
||||
|
||||
self.value_cache.clear();
|
||||
self.value_cache.extend(targets.iter().map(|(value, _)| value));
|
||||
let has_duplicates = targets.iter().len() != self.value_cache.len();
|
||||
if has_duplicates {
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"duplicated values in `SwitchInt` terminator: {:?}",
|
||||
terminator.kind,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Drop { target, unwind, .. } => {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::Call { func, args, destination, target, unwind, .. } => {
|
||||
TerminatorKind::Call { func, .. } => {
|
||||
let func_ty = func.ty(&self.body.local_decls, self.tcx);
|
||||
match func_ty.kind() {
|
||||
ty::FnPtr(..) | ty::FnDef(..) => {}
|
||||
|
|
@ -979,34 +1188,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
format!("encountered non-callable type {} in `Call` terminator", func_ty),
|
||||
),
|
||||
}
|
||||
if let Some(target) = target {
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
}
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
|
||||
// The call destination place and Operand::Move place used as an argument might be
|
||||
// passed by a reference to the callee. Consequently they must be non-overlapping.
|
||||
// Currently this simply checks for duplicate places.
|
||||
self.place_cache.clear();
|
||||
self.place_cache.insert(destination.as_ref());
|
||||
let mut has_duplicates = false;
|
||||
for arg in args {
|
||||
if let Operand::Move(place) = arg {
|
||||
has_duplicates |= !self.place_cache.insert(place.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
if has_duplicates {
|
||||
self.fail(
|
||||
location,
|
||||
format!(
|
||||
"encountered overlapping memory in `Call` terminator: {:?}",
|
||||
terminator.kind,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Assert { cond, target, unwind, .. } => {
|
||||
TerminatorKind::Assert { cond, .. } => {
|
||||
let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
|
||||
if cond_ty != self.tcx.types.bool {
|
||||
self.fail(
|
||||
|
|
@ -1017,88 +1200,20 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
),
|
||||
);
|
||||
}
|
||||
self.check_edge(location, *target, EdgeKind::Normal);
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::Yield { resume, drop, .. } => {
|
||||
if self.body.generator.is_none() {
|
||||
self.fail(location, "`Yield` cannot appear outside generator bodies");
|
||||
}
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`Yield` should have been replaced by generator lowering");
|
||||
}
|
||||
self.check_edge(location, *resume, EdgeKind::Normal);
|
||||
if let Some(drop) = drop {
|
||||
self.check_edge(location, *drop, EdgeKind::Normal);
|
||||
}
|
||||
}
|
||||
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FalseEdge` should have been removed after drop elaboration",
|
||||
);
|
||||
}
|
||||
self.check_edge(location, *real_target, EdgeKind::Normal);
|
||||
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
|
||||
}
|
||||
TerminatorKind::FalseUnwind { real_target, unwind } => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FalseUnwind` should have been removed after drop elaboration",
|
||||
);
|
||||
}
|
||||
self.check_edge(location, *real_target, EdgeKind::Normal);
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::InlineAsm { destination, unwind, .. } => {
|
||||
if let Some(destination) = destination {
|
||||
self.check_edge(location, *destination, EdgeKind::Normal);
|
||||
}
|
||||
self.check_unwind_edge(location, *unwind);
|
||||
}
|
||||
TerminatorKind::GeneratorDrop => {
|
||||
if self.body.generator.is_none() {
|
||||
self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
|
||||
}
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`GeneratorDrop` should have been replaced by generator lowering",
|
||||
);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Resume | TerminatorKind::Terminate => {
|
||||
let bb = location.block;
|
||||
if !self.body.basic_blocks[bb].is_cleanup {
|
||||
self.fail(
|
||||
location,
|
||||
"Cannot `Resume` or `Terminate` from non-cleanup basic block",
|
||||
)
|
||||
}
|
||||
}
|
||||
TerminatorKind::Return => {
|
||||
let bb = location.block;
|
||||
if self.body.basic_blocks[bb].is_cleanup {
|
||||
self.fail(location, "Cannot `Return` from cleanup basic block")
|
||||
}
|
||||
}
|
||||
TerminatorKind::Unreachable => {}
|
||||
TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::Drop { .. }
|
||||
| TerminatorKind::Yield { .. }
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. }
|
||||
| TerminatorKind::InlineAsm { .. }
|
||||
| TerminatorKind::GeneratorDrop
|
||||
| TerminatorKind::Resume
|
||||
| TerminatorKind::Terminate
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable => {}
|
||||
}
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
|
||||
fn visit_source_scope(&mut self, scope: SourceScope) {
|
||||
if self.body.source_scopes.get(scope).is_none() {
|
||||
self.tcx.sess.diagnostic().delay_span_bug(
|
||||
self.body.span,
|
||||
format!(
|
||||
"broken MIR in {:?} ({}):\ninvalid source scope {:?}",
|
||||
self.body.source.instance, self.when, scope,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,12 +137,6 @@ enum Scope<'a> {
|
|||
s: ScopeRef<'a>,
|
||||
},
|
||||
|
||||
/// A scope which either determines unspecified lifetimes or errors
|
||||
/// on them (e.g., due to ambiguity).
|
||||
Elision {
|
||||
s: ScopeRef<'a>,
|
||||
},
|
||||
|
||||
/// Use a specific lifetime (if `Some`) or leave it unset (to be
|
||||
/// inferred in a function body or potentially error outside one),
|
||||
/// for the default choice of lifetime in a trait object type.
|
||||
|
|
@ -211,7 +205,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
|
|||
Scope::Body { id, s: _ } => {
|
||||
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
|
||||
}
|
||||
Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(),
|
||||
Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
|
||||
.debug_struct("ObjectLifetimeDefault")
|
||||
.field("lifetime", lifetime)
|
||||
|
|
@ -325,9 +318,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
break (vec![], BinderScopeType::Normal);
|
||||
}
|
||||
|
||||
Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::AnonConstBoundary { s } => {
|
||||
Scope::ObjectLifetimeDefault { s, .. } | Scope::AnonConstBoundary { s } => {
|
||||
scope = s;
|
||||
}
|
||||
|
||||
|
|
@ -526,16 +517,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
|||
| hir::ItemKind::Macro(..)
|
||||
| hir::ItemKind::Mod(..)
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::Static(..)
|
||||
| hir::ItemKind::Const(..)
|
||||
| hir::ItemKind::GlobalAsm(..) => {
|
||||
// These sorts of items have no lifetime parameters at all.
|
||||
intravisit::walk_item(self, item);
|
||||
}
|
||||
hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
|
||||
// No lifetime parameters, but implied 'static.
|
||||
self.with(Scope::Elision { s: self.scope }, |this| {
|
||||
intravisit::walk_item(this, item)
|
||||
});
|
||||
}
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::TyAlias { .. },
|
||||
..
|
||||
|
|
@ -727,12 +714,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
|
|||
// Elided lifetimes are not allowed in non-return
|
||||
// position impl Trait
|
||||
let scope = Scope::TraitRefBoundary { s: self.scope };
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::Elision { s: this.scope };
|
||||
this.with(scope, |this| {
|
||||
intravisit::walk_item(this, opaque_ty);
|
||||
})
|
||||
});
|
||||
self.with(scope, |this| intravisit::walk_item(this, opaque_ty));
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -1293,8 +1275,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
scope = s;
|
||||
}
|
||||
|
||||
Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. }
|
||||
| Scope::AnonConstBoundary { s } => {
|
||||
|
|
@ -1357,7 +1338,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
Scope::Root { .. } => break,
|
||||
Scope::Binder { s, .. }
|
||||
| Scope::Body { s, .. }
|
||||
| Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. }
|
||||
|
|
@ -1409,8 +1389,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
scope = s;
|
||||
}
|
||||
|
||||
Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. } => {
|
||||
scope = s;
|
||||
|
|
@ -1483,7 +1462,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
Scope::Root { .. } => break,
|
||||
Scope::Binder { s, .. }
|
||||
| Scope::Body { s, .. }
|
||||
| Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. }
|
||||
|
|
@ -1564,7 +1542,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
Scope::Body { .. } => break true,
|
||||
|
||||
Scope::Binder { s, .. }
|
||||
| Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. }
|
||||
|
|
@ -1832,14 +1809,20 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
output: Option<&'tcx hir::Ty<'tcx>>,
|
||||
in_closure: bool,
|
||||
) {
|
||||
self.with(Scope::Elision { s: self.scope }, |this| {
|
||||
for input in inputs {
|
||||
this.visit_ty(input);
|
||||
}
|
||||
if !in_closure && let Some(output) = output {
|
||||
this.visit_ty(output);
|
||||
}
|
||||
});
|
||||
self.with(
|
||||
Scope::ObjectLifetimeDefault {
|
||||
lifetime: Some(ResolvedArg::StaticLifetime),
|
||||
s: self.scope,
|
||||
},
|
||||
|this| {
|
||||
for input in inputs {
|
||||
this.visit_ty(input);
|
||||
}
|
||||
if !in_closure && let Some(output) = output {
|
||||
this.visit_ty(output);
|
||||
}
|
||||
},
|
||||
);
|
||||
if in_closure && let Some(output) = output {
|
||||
self.visit_ty(output);
|
||||
}
|
||||
|
|
@ -1859,7 +1842,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
scope = s;
|
||||
}
|
||||
|
||||
Scope::Root { .. } | Scope::Elision { .. } => break ResolvedArg::StaticLifetime,
|
||||
Scope::Root { .. } => break ResolvedArg::StaticLifetime,
|
||||
|
||||
Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return,
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION};
|
|||
use rustc_span::symbol::sym;
|
||||
use rustc_span::FileName;
|
||||
use rustc_span::SourceFileHashAlgorithm;
|
||||
use rustc_target::abi::ReferenceNichePolicy;
|
||||
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
|
||||
use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
|
||||
|
||||
|
|
@ -821,7 +820,6 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(profile_emit, Some(PathBuf::from("abc")));
|
||||
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
|
||||
tracked!(profiler_runtime, "abc".to_string());
|
||||
tracked!(reference_niches, Some(ReferenceNichePolicy { size: true, align: false }));
|
||||
tracked!(relax_elf_relocations, Some(true));
|
||||
tracked!(relro_level, Some(RelroLevel::Full));
|
||||
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
|
||||
|
|
|
|||
|
|
@ -301,7 +301,6 @@ provide! { tcx, def_id, other, cdata,
|
|||
is_profiler_runtime => { cdata.root.profiler_runtime }
|
||||
required_panic_strategy => { cdata.root.required_panic_strategy }
|
||||
panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy }
|
||||
reference_niches_policy => { cdata.root.reference_niches_policy }
|
||||
extern_crate => {
|
||||
let r = *cdata.extern_crate.lock();
|
||||
r.map(|c| &*tcx.arena.alloc(c))
|
||||
|
|
|
|||
|
|
@ -673,7 +673,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(),
|
||||
required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE),
|
||||
panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop,
|
||||
reference_niches_policy: tcx.reference_niches_policy(LOCAL_CRATE),
|
||||
edition: tcx.sess.edition(),
|
||||
has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE),
|
||||
has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE),
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use rustc_span::edition::Edition;
|
|||
use rustc_span::hygiene::{ExpnIndex, MacroKind};
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span};
|
||||
use rustc_target::abi::{FieldIdx, ReferenceNichePolicy, VariantIdx};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_target::spec::{PanicStrategy, TargetTriple};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
|
@ -251,7 +251,6 @@ pub(crate) struct CrateRoot {
|
|||
stable_crate_id: StableCrateId,
|
||||
required_panic_strategy: Option<PanicStrategy>,
|
||||
panic_in_drop_strategy: PanicStrategy,
|
||||
reference_niches_policy: ReferenceNichePolicy,
|
||||
edition: Edition,
|
||||
has_global_allocator: bool,
|
||||
has_alloc_error_handler: bool,
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ pub enum ValidationErrorKind<'tcx> {
|
|||
MutableRefInConst,
|
||||
NullFnPtr,
|
||||
NeverVal,
|
||||
NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
|
||||
PtrOutOfRange { range: WrappingRange, max_value: u128 },
|
||||
OutOfRange { value: String, range: WrappingRange, max_value: u128 },
|
||||
UnsafeCell,
|
||||
|
|
|
|||
|
|
@ -19,19 +19,33 @@ pub trait PointerArithmetic: HasDataLayout {
|
|||
|
||||
#[inline(always)]
|
||||
fn max_size_of_val(&self) -> Size {
|
||||
Size::from_bytes(self.data_layout().target_isize_max())
|
||||
Size::from_bytes(self.target_isize_max())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn target_usize_max(&self) -> u64 {
|
||||
self.pointer_size().unsigned_int_max().try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn target_isize_min(&self) -> i64 {
|
||||
self.pointer_size().signed_int_min().try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn target_isize_max(&self) -> i64 {
|
||||
self.pointer_size().signed_int_max().try_into().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn target_usize_to_isize(&self, val: u64) -> i64 {
|
||||
let dl = self.data_layout();
|
||||
let val = val as i64;
|
||||
// Now wrap-around into the machine_isize range.
|
||||
if val > dl.target_isize_max() {
|
||||
if val > self.target_isize_max() {
|
||||
// This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into
|
||||
// i64.
|
||||
debug_assert!(dl.pointer_size.bits() < 64);
|
||||
let max_usize_plus_1 = 1u128 << dl.pointer_size.bits();
|
||||
debug_assert!(self.pointer_size().bits() < 64);
|
||||
let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
|
||||
val - i64::try_from(max_usize_plus_1).unwrap()
|
||||
} else {
|
||||
val
|
||||
|
|
@ -44,7 +58,7 @@ pub trait PointerArithmetic: HasDataLayout {
|
|||
#[inline]
|
||||
fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) {
|
||||
let val = u128::from(val);
|
||||
let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits();
|
||||
let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
|
||||
(u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1)
|
||||
}
|
||||
|
||||
|
|
@ -62,11 +76,11 @@ pub trait PointerArithmetic: HasDataLayout {
|
|||
let n = i.unsigned_abs();
|
||||
if i >= 0 {
|
||||
let (val, over) = self.overflowing_offset(val, n);
|
||||
(val, over || i > self.data_layout().target_isize_max())
|
||||
(val, over || i > self.target_isize_max())
|
||||
} else {
|
||||
let res = val.overflowing_sub(n);
|
||||
let (val, over) = self.truncate_to_ptr(res);
|
||||
(val, over || i < self.data_layout().target_isize_min())
|
||||
(val, over || i < self.target_isize_min())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,11 +111,6 @@ impl EraseType
|
|||
>()];
|
||||
}
|
||||
|
||||
impl EraseType for Result<ty::layout::TyAndNaiveLayout<'_>, &ty::layout::LayoutError<'_>> {
|
||||
type Result =
|
||||
[u8; size_of::<Result<ty::layout::TyAndNaiveLayout<'_>, &ty::layout::LayoutError<'_>>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Result<ty::Const<'_>, mir::interpret::LitToConstError> {
|
||||
type Result = [u8; size_of::<Result<ty::Const<'static>, mir::interpret::LitToConstError>>()];
|
||||
}
|
||||
|
|
@ -296,7 +291,6 @@ trivial! {
|
|||
rustc_span::Symbol,
|
||||
rustc_span::symbol::Ident,
|
||||
rustc_target::spec::PanicStrategy,
|
||||
rustc_target::abi::ReferenceNichePolicy,
|
||||
rustc_type_ir::Variance,
|
||||
u32,
|
||||
usize,
|
||||
|
|
|
|||
|
|
@ -1394,18 +1394,6 @@ rustc_queries! {
|
|||
desc { "computing layout of `{}`", key.value }
|
||||
}
|
||||
|
||||
/// Computes the naive layout approximation of a type. Note that this implicitly
|
||||
/// executes in "reveal all" mode, and will normalize the input type.
|
||||
///
|
||||
/// Unlike `layout_of`, this doesn't look past references (beyond the `Pointee::Metadata`
|
||||
/// projection), and as such can be called on generic types like `Option<&T>`.
|
||||
query naive_layout_of(
|
||||
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
|
||||
) -> Result<ty::layout::TyAndNaiveLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
|
||||
depth_limit
|
||||
desc { "computing layout (naive) of `{}`", key.value }
|
||||
}
|
||||
|
||||
/// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
|
||||
///
|
||||
/// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance`
|
||||
|
|
@ -1481,11 +1469,6 @@ rustc_queries! {
|
|||
desc { "getting a crate's configured panic-in-drop strategy" }
|
||||
separate_provide_extern
|
||||
}
|
||||
query reference_niches_policy(_: CrateNum) -> abi::ReferenceNichePolicy {
|
||||
fatal_cycle
|
||||
desc { "getting a crate's policy for size and alignment niches of references" }
|
||||
separate_provide_extern
|
||||
}
|
||||
query is_no_builtins(_: CrateNum) -> bool {
|
||||
fatal_cycle
|
||||
desc { "getting whether a crate has `#![no_builtins]`" }
|
||||
|
|
|
|||
|
|
@ -313,16 +313,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
|||
) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
debug_assert!(!ty.has_non_region_infer());
|
||||
|
||||
// First, try computing an exact naive layout (this covers simple types with generic
|
||||
// references, where a full static layout would fail).
|
||||
if let Ok(layout) = tcx.naive_layout_of(param_env.and(ty)) {
|
||||
if layout.exact {
|
||||
return Ok(SizeSkeleton::Known(layout.size));
|
||||
}
|
||||
}
|
||||
|
||||
// Second, try computing a full static layout (this covers cases when the naive layout
|
||||
// wasn't smart enough, but cannot deal with generic references).
|
||||
// First try computing a static layout.
|
||||
let err = match tcx.layout_of(param_env.and(ty)) {
|
||||
Ok(layout) => {
|
||||
return Ok(SizeSkeleton::Known(layout.size));
|
||||
|
|
@ -336,7 +327,6 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
|||
) => return Err(e),
|
||||
};
|
||||
|
||||
// Third, fall back to ad-hoc cases.
|
||||
match *ty.kind() {
|
||||
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
|
||||
let non_zero = !ty.is_unsafe_ptr();
|
||||
|
|
@ -631,219 +621,6 @@ impl<T, E> MaybeResult<T> for Result<T, E> {
|
|||
|
||||
pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, HashStable)]
|
||||
pub struct TyAndNaiveLayout<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
pub layout: NaiveLayout,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for TyAndNaiveLayout<'_> {
|
||||
type Target = NaiveLayout;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.layout
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for TyAndNaiveLayout<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.layout
|
||||
}
|
||||
}
|
||||
|
||||
/// Extremely simplified approximation of a type's layout returned by the
|
||||
/// `naive_layout_of` query.
|
||||
#[derive(Copy, Clone, Debug, HashStable)]
|
||||
pub struct NaiveLayout {
|
||||
pub abi: NaiveAbi,
|
||||
/// Niche information, required for tracking non-null enum optimizations.
|
||||
pub niches: NaiveNiches,
|
||||
/// An underestimate of the layout's size.
|
||||
pub size: Size,
|
||||
/// An underestimate of the layout's required alignment.
|
||||
pub align: Align,
|
||||
/// If `true`, `size` and `align` must be exact values.
|
||||
pub exact: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)]
|
||||
pub enum NaiveNiches {
|
||||
None,
|
||||
Some,
|
||||
Maybe,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)]
|
||||
pub enum NaiveAbi {
|
||||
/// A scalar layout, always implies `exact` and a non-zero `size`.
|
||||
Scalar(Primitive),
|
||||
/// An uninhabited layout. (needed to properly track `Scalar` and niches)
|
||||
Uninhabited,
|
||||
/// An unsized aggregate. (needed to properly track `Scalar` and niches)
|
||||
Unsized,
|
||||
/// Any other sized layout.
|
||||
Sized,
|
||||
}
|
||||
|
||||
impl NaiveAbi {
|
||||
#[inline]
|
||||
pub fn as_aggregate(self) -> Self {
|
||||
match self {
|
||||
NaiveAbi::Scalar(_) => NaiveAbi::Sized,
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NaiveLayout {
|
||||
/// The layout of an empty aggregate, e.g. `()`.
|
||||
pub const EMPTY: Self = Self {
|
||||
size: Size::ZERO,
|
||||
align: Align::ONE,
|
||||
exact: true,
|
||||
abi: NaiveAbi::Sized,
|
||||
niches: NaiveNiches::None,
|
||||
};
|
||||
|
||||
/// Returns whether `self` is a valid approximation of the given full `layout`.
|
||||
///
|
||||
/// This should always return `true` when both layouts are computed from the same type.
|
||||
pub fn is_refined_by(&self, layout: Layout<'_>) -> bool {
|
||||
if self.size > layout.size() || self.align > layout.align().abi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let NaiveAbi::Scalar(prim) = self.abi {
|
||||
if !self.exact
|
||||
|| self.size == Size::ZERO
|
||||
|| !matches!(layout.abi(), Abi::Scalar(s) if s.primitive() == prim)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
match (self.niches, layout.largest_niche()) {
|
||||
(NaiveNiches::None, Some(_)) => return false,
|
||||
(NaiveNiches::Some, None) => return false,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
!self.exact || (self.size, self.align) == (layout.size(), layout.align().abi)
|
||||
}
|
||||
|
||||
/// Returns if this layout is known to be pointer-like (`None` if uncertain)
|
||||
///
|
||||
/// See the corresponding `Layout::is_pointer_like` method.
|
||||
pub fn is_pointer_like(&self, dl: &TargetDataLayout) -> Option<bool> {
|
||||
match self.abi {
|
||||
NaiveAbi::Scalar(_) => {
|
||||
assert!(self.exact);
|
||||
Some(self.size == dl.pointer_size && self.align == dl.pointer_align.abi)
|
||||
}
|
||||
NaiveAbi::Uninhabited | NaiveAbi::Unsized => Some(false),
|
||||
NaiveAbi::Sized if self.exact => Some(false),
|
||||
NaiveAbi::Sized => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Artificially lowers the alignment of this layout.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn packed(mut self, align: Align) -> Self {
|
||||
if self.align > align {
|
||||
self.align = align;
|
||||
self.abi = self.abi.as_aggregate();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Artificially raises the alignment of this layout.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn align_to(mut self, align: Align) -> Self {
|
||||
if align > self.align {
|
||||
self.align = align;
|
||||
self.abi = self.abi.as_aggregate();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Artificially makes this layout inexact.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn inexact(mut self) -> Self {
|
||||
self.abi = self.abi.as_aggregate();
|
||||
self.exact = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Pads this layout so that its size is a multiple of `align`.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn pad_to_align(mut self, align: Align) -> Self {
|
||||
let new_size = self.size.align_to(align);
|
||||
if new_size > self.size {
|
||||
self.abi = self.abi.as_aggregate();
|
||||
self.size = new_size;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the layout of `self` immediately followed by `other`, without any
|
||||
/// padding between them, as in a packed `struct` or tuple.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn concat(&self, other: &Self, dl: &TargetDataLayout) -> Option<Self> {
|
||||
use NaiveAbi::*;
|
||||
|
||||
let size = self.size.checked_add(other.size, dl)?;
|
||||
let align = cmp::max(self.align, other.align);
|
||||
let exact = self.exact && other.exact;
|
||||
let abi = match (self.abi, other.abi) {
|
||||
// The uninhabited and unsized ABIs override everything.
|
||||
(Uninhabited, _) | (_, Uninhabited) => Uninhabited,
|
||||
(Unsized, _) | (_, Unsized) => Unsized,
|
||||
// A scalar struct must have a single non ZST-field.
|
||||
(_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s,
|
||||
(s @ Scalar(_), _) if exact && other.size == Size::ZERO => s,
|
||||
// Default case.
|
||||
(_, _) => Sized,
|
||||
};
|
||||
let niches = match (self.niches, other.niches) {
|
||||
(NaiveNiches::Some, _) | (_, NaiveNiches::Some) => NaiveNiches::Some,
|
||||
(NaiveNiches::None, NaiveNiches::None) => NaiveNiches::None,
|
||||
(_, _) => NaiveNiches::Maybe,
|
||||
};
|
||||
Some(Self { abi, size, align, exact, niches })
|
||||
}
|
||||
|
||||
/// Returns the layout of `self` superposed with `other`, as in an `enum`
|
||||
/// or an `union`.
|
||||
///
|
||||
/// Note: This always ignore niche information from `other`.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
use NaiveAbi::*;
|
||||
|
||||
let size = cmp::max(self.size, other.size);
|
||||
let align = cmp::max(self.align, other.align);
|
||||
let exact = self.exact && other.exact;
|
||||
let abi = match (self.abi, other.abi) {
|
||||
// The unsized ABI overrides everything.
|
||||
(Unsized, _) | (_, Unsized) => Unsized,
|
||||
// A scalar union must have a single non ZST-field...
|
||||
(_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s,
|
||||
(s @ Scalar(_), _) if exact && other.size == Size::ZERO => s,
|
||||
// ...or identical scalar fields.
|
||||
(Scalar(s1), Scalar(s2)) if s1 == s2 => Scalar(s1),
|
||||
// Default cases.
|
||||
(Uninhabited, Uninhabited) => Uninhabited,
|
||||
(_, _) => Sized,
|
||||
};
|
||||
Self { abi, size, align, exact, niches: self.niches }
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for contexts that want to be able to compute layouts of types.
|
||||
/// This automatically gives access to `LayoutOf`, through a blanket `impl`.
|
||||
pub trait LayoutOfHelpers<'tcx>: HasDataLayout + HasTyCtxt<'tcx> + HasParamEnv<'tcx> {
|
||||
|
|
@ -896,19 +673,6 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
|
|||
.map_err(|err| self.handle_layout_err(*err, span, ty)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the naive layout estimate of a type. Note that this implicitly
|
||||
/// executes in "reveal all" mode, and will normalize the input type.
|
||||
///
|
||||
/// Unlike `layout_of`, this doesn't look past references (beyond the `Pointee::Metadata`
|
||||
/// projection), and as such can be called on generic types like `Option<&T>`.
|
||||
#[inline]
|
||||
fn naive_layout_of(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<TyAndNaiveLayout<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
self.tcx().naive_layout_of(self.param_env().and(ty))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}
|
||||
|
|
@ -1205,9 +969,6 @@ where
|
|||
this: TyAndLayout<'tcx>,
|
||||
cx: &C,
|
||||
offset: Size,
|
||||
// If true, assume that pointers are either null or valid (according to their type),
|
||||
// enabling extra optimizations.
|
||||
mut assume_valid_ptr: bool,
|
||||
) -> Option<PointeeInfo> {
|
||||
let tcx = cx.tcx();
|
||||
let param_env = cx.param_env();
|
||||
|
|
@ -1230,19 +991,19 @@ where
|
|||
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
|
||||
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
let safe = match (assume_valid_ptr, mt) {
|
||||
(true, hir::Mutability::Not) => Some(PointerKind::SharedRef {
|
||||
let kind = match mt {
|
||||
hir::Mutability::Not => PointerKind::SharedRef {
|
||||
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
|
||||
}),
|
||||
(true, hir::Mutability::Mut) => Some(PointerKind::MutableRef {
|
||||
},
|
||||
hir::Mutability::Mut => PointerKind::MutableRef {
|
||||
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
|
||||
}),
|
||||
(false, _) => None,
|
||||
},
|
||||
};
|
||||
|
||||
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
|
||||
size: layout.size,
|
||||
align: layout.align.abi,
|
||||
safe,
|
||||
safe: Some(kind),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1251,21 +1012,19 @@ where
|
|||
// Within the discriminant field, only the niche itself is
|
||||
// always initialized, so we only check for a pointer at its
|
||||
// offset.
|
||||
//
|
||||
// If the niche is a pointer, it's either valid (according
|
||||
// to its type), or null (which the niche field's scalar
|
||||
// validity range encodes). This allows using
|
||||
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
|
||||
// this will continue to work as long as we don't start
|
||||
// using more niches than just null (e.g., the first page of
|
||||
// the address space, or unaligned pointers).
|
||||
Variants::Multiple {
|
||||
tag_encoding:
|
||||
TagEncoding::Niche {
|
||||
untagged_variant,
|
||||
niche_variants: ref variants,
|
||||
niche_start,
|
||||
},
|
||||
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
|
||||
tag_field,
|
||||
..
|
||||
} if this.fields.offset(tag_field) == offset => {
|
||||
// We can only continue assuming pointer validity if the only possible
|
||||
// discriminant value is null. The null special-case is permitted by LLVM's
|
||||
// `dereferenceable_or_null`, and allow types like `Option<&T>` to benefit
|
||||
// from optimizations.
|
||||
assume_valid_ptr &= niche_start == 0 && variants.start() == variants.end();
|
||||
Some(this.for_variant(cx, untagged_variant))
|
||||
}
|
||||
_ => Some(this),
|
||||
|
|
@ -1291,12 +1050,9 @@ where
|
|||
result = field.to_result().ok().and_then(|field| {
|
||||
if ptr_end <= field_start + field.size {
|
||||
// We found the right field, look inside it.
|
||||
Self::ty_and_layout_pointee_info_at(
|
||||
field,
|
||||
cx,
|
||||
offset - field_start,
|
||||
assume_valid_ptr,
|
||||
)
|
||||
let field_info =
|
||||
field.pointee_info_at(cx, offset - field_start);
|
||||
field_info
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -1311,7 +1067,7 @@ where
|
|||
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
|
||||
if let Some(ref mut pointee) = result {
|
||||
if let ty::Adt(def, _) = this.ty.kind() {
|
||||
if assume_valid_ptr && def.is_box() && offset.bytes() == 0 {
|
||||
if def.is_box() && offset.bytes() == 0 {
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
pointee.safe = Some(PointerKind::Box {
|
||||
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Inlining pass for MIR functions
|
||||
use crate::deref_separator::deref_finder;
|
||||
use rustc_attr::InlineAttr;
|
||||
use rustc_const_eval::transform::validate::validate_types;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::Idx;
|
||||
|
|
@ -10,7 +11,7 @@ use rustc_middle::mir::*;
|
|||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_session::config::OptLevel;
|
||||
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::simplify::{remove_dead_blocks, CfgSimplifier};
|
||||
|
|
@ -200,6 +201,19 @@ impl<'tcx> Inliner<'tcx> {
|
|||
return Err("failed to normalize callee body");
|
||||
};
|
||||
|
||||
// Normally, this shouldn't be required, but trait normalization failure can create a
|
||||
// validation ICE.
|
||||
if !validate_types(
|
||||
self.tcx,
|
||||
MirPhase::Runtime(RuntimePhase::Optimized),
|
||||
self.param_env,
|
||||
&callee_body,
|
||||
)
|
||||
.is_empty()
|
||||
{
|
||||
return Err("failed to validate callee body");
|
||||
}
|
||||
|
||||
// Check call signature compatibility.
|
||||
// Normally, this shouldn't be required, but trait normalization failure can create a
|
||||
// validation ICE.
|
||||
|
|
@ -437,13 +451,8 @@ impl<'tcx> Inliner<'tcx> {
|
|||
instance: callsite.callee,
|
||||
callee_body,
|
||||
cost: 0,
|
||||
validation: Ok(()),
|
||||
};
|
||||
|
||||
for var_debug_info in callee_body.var_debug_info.iter() {
|
||||
checker.visit_var_debug_info(var_debug_info);
|
||||
}
|
||||
|
||||
// Traverse the MIR manually so we can account for the effects of inlining on the CFG.
|
||||
let mut work_list = vec![START_BLOCK];
|
||||
let mut visited = BitSet::new_empty(callee_body.basic_blocks.len());
|
||||
|
|
@ -480,9 +489,6 @@ impl<'tcx> Inliner<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Abort if type validation found anything fishy.
|
||||
checker.validation?;
|
||||
|
||||
// N.B. We still apply our cost threshold to #[inline(always)] functions.
|
||||
// That attribute is often applied to very large functions that exceed LLVM's (very
|
||||
// generous) inlining threshold. Such functions are very poor MIR inlining candidates.
|
||||
|
|
@ -774,11 +780,10 @@ struct CostChecker<'b, 'tcx> {
|
|||
cost: usize,
|
||||
callee_body: &'b Body<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
validation: Result<(), &'static str>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
|
||||
// Don't count StorageLive/StorageDead in the inlining cost.
|
||||
match statement.kind {
|
||||
StatementKind::StorageLive(_)
|
||||
|
|
@ -787,11 +792,9 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
|||
| StatementKind::Nop => {}
|
||||
_ => self.cost += INSTR_COST,
|
||||
}
|
||||
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) {
|
||||
let tcx = self.tcx;
|
||||
match terminator.kind {
|
||||
TerminatorKind::Drop { ref place, unwind, .. } => {
|
||||
|
|
@ -835,109 +838,6 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
|||
}
|
||||
_ => self.cost += INSTR_COST,
|
||||
}
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
|
||||
/// This method duplicates code from MIR validation in an attempt to detect type mismatches due
|
||||
/// to normalization failure.
|
||||
fn visit_projection_elem(
|
||||
&mut self,
|
||||
place_ref: PlaceRef<'tcx>,
|
||||
elem: PlaceElem<'tcx>,
|
||||
context: PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
if let ProjectionElem::Field(f, ty) = elem {
|
||||
let parent_ty = place_ref.ty(&self.callee_body.local_decls, self.tcx);
|
||||
let check_equal = |this: &mut Self, f_ty| {
|
||||
// Fast path if there is nothing to substitute.
|
||||
if ty == f_ty {
|
||||
return;
|
||||
}
|
||||
let ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&ty));
|
||||
let f_ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&f_ty));
|
||||
if ty == f_ty {
|
||||
return;
|
||||
}
|
||||
if !util::is_subtype(this.tcx, this.param_env, ty, f_ty) {
|
||||
trace!(?ty, ?f_ty);
|
||||
this.validation = Err("failed to normalize projection type");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let kind = match parent_ty.ty.kind() {
|
||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
self.tcx.type_of(def_id).instantiate(self.tcx, args).kind()
|
||||
}
|
||||
kind => kind,
|
||||
};
|
||||
|
||||
match kind {
|
||||
ty::Tuple(fields) => {
|
||||
let Some(f_ty) = fields.get(f.as_usize()) else {
|
||||
self.validation = Err("malformed MIR");
|
||||
return;
|
||||
};
|
||||
check_equal(self, *f_ty);
|
||||
}
|
||||
ty::Adt(adt_def, args) => {
|
||||
let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
|
||||
let Some(field) = adt_def.variant(var).fields.get(f) else {
|
||||
self.validation = Err("malformed MIR");
|
||||
return;
|
||||
};
|
||||
check_equal(self, field.ty(self.tcx, args));
|
||||
}
|
||||
ty::Closure(_, args) => {
|
||||
let args = args.as_closure();
|
||||
let Some(f_ty) = args.upvar_tys().nth(f.as_usize()) else {
|
||||
self.validation = Err("malformed MIR");
|
||||
return;
|
||||
};
|
||||
check_equal(self, f_ty);
|
||||
}
|
||||
&ty::Generator(def_id, args, _) => {
|
||||
let f_ty = if let Some(var) = parent_ty.variant_index {
|
||||
let gen_body = if def_id == self.callee_body.source.def_id() {
|
||||
self.callee_body
|
||||
} else {
|
||||
self.tcx.optimized_mir(def_id)
|
||||
};
|
||||
|
||||
let Some(layout) = gen_body.generator_layout() else {
|
||||
self.validation = Err("malformed MIR");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(&local) = layout.variant_fields[var].get(f) else {
|
||||
self.validation = Err("malformed MIR");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(f_ty) = layout.field_tys.get(local) else {
|
||||
self.validation = Err("malformed MIR");
|
||||
return;
|
||||
};
|
||||
|
||||
f_ty.ty
|
||||
} else {
|
||||
let Some(f_ty) = args.as_generator().prefix_tys().nth(f.index()) else {
|
||||
self.validation = Err("malformed MIR");
|
||||
return;
|
||||
};
|
||||
|
||||
f_ty
|
||||
};
|
||||
|
||||
check_equal(self, f_ty);
|
||||
}
|
||||
_ => self.validation = Err("malformed MIR"),
|
||||
}
|
||||
}
|
||||
|
||||
self.super_projection_elem(place_ref, elem, context, location);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3003,7 +3003,8 @@ impl<'a> Parser<'a> {
|
|||
fn is_do_catch_block(&self) -> bool {
|
||||
self.token.is_keyword(kw::Do)
|
||||
&& self.is_keyword_ahead(1, &[kw::Catch])
|
||||
&& self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
|
||||
&& self
|
||||
.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
|
||||
&& !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
|
||||
}
|
||||
|
||||
|
|
@ -3013,7 +3014,8 @@ impl<'a> Parser<'a> {
|
|||
|
||||
fn is_try_block(&self) -> bool {
|
||||
self.token.is_keyword(kw::Try)
|
||||
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
|
||||
&& self
|
||||
.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block())
|
||||
&& self.token.uninterpolated_span().at_least_rust_2018()
|
||||
}
|
||||
|
||||
|
|
@ -3032,10 +3034,14 @@ impl<'a> Parser<'a> {
|
|||
&& ((
|
||||
// `async move {`
|
||||
self.is_keyword_ahead(1, &[kw::Move])
|
||||
&& self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
|
||||
&& self.look_ahead(2, |t| {
|
||||
*t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
|
||||
})
|
||||
) || (
|
||||
// `async {`
|
||||
self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
|
||||
self.look_ahead(1, |t| {
|
||||
*t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,8 +176,7 @@ impl QueryJobId {
|
|||
while let Some(id) = current_id {
|
||||
let info = query_map.get(&id).unwrap();
|
||||
// FIXME: This string comparison should probably not be done.
|
||||
let query_name = format!("{:?}", info.query.dep_kind);
|
||||
if query_name == "layout_of" || query_name == "naive_layout_of" {
|
||||
if format!("{:?}", info.query.dep_kind) == "layout_of" {
|
||||
depth += 1;
|
||||
last_layout = Some((info.clone(), depth));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2569,7 +2569,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
let res = match kind {
|
||||
RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()),
|
||||
RibKind::Normal => {
|
||||
if self.r.tcx.sess.features_untracked().non_lifetime_binders {
|
||||
if self.r.tcx.features().non_lifetime_binders {
|
||||
Res::Def(def_kind, def_id.to_def_id())
|
||||
} else {
|
||||
Res::Err
|
||||
|
|
|
|||
|
|
@ -576,10 +576,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// We are trying to avoid reporting this error if other related errors were reported.
|
||||
if res != Res::Err
|
||||
&& inner_attr
|
||||
&& !self.tcx.sess.features_untracked().custom_inner_attributes
|
||||
{
|
||||
if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes {
|
||||
let msg = match res {
|
||||
Res::Def(..) => "inner macro attributes are unstable",
|
||||
Res::NonMacroAttr(..) => "custom inner attributes are unstable",
|
||||
|
|
|
|||
|
|
@ -3117,7 +3117,6 @@ pub(crate) mod dep_tracking {
|
|||
use rustc_feature::UnstableFeatures;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::RealFileName;
|
||||
use rustc_target::abi::ReferenceNichePolicy;
|
||||
use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
|
||||
use rustc_target::spec::{
|
||||
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
|
||||
|
|
@ -3213,7 +3212,6 @@ pub(crate) mod dep_tracking {
|
|||
OomStrategy,
|
||||
LanguageIdentifier,
|
||||
TraitSolver,
|
||||
ReferenceNichePolicy,
|
||||
);
|
||||
|
||||
impl<T1, T2> DepTrackingHash for (T1, T2)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::{lint, EarlyErrorHandler};
|
|||
use rustc_data_structures::profiling::TimePassesFormat;
|
||||
use rustc_errors::ColorConfig;
|
||||
use rustc_errors::{LanguageIdentifier, TerminalUrl};
|
||||
use rustc_target::abi::ReferenceNichePolicy;
|
||||
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
|
||||
use rustc_target::spec::{
|
||||
RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
|
||||
|
|
@ -422,8 +421,6 @@ mod desc {
|
|||
pub const parse_proc_macro_execution_strategy: &str =
|
||||
"one of supported execution strategies (`same-thread`, or `cross-thread`)";
|
||||
pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
|
||||
pub const parse_opt_reference_niches: &str =
|
||||
"`null`, or a `,` separated combination of `size` or `align`";
|
||||
}
|
||||
|
||||
mod parse {
|
||||
|
|
@ -1256,31 +1253,6 @@ mod parse {
|
|||
};
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn parse_opt_reference_niches(
|
||||
slot: &mut Option<ReferenceNichePolicy>,
|
||||
v: Option<&str>,
|
||||
) -> bool {
|
||||
let Some(s) = v else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let slot = slot.get_or_insert_default();
|
||||
|
||||
if s == "null" {
|
||||
return true;
|
||||
}
|
||||
|
||||
for opt in s.split(",") {
|
||||
match opt {
|
||||
"size" => slot.size = true,
|
||||
"align" => slot.align = true,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
options! {
|
||||
|
|
@ -1729,8 +1701,6 @@ options! {
|
|||
"enable queries of the dependency graph for regression testing (default: no)"),
|
||||
randomize_layout: bool = (false, parse_bool, [TRACKED],
|
||||
"randomize the layout of types (default: no)"),
|
||||
reference_niches: Option<ReferenceNichePolicy> = (None, parse_opt_reference_niches, [TRACKED],
|
||||
"override the set of discriminant niches that may be exposed by references"),
|
||||
relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"whether ELF relocations can be relaxed"),
|
||||
relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
|
||||
|
|
|
|||
|
|
@ -50,9 +50,6 @@ pub trait TyAbiInterface<'a, C>: Sized {
|
|||
this: TyAndLayout<'a, Self>,
|
||||
cx: &C,
|
||||
offset: Size,
|
||||
// If true, assume that pointers are either null or valid (according to their type),
|
||||
// enabling extra optimizations.
|
||||
assume_valid_ptr: bool,
|
||||
) -> Option<PointeeInfo>;
|
||||
fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
|
||||
fn is_never(this: TyAndLayout<'a, Self>) -> bool;
|
||||
|
|
@ -79,8 +76,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
where
|
||||
Ty: TyAbiInterface<'a, C>,
|
||||
{
|
||||
let assume_valid_ptr = true;
|
||||
Ty::ty_and_layout_pointee_info_at(self, cx, offset, assume_valid_ptr)
|
||||
Ty::ty_and_layout_pointee_info_at(self, cx, offset)
|
||||
}
|
||||
|
||||
pub fn is_single_fp_element<C>(self, cx: &C) -> bool
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
trait_selection_adjust_signature_borrow = consider adjusting the signature so it borrows its {$len ->
|
||||
[one] argument
|
||||
*[other] arguments
|
||||
}
|
||||
|
||||
trait_selection_adjust_signature_remove_borrow = consider adjusting the signature so it does not borrow its {$len ->
|
||||
[one] argument
|
||||
*[other] arguments
|
||||
}
|
||||
|
||||
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
|
||||
|
||||
trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use crate::fluent_generated as fluent;
|
||||
use rustc_errors::{ErrorGuaranteed, Handler, IntoDiagnostic};
|
||||
use rustc_errors::{
|
||||
AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
|
||||
SubdiagnosticMessage,
|
||||
};
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_middle::ty::{self, PolyTraitRef, Ty};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
|
@ -97,3 +100,34 @@ pub struct InherentProjectionNormalizationOverflow {
|
|||
pub span: Span,
|
||||
pub ty: String,
|
||||
}
|
||||
|
||||
pub enum AdjustSignatureBorrow {
|
||||
Borrow { to_borrow: Vec<(Span, String)> },
|
||||
RemoveBorrow { remove_borrow: Vec<(Span, String)> },
|
||||
}
|
||||
|
||||
impl AddToDiagnostic for AdjustSignatureBorrow {
|
||||
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||
where
|
||||
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||
{
|
||||
match self {
|
||||
AdjustSignatureBorrow::Borrow { to_borrow } => {
|
||||
diag.set_arg("len", to_borrow.len());
|
||||
diag.multipart_suggestion_verbose(
|
||||
fluent::trait_selection_adjust_signature_borrow,
|
||||
to_borrow,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => {
|
||||
diag.set_arg("len", remove_borrow.len());
|
||||
diag.multipart_suggestion_verbose(
|
||||
fluent::trait_selection_adjust_signature_remove_borrow,
|
||||
remove_borrow,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,24 +66,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
Invert::Yes,
|
||||
));
|
||||
// Relate via args
|
||||
let subst_relate_response = self
|
||||
.assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction);
|
||||
candidates.extend(subst_relate_response);
|
||||
candidates.extend(
|
||||
self.assemble_subst_relate_candidate(
|
||||
param_env, alias_lhs, alias_rhs, direction,
|
||||
),
|
||||
);
|
||||
debug!(?candidates);
|
||||
|
||||
if let Some(merged) = self.try_merge_responses(&candidates) {
|
||||
Ok(merged)
|
||||
} else {
|
||||
// When relating two aliases and we have ambiguity, we prefer
|
||||
// relating the generic arguments of the aliases over normalizing
|
||||
// them. This is necessary for inference during typeck.
|
||||
// When relating two aliases and we have ambiguity, if both
|
||||
// aliases can be normalized to something, we prefer
|
||||
// "bidirectionally normalizing" both of them within the same
|
||||
// candidate.
|
||||
//
|
||||
// See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/25>.
|
||||
//
|
||||
// As this is incomplete, we must not do so during coherence.
|
||||
match self.solver_mode() {
|
||||
SolverMode::Normal => {
|
||||
if let Ok(subst_relate_response) = subst_relate_response {
|
||||
Ok(subst_relate_response)
|
||||
} else if let Ok(bidirectional_normalizes_to_response) = self
|
||||
if let Ok(bidirectional_normalizes_to_response) = self
|
||||
.assemble_bidirectional_normalizes_to_candidate(
|
||||
param_env, lhs, rhs, direction,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -223,20 +223,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
|||
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
||||
}
|
||||
|
||||
// First, try computing an exact naive layout in case the type is generic.
|
||||
let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) {
|
||||
layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| {
|
||||
// Second, we fall back to full layout computation.
|
||||
tcx.layout_of(key)
|
||||
.ok()
|
||||
.filter(|l| l.layout.is_pointer_like(&tcx.data_layout))
|
||||
.is_some()
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if is_pointer_like {
|
||||
if let Ok(layout) = tcx.layout_of(key)
|
||||
&& layout.layout.is_pointer_like(&tcx.data_layout)
|
||||
{
|
||||
// FIXME: We could make this faster by making a no-constraints response
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use super::{
|
|||
PredicateObligation,
|
||||
};
|
||||
|
||||
use crate::errors;
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::{NormalizeExt, ObligationCtxt};
|
||||
|
||||
|
|
@ -4031,6 +4032,10 @@ fn hint_missing_borrow<'tcx>(
|
|||
found_node: Node<'_>,
|
||||
err: &mut Diagnostic,
|
||||
) {
|
||||
if matches!(found_node, Node::TraitItem(..)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let found_args = match found.kind() {
|
||||
ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
|
||||
kind => {
|
||||
|
|
@ -4102,19 +4107,11 @@ fn hint_missing_borrow<'tcx>(
|
|||
}
|
||||
|
||||
if !to_borrow.is_empty() {
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider borrowing the argument",
|
||||
to_borrow,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow });
|
||||
}
|
||||
|
||||
if !remove_borrow.is_empty() {
|
||||
err.multipart_suggestion_verbose(
|
||||
"do not borrow the argument",
|
||||
remove_borrow,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -979,20 +979,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
// First, try computing an exact naive layout in case the type is generic.
|
||||
let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) {
|
||||
layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| {
|
||||
// Second, we fall back to full layout computation.
|
||||
tcx.layout_of(key)
|
||||
.ok()
|
||||
.filter(|l| l.layout.is_pointer_like(&tcx.data_layout))
|
||||
.is_some()
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if is_pointer_like {
|
||||
if let Ok(layout) = tcx.layout_of(key)
|
||||
&& layout.layout.is_pointer_like(&tcx.data_layout)
|
||||
{
|
||||
candidates.vec.push(BuiltinCandidate { has_nested: false });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use rustc_hir as hir;
|
|||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal};
|
||||
use rustc_middle::query::{LocalCrate, Providers};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::layout::{
|
||||
IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
|
||||
};
|
||||
|
|
@ -24,28 +24,32 @@ use crate::errors::{
|
|||
use crate::layout_sanity_check::sanity_check_layout;
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { layout_of, reference_niches_policy, ..*providers };
|
||||
*providers = Providers { layout_of, ..*providers };
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
fn reference_niches_policy<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> ReferenceNichePolicy {
|
||||
tcx.sess.opts.unstable_opts.reference_niches.unwrap_or(DEFAULT_REF_NICHES)
|
||||
}
|
||||
|
||||
/// The reference niche policy for builtin types, and for types in
|
||||
/// crates not specifying `-Z reference-niches`.
|
||||
const DEFAULT_REF_NICHES: ReferenceNichePolicy = ReferenceNichePolicy { size: false, align: false };
|
||||
|
||||
#[instrument(skip(tcx, query), level = "debug")]
|
||||
fn layout_of<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
let (param_env, unnormalized_ty) = query.into_parts();
|
||||
let (param_env, ty) = query.into_parts();
|
||||
debug!(?ty);
|
||||
|
||||
let param_env = param_env.with_reveal_all_normalized(tcx);
|
||||
// `naive_layout_of` takes care of normalizing the type.
|
||||
let naive = tcx.naive_layout_of(query)?;
|
||||
let ty = naive.ty;
|
||||
let unnormalized_ty = ty;
|
||||
|
||||
// FIXME: We might want to have two different versions of `layout_of`:
|
||||
// One that can be called after typecheck has completed and can use
|
||||
// `normalize_erasing_regions` here and another one that can be called
|
||||
// before typecheck has completed and uses `try_normalize_erasing_regions`.
|
||||
let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
|
||||
Ok(t) => t,
|
||||
Err(normalization_error) => {
|
||||
return Err(tcx
|
||||
.arena
|
||||
.alloc(LayoutError::NormalizationFailure(ty, normalization_error)));
|
||||
}
|
||||
};
|
||||
|
||||
if ty != unnormalized_ty {
|
||||
// Ensure this layout is also cached for the normalized type.
|
||||
|
|
@ -53,11 +57,13 @@ fn layout_of<'tcx>(
|
|||
}
|
||||
|
||||
let cx = LayoutCx { tcx, param_env };
|
||||
let layout = layout_of_uncached(&cx, ty)?;
|
||||
|
||||
let layout = layout_of_uncached(&cx, ty)?;
|
||||
let layout = TyAndLayout { ty, layout };
|
||||
|
||||
record_layout_for_printing(&cx, layout);
|
||||
sanity_check_layout(&cx, &layout, &naive);
|
||||
|
||||
sanity_check_layout(&cx, &layout);
|
||||
|
||||
Ok(layout)
|
||||
}
|
||||
|
|
@ -77,10 +83,12 @@ fn univariant_uninterned<'tcx>(
|
|||
kind: StructKind,
|
||||
) -> Result<LayoutS, &'tcx LayoutError<'tcx>> {
|
||||
let dl = cx.data_layout();
|
||||
assert!(
|
||||
!(repr.pack.is_some() && repr.align.is_some()),
|
||||
"already rejected by `naive_layout_of`"
|
||||
);
|
||||
let pack = repr.pack;
|
||||
if pack.is_some() && repr.align.is_some() {
|
||||
cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
|
||||
return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty)));
|
||||
}
|
||||
|
||||
cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))
|
||||
}
|
||||
|
||||
|
|
@ -138,35 +146,75 @@ fn layout_of_uncached<'tcx>(
|
|||
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
|
||||
let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA));
|
||||
if !ty.is_unsafe_ptr() {
|
||||
// Calling `layout_of` here would cause a query cycle for recursive types;
|
||||
// so use a conservative estimate that doesn't look past references.
|
||||
let naive = cx.naive_layout_of(pointee)?.layout;
|
||||
data_ptr.valid_range_mut().start = 1;
|
||||
}
|
||||
|
||||
let niches = match *pointee.kind() {
|
||||
ty::FnDef(def, ..)
|
||||
| ty::Foreign(def)
|
||||
| ty::Generator(def, ..)
|
||||
| ty::Closure(def, ..) => tcx.reference_niches_policy(def.krate),
|
||||
ty::Adt(def, _) => tcx.reference_niches_policy(def.did().krate),
|
||||
_ => DEFAULT_REF_NICHES,
|
||||
let pointee = tcx.normalize_erasing_regions(param_env, pointee);
|
||||
if pointee.is_sized(tcx, param_env) {
|
||||
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
|
||||
}
|
||||
|
||||
let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
|
||||
// Projection eagerly bails out when the pointee references errors,
|
||||
// fall back to structurally deducing metadata.
|
||||
&& !pointee.references_error()
|
||||
{
|
||||
let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
|
||||
let metadata_ty = match tcx.try_normalize_erasing_regions(
|
||||
param_env,
|
||||
pointee_metadata,
|
||||
) {
|
||||
Ok(metadata_ty) => metadata_ty,
|
||||
Err(mut err) => {
|
||||
// Usually `<Ty as Pointee>::Metadata` can't be normalized because
|
||||
// its struct tail cannot be normalized either, so try to get a
|
||||
// more descriptive layout error here, which will lead to less confusing
|
||||
// diagnostics.
|
||||
match tcx.try_normalize_erasing_regions(
|
||||
param_env,
|
||||
tcx.struct_tail_without_normalization(pointee),
|
||||
) {
|
||||
Ok(_) => {},
|
||||
Err(better_err) => {
|
||||
err = better_err;
|
||||
}
|
||||
}
|
||||
return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
|
||||
},
|
||||
};
|
||||
|
||||
let (min_addr, max_addr) = dl.address_range_for(
|
||||
if niches.size { naive.size } else { Size::ZERO },
|
||||
if niches.align { naive.align } else { Align::ONE },
|
||||
);
|
||||
let metadata_layout = cx.layout_of(metadata_ty)?;
|
||||
// If the metadata is a 1-zst, then the pointer is thin.
|
||||
if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
|
||||
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
|
||||
}
|
||||
|
||||
*data_ptr.valid_range_mut() =
|
||||
WrappingRange { start: min_addr.into(), end: max_addr.into() };
|
||||
}
|
||||
let Abi::Scalar(metadata) = metadata_layout.abi else {
|
||||
return Err(error(cx, LayoutError::Unknown(pointee)));
|
||||
};
|
||||
|
||||
if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? {
|
||||
// Effectively a (ptr, meta) tuple.
|
||||
tcx.mk_layout(cx.scalar_pair(data_ptr, metadata))
|
||||
metadata
|
||||
} else {
|
||||
// No metadata, this is a thin pointer.
|
||||
tcx.mk_layout(LayoutS::scalar(cx, data_ptr))
|
||||
}
|
||||
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
|
||||
|
||||
match unsized_part.kind() {
|
||||
ty::Foreign(..) => {
|
||||
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
|
||||
}
|
||||
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
|
||||
ty::Dynamic(..) => {
|
||||
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
|
||||
vtable.valid_range_mut().start = 1;
|
||||
vtable
|
||||
}
|
||||
_ => {
|
||||
return Err(error(cx, LayoutError::Unknown(pointee)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Effectively a (ptr, meta) tuple.
|
||||
tcx.mk_layout(cx.scalar_pair(data_ptr, metadata))
|
||||
}
|
||||
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
|
|
@ -178,8 +226,16 @@ fn layout_of_uncached<'tcx>(
|
|||
}
|
||||
|
||||
// Arrays and slices.
|
||||
ty::Array(element, count) => {
|
||||
let count = compute_array_count(cx, count)
|
||||
ty::Array(element, mut count) => {
|
||||
if count.has_projections() {
|
||||
count = tcx.normalize_erasing_regions(param_env, count);
|
||||
if count.has_projections() {
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
}
|
||||
|
||||
let count = count
|
||||
.try_eval_target_usize(tcx, param_env)
|
||||
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
|
||||
let element = cx.layout_of(element)?;
|
||||
let size = element
|
||||
|
|
@ -502,106 +558,22 @@ fn layout_of_uncached<'tcx>(
|
|||
}
|
||||
|
||||
// Types with no meaningful known layout.
|
||||
ty::Alias(..)
|
||||
| ty::Bound(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Infer(_)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Param(_)
|
||||
| ty::Error(_) => {
|
||||
unreachable!("already rejected by `naive_layout_of`");
|
||||
ty::Alias(..) => {
|
||||
// NOTE(eddyb) `layout_of` query should've normalized these away,
|
||||
// if that was possible, so there's no reason to try again here.
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
|
||||
ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
|
||||
bug!("Layout::compute: unexpected type `{}`", ty)
|
||||
}
|
||||
|
||||
ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_array_count<'tcx>(
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
mut count: ty::Const<'tcx>,
|
||||
) -> Option<u64> {
|
||||
let LayoutCx { tcx, param_env } = *cx;
|
||||
if count.has_projections() {
|
||||
count = tcx.normalize_erasing_regions(param_env, count);
|
||||
if count.has_projections() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
count.try_eval_target_usize(tcx, param_env)
|
||||
}
|
||||
|
||||
pub(crate) fn ptr_metadata_scalar<'tcx>(
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
pointee: Ty<'tcx>,
|
||||
) -> Result<Option<Scalar>, &'tcx LayoutError<'tcx>> {
|
||||
let dl = cx.data_layout();
|
||||
let scalar_unit = |value: Primitive| {
|
||||
let size = value.size(dl);
|
||||
assert!(size.bits() <= 128);
|
||||
Scalar::Initialized { value, valid_range: WrappingRange::full(size) }
|
||||
};
|
||||
|
||||
let LayoutCx { tcx, param_env } = *cx;
|
||||
|
||||
let pointee = tcx.normalize_erasing_regions(param_env, pointee);
|
||||
if pointee.is_sized(tcx, param_env) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
|
||||
// Projection eagerly bails out when the pointee references errors,
|
||||
// fall back to structurally deducing metadata.
|
||||
&& !pointee.references_error()
|
||||
{
|
||||
let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
|
||||
let metadata_ty = match tcx.try_normalize_erasing_regions(
|
||||
param_env,
|
||||
pointee_metadata,
|
||||
) {
|
||||
Ok(metadata_ty) => metadata_ty,
|
||||
Err(mut err) => {
|
||||
// Usually `<Ty as Pointee>::Metadata` can't be normalized because
|
||||
// its struct tail cannot be normalized either, so try to get a
|
||||
// more descriptive layout error here, which will lead to less confusing
|
||||
// diagnostics.
|
||||
match tcx.try_normalize_erasing_regions(
|
||||
param_env,
|
||||
tcx.struct_tail_without_normalization(pointee),
|
||||
) {
|
||||
Ok(_) => {},
|
||||
Err(better_err) => {
|
||||
err = better_err;
|
||||
}
|
||||
}
|
||||
return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
|
||||
},
|
||||
};
|
||||
|
||||
let metadata_layout = cx.layout_of(metadata_ty)?;
|
||||
|
||||
if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
|
||||
Ok(None) // If the metadata is a 1-zst, then the pointer is thin.
|
||||
} else if let Abi::Scalar(metadata) = metadata_layout.abi {
|
||||
Ok(Some(metadata))
|
||||
} else {
|
||||
Err(error(cx, LayoutError::Unknown(pointee)))
|
||||
}
|
||||
} else {
|
||||
let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
|
||||
|
||||
match unsized_part.kind() {
|
||||
ty::Foreign(..) => Ok(None),
|
||||
ty::Slice(_) | ty::Str => Ok(Some(scalar_unit(Int(dl.ptr_sized_integer(), false)))),
|
||||
ty::Dynamic(..) => {
|
||||
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
|
||||
vtable.valid_range_mut().start = 1;
|
||||
Ok(Some(vtable))
|
||||
}
|
||||
_ => Err(error(cx, LayoutError::Unknown(pointee))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Overlap eligibility and variant assignment for each GeneratorSavedLocal.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum SavedLocalEligibility {
|
||||
|
|
|
|||
|
|
@ -1,322 +0,0 @@
|
|||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::layout::{
|
||||
IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveAbi, NaiveLayout, NaiveNiches,
|
||||
TyAndNaiveLayout,
|
||||
};
|
||||
use rustc_middle::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_target::abi::*;
|
||||
|
||||
use std::ops::Bound;
|
||||
|
||||
use crate::layout::{compute_array_count, ptr_metadata_scalar};
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { naive_layout_of, ..*providers };
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, query), level = "debug")]
|
||||
fn naive_layout_of<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
) -> Result<TyAndNaiveLayout<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
let (param_env, ty) = query.into_parts();
|
||||
debug!(?ty);
|
||||
|
||||
let param_env = param_env.with_reveal_all_normalized(tcx);
|
||||
let unnormalized_ty = ty;
|
||||
|
||||
// FIXME: We might want to have two different versions of `layout_of`:
|
||||
// One that can be called after typecheck has completed and can use
|
||||
// `normalize_erasing_regions` here and another one that can be called
|
||||
// before typecheck has completed and uses `try_normalize_erasing_regions`.
|
||||
let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
|
||||
Ok(t) => t,
|
||||
Err(normalization_error) => {
|
||||
return Err(tcx
|
||||
.arena
|
||||
.alloc(LayoutError::NormalizationFailure(ty, normalization_error)));
|
||||
}
|
||||
};
|
||||
|
||||
if ty != unnormalized_ty {
|
||||
// Ensure this layout is also cached for the normalized type.
|
||||
return tcx.naive_layout_of(param_env.and(ty));
|
||||
}
|
||||
|
||||
let cx = LayoutCx { tcx, param_env };
|
||||
let layout = naive_layout_of_uncached(&cx, ty)?;
|
||||
Ok(TyAndNaiveLayout { ty, layout })
|
||||
}
|
||||
|
||||
fn error<'tcx>(
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
err: LayoutError<'tcx>,
|
||||
) -> &'tcx LayoutError<'tcx> {
|
||||
cx.tcx.arena.alloc(err)
|
||||
}
|
||||
|
||||
fn naive_layout_of_uncached<'tcx>(
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<NaiveLayout, &'tcx LayoutError<'tcx>> {
|
||||
let tcx = cx.tcx;
|
||||
let dl = cx.data_layout();
|
||||
|
||||
let scalar = |niched: bool, value: Primitive| NaiveLayout {
|
||||
abi: NaiveAbi::Scalar(value),
|
||||
niches: if niched { NaiveNiches::Some } else { NaiveNiches::None },
|
||||
size: value.size(dl),
|
||||
align: value.align(dl).abi,
|
||||
exact: true,
|
||||
};
|
||||
|
||||
let univariant = |fields: &mut dyn Iterator<Item = Ty<'tcx>>,
|
||||
repr: &ReprOptions|
|
||||
-> Result<NaiveLayout, &'tcx LayoutError<'tcx>> {
|
||||
if repr.pack.is_some() && repr.align.is_some() {
|
||||
cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
|
||||
let linear = repr.inhibit_struct_field_reordering_opt();
|
||||
let pack = repr.pack.unwrap_or(Align::MAX);
|
||||
let mut layout = NaiveLayout::EMPTY;
|
||||
|
||||
for field in fields {
|
||||
let field = cx.naive_layout_of(field)?.packed(pack);
|
||||
if linear {
|
||||
layout = layout.pad_to_align(field.align);
|
||||
}
|
||||
layout = layout
|
||||
.concat(&field, dl)
|
||||
.ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?;
|
||||
}
|
||||
|
||||
if let Some(align) = repr.align {
|
||||
layout = layout.align_to(align);
|
||||
}
|
||||
|
||||
if linear {
|
||||
layout.abi = layout.abi.as_aggregate();
|
||||
}
|
||||
|
||||
Ok(layout.pad_to_align(layout.align))
|
||||
};
|
||||
|
||||
debug_assert!(!ty.has_non_region_infer());
|
||||
|
||||
Ok(match *ty.kind() {
|
||||
// Basic scalars
|
||||
ty::Bool => scalar(true, Int(I8, false)),
|
||||
ty::Char => scalar(true, Int(I32, false)),
|
||||
ty::Int(ity) => scalar(false, Int(Integer::from_int_ty(dl, ity), true)),
|
||||
ty::Uint(ity) => scalar(false, Int(Integer::from_uint_ty(dl, ity), false)),
|
||||
ty::Float(fty) => scalar(
|
||||
false,
|
||||
match fty {
|
||||
ty::FloatTy::F32 => F32,
|
||||
ty::FloatTy::F64 => F64,
|
||||
},
|
||||
),
|
||||
ty::FnPtr(_) => scalar(true, Pointer(dl.instruction_address_space)),
|
||||
|
||||
// The never type.
|
||||
ty::Never => NaiveLayout { abi: NaiveAbi::Uninhabited, ..NaiveLayout::EMPTY },
|
||||
|
||||
// Potentially-wide pointers.
|
||||
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
|
||||
let data_ptr = scalar(!ty.is_unsafe_ptr(), Pointer(AddressSpace::DATA));
|
||||
if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? {
|
||||
// Effectively a (ptr, meta) tuple.
|
||||
let meta = scalar(!metadata.is_always_valid(dl), metadata.primitive());
|
||||
let l = data_ptr
|
||||
.concat(&meta, dl)
|
||||
.ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?;
|
||||
l.pad_to_align(l.align)
|
||||
} else {
|
||||
// No metadata, this is a thin pointer.
|
||||
data_ptr
|
||||
}
|
||||
}
|
||||
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
let ptr = scalar(false, Pointer(AddressSpace::DATA));
|
||||
let vtable = scalar(true, Pointer(AddressSpace::DATA));
|
||||
ptr.concat(&vtable, dl).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?
|
||||
}
|
||||
|
||||
// Arrays and slices.
|
||||
ty::Array(element, count) => {
|
||||
let count = compute_array_count(cx, count)
|
||||
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
|
||||
let element = cx.naive_layout_of(element)?;
|
||||
NaiveLayout {
|
||||
abi: element.abi.as_aggregate(),
|
||||
size: element
|
||||
.size
|
||||
.checked_mul(count, cx)
|
||||
.ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?,
|
||||
niches: if count == 0 { NaiveNiches::None } else { element.niches },
|
||||
..*element
|
||||
}
|
||||
}
|
||||
ty::Slice(element) => NaiveLayout {
|
||||
abi: NaiveAbi::Unsized,
|
||||
size: Size::ZERO,
|
||||
niches: NaiveNiches::None,
|
||||
..*cx.naive_layout_of(element)?
|
||||
},
|
||||
|
||||
ty::FnDef(..) => NaiveLayout::EMPTY,
|
||||
|
||||
// Unsized types.
|
||||
ty::Str | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => {
|
||||
NaiveLayout { abi: NaiveAbi::Unsized, ..NaiveLayout::EMPTY }
|
||||
}
|
||||
|
||||
// FIXME(reference_niches): try to actually compute a reasonable layout estimate,
|
||||
// without duplicating too much code from `generator_layout`.
|
||||
ty::Generator(..) => {
|
||||
NaiveLayout { exact: false, niches: NaiveNiches::Maybe, ..NaiveLayout::EMPTY }
|
||||
}
|
||||
|
||||
ty::Closure(_, ref substs) => {
|
||||
univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())?
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => univariant(&mut tys.iter(), &ReprOptions::default())?,
|
||||
|
||||
ty::Adt(def, substs) if def.is_union() => {
|
||||
assert_eq!(def.variants().len(), 1, "union should have a single variant");
|
||||
let repr = def.repr();
|
||||
let pack = repr.pack.unwrap_or(Align::MAX);
|
||||
if repr.pack.is_some() && repr.align.is_some() {
|
||||
cx.tcx.sess.delay_span_bug(DUMMY_SP, "union cannot be packed and aligned");
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
|
||||
let mut layout = NaiveLayout {
|
||||
// Unions never have niches.
|
||||
niches: NaiveNiches::None,
|
||||
..NaiveLayout::EMPTY
|
||||
};
|
||||
|
||||
for f in &def.variants()[FIRST_VARIANT].fields {
|
||||
let field = cx.naive_layout_of(f.ty(tcx, substs))?;
|
||||
layout = layout.union(&field.packed(pack));
|
||||
}
|
||||
|
||||
// Unions are always inhabited, and never scalar if `repr(C)`.
|
||||
if !matches!(layout.abi, NaiveAbi::Scalar(_)) || repr.inhibit_enum_layout_opt() {
|
||||
layout.abi = NaiveAbi::Sized;
|
||||
}
|
||||
|
||||
if let Some(align) = repr.align {
|
||||
layout = layout.align_to(align);
|
||||
}
|
||||
layout.pad_to_align(layout.align)
|
||||
}
|
||||
|
||||
ty::Adt(def, substs) => {
|
||||
let repr = def.repr();
|
||||
let mut layout = NaiveLayout {
|
||||
// An ADT with no inhabited variants should have an uninhabited ABI.
|
||||
abi: NaiveAbi::Uninhabited,
|
||||
..NaiveLayout::EMPTY
|
||||
};
|
||||
|
||||
let mut empty_variants = 0;
|
||||
for v in def.variants() {
|
||||
let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs));
|
||||
let vlayout = univariant(&mut fields, &repr)?;
|
||||
|
||||
if vlayout.size == Size::ZERO && vlayout.exact {
|
||||
empty_variants += 1;
|
||||
} else {
|
||||
// Remember the niches of the last seen variant.
|
||||
layout.niches = vlayout.niches;
|
||||
}
|
||||
|
||||
layout = layout.union(&vlayout);
|
||||
}
|
||||
|
||||
if def.is_enum() {
|
||||
let may_need_discr = match def.variants().len() {
|
||||
0 | 1 => false,
|
||||
// Simple Option-like niche optimization.
|
||||
// Handling this special case allows enums like `Option<&T>`
|
||||
// to be recognized as `PointerLike` and to be transmutable
|
||||
// in generic contexts.
|
||||
2 if empty_variants == 1 && layout.niches == NaiveNiches::Some => {
|
||||
layout.niches = NaiveNiches::Maybe; // fill up the niche.
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if may_need_discr || repr.inhibit_enum_layout_opt() {
|
||||
// For simplicity, assume that the discriminant always get niched.
|
||||
// This will be wrong in many cases, which will cause the size (and
|
||||
// sometimes the alignment) to be underestimated.
|
||||
// FIXME(reference_niches): Be smarter here.
|
||||
layout.niches = NaiveNiches::Maybe;
|
||||
layout = layout.inexact();
|
||||
}
|
||||
} else {
|
||||
assert_eq!(def.variants().len(), 1, "struct should have a single variant");
|
||||
|
||||
// We don't compute exact alignment for SIMD structs.
|
||||
if repr.simd() {
|
||||
layout = layout.inexact();
|
||||
}
|
||||
|
||||
// `UnsafeCell` hides all niches.
|
||||
if def.is_unsafe_cell() {
|
||||
layout.niches = NaiveNiches::None;
|
||||
}
|
||||
}
|
||||
|
||||
let valid_range = tcx.layout_scalar_valid_range(def.did());
|
||||
if valid_range != (Bound::Unbounded, Bound::Unbounded) {
|
||||
let get = |bound, default| match bound {
|
||||
Bound::Unbounded => default,
|
||||
Bound::Included(v) => v,
|
||||
Bound::Excluded(_) => bug!("exclusive `layout_scalar_valid_range` bound"),
|
||||
};
|
||||
|
||||
let valid_range = WrappingRange {
|
||||
start: get(valid_range.0, 0),
|
||||
// FIXME: this is wrong for scalar-pair ABIs. Fortunately, the
|
||||
// only type this could currently affect is`NonNull<T: !Sized>`,
|
||||
// and the `NaiveNiches` result still ends up correct.
|
||||
end: get(valid_range.1, layout.size.unsigned_int_max()),
|
||||
};
|
||||
assert!(
|
||||
valid_range.is_in_range_for(layout.size),
|
||||
"`layout_scalar_valid_range` values are out of bounds",
|
||||
);
|
||||
if !valid_range.is_full_for(layout.size) {
|
||||
layout.niches = NaiveNiches::Some;
|
||||
}
|
||||
}
|
||||
|
||||
layout.pad_to_align(layout.align)
|
||||
}
|
||||
|
||||
// Types with no meaningful known layout.
|
||||
ty::Alias(..) => {
|
||||
// NOTE(eddyb) `layout_of` query should've normalized these away,
|
||||
// if that was possible, so there's no reason to try again here.
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
|
||||
ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
|
||||
bug!("Layout::compute: unexpected type `{}`", ty)
|
||||
}
|
||||
|
||||
ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
|
||||
return Err(error(cx, LayoutError::Unknown(ty)));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_middle::ty::{
|
||||
layout::{LayoutCx, NaiveLayout, TyAndLayout},
|
||||
layout::{LayoutCx, TyAndLayout},
|
||||
TyCtxt,
|
||||
};
|
||||
use rustc_target::abi::*;
|
||||
|
|
@ -10,7 +10,6 @@ use std::assert_matches::assert_matches;
|
|||
pub(super) fn sanity_check_layout<'tcx>(
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
layout: &TyAndLayout<'tcx>,
|
||||
naive: &NaiveLayout,
|
||||
) {
|
||||
// Type-level uninhabitedness should always imply ABI uninhabitedness.
|
||||
if layout.ty.is_privately_uninhabited(cx.tcx, cx.param_env) {
|
||||
|
|
@ -21,10 +20,6 @@ pub(super) fn sanity_check_layout<'tcx>(
|
|||
bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
|
||||
}
|
||||
|
||||
if !naive.is_refined_by(layout.layout) {
|
||||
bug!("the naive layout isn't refined by the actual layout:\n{:#?}\n{:#?}", naive, layout);
|
||||
}
|
||||
|
||||
if !cfg!(debug_assertions) {
|
||||
// Stop here, the rest is kind of expensive.
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ mod errors;
|
|||
mod implied_bounds;
|
||||
pub mod instance;
|
||||
mod layout;
|
||||
mod layout_naive;
|
||||
mod layout_sanity_check;
|
||||
mod needs_drop;
|
||||
mod opaque_types;
|
||||
|
|
@ -48,7 +47,6 @@ pub fn provide(providers: &mut Providers) {
|
|||
consts::provide(providers);
|
||||
implied_bounds::provide(providers);
|
||||
layout::provide(providers);
|
||||
layout_naive::provide(providers);
|
||||
needs_drop::provide(providers);
|
||||
opaque_types::provide(providers);
|
||||
representability::provide(providers);
|
||||
|
|
|
|||
|
|
@ -1290,11 +1290,11 @@ impl String {
|
|||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// let mut s = String::from("foo");
|
||||
/// let mut s = String::from("abč");
|
||||
///
|
||||
/// assert_eq!(s.pop(), Some('o'));
|
||||
/// assert_eq!(s.pop(), Some('o'));
|
||||
/// assert_eq!(s.pop(), Some('f'));
|
||||
/// assert_eq!(s.pop(), Some('č'));
|
||||
/// assert_eq!(s.pop(), Some('b'));
|
||||
/// assert_eq!(s.pop(), Some('a'));
|
||||
///
|
||||
/// assert_eq!(s.pop(), None);
|
||||
/// ```
|
||||
|
|
@ -1324,11 +1324,11 @@ impl String {
|
|||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// let mut s = String::from("foo");
|
||||
/// let mut s = String::from("abç");
|
||||
///
|
||||
/// assert_eq!(s.remove(0), 'f');
|
||||
/// assert_eq!(s.remove(1), 'o');
|
||||
/// assert_eq!(s.remove(0), 'o');
|
||||
/// assert_eq!(s.remove(0), 'a');
|
||||
/// assert_eq!(s.remove(1), 'ç');
|
||||
/// assert_eq!(s.remove(0), 'b');
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
|
|
|||
|
|
@ -2961,7 +2961,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
|||
/// [`copy_from_slice`]: slice::copy_from_slice
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "extend_ref", since = "1.2.0")]
|
||||
impl<'a, T: Copy + 'a, A: Allocator + 'a> Extend<&'a T> for Vec<T, A> {
|
||||
impl<'a, T: Copy + 'a, A: Allocator> Extend<&'a T> for Vec<T, A> {
|
||||
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
|
||||
self.spec_extend(iter.into_iter())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl<T, A: Allocator> SpecExtend<T, IntoIter<T>> for Vec<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, I, A: Allocator + 'a> SpecExtend<&'a T, I> for Vec<T, A>
|
||||
impl<'a, T: 'a, I, A: Allocator> SpecExtend<&'a T, I> for Vec<T, A>
|
||||
where
|
||||
I: Iterator<Item = &'a T>,
|
||||
T: Clone,
|
||||
|
|
@ -46,7 +46,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, A: Allocator + 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec<T, A>
|
||||
impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec<T, A>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1738,6 +1738,28 @@ fn test_utf16_code_units() {
|
|||
assert_eq!("é\u{1F4A9}".encode_utf16().collect::<Vec<u16>>(), [0xE9, 0xD83D, 0xDCA9])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utf16_size_hint() {
|
||||
assert_eq!("".encode_utf16().size_hint(), (0, Some(0)));
|
||||
assert_eq!("123".encode_utf16().size_hint(), (1, Some(3)));
|
||||
assert_eq!("1234".encode_utf16().size_hint(), (2, Some(4)));
|
||||
assert_eq!("12345678".encode_utf16().size_hint(), (3, Some(8)));
|
||||
|
||||
fn hint_vec(src: &str) -> Vec<(usize, Option<usize>)> {
|
||||
let mut it = src.encode_utf16();
|
||||
let mut result = Vec::new();
|
||||
result.push(it.size_hint());
|
||||
while it.next().is_some() {
|
||||
result.push(it.size_hint())
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
assert_eq!(hint_vec("12"), [(1, Some(2)), (1, Some(1)), (0, Some(0))]);
|
||||
assert_eq!(hint_vec("\u{101234}"), [(2, Some(4)), (1, Some(1)), (0, Some(0))]);
|
||||
assert_eq!(hint_vec("\u{101234}a"), [(2, Some(5)), (2, Some(2)), (1, Some(1)), (0, Some(0))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn starts_with_in_unicode() {
|
||||
assert!(!"├── Cargo.toml".starts_with("# "));
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ impl CStr {
|
|||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "101719")]
|
||||
#[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")]
|
||||
pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
|
||||
// SAFETY: The caller has provided a pointer that points to a valid C
|
||||
// string with a NUL terminator of size less than `isize::MAX`, whose
|
||||
|
|
|
|||
|
|
@ -52,11 +52,6 @@ macro_rules! type_alias {
|
|||
}
|
||||
|
||||
type_alias! { "c_char.md", c_char = c_char_definition::c_char, NonZero_c_char = c_char_definition::NonZero_c_char;
|
||||
// Make this type alias appear cfg-dependent so that Clippy does not suggest
|
||||
// replacing `0 as c_char` with `0_i8`/`0_u8`. This #[cfg(all())] can be removed
|
||||
// after the false positive in https://github.com/rust-lang/rust-clippy/issues/8093
|
||||
// is fixed.
|
||||
#[cfg(all())]
|
||||
#[doc(cfg(all()))] }
|
||||
|
||||
type_alias! { "c_schar.md", c_schar = i8, NonZero_c_schar = NonZeroI8; }
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@
|
|||
#![feature(duration_consts_float)]
|
||||
#![feature(internal_impls_macro)]
|
||||
#![feature(ip)]
|
||||
#![feature(ip_bits)]
|
||||
#![feature(is_ascii_octdigit)]
|
||||
#![feature(maybe_uninit_uninit_array)]
|
||||
#![feature(ptr_alignment_type)]
|
||||
|
|
|
|||
|
|
@ -450,6 +450,57 @@ impl Ipv4Addr {
|
|||
Ipv4Addr { octets: [a, b, c, d] }
|
||||
}
|
||||
|
||||
/// The size of an IPv4 address in bits.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ip_bits)]
|
||||
/// use std::net::Ipv4Addr;
|
||||
///
|
||||
/// assert_eq!(Ipv4Addr::BITS, 32);
|
||||
/// ```
|
||||
#[unstable(feature = "ip_bits", issue = "113744")]
|
||||
pub const BITS: u32 = 32;
|
||||
|
||||
/// Converts an IPv4 address into host byte order `u32`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ip_bits)]
|
||||
/// use std::net::Ipv4Addr;
|
||||
///
|
||||
/// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78);
|
||||
/// assert_eq!(0x12345678, addr.to_bits());
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn to_bits(self) -> u32 {
|
||||
u32::from_be_bytes(self.octets)
|
||||
}
|
||||
|
||||
/// Converts a host byte order `u32` into an IPv4 address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ip_bits)]
|
||||
/// use std::net::Ipv4Addr;
|
||||
///
|
||||
/// let addr = Ipv4Addr::from(0x12345678);
|
||||
/// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr);
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn from_bits(bits: u32) -> Ipv4Addr {
|
||||
Ipv4Addr { octets: bits.to_be_bytes() }
|
||||
}
|
||||
|
||||
/// An IPv4 address with the address pointing to localhost: `127.0.0.1`
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -1069,37 +1120,19 @@ impl Ord for Ipv4Addr {
|
|||
|
||||
#[stable(feature = "ip_u32", since = "1.1.0")]
|
||||
impl From<Ipv4Addr> for u32 {
|
||||
/// Converts an `Ipv4Addr` into a host byte order `u32`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::Ipv4Addr;
|
||||
///
|
||||
/// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78);
|
||||
/// assert_eq!(0x12345678, u32::from(addr));
|
||||
/// ```
|
||||
/// Uses [`Ipv4Addr::to_bits`] to convert an IPv4 address to a host byte order `u32`.
|
||||
#[inline]
|
||||
fn from(ip: Ipv4Addr) -> u32 {
|
||||
u32::from_be_bytes(ip.octets)
|
||||
ip.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "ip_u32", since = "1.1.0")]
|
||||
impl From<u32> for Ipv4Addr {
|
||||
/// Converts a host byte order `u32` into an `Ipv4Addr`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::Ipv4Addr;
|
||||
///
|
||||
/// let addr = Ipv4Addr::from(0x12345678);
|
||||
/// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr);
|
||||
/// ```
|
||||
/// Uses [`Ipv4Addr::from_bits`] to convert a host byte order `u32` into an IPv4 address.
|
||||
#[inline]
|
||||
fn from(ip: u32) -> Ipv4Addr {
|
||||
Ipv4Addr { octets: ip.to_be_bytes() }
|
||||
Ipv4Addr::from_bits(ip)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1173,6 +1206,65 @@ impl Ipv6Addr {
|
|||
}
|
||||
}
|
||||
|
||||
/// The size of an IPv6 address in bits.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ip_bits)]
|
||||
/// use std::net::Ipv6Addr;
|
||||
///
|
||||
/// assert_eq!(Ipv6Addr::BITS, 128);
|
||||
/// ```
|
||||
#[unstable(feature = "ip_bits", issue = "113744")]
|
||||
pub const BITS: u32 = 128;
|
||||
|
||||
/// Converts an IPv6 address into host byte order `u128`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ip_bits)]
|
||||
/// use std::net::Ipv6Addr;
|
||||
///
|
||||
/// let addr = Ipv6Addr::new(
|
||||
/// 0x1020, 0x3040, 0x5060, 0x7080,
|
||||
/// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
|
||||
/// );
|
||||
/// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr));
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn to_bits(self) -> u128 {
|
||||
u128::from_be_bytes(self.octets)
|
||||
}
|
||||
|
||||
/// Converts a host byte order `u128` into an IPv6 address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ip_bits)]
|
||||
/// use std::net::Ipv6Addr;
|
||||
///
|
||||
/// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128);
|
||||
/// assert_eq!(
|
||||
/// Ipv6Addr::new(
|
||||
/// 0x1020, 0x3040, 0x5060, 0x7080,
|
||||
/// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
|
||||
/// ),
|
||||
/// addr);
|
||||
/// ```
|
||||
#[rustc_const_unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[unstable(feature = "ip_bits", issue = "113744")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn from_bits(bits: u128) -> Ipv6Addr {
|
||||
Ipv6Addr { octets: bits.to_be_bytes() }
|
||||
}
|
||||
|
||||
/// An IPv6 address representing localhost: `::1`.
|
||||
///
|
||||
/// This corresponds to constant `IN6ADDR_LOOPBACK_INIT` or `in6addr_loopback` in other
|
||||
|
|
@ -1905,44 +1997,18 @@ impl Ord for Ipv6Addr {
|
|||
|
||||
#[stable(feature = "i128", since = "1.26.0")]
|
||||
impl From<Ipv6Addr> for u128 {
|
||||
/// Convert an `Ipv6Addr` into a host byte order `u128`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::Ipv6Addr;
|
||||
///
|
||||
/// let addr = Ipv6Addr::new(
|
||||
/// 0x1020, 0x3040, 0x5060, 0x7080,
|
||||
/// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
|
||||
/// );
|
||||
/// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr));
|
||||
/// ```
|
||||
/// Uses [`Ipv6Addr::to_bits`] to convert an IPv6 address to a host byte order `u128`.
|
||||
#[inline]
|
||||
fn from(ip: Ipv6Addr) -> u128 {
|
||||
u128::from_be_bytes(ip.octets)
|
||||
ip.to_bits()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "i128", since = "1.26.0")]
|
||||
impl From<u128> for Ipv6Addr {
|
||||
/// Convert a host byte order `u128` into an `Ipv6Addr`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::Ipv6Addr;
|
||||
///
|
||||
/// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128);
|
||||
/// assert_eq!(
|
||||
/// Ipv6Addr::new(
|
||||
/// 0x1020, 0x3040, 0x5060, 0x7080,
|
||||
/// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
|
||||
/// ),
|
||||
/// addr);
|
||||
/// ```
|
||||
/// Uses [`Ipv6Addr::from_bits`] to convert a host byte order `u128` to an IPv6 address.
|
||||
#[inline]
|
||||
fn from(ip: u128) -> Ipv6Addr {
|
||||
Ipv6Addr::from(ip.to_be_bytes())
|
||||
Ipv6Addr::from_bits(ip)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1439,11 +1439,22 @@ impl<'a> Iterator for EncodeUtf16<'a> {
|
|||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (low, high) = self.chars.size_hint();
|
||||
// every char gets either one u16 or two u16,
|
||||
// so this iterator is between 1 or 2 times as
|
||||
// long as the underlying iterator.
|
||||
(low, high.and_then(|n| n.checked_mul(2)))
|
||||
let len = self.chars.iter.len();
|
||||
// The highest bytes:code units ratio occurs for 3-byte sequences,
|
||||
// since a 4-byte sequence results in 2 code units. The lower bound
|
||||
// is therefore determined by assuming the remaining bytes contain as
|
||||
// many 3-byte sequences as possible. The highest bytes:code units
|
||||
// ratio is for 1-byte sequences, so use this for the upper bound.
|
||||
// `(len + 2)` can't overflow, because we know that the `slice::Iter`
|
||||
// belongs to a slice in memory which has a maximum length of
|
||||
// `isize::MAX` (that's well below `usize::MAX`)
|
||||
if self.extra == 0 {
|
||||
((len + 2) / 3, Some(len))
|
||||
} else {
|
||||
// We're in the middle of a surrogate pair, so add the remaining
|
||||
// surrogate to the bounds.
|
||||
((len + 2) / 3 + 1, Some(len + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,6 +141,51 @@ impl OsString {
|
|||
OsString { inner: Buf::from_string(String::new()) }
|
||||
}
|
||||
|
||||
/// Converts bytes to an `OsString` without checking that the bytes contains
|
||||
/// valid [`OsStr`]-encoded data.
|
||||
///
|
||||
/// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8.
|
||||
/// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit
|
||||
/// ASCII.
|
||||
///
|
||||
/// See the [module's toplevel documentation about conversions][conversions] for safe,
|
||||
/// cross-platform [conversions] from/to native representations.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of
|
||||
/// validated UTF-8 and bytes from [`OsStr::as_os_str_bytes`] from within the same rust version
|
||||
/// built for the same target platform. For example, reconstructing an `OsString` from bytes sent
|
||||
/// over the network or stored in a file will likely violate these safety rules.
|
||||
///
|
||||
/// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_os_str_bytes`] can be
|
||||
/// split either immediately before or immediately after any valid non-empty UTF-8 substring.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(os_str_bytes)]
|
||||
///
|
||||
/// use std::ffi::OsStr;
|
||||
///
|
||||
/// let os_str = OsStr::new("Mary had a little lamb");
|
||||
/// let bytes = os_str.as_os_str_bytes();
|
||||
/// let words = bytes.split(|b| *b == b' ');
|
||||
/// let words: Vec<&OsStr> = words.map(|word| {
|
||||
/// // SAFETY:
|
||||
/// // - Each `word` only contains content that originated from `OsStr::as_os_str_bytes`
|
||||
/// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring
|
||||
/// unsafe { OsStr::from_os_str_bytes_unchecked(word) }
|
||||
/// }).collect();
|
||||
/// ```
|
||||
///
|
||||
/// [conversions]: super#conversions
|
||||
#[inline]
|
||||
#[unstable(feature = "os_str_bytes", issue = "111544")]
|
||||
pub unsafe fn from_os_str_bytes_unchecked(bytes: Vec<u8>) -> Self {
|
||||
OsString { inner: Buf::from_os_str_bytes_unchecked(bytes) }
|
||||
}
|
||||
|
||||
/// Converts to an [`OsStr`] slice.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -159,6 +204,26 @@ impl OsString {
|
|||
self
|
||||
}
|
||||
|
||||
/// Converts the `OsString` into a byte slice. To convert the byte slice back into an
|
||||
/// `OsString`, use the [`OsStr::from_os_str_bytes_unchecked`] function.
|
||||
///
|
||||
/// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8.
|
||||
/// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit
|
||||
/// ASCII.
|
||||
///
|
||||
/// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should
|
||||
/// be treated as opaque and only comparable within the same rust version built for the same
|
||||
/// target platform. For example, sending the bytes over the network or storing it in a file
|
||||
/// will likely result in incompatible data. See [`OsString`] for more encoding details
|
||||
/// and [`std::ffi`] for platform-specific, specified conversions.
|
||||
///
|
||||
/// [`std::ffi`]: crate::ffi
|
||||
#[inline]
|
||||
#[unstable(feature = "os_str_bytes", issue = "111544")]
|
||||
pub fn into_os_str_bytes(self) -> Vec<u8> {
|
||||
self.inner.into_os_str_bytes()
|
||||
}
|
||||
|
||||
/// Converts the `OsString` into a [`String`] if it contains valid Unicode data.
|
||||
///
|
||||
/// On failure, ownership of the original `OsString` is returned.
|
||||
|
|
|
|||
|
|
@ -9,11 +9,6 @@ macro_rules! alias_core_ffi {
|
|||
($($t:ident)*) => {$(
|
||||
#[stable(feature = "raw_os", since = "1.1.0")]
|
||||
#[doc = include_str!(concat!("../../../../core/src/ffi/", stringify!($t), ".md"))]
|
||||
// Make this type alias appear cfg-dependent so that Clippy does not suggest
|
||||
// replacing expressions like `0 as c_char` with `0_i8`/`0_u8`. This #[cfg(all())] can be
|
||||
// removed after the false positive in https://github.com/rust-lang/rust-clippy/issues/8093
|
||||
// is fixed.
|
||||
#[cfg(all())]
|
||||
#[doc(cfg(all()))]
|
||||
pub type $t = core::ffi::$t;
|
||||
)*}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,36 @@ pub trait FileExt {
|
|||
/// Note that similar to [`File::write`], it is not an error to return a
|
||||
/// short write.
|
||||
///
|
||||
/// # Bug
|
||||
/// On some systems, `write_at` utilises [`pwrite64`] to write to files.
|
||||
/// However, this syscall has a [bug] where files opened with the `O_APPEND`
|
||||
/// flag fail to respect the offset parameter, always appending to the end
|
||||
/// of the file instead.
|
||||
///
|
||||
/// It is possible to inadvertantly set this flag, like in the example below.
|
||||
/// Therefore, it is important to be vigilant while changing options to mitigate
|
||||
/// unexpected behaviour.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io;
|
||||
/// use std::os::unix::prelude::FileExt;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// // Open a file with the append option (sets the `O_APPEND` flag)
|
||||
/// let file = File::options().append(true).open("foo.txt")?;
|
||||
///
|
||||
/// // We attempt to write at offset 10; instead appended to EOF
|
||||
/// file.write_at(b"sushi", 10)?;
|
||||
///
|
||||
/// // foo.txt is 5 bytes long instead of 15
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`File::write`]: fs::File::write
|
||||
/// [`pwrite64`]: https://man7.org/linux/man-pages/man2/pwrite.2.html
|
||||
/// [bug]: https://man7.org/linux/man-pages/man2/pwrite.2.html#BUGS
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -159,7 +188,7 @@ pub trait FileExt {
|
|||
/// use std::os::unix::prelude::FileExt;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let file = File::open("foo.txt")?;
|
||||
/// let file = File::create("foo.txt")?;
|
||||
///
|
||||
/// // We now write at the offset 10.
|
||||
/// file.write_at(b"sushi", 10)?;
|
||||
|
|
@ -971,7 +1000,6 @@ impl DirBuilderExt for fs::DirBuilder {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(unix_chown)]
|
||||
/// use std::os::unix::fs;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
|
|
@ -979,7 +1007,7 @@ impl DirBuilderExt for fs::DirBuilder {
|
|||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "unix_chown", issue = "88989")]
|
||||
#[stable(feature = "unix_chown", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
|
||||
sys::fs::chown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX))
|
||||
}
|
||||
|
|
@ -991,7 +1019,6 @@ pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(unix_chown)]
|
||||
/// use std::os::unix::fs;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
|
|
@ -1000,7 +1027,7 @@ pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::
|
|||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "unix_chown", issue = "88989")]
|
||||
#[stable(feature = "unix_chown", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
|
||||
sys::fs::fchown(fd.as_fd().as_raw_fd(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX))
|
||||
}
|
||||
|
|
@ -1013,7 +1040,6 @@ pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(unix_chown)]
|
||||
/// use std::os::unix::fs;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
|
|
@ -1021,7 +1047,7 @@ pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<
|
|||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "unix_chown", issue = "88989")]
|
||||
#[stable(feature = "unix_chown", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub fn lchown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
|
||||
sys::fs::lchown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,16 @@ impl AsInner<[u8]> for Buf {
|
|||
}
|
||||
|
||||
impl Buf {
|
||||
#[inline]
|
||||
pub fn into_os_str_bytes(self) -> Vec<u8> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn from_os_str_bytes_unchecked(s: Vec<u8>) -> Self {
|
||||
Self { inner: s }
|
||||
}
|
||||
|
||||
pub fn from_string(s: String) -> Buf {
|
||||
Buf { inner: s.into_bytes() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,16 @@ impl fmt::Display for Slice {
|
|||
}
|
||||
|
||||
impl Buf {
|
||||
#[inline]
|
||||
pub fn into_os_str_bytes(self) -> Vec<u8> {
|
||||
self.inner.into_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn from_os_str_bytes_unchecked(s: Vec<u8>) -> Self {
|
||||
Self { inner: Wtf8Buf::from_bytes_unchecked(s) }
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Buf {
|
||||
Buf { inner: Wtf8Buf::with_capacity(capacity) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,15 @@ impl Wtf8Buf {
|
|||
Wtf8Buf { bytes: Vec::with_capacity(capacity), is_known_utf8: true }
|
||||
}
|
||||
|
||||
/// Creates a WTF-8 string from a WTF-8 byte vec.
|
||||
///
|
||||
/// Since the byte vec is not checked for valid WTF-8, this functions is
|
||||
/// marked unsafe.
|
||||
#[inline]
|
||||
pub unsafe fn from_bytes_unchecked(value: Vec<u8>) -> Wtf8Buf {
|
||||
Wtf8Buf { bytes: value, is_known_utf8: false }
|
||||
}
|
||||
|
||||
/// Creates a WTF-8 string from a UTF-8 `String`.
|
||||
///
|
||||
/// This takes ownership of the `String` and does not copy.
|
||||
|
|
@ -402,6 +411,12 @@ impl Wtf8Buf {
|
|||
self.bytes.truncate(new_len)
|
||||
}
|
||||
|
||||
/// Consumes the WTF-8 string and tries to convert it to a vec of bytes.
|
||||
#[inline]
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
/// Consumes the WTF-8 string and tries to convert it to UTF-8.
|
||||
///
|
||||
/// This does not copy the data.
|
||||
|
|
|
|||
|
|
@ -2043,6 +2043,13 @@ impl<'a> Builder<'a> {
|
|||
rustflags.arg("-Zinline-mir");
|
||||
}
|
||||
|
||||
// set rustc args passed from command line
|
||||
let rustc_args =
|
||||
self.config.cmd.rustc_args().iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
||||
if !rustc_args.is_empty() {
|
||||
cargo.env("RUSTFLAGS", &rustc_args.join(" "));
|
||||
}
|
||||
|
||||
Cargo { command: cargo, rustflags, rustdocflags, allow_features }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ impl<'mir, 'tcx> GlobalStateInner {
|
|||
.checked_add(max(size.bytes(), 1))
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
// Even if `Size` didn't overflow, we might still have filled up the address space.
|
||||
if global_state.next_base_addr > ecx.data_layout().target_usize_max() {
|
||||
if global_state.next_base_addr > ecx.target_usize_max() {
|
||||
throw_exhaust!(AddressSpaceFull);
|
||||
}
|
||||
// Given that `next_base_addr` increases in each allocation, pushing the
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ pub mod tls;
|
|||
use log::trace;
|
||||
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::abi::HasDataLayout as _;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::*;
|
||||
|
|
@ -109,8 +108,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
}
|
||||
|
||||
// Return error result (usize::MAX), and jump to caller.
|
||||
let usize_max = this.data_layout().target_usize_max();
|
||||
this.write_scalar(Scalar::from_target_usize(usize_max, this), dest)?;
|
||||
this.write_scalar(Scalar::from_target_usize(this.target_usize_max(), this), dest)?;
|
||||
this.go_to_block(ret);
|
||||
Ok(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use log::trace;
|
|||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_target::abi::{Align, HasDataLayout as _, Size};
|
||||
use rustc_target::abi::{Align, Size};
|
||||
|
||||
use crate::shims::os_str::bytes_to_os_str;
|
||||
use crate::*;
|
||||
|
|
@ -753,7 +753,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
// We cap the number of read bytes to the largest value that we are able to fit in both the
|
||||
// host's and target's `isize`. This saves us from having to handle overflows later.
|
||||
let count = count
|
||||
.min(u64::try_from(this.data_layout().target_isize_max()).unwrap())
|
||||
.min(u64::try_from(this.target_isize_max()).unwrap())
|
||||
.min(u64::try_from(isize::MAX).unwrap());
|
||||
let communicate = this.machine.communicate();
|
||||
|
||||
|
|
@ -807,7 +807,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
// We cap the number of written bytes to the largest value that we are able to fit in both the
|
||||
// host's and target's `isize`. This saves us from having to handle overflows later.
|
||||
let count = count
|
||||
.min(u64::try_from(this.data_layout().target_isize_max()).unwrap())
|
||||
.min(u64::try_from(this.target_isize_max()).unwrap())
|
||||
.min(u64::try_from(isize::MAX).unwrap());
|
||||
let communicate = this.machine.communicate();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//@error-in-other-file: a cycle occurred during layout computation
|
||||
//~^ ERROR: cycle detected when computing layout (naive) of
|
||||
//~^ ERROR: cycle detected when computing layout of
|
||||
|
||||
use std::mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
error[E0391]: cycle detected when computing layout (naive) of `S<S<()>>`
|
||||
error[E0391]: cycle detected when computing layout of `S<S<()>>`
|
||||
|
|
||||
= note: ...which requires computing layout (naive) of `<S<()> as Tr>::I`...
|
||||
= note: ...which again requires computing layout (naive) of `S<S<()>>`, completing the cycle
|
||||
= note: cycle used when computing layout of `S<S<()>>`
|
||||
= note: ...which requires computing layout of `<S<()> as Tr>::I`...
|
||||
= note: ...which again requires computing layout of `S<S<()>>`, completing the cycle
|
||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||
|
||||
error: post-monomorphization error: a cycle occurred during layout computation
|
||||
|
|
|
|||
|
|
@ -334,6 +334,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
|
|||
"cranelift-native",
|
||||
"cranelift-object",
|
||||
"crc32fast",
|
||||
"equivalent",
|
||||
"fallible-iterator",
|
||||
"gimli",
|
||||
"hashbrown",
|
||||
|
|
|
|||
33
tests/codegen/target-feature-inline-closure.rs
Normal file
33
tests/codegen/target-feature-inline-closure.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// only-x86_64
|
||||
// compile-flags: -Copt-level=3
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(target_feature_11)]
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
// CHECK-LABEL: @with_avx
|
||||
#[no_mangle]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[target_feature(enable = "avx")]
|
||||
fn with_avx(x: __m256) -> __m256 {
|
||||
// CHECK: fadd
|
||||
let add = {
|
||||
#[inline(always)]
|
||||
|x, y| unsafe { _mm256_add_ps(x, y) }
|
||||
};
|
||||
add(x, x)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @without_avx
|
||||
#[no_mangle]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe fn without_avx(x: __m256) -> __m256 {
|
||||
// CHECK-NOT: fadd
|
||||
let add = {
|
||||
#[inline(always)]
|
||||
|x, y| unsafe { _mm256_add_ps(x, y) }
|
||||
};
|
||||
add(x, x)
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ define-function: (
|
|||
block {
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.WithGenerics.html"
|
||||
show-text: true
|
||||
|
||||
set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
|
||||
reload:
|
||||
assert-css: (".item-decl .code-attribute", {"color": |attr_color|}, ALL)
|
||||
|
|
@ -40,41 +41,41 @@ call-function: (
|
|||
"check-colors",
|
||||
{
|
||||
"theme": "ayu",
|
||||
"attr_color": "rgb(153, 153, 153)",
|
||||
"trait_color": "rgb(57, 175, 215)",
|
||||
"struct_color": "rgb(255, 160, 165)",
|
||||
"enum_color": "rgb(255, 160, 165)",
|
||||
"primitive_color": "rgb(255, 160, 165)",
|
||||
"constant_color": "rgb(57, 175, 215)",
|
||||
"fn_color": "rgb(253, 214, 135)",
|
||||
"assoc_type_color": "rgb(57, 175, 215)",
|
||||
"attr_color": "#999",
|
||||
"trait_color": "#39afd7",
|
||||
"struct_color": "#ffa0a5",
|
||||
"enum_color": "#ffa0a5",
|
||||
"primitive_color": "#ffa0a5",
|
||||
"constant_color": "#39afd7",
|
||||
"fn_color": "#fdd687",
|
||||
"assoc_type_color": "#39afd7",
|
||||
},
|
||||
)
|
||||
call-function: (
|
||||
"check-colors",
|
||||
{
|
||||
"theme": "dark",
|
||||
"attr_color": "rgb(153, 153, 153)",
|
||||
"trait_color": "rgb(183, 140, 242)",
|
||||
"struct_color": "rgb(45, 191, 184)",
|
||||
"enum_color": "rgb(45, 191, 184)",
|
||||
"primitive_color": "rgb(45, 191, 184)",
|
||||
"constant_color": "rgb(210, 153, 29)",
|
||||
"fn_color": "rgb(43, 171, 99)",
|
||||
"assoc_type_color": "rgb(210, 153, 29)",
|
||||
"attr_color": "#999",
|
||||
"trait_color": "#b78cf2",
|
||||
"struct_color": "#2dbfb8",
|
||||
"enum_color": "#2dbfb8",
|
||||
"primitive_color": "#2dbfb8",
|
||||
"constant_color": "#d2991d",
|
||||
"fn_color": "#2bab63",
|
||||
"assoc_type_color": "#d2991d",
|
||||
},
|
||||
)
|
||||
call-function: (
|
||||
"check-colors",
|
||||
{
|
||||
"theme": "light",
|
||||
"attr_color": "rgb(153, 153, 153)",
|
||||
"trait_color": "rgb(110, 79, 201)",
|
||||
"struct_color": "rgb(173, 55, 138)",
|
||||
"enum_color": "rgb(173, 55, 138)",
|
||||
"primitive_color": "rgb(173, 55, 138)",
|
||||
"constant_color": "rgb(56, 115, 173)",
|
||||
"fn_color": "rgb(173, 124, 55)",
|
||||
"assoc_type_color": "rgb(56, 115, 173)",
|
||||
"attr_color": "#999",
|
||||
"trait_color": "#6e4fc9",
|
||||
"struct_color": "#ad378a",
|
||||
"enum_color": "#ad378a",
|
||||
"primitive_color": "#ad378a",
|
||||
"constant_color": "#3873ad",
|
||||
"fn_color": "#ad7c37",
|
||||
"assoc_type_color": "#3873ad",
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ note: required by a bound in `f1`
|
|||
|
|
||||
LL | fn f1<F>(_: F) where F: Fn(&(), &()) {}
|
||||
| ^^^^^^^^^^^^ required by this bound in `f1`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its arguments
|
||||
|
|
||||
LL | f1(|_: &(), _: &()| {});
|
||||
| + +
|
||||
|
|
@ -33,7 +33,7 @@ note: required by a bound in `f2`
|
|||
|
|
||||
LL | fn f2<F>(_: F) where F: for<'a> Fn(&'a (), &()) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `f2`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its arguments
|
||||
|
|
||||
LL | f2(|_: &(), _: &()| {});
|
||||
| + +
|
||||
|
|
@ -53,7 +53,7 @@ note: required by a bound in `f3`
|
|||
|
|
||||
LL | fn f3<'a, F>(_: F) where F: Fn(&'a (), &()) {}
|
||||
| ^^^^^^^^^^^^^^^ required by this bound in `f3`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its arguments
|
||||
|
|
||||
LL | f3(|_: &(), _: &()| {});
|
||||
| + +
|
||||
|
|
@ -73,7 +73,7 @@ note: required by a bound in `f4`
|
|||
|
|
||||
LL | fn f4<F>(_: F) where F: for<'r> Fn(&(), &'r ()) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `f4`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its arguments
|
||||
|
|
||||
LL | f4(|_: &(), _: &()| {});
|
||||
| + +
|
||||
|
|
@ -93,7 +93,7 @@ note: required by a bound in `f5`
|
|||
|
|
||||
LL | fn f5<F>(_: F) where F: for<'r> Fn(&'r (), &'r ()) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `f5`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its arguments
|
||||
|
|
||||
LL | f5(|_: &(), _: &()| {});
|
||||
| + +
|
||||
|
|
@ -113,7 +113,7 @@ note: required by a bound in `g1`
|
|||
|
|
||||
LL | fn g1<F>(_: F) where F: Fn(&(), Box<dyn Fn(&())>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `g1`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its argument
|
||||
|
|
||||
LL | g1(|_: &(), _: ()| {});
|
||||
| +
|
||||
|
|
@ -133,7 +133,7 @@ note: required by a bound in `g2`
|
|||
|
|
||||
LL | fn g2<F>(_: F) where F: Fn(&(), fn(&())) {}
|
||||
| ^^^^^^^^^^^^^^^^ required by this bound in `g2`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its argument
|
||||
|
|
||||
LL | g2(|_: &(), _: ()| {});
|
||||
| +
|
||||
|
|
@ -153,7 +153,7 @@ note: required by a bound in `g3`
|
|||
|
|
||||
LL | fn g3<F>(_: F) where F: for<'s> Fn(&'s (), Box<dyn Fn(&())>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `g3`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its argument
|
||||
|
|
||||
LL | g3(|_: &(), _: ()| {});
|
||||
| +
|
||||
|
|
@ -173,7 +173,7 @@ note: required by a bound in `g4`
|
|||
|
|
||||
LL | fn g4<F>(_: F) where F: Fn(&(), for<'r> fn(&'r ())) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `g4`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its argument
|
||||
|
|
||||
LL | g4(|_: &(), _: ()| {});
|
||||
| +
|
||||
|
|
@ -193,7 +193,7 @@ note: required by a bound in `h1`
|
|||
|
|
||||
LL | fn h1<F>(_: F) where F: Fn(&(), Box<dyn Fn(&())>, &(), fn(&(), &())) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `h1`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its arguments
|
||||
|
|
||||
LL | h1(|_: &(), _: (), _: &(), _: ()| {});
|
||||
| + +
|
||||
|
|
@ -213,7 +213,7 @@ note: required by a bound in `h2`
|
|||
|
|
||||
LL | fn h2<F>(_: F) where F: for<'t0> Fn(&(), Box<dyn Fn(&())>, &'t0 (), fn(&(), &())) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `h2`
|
||||
help: consider borrowing the argument
|
||||
help: consider adjusting the signature so it borrows its arguments
|
||||
|
|
||||
LL | h2(|_: &(), _: (), _: &(), _: ()| {});
|
||||
| + +
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ note: required by a bound in `foo`
|
|||
|
|
||||
LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
|
||||
| ^^^^^^^^^^^^^^^^ required by this bound in `foo`
|
||||
help: do not borrow the argument
|
||||
help: consider adjusting the signature so it does not borrow its argument
|
||||
|
|
||||
LL | foo(move |char| v);
|
||||
| ~~~~
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`..
|
|||
LL | bytes: [u8; std::mem::size_of::<Foo>()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: ...which requires computing layout of `Foo`...
|
||||
= note: ...which requires computing layout (naive) of `Foo`...
|
||||
= note: ...which requires computing layout (naive) of `[u8; std::mem::size_of::<Foo>()]`...
|
||||
= note: ...which requires computing layout of `[u8; std::mem::size_of::<Foo>()]`...
|
||||
= note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
|
||||
= note: ...which again requires evaluating type-level constant, completing the cycle
|
||||
note: cycle used when checking that `Foo` is well-formed
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`..
|
|||
LL | bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: ...which requires computing layout of `Foo`...
|
||||
= note: ...which requires computing layout (naive) of `Foo`...
|
||||
= note: ...which requires computing layout (naive) of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
|
||||
= note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
|
||||
= note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
|
||||
= note: ...which again requires evaluating type-level constant, completing the cycle
|
||||
note: cycle used when checking that `Foo` is well-formed
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply
|
|||
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0277]: the trait bound `T: Copy` is not satisfied
|
||||
--> $DIR/explicit-drop-bounds.rs:32:13
|
||||
--> $DIR/explicit-drop-bounds.rs:32:18
|
||||
|
|
||||
LL | fn drop(&mut self) {}
|
||||
| ^^^^^^^^^ the trait `Copy` is not implemented for `T`
|
||||
| ^^^^ the trait `Copy` is not implemented for `T`
|
||||
|
|
||||
note: required by a bound in `DropMe`
|
||||
--> $DIR/explicit-drop-bounds.rs:7:18
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue