Auto merge of #152632 - JonathanBrouwer:rollup-hVWecYA, r=JonathanBrouwer

Rollup of 7 pull requests

Successful merges:

 - rust-lang/rust#152622 (Update GCC subtree)
 - rust-lang/rust#145024 (Optimize indexing slices and strs with inclusive ranges)
 - rust-lang/rust#151365 (UnsafePinned: implement opsem effects of UnsafeUnpin)
 - rust-lang/rust#152381 (Do not require `'static` for obtaining reflection information.)
 - rust-lang/rust#143575 (Remove named lifetimes in some `PartialOrd` & `PartialEq` `impl`s)
 - rust-lang/rust#152404 (tests: adapt align-offset.rs for InstCombine improvements in LLVM 23)
 - rust-lang/rust#152582 (rustc_query_impl: Use `ControlFlow` in `visit_waiters` instead of nested options)
This commit is contained in:
bors 2026-02-14 23:32:04 +00:00
commit 7bee525095
69 changed files with 2313 additions and 1886 deletions

View file

@ -18,6 +18,7 @@
], ],
"ignorePaths": [ "ignorePaths": [
"src/intrinsic/archs.rs", "src/intrinsic/archs.rs",
"src/intrinsic/old_archs.rs",
"src/intrinsic/llvm.rs" "src/intrinsic/llvm.rs"
], ],
"ignoreRegExpList": [ "ignoreRegExpList": [

View file

@ -113,6 +113,10 @@ jobs:
git config --global user.name "User" git config --global user.name "User"
./y.sh prepare ./y.sh prepare
- name: Add more failing tests for GCC without 128-bit integers support
if: ${{ matrix.libgccjit_version.gcc == 'gcc-15-without-int128.deb' }}
run: cat tests/failing-ui-tests-without-128bit-integers.txt >> tests/failing-ui-tests.txt
- name: Run tests - name: Run tests
run: | run: |
./y.sh test --release --clean --build-sysroot ${{ matrix.commands }} ./y.sh test --release --clean --build-sysroot ${{ matrix.commands }}

View file

@ -83,7 +83,7 @@ jobs:
run: | run: |
./y.sh prepare --only-libcore --cross ./y.sh prepare --only-libcore --cross
./y.sh build --sysroot --target-triple m68k-unknown-linux-gnu --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json ./y.sh build --sysroot --target-triple m68k-unknown-linux-gnu --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json
CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ./y.sh cargo build --manifest-path=./tests/hello-world/Cargo.toml --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ./y.sh cargo build -Zjson-target-spec --manifest-path=./tests/hello-world/Cargo.toml --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json
./y.sh clean all ./y.sh clean all
- name: Build - name: Build

View file

@ -56,18 +56,18 @@ dependencies = [
[[package]] [[package]]
name = "gccjit" name = "gccjit"
version = "3.1.1" version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff80f4d6d0749eab3a69122210b3a1fdd52edb6162781aadd7c4842e26983683" checksum = "26b73d18b642ce16378af78f89664841d7eeafa113682ff5d14573424eb0232a"
dependencies = [ dependencies = [
"gccjit_sys", "gccjit_sys",
] ]
[[package]] [[package]]
name = "gccjit_sys" name = "gccjit_sys"
version = "1.1.2" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f81d901767ddba371a619fa9bba657066a4d3c5607ee69bbb557c1c5ba9bf85" checksum = "ee689456c013616942d5aef9a84d613cefcc3b335340d036f3650fc1a7459e15"
dependencies = [ dependencies = [
"libc", "libc",
] ]

View file

@ -24,7 +24,7 @@ default = ["master"]
[dependencies] [dependencies]
object = { version = "0.37.0", default-features = false, features = ["std", "read"] } object = { version = "0.37.0", default-features = false, features = ["std", "read"] }
tempfile = "3.20" tempfile = "3.20"
gccjit = { version = "3.1.1", features = ["dlopen"] } gccjit = { version = "3.3.0", features = ["dlopen"] }
#gccjit = { git = "https://github.com/rust-lang/gccjit.rs", branch = "error-dlopen", features = ["dlopen"] } #gccjit = { git = "https://github.com/rust-lang/gccjit.rs", branch = "error-dlopen", features = ["dlopen"] }
# Local copy. # Local copy.

View file

@ -45,12 +45,12 @@ The default configuration (see below in the [Quick start](#quick-start) section)
./y.sh test --release ./y.sh test --release
``` ```
If don't need to test GCC patches you wrote in our GCC fork, then the default configuration should If you don't need to test GCC patches you wrote in our GCC fork, then the default configuration should
be all you need. You can update the `rustc_codegen_gcc` without worrying about GCC. be all you need. You can update the `rustc_codegen_gcc` without worrying about GCC.
### Building with your own GCC version ### Building with your own GCC version
If you wrote a patch for GCC and want to test it without this backend, you will need If you wrote a patch for GCC and want to test it with this backend, you will need
to do a few more things. to do a few more things.
To build it (most of these instructions come from [here](https://gcc.gnu.org/onlinedocs/jit/internals/index.html), so don't hesitate to take a look there if you encounter an issue): To build it (most of these instructions come from [here](https://gcc.gnu.org/onlinedocs/jit/internals/index.html), so don't hesitate to take a look there if you encounter an issue):
@ -127,7 +127,7 @@ You have to run these commands, in the corresponding order:
$ ./y.sh prepare $ ./y.sh prepare
$ ./y.sh build --sysroot $ ./y.sh build --sysroot
``` ```
To check if all is working correctly, run: To check if all is working correctly, run:
```bash ```bash
$ ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml $ ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml

View file

@ -6,4 +6,4 @@ seh = "seh"
typ = "typ" typ = "typ"
[files] [files]
extend-exclude = ["src/intrinsic/archs.rs"] extend-exclude = ["src/intrinsic/archs.rs", "src/intrinsic/old_archs.rs"]

View file

@ -141,6 +141,10 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu
} }
let mut args: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"build", &"--target", &config.target]; let mut args: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"build", &"--target", &config.target];
if config.target.ends_with(".json") {
args.push(&"-Zjson-target-spec");
}
for feature in &config.features { for feature in &config.features {
args.push(&"--features"); args.push(&"--features");
args.push(feature); args.push(feature);

View file

@ -679,10 +679,10 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> {
create_dir(projects_path)?; create_dir(projects_path)?;
let nb_parts = args.nb_parts.unwrap_or(0); let nb_parts = args.nb_parts.unwrap_or(0);
if nb_parts > 0 { if let Some(count) = projects.len().checked_div(nb_parts) {
// We increment the number of tests by one because if this is an odd number, we would skip // We increment the number of tests by one because if this is an odd number, we would skip
// one test. // one test.
let count = projects.len() / nb_parts + 1; let count = count + 1;
let current_part = args.current_part.unwrap(); let current_part = args.current_part.unwrap();
let start = current_part * count; let start = current_part * count;
// We remove the projects we don't want to test. // We remove the projects we don't want to test.

View file

@ -2,9 +2,9 @@
## How to debug GCC LTO ## How to debug GCC LTO
Run do the command with `-v -save-temps` and then extract the `lto1` line from the output and run that under the debugger. Run the command with `-v -save-temps` and then extract the `lto1` line from the output and run that under the debugger.
## How to debug stdarch tests that cannot be ran locally ## How to debug stdarch tests that cannot be run locally
First, run the tests normally: First, run the tests normally:

View file

@ -15,7 +15,7 @@ That can be caused by the fact that you try to compile with `lto = "fat"`, but y
### ld: cannot find crtbegin.o ### ld: cannot find crtbegin.o
When compiling an executable with libgccijt, if setting the `*LIBRARY_PATH` variables to the install directory, you will get the following errors: When compiling an executable with libgccjit, if setting the `*LIBRARY_PATH` variables to the install directory, you will get the following errors:
``` ```
ld: cannot find crtbegin.o: No such file or directory ld: cannot find crtbegin.o: No such file or directory

View file

@ -3,7 +3,7 @@
You can see the full documentation about what GIMPLE is [here](https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html). In this document we will explain how to generate it. You can see the full documentation about what GIMPLE is [here](https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html). In this document we will explain how to generate it.
First, we'll copy the content from `gcc/gcc/testsuite/jit.dg/test-const-attribute.c` into a First, we'll copy the content from `gcc/gcc/testsuite/jit.dg/test-const-attribute.c` into a
file named `local.c` and remove the content we're not interested into: file named `local.c` and remove the content we're not interested in:
```diff ```diff
- /* { dg-do compile { target x86_64-*-* } } */ - /* { dg-do compile { target x86_64-*-* } } */

View file

@ -53,7 +53,7 @@ If you wish to build a custom sysroot, pass the path of your sysroot source to `
### How to use [mem-trace](https://github.com/antoyo/mem-trace) ### How to use [mem-trace](https://github.com/antoyo/mem-trace)
`rustc` needs to be built without `jemalloc` so that `mem-trace` can overload `malloc` since `jemalloc` is linked statically, so a `LD_PRELOAD`-ed library won't a chance to intercept the calls to `malloc`. `rustc` needs to be built without `jemalloc` so that `mem-trace` can overload `malloc` since `jemalloc` is linked statically, so a `LD_PRELOAD`-ed library won't have a chance to intercept the calls to `malloc`.
### How to generate GIMPLE ### How to generate GIMPLE

View file

@ -1 +1 @@
0081ca6631abdfa02bf42bc85aaf507b8a0e6beb efdd0a7290c22f5438d7c5380105d353ee3e8518

View file

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly-2025-12-20" channel = "nightly-2026-02-14"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"] components = ["rust-src", "rustc-dev", "llvm-tools-preview"]

View file

@ -575,9 +575,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
} }
if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) { if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) {
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable"); let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
let builtin_unreachable: RValue<'gcc> = self.llbb().add_eval(None, self.context.new_call(None, builtin_unreachable, &[]));
unsafe { std::mem::transmute(builtin_unreachable) };
self.call(self.type_void(), None, None, builtin_unreachable, &[], None, None);
} }
// Write results to outputs. // Write results to outputs.

View file

@ -1495,6 +1495,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
#[cfg(not(feature = "master"))] #[cfg(not(feature = "master"))]
fn extract_element(&mut self, vec: RValue<'gcc>, idx: RValue<'gcc>) -> RValue<'gcc> { fn extract_element(&mut self, vec: RValue<'gcc>, idx: RValue<'gcc>) -> RValue<'gcc> {
use crate::context::new_array_type;
let vector_type = vec let vector_type = vec
.get_type() .get_type()
.unqualified() .unqualified()
@ -1503,7 +1505,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
let element_type = vector_type.get_element_type(); let element_type = vector_type.get_element_type();
let vec_num_units = vector_type.get_num_units(); let vec_num_units = vector_type.get_num_units();
let array_type = let array_type =
self.context.new_array_type(self.location, element_type, vec_num_units as u64); new_array_type(self.context, self.location, element_type, vec_num_units as u64);
let array = self.context.new_bitcast(self.location, vec, array_type).to_rvalue(); let array = self.context.new_bitcast(self.location, vec, array_type).to_rvalue();
self.context.new_array_access(self.location, array, idx).to_rvalue() self.context.new_array_access(self.location, array, idx).to_rvalue()
} }
@ -1871,32 +1873,31 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because
// we're rounding towards zero, we just get float_ty::MAX (which is always an integer). // we're rounding towards zero, we just get float_ty::MAX (which is always an integer).
// This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX.
let int_max = |signed: bool, int_width: u64| -> u128 { fn int_max(signed: bool, int_width: u64) -> u128 {
let shift_amount = 128 - int_width; let shift_amount = 128 - int_width;
if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount }
}; }
let int_min = |signed: bool, int_width: u64| -> i128 { fn int_min(signed: bool, int_width: u64) -> i128 {
if signed { i128::MIN >> (128 - int_width) } else { 0 } if signed { i128::MIN >> (128 - int_width) } else { 0 }
}; }
let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { // TODO: rewrite using a generic function with <F: Float>.
let compute_clamp_bounds_half = |signed: bool, int_width: u64| -> (u128, u128) {
let rounded_min = let rounded_min =
ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero); ieee::Half::from_i128_r(int_min(signed, int_width), Round::TowardZero);
assert_eq!(rounded_min.status, Status::OK); //assert_eq!(rounded_min.status, Status::OK);
let rounded_max = let rounded_max =
ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero); ieee::Half::from_u128_r(int_max(signed, int_width), Round::TowardZero);
assert!(rounded_max.value.is_finite()); assert!(rounded_max.value.is_finite());
(rounded_min.value.to_bits(), rounded_max.value.to_bits()) (rounded_min.value.to_bits(), rounded_max.value.to_bits())
}; };
let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) { fn compute_clamp_bounds<F: Float>(signed: bool, int_width: u64) -> (u128, u128) {
let rounded_min = let rounded_min = F::from_i128_r(int_min(signed, int_width), Round::TowardZero);
ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero);
assert_eq!(rounded_min.status, Status::OK); assert_eq!(rounded_min.status, Status::OK);
let rounded_max = let rounded_max = F::from_u128_r(int_max(signed, int_width), Round::TowardZero);
ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero);
assert!(rounded_max.value.is_finite()); assert!(rounded_max.value.is_finite());
(rounded_min.value.to_bits(), rounded_max.value.to_bits()) (rounded_min.value.to_bits(), rounded_max.value.to_bits())
}; }
// To implement saturation, we perform the following steps: // To implement saturation, we perform the following steps:
// //
// 1. Cast val to an integer with fpto[su]i. This may result in undef. // 1. Cast val to an integer with fpto[su]i. This may result in undef.
@ -1926,15 +1927,19 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let float_bits_to_llval = |bx: &mut Self, bits| { let float_bits_to_llval = |bx: &mut Self, bits| {
let bits_llval = match float_width { let bits_llval = match float_width {
16 => bx.cx().const_u16(bits as u16),
32 => bx.cx().const_u32(bits as u32), 32 => bx.cx().const_u32(bits as u32),
64 => bx.cx().const_u64(bits as u64), 64 => bx.cx().const_u64(bits as u64),
128 => bx.cx().const_u128(bits),
n => bug!("unsupported float width {}", n), n => bug!("unsupported float width {}", n),
}; };
bx.bitcast(bits_llval, float_ty) bx.bitcast(bits_llval, float_ty)
}; };
let (f_min, f_max) = match float_width { let (f_min, f_max) = match float_width {
32 => compute_clamp_bounds_single(signed, int_width), 16 => compute_clamp_bounds_half(signed, int_width),
64 => compute_clamp_bounds_double(signed, int_width), 32 => compute_clamp_bounds::<ieee::Single>(signed, int_width),
64 => compute_clamp_bounds::<ieee::Double>(signed, int_width),
128 => compute_clamp_bounds::<ieee::Quad>(signed, int_width),
n => bug!("unsupported float width {}", n), n => bug!("unsupported float width {}", n),
}; };
let f_min = float_bits_to_llval(self, f_min); let f_min = float_bits_to_llval(self, f_min);

View file

@ -8,7 +8,7 @@ use rustc_middle::mir::Mutability;
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use crate::context::CodegenCx; use crate::context::{CodegenCx, new_array_type};
use crate::type_of::LayoutGccExt; use crate::type_of::LayoutGccExt;
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
@ -20,6 +20,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
bytes_in_context(self, bytes) bytes_in_context(self, bytes)
} }
pub fn const_u16(&self, i: u16) -> RValue<'gcc> {
self.const_uint(self.type_u16(), i as u64)
}
fn global_string(&self, string: &str) -> LValue<'gcc> { fn global_string(&self, string: &str) -> LValue<'gcc> {
// TODO(antoyo): handle non-null-terminated strings. // TODO(antoyo): handle non-null-terminated strings.
let string = self.context.new_string_literal(string); let string = self.context.new_string_literal(string);
@ -55,7 +59,7 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) ->
0 => { 0 => {
let context = &cx.context; let context = &cx.context;
let byte_type = context.new_type::<u64>(); let byte_type = context.new_type::<u64>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 8); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 8);
let elements: Vec<_> = bytes let elements: Vec<_> = bytes
.chunks_exact(8) .chunks_exact(8)
.map(|arr| { .map(|arr| {
@ -76,7 +80,7 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) ->
4 => { 4 => {
let context = &cx.context; let context = &cx.context;
let byte_type = context.new_type::<u32>(); let byte_type = context.new_type::<u32>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64 / 4); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 4);
let elements: Vec<_> = bytes let elements: Vec<_> = bytes
.chunks_exact(4) .chunks_exact(4)
.map(|arr| { .map(|arr| {
@ -95,7 +99,7 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) ->
_ => { _ => {
let context = cx.context; let context = cx.context;
let byte_type = context.new_type::<u8>(); let byte_type = context.new_type::<u8>();
let typ = context.new_array_type(None, byte_type, bytes.len() as u64); let typ = new_array_type(context, None, byte_type, bytes.len() as u64);
let elements: Vec<_> = bytes let elements: Vec<_> = bytes
.iter() .iter()
.map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32)) .map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32))

View file

@ -19,6 +19,8 @@ use rustc_middle::ty::layout::{
}; };
use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt}; use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt};
use rustc_session::Session; use rustc_session::Session;
#[cfg(feature = "master")]
use rustc_session::config::DebugInfo;
use rustc_span::source_map::respan; use rustc_span::source_map::respan;
use rustc_span::{DUMMY_SP, Span}; use rustc_span::{DUMMY_SP, Span};
use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi}; use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi};
@ -145,6 +147,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
supports_f64_type: bool, supports_f64_type: bool,
supports_f128_type: bool, supports_f128_type: bool,
) -> Self { ) -> Self {
#[cfg(feature = "master")]
if tcx.sess.opts.debuginfo != DebugInfo::None {
context.set_filename(codegen_unit.name().as_str());
}
let create_type = |ctype, rust_type| { let create_type = |ctype, rust_type| {
let layout = tcx let layout = tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(rust_type)) .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(rust_type))
@ -194,8 +201,8 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
// TODO(antoyo): re-enable the alignment when libgccjit fixed the issue in // TODO(antoyo): re-enable the alignment when libgccjit fixed the issue in
// gcc_jit_context_new_array_constructor (it should not use reinterpret_cast). // gcc_jit_context_new_array_constructor (it should not use reinterpret_cast).
let i128_type = context.new_array_type(None, i64_type, 2)/*.get_aligned(i128_align)*/; let i128_type = new_array_type(context, None, i64_type, 2)/*.get_aligned(i128_align)*/;
let u128_type = context.new_array_type(None, u64_type, 2)/*.get_aligned(u128_align)*/; let u128_type = new_array_type(context, None, u64_type, 2)/*.get_aligned(u128_align)*/;
(i128_type, u128_type) (i128_type, u128_type)
}; };
@ -601,3 +608,17 @@ fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
TlsModel::Emulated => gccjit::TlsModel::GlobalDynamic, TlsModel::Emulated => gccjit::TlsModel::GlobalDynamic,
} }
} }
pub fn new_array_type<'gcc>(
context: &'gcc Context<'gcc>,
location: Option<Location<'gcc>>,
typ: Type<'gcc>,
size: u64,
) -> Type<'gcc> {
#[cfg(feature = "master")]
{
context.new_array_type_u64(location, typ, size)
}
#[cfg(not(feature = "master"))]
context.new_array_type(location, typ, size)
}

View file

@ -942,7 +942,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
fn float_to_int_cast( fn float_to_int_cast(
&self, &self,
signed: bool, signed: bool,
value: RValue<'gcc>, mut value: RValue<'gcc>,
dest_typ: Type<'gcc>, dest_typ: Type<'gcc>,
) -> RValue<'gcc> { ) -> RValue<'gcc> {
let value_type = value.get_type(); let value_type = value.get_type();
@ -951,16 +951,22 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
} }
debug_assert!(dest_typ.dyncast_array().is_some()); debug_assert!(dest_typ.dyncast_array().is_some());
let (dest_type, param_type) = match self.type_kind(value_type) {
TypeKind::Half => (Some(self.float_type), self.float_type),
_ => (None, value_type),
};
let name_suffix = match self.type_kind(value_type) { let name_suffix = match self.type_kind(value_type) {
// cSpell:disable // cSpell:disable
TypeKind::Float => "sfti", // Since we will cast Half to a float, we use sfti for both.
TypeKind::Half | TypeKind::Float => "sfti",
TypeKind::Double => "dfti", TypeKind::Double => "dfti",
TypeKind::FP128 => "tfti",
// cSpell:enable // cSpell:enable
kind => panic!("cannot cast a {:?} to non-native integer", kind), kind => panic!("cannot cast a {:?} to non-native integer", kind),
}; };
let sign = if signed { "" } else { "uns" }; let sign = if signed { "" } else { "uns" };
let func_name = format!("__fix{}{}", sign, name_suffix); let func_name = format!("__fix{}{}", sign, name_suffix);
let param = self.context.new_parameter(None, value_type, "n"); let param = self.context.new_parameter(None, param_type, "n");
let func = self.context.new_function( let func = self.context.new_function(
None, None,
FunctionType::Extern, FunctionType::Extern,
@ -969,6 +975,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
func_name, func_name,
false, false,
); );
if let Some(dest_type) = dest_type {
value = self.context.new_cast(None, value, dest_type);
}
self.context.new_call(None, func, &[value]) self.context.new_call(None, func, &[value])
} }

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@ use gccjit::{CType, Context, Field, Function, FunctionPtrType, RValue, ToRValue,
use rustc_codegen_ssa::traits::BuilderMethods; use rustc_codegen_ssa::traits::BuilderMethods;
use crate::builder::Builder; use crate::builder::Builder;
use crate::context::CodegenCx; use crate::context::{CodegenCx, new_array_type};
fn encode_key_128_type<'a, 'gcc, 'tcx>( fn encode_key_128_type<'a, 'gcc, 'tcx>(
builder: &Builder<'a, 'gcc, 'tcx>, builder: &Builder<'a, 'gcc, 'tcx>,
@ -585,7 +585,7 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(
"__builtin_ia32_encodekey128_u32" => { "__builtin_ia32_encodekey128_u32" => {
let mut new_args = args.to_vec(); let mut new_args = args.to_vec();
let m128i = builder.context.new_vector_type(builder.i64_type, 2); let m128i = builder.context.new_vector_type(builder.i64_type, 2);
let array_type = builder.context.new_array_type(None, m128i, 6); let array_type = new_array_type(builder.context, None, m128i, 6);
let result = builder.current_func().new_local(None, array_type, "result"); let result = builder.current_func().new_local(None, array_type, "result");
new_args.push(result.get_address(None)); new_args.push(result.get_address(None));
args = new_args.into(); args = new_args.into();
@ -593,7 +593,7 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(
"__builtin_ia32_encodekey256_u32" => { "__builtin_ia32_encodekey256_u32" => {
let mut new_args = args.to_vec(); let mut new_args = args.to_vec();
let m128i = builder.context.new_vector_type(builder.i64_type, 2); let m128i = builder.context.new_vector_type(builder.i64_type, 2);
let array_type = builder.context.new_array_type(None, m128i, 7); let array_type = new_array_type(builder.context, None, m128i, 7);
let result = builder.current_func().new_local(None, array_type, "result"); let result = builder.current_func().new_local(None, array_type, "result");
new_args.push(result.get_address(None)); new_args.push(result.get_address(None));
args = new_args.into(); args = new_args.into();
@ -620,7 +620,7 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(
let first_value = old_args.swap_remove(0); let first_value = old_args.swap_remove(0);
let element_type = first_value.get_type(); let element_type = first_value.get_type();
let array_type = builder.context.new_array_type(None, element_type, 8); let array_type = new_array_type(builder.context, None, element_type, 8);
let result = builder.current_func().new_local(None, array_type, "result"); let result = builder.current_func().new_local(None, array_type, "result");
new_args.push(result.get_address(None)); new_args.push(result.get_address(None));
@ -869,7 +869,7 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>(
builder.llbb().add_assignment(None, field1, return_value); builder.llbb().add_assignment(None, field1, return_value);
let field2 = result.access_field(None, field2); let field2 = result.access_field(None, field2);
let field2_type = field2.to_rvalue().get_type(); let field2_type = field2.to_rvalue().get_type();
let array_type = builder.context.new_array_type(None, field2_type, 6); let array_type = new_array_type(builder.context, None, field2_type, 6);
let ptr = builder.context.new_cast(None, args[2], array_type.make_pointer()); let ptr = builder.context.new_cast(None, args[2], array_type.make_pointer());
let field2_ptr = let field2_ptr =
builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer());
@ -891,7 +891,7 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>(
builder.llbb().add_assignment(None, field1, return_value); builder.llbb().add_assignment(None, field1, return_value);
let field2 = result.access_field(None, field2); let field2 = result.access_field(None, field2);
let field2_type = field2.to_rvalue().get_type(); let field2_type = field2.to_rvalue().get_type();
let array_type = builder.context.new_array_type(None, field2_type, 7); let array_type = new_array_type(builder.context, None, field2_type, 7);
let ptr = builder.context.new_cast(None, args[3], array_type.make_pointer()); let ptr = builder.context.new_cast(None, args[3], array_type.make_pointer());
let field2_ptr = let field2_ptr =
builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer());
@ -937,7 +937,7 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>(
builder.llbb().add_assignment(None, field1, return_value); builder.llbb().add_assignment(None, field1, return_value);
let field2 = result.access_field(None, field2); let field2 = result.access_field(None, field2);
let field2_type = field2.to_rvalue().get_type(); let field2_type = field2.to_rvalue().get_type();
let array_type = builder.context.new_array_type(None, field2_type, 8); let array_type = new_array_type(builder.context, None, field2_type, 8);
let ptr = builder.context.new_cast(None, args[0], array_type.make_pointer()); let ptr = builder.context.new_cast(None, args[0], array_type.make_pointer());
let field2_ptr = let field2_ptr =
builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer());
@ -1061,7 +1061,18 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
"llvm.x86.xgetbv" => "__builtin_ia32_xgetbv", "llvm.x86.xgetbv" => "__builtin_ia32_xgetbv",
// NOTE: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html // NOTE: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html
// FIXME: Should handle other targets than `ia32`.
"llvm.sqrt.v2f64" => "__builtin_ia32_sqrtpd", "llvm.sqrt.v2f64" => "__builtin_ia32_sqrtpd",
// FIXME: Should handle other targets than `ia32`.
"llvm.sqrt.v4f32" => "__builtin_ia32_sqrtps",
"llvm.sqrt.f32" => {
let gcc_name = "__builtin_sqrtf";
let func = cx.context.get_builtin_function(gcc_name);
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
return func;
}
// FIXME: Should handle other targets than `ia32`.
"llvm.smax.v4i32" => "__builtin_ia32_pmaxsd128",
"llvm.x86.avx512.pmul.dq.512" => "__builtin_ia32_pmuldq512_mask", "llvm.x86.avx512.pmul.dq.512" => "__builtin_ia32_pmuldq512_mask",
"llvm.x86.avx512.pmulu.dq.512" => "__builtin_ia32_pmuludq512_mask", "llvm.x86.avx512.pmulu.dq.512" => "__builtin_ia32_pmuludq512_mask",
"llvm.x86.avx512.max.ps.512" => "__builtin_ia32_maxps512_mask", "llvm.x86.avx512.max.ps.512" => "__builtin_ia32_maxps512_mask",
@ -1604,5 +1615,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
func func
} }
#[cfg(feature = "master")]
include!("old_archs.rs");
#[cfg(feature = "master")] #[cfg(feature = "master")]
include!("archs.rs"); include!("archs.rs");

View file

@ -208,6 +208,7 @@ fn get_simple_function_f128<'gcc, 'tcx>(
let f128_type = cx.type_f128(); let f128_type = cx.type_f128();
let func_name = match name { let func_name = match name {
sym::ceilf128 => "ceilf128", sym::ceilf128 => "ceilf128",
sym::fabsf128 => "fabsf128",
sym::floorf128 => "floorf128", sym::floorf128 => "floorf128",
sym::truncf128 => "truncf128", sym::truncf128 => "truncf128",
sym::roundf128 => "roundf128", sym::roundf128 => "roundf128",
@ -262,6 +263,7 @@ fn f16_builtin<'gcc, 'tcx>(
let builtin_name = match name { let builtin_name = match name {
sym::ceilf16 => "__builtin_ceilf", sym::ceilf16 => "__builtin_ceilf",
sym::copysignf16 => "__builtin_copysignf", sym::copysignf16 => "__builtin_copysignf",
sym::fabsf16 => "fabsf",
sym::floorf16 => "__builtin_floorf", sym::floorf16 => "__builtin_floorf",
sym::fmaf16 => "fmaf", sym::fmaf16 => "fmaf",
sym::maxnumf16 => "__builtin_fmaxf", sym::maxnumf16 => "__builtin_fmaxf",
@ -328,6 +330,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
} }
sym::ceilf16 sym::ceilf16
| sym::copysignf16 | sym::copysignf16
| sym::fabsf16
| sym::floorf16 | sym::floorf16
| sym::fmaf16 | sym::fmaf16
| sym::maxnumf16 | sym::maxnumf16
@ -648,15 +651,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
let fn_ptr = func.get_address(None); let fn_ptr = func.get_address(None);
let fn_ty = fn_ptr.get_type(); let fn_ty = fn_ptr.get_type();
let mut llargs = vec![]; let mut call_args = vec![];
for arg in args { for arg in args {
match arg.val { match arg.val {
OperandValue::ZeroSized => {} OperandValue::ZeroSized => {}
OperandValue::Immediate(_) => llargs.push(arg.immediate()), OperandValue::Immediate(_) => call_args.push(arg.immediate()),
OperandValue::Pair(a, b) => { OperandValue::Pair(a, b) => {
llargs.push(a); call_args.push(a);
llargs.push(b); call_args.push(b);
} }
OperandValue::Ref(op_place_val) => { OperandValue::Ref(op_place_val) => {
let mut llval = op_place_val.llval; let mut llval = op_place_val.llval;
@ -673,13 +676,13 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
// We store bools as `i8` so we need to truncate to `i1`. // We store bools as `i8` so we need to truncate to `i1`.
llval = self.to_immediate_scalar(llval, scalar); llval = self.to_immediate_scalar(llval, scalar);
} }
llargs.push(llval); call_args.push(llval);
} }
} }
} }
// FIXME directly use the llvm intrinsic adjustment functions here // FIXME directly use the llvm intrinsic adjustment functions here
let llret = self.call(fn_ty, None, None, fn_ptr, &llargs, None, None); let llret = self.call(fn_ty, None, None, fn_ptr, &call_args, None, None);
if is_cleanup { if is_cleanup {
self.apply_attrs_to_cleanup_callsite(llret); self.apply_attrs_to_cleanup_callsite(llret);
} }
@ -720,7 +723,8 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
} }
fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> {
unimplemented!(); // TODO(antoyo): implement.
self.context.new_rvalue_from_int(self.int_type, 0)
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -234,6 +234,8 @@ impl CodegenBackend for GccCodegenBackend {
#[cfg(feature = "master")] #[cfg(feature = "master")]
{ {
gccjit::set_lang_name(c"GNU Rust");
let target_cpu = target_cpu(sess); let target_cpu = target_cpu(sess);
// Get the second TargetInfo with the correct CPU features by setting the arch. // Get the second TargetInfo with the correct CPU features by setting the arch.

View file

@ -13,7 +13,7 @@ use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::{bug, ty}; use rustc_middle::{bug, ty};
use crate::common::TypeReflection; use crate::common::TypeReflection;
use crate::context::CodegenCx; use crate::context::{CodegenCx, new_array_type};
use crate::type_of::LayoutGccExt; use crate::type_of::LayoutGccExt;
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
@ -311,7 +311,7 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
len = 0; len = 0;
} }
self.context.new_array_type(None, ty, len) new_array_type(self.context, None, ty, len)
} }
} }

View file

@ -11,4 +11,4 @@ tests/run-make/foreign-exceptions/
tests/run-make/glibc-staticlib-args/ tests/run-make/glibc-staticlib-args/
tests/run-make/lto-smoke-c/ tests/run-make/lto-smoke-c/
tests/run-make/return-non-c-like-enum/ tests/run-make/return-non-c-like-enum/
tests/run-make/short-ice

View file

@ -0,0 +1 @@
tests/ui/simd/intrinsic/splat.rs

View file

@ -89,11 +89,13 @@ tests/ui/thir-print/offset_of.rs
tests/ui/iterators/rangefrom-overflow-debug.rs tests/ui/iterators/rangefrom-overflow-debug.rs
tests/ui/iterators/rangefrom-overflow-overflow-checks.rs tests/ui/iterators/rangefrom-overflow-overflow-checks.rs
tests/ui/iterators/iter-filter-count-debug-check.rs tests/ui/iterators/iter-filter-count-debug-check.rs
tests/ui/eii/codegen_single_crate.rs tests/ui/eii/linking/codegen_single_crate.rs
tests/ui/eii/codegen_cross_crate.rs tests/ui/eii/linking/codegen_cross_crate.rs
tests/ui/eii/default/local_crate.rs tests/ui/eii/default/local_crate.rs
tests/ui/eii/multiple_impls.rs tests/ui/eii/duplicate/multiple_impls.rs
tests/ui/eii/default/call_default.rs tests/ui/eii/default/call_default.rs
tests/ui/eii/same-symbol.rs tests/ui/eii/linking/same-symbol.rs
tests/ui/eii/privacy1.rs tests/ui/eii/privacy1.rs
tests/ui/eii/default/call_impl.rs tests/ui/eii/default/call_impl.rs
tests/ui/c-variadic/copy.rs
tests/ui/asm/x86_64/global_asm_escape.rs

View file

@ -0,0 +1,38 @@
// Compiler:
//
// Run-time:
// status: 0
// FIXME: Remove this test once rustc's `./tests/codegen/riscv-abi/call-llvm-intrinsics.rs`
// stops ignoring GCC backend.
#![feature(link_llvm_intrinsics)]
#![allow(internal_features)]
struct A;
impl Drop for A {
fn drop(&mut self) {
println!("A");
}
}
extern "C" {
#[link_name = "llvm.sqrt.f32"]
fn sqrt(x: f32) -> f32;
}
pub fn do_call() {
let _a = A;
unsafe {
// Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them
// CHECK: store float 4.000000e+00, float* %{{.}}, align 4
// CHECK: call float @llvm.sqrt.f32(float %{{.}}
sqrt(4.0);
}
}
fn main() {
do_call();
}

View file

@ -0,0 +1,102 @@
// Compiler:
//
// Run-time:
// status: 0
// FIXME: Remove this test once <tests/run-make/simd-ffi/simd.rs> stops
// ignoring GCC backend.
#![allow(internal_features, non_camel_case_types)]
// we can compile to a variety of platforms, because we don't need
// cross-compiled standard libraries.
#![feature(no_core, auto_traits)]
#![no_core]
#![feature(repr_simd, simd_ffi, link_llvm_intrinsics, lang_items, rustc_attrs)]
#[derive(Copy)]
#[repr(simd)]
pub struct f32x4([f32; 4]);
extern "C" {
#[link_name = "llvm.sqrt.v4f32"]
fn vsqrt(x: f32x4) -> f32x4;
}
pub fn foo(x: f32x4) -> f32x4 {
unsafe { vsqrt(x) }
}
#[derive(Copy)]
#[repr(simd)]
pub struct i32x4([i32; 4]);
extern "C" {
// _mm_sll_epi32
#[cfg(all(any(target_arch = "x86", target_arch = "x86-64"), target_feature = "sse2"))]
#[link_name = "llvm.x86.sse2.psll.d"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
// vmaxq_s32
#[cfg(target_arch = "arm")]
#[link_name = "llvm.arm.neon.vmaxs.v4i32"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
// vmaxq_s32
#[cfg(target_arch = "aarch64")]
#[link_name = "llvm.aarch64.neon.maxs.v4i32"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
// Use a generic LLVM intrinsic to do type checking on other platforms
#[cfg(not(any(
all(any(target_arch = "x86", target_arch = "x86-64"), target_feature = "sse2"),
target_arch = "arm",
target_arch = "aarch64"
)))]
#[link_name = "llvm.smax.v4i32"]
fn integer(a: i32x4, b: i32x4) -> i32x4;
}
pub fn bar(a: i32x4, b: i32x4) -> i32x4 {
unsafe { integer(a, b) }
}
#[lang = "pointee_sized"]
pub trait PointeeSized {}
#[lang = "meta_sized"]
pub trait MetaSized: PointeeSized {}
#[lang = "sized"]
pub trait Sized: MetaSized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for f32 {}
impl Copy for i32 {}
impl Copy for [f32; 4] {}
impl Copy for [i32; 4] {}
pub mod marker {
pub use Copy;
}
#[lang = "freeze"]
auto trait Freeze {}
#[macro_export]
#[rustc_builtin_macro]
macro_rules! Copy {
() => {};
}
#[macro_export]
#[rustc_builtin_macro]
macro_rules! derive {
() => {};
}
#[lang = "start"]
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
0
}
fn main() {}

View file

@ -0,0 +1,27 @@
// Compiler:
//
// Run-time:
// status: 0
use std::arch::asm;
fn exit_syscall(status: i32) -> ! {
#[cfg(target_arch = "x86_64")]
unsafe {
asm!(
"syscall",
in("rax") 60,
in("rdi") status,
options(noreturn)
);
}
#[cfg(not(target_arch = "x86_64"))]
std::process::exit(status);
}
fn main() {
// Used to crash with rustc_codegen_gcc.
exit_syscall(0);
std::process::exit(1);
}

View file

@ -12,7 +12,7 @@ def run_command(command, cwd=None):
sys.exit(1) sys.exit(1)
def clone_repository(repo_name, path, repo_url, branch="master", sub_paths=None): def clone_repository(repo_name, path, repo_url, sub_paths):
if os.path.exists(path): if os.path.exists(path):
while True: while True:
choice = input("There is already a `{}` folder, do you want to update it? [y/N]".format(path)) choice = input("There is already a `{}` folder, do you want to update it? [y/N]".format(path))
@ -21,18 +21,15 @@ def clone_repository(repo_name, path, repo_url, branch="master", sub_paths=None)
return return
elif choice.lower() == "y": elif choice.lower() == "y":
print("Updating repository...") print("Updating repository...")
run_command(["git", "pull", "origin", branch], cwd=path) run_command(["git", "pull", "origin", "main"], cwd=path)
return return
else: else:
print("Didn't understand answer...") print("Didn't understand answer...")
print("Cloning {} repository...".format(repo_name)) print("Cloning {} repository...".format(repo_name))
if sub_paths is None: run_command(["git", "clone", repo_url, "--filter=tree:0", "--no-checkout", path])
run_command(["git", "clone", repo_url, "--depth", "1", path]) run_command(["git", "sparse-checkout", "init"], cwd=path)
else: run_command(["git", "sparse-checkout", "set", *sub_paths], cwd=path)
run_command(["git", "clone", repo_url, "--filter=tree:0", "--no-checkout", path]) run_command(["git", "checkout"], cwd=path)
run_command(["git", "sparse-checkout", "init"], cwd=path)
run_command(["git", "sparse-checkout", "set", *sub_paths], cwd=path)
run_command(["git", "checkout"], cwd=path)
def append_intrinsic(array, intrinsic_name, translation): def append_intrinsic(array, intrinsic_name, translation):
@ -45,121 +42,36 @@ def convert_to_string(content):
return content return content
def extract_intrinsics_from_llvm(llvm_path, intrinsics): def extract_intrinsics_from_llvm(llvm_path):
command = ["llvm-tblgen", "llvm/IR/Intrinsics.td"] intrinsics = {}
command = ["llvm-tblgen", "llvm/IR/Intrinsics.td", "--dump-json"]
cwd = os.path.join(llvm_path, "llvm/include") cwd = os.path.join(llvm_path, "llvm/include")
print("=> Running command `{}` from `{}`".format(command, cwd)) print("=> Running command `{}` from `{}`".format(command, cwd))
p = subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE) p = subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE)
output, err = p.communicate() output, err = p.communicate()
lines = convert_to_string(output).splitlines() content = json.loads(convert_to_string(output))
pos = 0 for intrinsic in content:
while pos < len(lines): data = content[intrinsic]
line = lines[pos] if not isinstance(data, dict):
if not line.startswith("def "):
pos += 1
continue continue
intrinsic = line.split(" ")[1].strip() current_arch = data.get("TargetPrefix")
content = line builtin_name = data.get("ClangBuiltinName")
while pos < len(lines): if current_arch is None or current_arch == "" or builtin_name is None:
line = lines[pos].split(" // ")[0].strip()
content += line
pos += 1
if line == "}":
break
entries = re.findall('string ClangBuiltinName = "(\\w+)";', content)
current_arch = re.findall('string TargetPrefix = "(\\w+)";', content)
if len(entries) == 1 and len(current_arch) == 1:
current_arch = current_arch[0]
intrinsic = intrinsic.split("_")
if len(intrinsic) < 2 or intrinsic[0] != "int":
continue
intrinsic[0] = "llvm"
intrinsic = ".".join(intrinsic)
if current_arch not in intrinsics:
intrinsics[current_arch] = []
append_intrinsic(intrinsics[current_arch], intrinsic, entries[0])
def append_translation(json_data, p, array):
it = json_data["index"][p]
content = it["docs"].split('`')
if len(content) != 5:
return
append_intrinsic(array, content[1], content[3])
def extract_intrinsics_from_llvmint(llvmint, intrinsics):
archs = [
"AMDGPU",
"aarch64",
"arm",
"cuda",
"hexagon",
"mips",
"nvvm",
"ppc",
"ptx",
"x86",
"xcore",
]
json_file = os.path.join(llvmint, "target/doc/llvmint.json")
# We need to regenerate the documentation!
run_command(
["cargo", "rustdoc", "--", "-Zunstable-options", "--output-format", "json"],
cwd=llvmint,
)
with open(json_file, "r", encoding="utf8") as f:
json_data = json.loads(f.read())
for p in json_data["paths"]:
it = json_data["paths"][p]
if it["crate_id"] != 0:
# This is from an external crate.
continue continue
if it["kind"] != "function": intrinsic = intrinsic.split("_")
# We're only looking for functions. if len(intrinsic) < 2 or intrinsic[0] != "int":
continue continue
# if len(it["path"]) == 2: intrinsic[0] = "llvm"
# # This is a "general" intrinsic, not bound to a specific arch. intrinsic = ".".join(intrinsic)
# append_translation(json_data, p, general) if current_arch not in intrinsics:
# continue intrinsics[current_arch] = []
if len(it["path"]) != 3 or it["path"][1] not in archs: append_intrinsic(intrinsics[current_arch], intrinsic, builtin_name)
continue
arch = it["path"][1] return intrinsics
if arch not in intrinsics:
intrinsics[arch] = []
append_translation(json_data, p, intrinsics[arch])
def fill_intrinsics(intrinsics, from_intrinsics, all_intrinsics): def update_intrinsics(llvm_path):
for arch in from_intrinsics: intrinsics = extract_intrinsics_from_llvm(llvm_path)
if arch not in intrinsics:
intrinsics[arch] = []
for entry in from_intrinsics[arch]:
if entry[0] in all_intrinsics:
if all_intrinsics[entry[0]] == entry[1]:
# This is a "full" duplicate, both the LLVM instruction and the GCC
# translation are the same.
continue
intrinsics[arch].append((entry[0], entry[1], True))
else:
intrinsics[arch].append((entry[0], entry[1], False))
all_intrinsics[entry[0]] = entry[1]
def update_intrinsics(llvm_path, llvmint, llvmint2):
intrinsics_llvm = {}
intrinsics_llvmint = {}
all_intrinsics = {}
extract_intrinsics_from_llvm(llvm_path, intrinsics_llvm)
extract_intrinsics_from_llvmint(llvmint, intrinsics_llvmint)
extract_intrinsics_from_llvmint(llvmint2, intrinsics_llvmint)
intrinsics = {}
# We give priority to translations from LLVM over the ones from llvmint.
fill_intrinsics(intrinsics, intrinsics_llvm, all_intrinsics)
fill_intrinsics(intrinsics, intrinsics_llvmint, all_intrinsics)
archs = [arch for arch in intrinsics] archs = [arch for arch in intrinsics]
archs.sort() archs.sort()
@ -173,33 +85,41 @@ def update_intrinsics(llvm_path, llvmint, llvmint2):
# Since all intrinsic names start with "llvm.", we skip that prefix. # Since all intrinsic names start with "llvm.", we skip that prefix.
print("Updating content of `{}`...".format(output_file)) print("Updating content of `{}`...".format(output_file))
with open(output_file, "w", encoding="utf8") as out: with open(output_file, "w", encoding="utf8") as out:
out.write("// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`\n") out.write("""// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`
out.write("// DO NOT EDIT IT!\n") // DO NOT EDIT IT!
out.write("/// Translate a given LLVM intrinsic name to an equivalent GCC one.\n") /// Translate a given LLVM intrinsic name to an equivalent GCC one.
out.write("fn map_arch_intrinsic(full_name:&str)->&'static str{\n") fn map_arch_intrinsic(full_name:&str)-> &'static str {
out.write('let Some(name) = full_name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", full_name) };\n') let Some(name) = full_name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", full_name) };
out.write('let Some((arch, name)) = name.split_once(\'.\') else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n') let Some((arch, name)) = name.split_once('.') else { unimplemented!("***** unsupported LLVM intrinsic llvm.{}", name) };
out.write("match arch {\n") let old_arch_res = old_archs(arch, name);
if let ArchCheckResult::Ok(res) = old_arch_res {
return res;
}
match arch {""")
for arch in archs: for arch in archs:
if len(intrinsics[arch]) == 0: if len(intrinsics[arch]) == 0:
continue continue
attribute = "#[expect(non_snake_case)]" if arch[0].isupper() else "" attribute = "#[expect(non_snake_case)]" if arch[0].isupper() else ""
out.write("\"{}\" => {{ {} fn {}(name: &str,full_name:&str) -> &'static str {{ match name {{".format(arch, attribute, arch)) out.write("\"{}\" => {{ {} fn {}(name: &str,full_name:&str) -> &'static str {{ match name {{".format(arch, attribute, arch))
intrinsics[arch].sort(key=lambda x: (x[0], x[2])) intrinsics[arch].sort(key=lambda x: (x[0], x[1]))
out.write(' // {}\n'.format(arch)) out.write(' // {}\n'.format(arch))
for entry in intrinsics[arch]: for entry in intrinsics[arch]:
llvm_name = entry[0].removeprefix("llvm."); llvm_name = entry[0].removeprefix("llvm.");
llvm_name = llvm_name.removeprefix(arch); llvm_name = llvm_name.removeprefix(arch);
llvm_name = llvm_name.removeprefix("."); llvm_name = llvm_name.removeprefix(".");
if entry[2] is True: # if it is a duplicate if "_round_mask" in entry[1]:
out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
elif "_round_mask" in entry[1]:
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1])) out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
else: else:
out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1])) out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1]))
out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),\n') out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),\n')
out.write("}} }} {}(name,full_name) }}\n,".format(arch)) out.write("}} }} {}(name,full_name) }}\n,".format(arch))
out.write(' _ => unimplemented!("***** unsupported LLVM architecture {arch}, intrinsic:{full_name}"),\n') out.write(""" _ => {
match old_arch_res {
ArchCheckResult::UnknownIntrinsic => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),
ArchCheckResult::UnknownArch => unimplemented!("***** unsupported LLVM architecture {arch}, intrinsic: {full_name}"),
ArchCheckResult::Ok(_) => unreachable!(),
}
}""")
out.write("}\n}") out.write("}\n}")
subprocess.call(["rustfmt", output_file]) subprocess.call(["rustfmt", output_file])
print("Done!") print("Done!")
@ -210,35 +130,21 @@ def main():
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
"llvm-project", "llvm-project",
) )
llvmint_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"llvmint",
)
llvmint2_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"llvmint-2",
)
# First, we clone the LLVM repository if it's not already here. # First, we clone the LLVM repository if it's not already here.
clone_repository( clone_repository(
"llvm-project", "llvm-project",
llvm_path, llvm_path,
"https://github.com/llvm/llvm-project", "https://github.com/llvm/llvm-project",
branch="main", ["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"],
sub_paths=["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"],
) )
clone_repository( update_intrinsics(llvm_path)
"llvmint",
llvmint_path,
"https://github.com/GuillaumeGomez/llvmint",
)
clone_repository(
"llvmint2",
llvmint2_path,
"https://github.com/antoyo/llvmint",
)
update_intrinsics(llvm_path, llvmint_path, llvmint2_path)
# llvm-tblgen can be built with:
#
# mkdir llvm-tblgen-build && cd llvm-tblgen-build
# cmake -G Ninja -DLLVM_ENABLE_PROJECTS="llvm" -DCMAKE_BUILD_TYPE=Release ../llvm
# ninja llvm-tblgen
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(main()) sys.exit(main())

View file

@ -1693,6 +1693,10 @@ rustc_queries! {
query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is freeze", env.value } desc { "computing whether `{}` is freeze", env.value }
} }
/// Query backing `Ty::is_unsafe_unpin`.
query is_unsafe_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `UnsafeUnpin`", env.value }
}
/// Query backing `Ty::is_unpin`. /// Query backing `Ty::is_unpin`.
query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Unpin`", env.value } desc { "computing whether `{}` is `Unpin`", env.value }

View file

@ -1046,9 +1046,11 @@ where
hir::Mutability::Not => { hir::Mutability::Not => {
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) } PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
} }
hir::Mutability::Mut => { hir::Mutability::Mut => PointerKind::MutableRef {
PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, typing_env) } unpin: optimize
} && ty.is_unpin(tcx, typing_env)
&& ty.is_unsafe_unpin(tcx, typing_env),
},
}; };
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo { tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
@ -1143,7 +1145,9 @@ where
debug_assert!(pointee.safe.is_none()); debug_assert!(pointee.safe.is_none());
let optimize = tcx.sess.opts.optimize != OptLevel::No; let optimize = tcx.sess.opts.optimize != OptLevel::No;
pointee.safe = Some(PointerKind::Box { pointee.safe = Some(PointerKind::Box {
unpin: optimize && boxed_ty.is_unpin(tcx, typing_env), unpin: optimize
&& boxed_ty.is_unpin(tcx, typing_env)
&& boxed_ty.is_unsafe_unpin(tcx, typing_env),
global: this.ty.is_box_global(tcx), global: this.ty.is_box_global(tcx),
}); });
} }

View file

@ -1189,14 +1189,23 @@ impl<'tcx> Ty<'tcx> {
} }
} }
/// Checks whether values of this type `T` implement the `UnsafeUnpin` trait.
pub fn is_unsafe_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
self.is_trivially_unpin() || tcx.is_unsafe_unpin_raw(typing_env.as_query_input(self))
}
/// Checks whether values of this type `T` implement the `Unpin` trait. /// Checks whether values of this type `T` implement the `Unpin` trait.
///
/// Note that this is a safe trait, so it cannot be very semantically meaningful.
/// However, as a hack to mitigate <https://github.com/rust-lang/rust/issues/63818> until a
/// proper solution is implemented, we do give special semantics to the `Unpin` trait.
pub fn is_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { pub fn is_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
self.is_trivially_unpin() || tcx.is_unpin_raw(typing_env.as_query_input(self)) self.is_trivially_unpin() || tcx.is_unpin_raw(typing_env.as_query_input(self))
} }
/// Fast path helper for testing if a type is `Unpin`. /// Fast path helper for testing if a type is `Unpin` *and* `UnsafeUnpin`.
/// ///
/// Returning true means the type is known to be `Unpin`. Returning /// Returning true means the type is known to be `Unpin` and `UnsafeUnpin`. Returning
/// `false` means nothing -- could be `Unpin`, might not be. /// `false` means nothing -- could be `Unpin`, might not be.
fn is_trivially_unpin(self) -> bool { fn is_trivially_unpin(self) -> bool {
match self.kind() { match self.kind() {

View file

@ -1,5 +1,6 @@
use std::io::Write; use std::io::Write;
use std::iter; use std::iter;
use std::ops::ControlFlow;
use std::sync::Arc; use std::sync::Arc;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@ -117,41 +118,33 @@ type Waiter = (QueryJobId, usize);
/// Visits all the non-resumable and resumable waiters of a query. /// Visits all the non-resumable and resumable waiters of a query.
/// Only waiters in a query are visited. /// Only waiters in a query are visited.
/// `visit` is called for every waiter and is passed a query waiting on `query_ref` /// `visit` is called for every waiter and is passed a query waiting on `query`
/// and a span indicating the reason the query waited on `query_ref`. /// and a span indicating the reason the query waited on `query`.
/// If `visit` returns Some, this function returns. /// If `visit` returns `Break`, this function also returns `Break`,
/// and if all `visit` calls returns `Continue` it also returns `Continue`.
/// For visits of non-resumable waiters it returns the return value of `visit`. /// For visits of non-resumable waiters it returns the return value of `visit`.
/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the /// For visits of resumable waiters it returns information required to resume that waiter.
/// required information to resume the waiter. fn visit_waiters<'tcx>(
/// If all `visit` calls returns None, this function also returns None.
fn visit_waiters<'tcx, F>(
job_map: &QueryJobMap<'tcx>, job_map: &QueryJobMap<'tcx>,
query: QueryJobId, query: QueryJobId,
mut visit: F, mut visit: impl FnMut(Span, QueryJobId) -> ControlFlow<Option<Waiter>>,
) -> Option<Option<Waiter>> ) -> ControlFlow<Option<Waiter>> {
where
F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
{
// Visit the parent query which is a non-resumable waiter since it's on the same stack // Visit the parent query which is a non-resumable waiter since it's on the same stack
if let Some(parent) = job_map.parent_of(query) if let Some(parent) = job_map.parent_of(query) {
&& let Some(cycle) = visit(job_map.span_of(query), parent) visit(job_map.span_of(query), parent)?;
{
return Some(cycle);
} }
// Visit the explicit waiters which use condvars and are resumable // Visit the explicit waiters which use condvars and are resumable
if let Some(latch) = job_map.latch_of(query) { if let Some(latch) = job_map.latch_of(query) {
for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
if let Some(waiter_query) = waiter.query { if let Some(waiter_query) = waiter.query {
if visit(waiter.span, waiter_query).is_some() { // Return a value which indicates that this waiter can be resumed
// Return a value which indicates that this waiter can be resumed visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?;
return Some(Some((query, i)));
}
} }
} }
} }
None ControlFlow::Continue(())
} }
/// Look for query cycles by doing a depth first search starting at `query`. /// Look for query cycles by doing a depth first search starting at `query`.
@ -164,7 +157,7 @@ fn cycle_check<'tcx>(
span: Span, span: Span,
stack: &mut Vec<(Span, QueryJobId)>, stack: &mut Vec<(Span, QueryJobId)>,
visited: &mut FxHashSet<QueryJobId>, visited: &mut FxHashSet<QueryJobId>,
) -> Option<Option<Waiter>> { ) -> ControlFlow<Option<Waiter>> {
if !visited.insert(query) { if !visited.insert(query) {
return if let Some(p) = stack.iter().position(|q| q.1 == query) { return if let Some(p) = stack.iter().position(|q| q.1 == query) {
// We detected a query cycle, fix up the initial span and return Some // We detected a query cycle, fix up the initial span and return Some
@ -173,9 +166,9 @@ fn cycle_check<'tcx>(
stack.drain(0..p); stack.drain(0..p);
// Replace the span for the first query with the cycle cause // Replace the span for the first query with the cycle cause
stack[0].0 = span; stack[0].0 = span;
Some(None) ControlFlow::Break(None)
} else { } else {
None ControlFlow::Continue(())
}; };
} }
@ -188,7 +181,7 @@ fn cycle_check<'tcx>(
}); });
// Remove the entry in our stack if we didn't find a cycle // Remove the entry in our stack if we didn't find a cycle
if r.is_none() { if r.is_continue() {
stack.pop(); stack.pop();
} }
@ -202,21 +195,18 @@ fn connected_to_root<'tcx>(
job_map: &QueryJobMap<'tcx>, job_map: &QueryJobMap<'tcx>,
query: QueryJobId, query: QueryJobId,
visited: &mut FxHashSet<QueryJobId>, visited: &mut FxHashSet<QueryJobId>,
) -> bool { ) -> ControlFlow<Option<Waiter>> {
// We already visited this or we're deliberately ignoring it // We already visited this or we're deliberately ignoring it
if !visited.insert(query) { if !visited.insert(query) {
return false; return ControlFlow::Continue(());
} }
// This query is connected to the root (it has no query parent), return true // This query is connected to the root (it has no query parent), return true
if job_map.parent_of(query).is_none() { if job_map.parent_of(query).is_none() {
return true; return ControlFlow::Break(None);
} }
visit_waiters(job_map, query, |_, successor| { visit_waiters(job_map, query, |_, successor| connected_to_root(job_map, successor, visited))
connected_to_root(job_map, successor, visited).then_some(None)
})
.is_some()
} }
// Deterministically pick an query from a list // Deterministically pick an query from a list
@ -253,7 +243,7 @@ fn remove_cycle<'tcx>(
let mut visited = FxHashSet::default(); let mut visited = FxHashSet::default();
let mut stack = Vec::new(); let mut stack = Vec::new();
// Look for a cycle starting with the last query in `jobs` // Look for a cycle starting with the last query in `jobs`
if let Some(waiter) = if let ControlFlow::Break(waiter) =
cycle_check(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) cycle_check(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
{ {
// The stack is a vector of pairs of spans and queries; reverse it so that // The stack is a vector of pairs of spans and queries; reverse it so that
@ -284,15 +274,15 @@ fn remove_cycle<'tcx>(
} else { } else {
let mut waiters = Vec::new(); let mut waiters = Vec::new();
// Find all the direct waiters who lead to the root // Find all the direct waiters who lead to the root
visit_waiters(job_map, query, |span, waiter| { let _ = visit_waiters(job_map, query, |span, waiter| {
// Mark all the other queries in the cycle as already visited // Mark all the other queries in the cycle as already visited
let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
if connected_to_root(job_map, waiter, &mut visited) { if connected_to_root(job_map, waiter, &mut visited).is_break() {
waiters.push((span, waiter)); waiters.push((span, waiter));
} }
None ControlFlow::Continue(())
}); });
if waiters.is_empty() { if waiters.is_empty() {
None None

View file

@ -306,8 +306,12 @@ fn arg_attrs_for_rust_scalar<'tcx>(
let kind = if let Some(kind) = pointee.safe { let kind = if let Some(kind) = pointee.safe {
Some(kind) Some(kind)
} else if let Some(pointee) = drop_target_pointee { } else if let Some(pointee) = drop_target_pointee {
assert_eq!(pointee, layout.ty.builtin_deref(true).unwrap());
assert_eq!(offset, Size::ZERO);
// The argument to `drop_in_place` is semantically equivalent to a mutable reference. // The argument to `drop_in_place` is semantically equivalent to a mutable reference.
Some(PointerKind::MutableRef { unpin: pointee.is_unpin(tcx, cx.typing_env) }) let mutref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, pointee);
let layout = cx.layout_of(mutref).unwrap();
layout.pointee_info_at(&cx, offset).and_then(|pi| pi.safe)
} else { } else {
None None
}; };

View file

@ -8,36 +8,43 @@ use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
is_item_raw(tcx, query, LangItem::Copy) is_trait_raw(tcx, query, LangItem::Copy)
} }
fn is_use_cloned_raw<'tcx>( fn is_use_cloned_raw<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
) -> bool { ) -> bool {
is_item_raw(tcx, query, LangItem::UseCloned) is_trait_raw(tcx, query, LangItem::UseCloned)
} }
fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
is_item_raw(tcx, query, LangItem::Sized) is_trait_raw(tcx, query, LangItem::Sized)
} }
fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
is_item_raw(tcx, query, LangItem::Freeze) is_trait_raw(tcx, query, LangItem::Freeze)
}
fn is_unsafe_unpin_raw<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
) -> bool {
is_trait_raw(tcx, query, LangItem::UnsafeUnpin)
} }
fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
is_item_raw(tcx, query, LangItem::Unpin) is_trait_raw(tcx, query, LangItem::Unpin)
} }
fn is_async_drop_raw<'tcx>( fn is_async_drop_raw<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
) -> bool { ) -> bool {
is_item_raw(tcx, query, LangItem::AsyncDrop) is_trait_raw(tcx, query, LangItem::AsyncDrop)
} }
fn is_item_raw<'tcx>( fn is_trait_raw<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
item: LangItem, item: LangItem,
@ -53,6 +60,7 @@ pub(crate) fn provide(providers: &mut Providers) {
is_use_cloned_raw, is_use_cloned_raw,
is_sized_raw, is_sized_raw,
is_freeze_raw, is_freeze_raw,
is_unsafe_unpin_raw,
is_unpin_raw, is_unpin_raw,
is_async_drop_raw, is_async_drop_raw,
..*providers ..*providers

View file

@ -477,9 +477,8 @@ impl PartialEq for ByteString {
macro_rules! impl_partial_eq_ord_cow { macro_rules! impl_partial_eq_ord_cow {
($lhs:ty, $rhs:ty) => { ($lhs:ty, $rhs:ty) => {
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<'a> PartialEq<$rhs> for $lhs { impl PartialEq<$rhs> for $lhs {
#[inline] #[inline]
fn eq(&self, other: &$rhs) -> bool { fn eq(&self, other: &$rhs) -> bool {
let other: &[u8] = (&**other).as_ref(); let other: &[u8] = (&**other).as_ref();
@ -487,9 +486,8 @@ macro_rules! impl_partial_eq_ord_cow {
} }
} }
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<'a> PartialEq<$lhs> for $rhs { impl PartialEq<$lhs> for $rhs {
#[inline] #[inline]
fn eq(&self, other: &$lhs) -> bool { fn eq(&self, other: &$lhs) -> bool {
let this: &[u8] = (&**self).as_ref(); let this: &[u8] = (&**self).as_ref();
@ -497,9 +495,8 @@ macro_rules! impl_partial_eq_ord_cow {
} }
} }
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<'a> PartialOrd<$rhs> for $lhs { impl PartialOrd<$rhs> for $lhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
let other: &[u8] = (&**other).as_ref(); let other: &[u8] = (&**other).as_ref();
@ -507,9 +504,8 @@ macro_rules! impl_partial_eq_ord_cow {
} }
} }
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<'a> PartialOrd<$lhs> for $rhs { impl PartialOrd<$lhs> for $rhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
let this: &[u8] = (&**self).as_ref(); let this: &[u8] = (&**self).as_ref();
@ -667,9 +663,9 @@ impl From<Arc<ByteStr>> for Arc<[u8]> {
impl_partial_eq!(ByteStr, Vec<u8>); impl_partial_eq!(ByteStr, Vec<u8>);
// PartialOrd with `String` omitted to avoid inference failures // PartialOrd with `String` omitted to avoid inference failures
impl_partial_eq!(ByteStr, String); impl_partial_eq!(ByteStr, String);
impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, ByteStr>); impl_partial_eq_ord_cow!(&ByteStr, Cow<'_, ByteStr>);
impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, str>); impl_partial_eq_ord_cow!(&ByteStr, Cow<'_, str>);
impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, [u8]>); impl_partial_eq_ord_cow!(&ByteStr, Cow<'_, [u8]>);
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<'a> TryFrom<&'a ByteStr> for String { impl<'a> TryFrom<&'a ByteStr> for String {

View file

@ -2661,8 +2661,7 @@ impl<'b> Pattern for &'b String {
macro_rules! impl_eq { macro_rules! impl_eq {
($lhs:ty, $rhs: ty) => { ($lhs:ty, $rhs: ty) => {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow(unused_lifetimes)] impl PartialEq<$rhs> for $lhs {
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline] #[inline]
fn eq(&self, other: &$rhs) -> bool { fn eq(&self, other: &$rhs) -> bool {
PartialEq::eq(&self[..], &other[..]) PartialEq::eq(&self[..], &other[..])
@ -2674,8 +2673,7 @@ macro_rules! impl_eq {
} }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[allow(unused_lifetimes)] impl PartialEq<$lhs> for $rhs {
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline] #[inline]
fn eq(&self, other: &$lhs) -> bool { fn eq(&self, other: &$lhs) -> bool {
PartialEq::eq(&self[..], &other[..]) PartialEq::eq(&self[..], &other[..])
@ -2689,13 +2687,13 @@ macro_rules! impl_eq {
} }
impl_eq! { String, str } impl_eq! { String, str }
impl_eq! { String, &'a str } impl_eq! { String, &str }
#[cfg(not(no_global_oom_handling))] #[cfg(not(no_global_oom_handling))]
impl_eq! { Cow<'a, str>, str } impl_eq! { Cow<'_, str>, str }
#[cfg(not(no_global_oom_handling))] #[cfg(not(no_global_oom_handling))]
impl_eq! { Cow<'a, str>, &'b str } impl_eq! { Cow<'_, str>, &'_ str }
#[cfg(not(no_global_oom_handling))] #[cfg(not(no_global_oom_handling))]
impl_eq! { Cow<'a, str>, String } impl_eq! { Cow<'_, str>, String }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_default", issue = "143894")] #[rustc_const_unstable(feature = "const_default", issue = "143894")]

View file

@ -612,14 +612,14 @@ mod slice_index {
data: "abcdef"; data: "abcdef";
good: data[4..4] == ""; good: data[4..4] == "";
bad: data[4..3]; bad: data[4..3];
message: "begin <= end (4 <= 3)"; message: "begin > end (4 > 3)";
} }
in mod rangeinclusive_neg_width { in mod rangeinclusive_neg_width {
data: "abcdef"; data: "abcdef";
good: data[4..=3] == ""; good: data[4..=3] == "";
bad: data[4..=2]; bad: data[4..=2];
message: "begin <= end (4 <= 3)"; message: "begin > end (4 > 3)";
} }
} }
@ -630,13 +630,13 @@ mod slice_index {
// note: using 0 specifically ensures that the result of overflowing is 0..0, // note: using 0 specifically ensures that the result of overflowing is 0..0,
// so that `get` doesn't simply return None for the wrong reason. // so that `get` doesn't simply return None for the wrong reason.
bad: data[0..=usize::MAX]; bad: data[0..=usize::MAX];
message: "maximum usize"; message: "out of bounds";
} }
in mod rangetoinclusive { in mod rangetoinclusive {
data: "hello"; data: "hello";
bad: data[..=usize::MAX]; bad: data[..=usize::MAX];
message: "maximum usize"; message: "out of bounds";
} }
} }
} }
@ -659,49 +659,49 @@ mod slice_index {
data: super::DATA; data: super::DATA;
bad: data[super::BAD_START..super::GOOD_END]; bad: data[super::BAD_START..super::GOOD_END];
message: message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; "start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
} }
in mod range_2 { in mod range_2 {
data: super::DATA; data: super::DATA;
bad: data[super::GOOD_START..super::BAD_END]; bad: data[super::GOOD_START..super::BAD_END];
message: message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; "end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
} }
in mod rangefrom { in mod rangefrom {
data: super::DATA; data: super::DATA;
bad: data[super::BAD_START..]; bad: data[super::BAD_START..];
message: message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; "start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
} }
in mod rangeto { in mod rangeto {
data: super::DATA; data: super::DATA;
bad: data[..super::BAD_END]; bad: data[..super::BAD_END];
message: message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; "end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
} }
in mod rangeinclusive_1 { in mod rangeinclusive_1 {
data: super::DATA; data: super::DATA;
bad: data[super::BAD_START..=super::GOOD_END_INCL]; bad: data[super::BAD_START..=super::GOOD_END_INCL];
message: message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; "start byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
} }
in mod rangeinclusive_2 { in mod rangeinclusive_2 {
data: super::DATA; data: super::DATA;
bad: data[super::GOOD_START..=super::BAD_END_INCL]; bad: data[super::GOOD_START..=super::BAD_END_INCL];
message: message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; "end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
} }
in mod rangetoinclusive { in mod rangetoinclusive {
data: super::DATA; data: super::DATA;
bad: data[..=super::BAD_END_INCL]; bad: data[..=super::BAD_END_INCL];
message: message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; "end byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
} }
} }
} }
@ -716,7 +716,9 @@ mod slice_index {
// check the panic includes the prefix of the sliced string // check the panic includes the prefix of the sliced string
#[test] #[test]
#[should_panic(expected = "byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")] #[should_panic(
expected = "end byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet"
)]
fn test_slice_fail_truncated_1() { fn test_slice_fail_truncated_1() {
let _ = &LOREM_PARAGRAPH[..1024]; let _ = &LOREM_PARAGRAPH[..1024];
} }

View file

@ -45,8 +45,7 @@ impl hash::Hash for ByteStr {
#[unstable(feature = "bstr_internals", issue = "none")] #[unstable(feature = "bstr_internals", issue = "none")]
macro_rules! impl_partial_eq { macro_rules! impl_partial_eq {
($lhs:ty, $rhs:ty) => { ($lhs:ty, $rhs:ty) => {
#[allow(unused_lifetimes)] impl PartialEq<$rhs> for $lhs {
impl<'a> PartialEq<$rhs> for $lhs {
#[inline] #[inline]
fn eq(&self, other: &$rhs) -> bool { fn eq(&self, other: &$rhs) -> bool {
let other: &[u8] = other.as_ref(); let other: &[u8] = other.as_ref();
@ -54,8 +53,7 @@ macro_rules! impl_partial_eq {
} }
} }
#[allow(unused_lifetimes)] impl PartialEq<$lhs> for $rhs {
impl<'a> PartialEq<$lhs> for $rhs {
#[inline] #[inline]
fn eq(&self, other: &$lhs) -> bool { fn eq(&self, other: &$lhs) -> bool {
let this: &[u8] = self.as_ref(); let this: &[u8] = self.as_ref();
@ -76,9 +74,8 @@ macro_rules! impl_partial_eq_ord {
($lhs:ty, $rhs:ty) => { ($lhs:ty, $rhs:ty) => {
$crate::bstr::impl_partial_eq!($lhs, $rhs); $crate::bstr::impl_partial_eq!($lhs, $rhs);
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<'a> PartialOrd<$rhs> for $lhs { impl PartialOrd<$rhs> for $lhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
let other: &[u8] = other.as_ref(); let other: &[u8] = other.as_ref();
@ -86,9 +83,8 @@ macro_rules! impl_partial_eq_ord {
} }
} }
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<'a> PartialOrd<$lhs> for $rhs { impl PartialOrd<$lhs> for $rhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
let this: &[u8] = self.as_ref(); let this: &[u8] = self.as_ref();
@ -107,7 +103,6 @@ pub use impl_partial_eq_ord;
#[unstable(feature = "bstr_internals", issue = "none")] #[unstable(feature = "bstr_internals", issue = "none")]
macro_rules! impl_partial_eq_n { macro_rules! impl_partial_eq_n {
($lhs:ty, $rhs:ty) => { ($lhs:ty, $rhs:ty) => {
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<const N: usize> PartialEq<$rhs> for $lhs { impl<const N: usize> PartialEq<$rhs> for $lhs {
#[inline] #[inline]
@ -117,7 +112,6 @@ macro_rules! impl_partial_eq_n {
} }
} }
#[allow(unused_lifetimes)]
#[unstable(feature = "bstr", issue = "134915")] #[unstable(feature = "bstr", issue = "134915")]
impl<const N: usize> PartialEq<$lhs> for $rhs { impl<const N: usize> PartialEq<$lhs> for $rhs {
#[inline] #[inline]

View file

@ -2886,7 +2886,7 @@ pub const fn type_name<T: ?Sized>() -> &'static str;
#[rustc_nounwind] #[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic] #[rustc_intrinsic]
pub const fn type_id<T: ?Sized + 'static>() -> crate::any::TypeId; pub const fn type_id<T: ?Sized>() -> crate::any::TypeId;
/// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the /// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the
/// same type. This is necessary because at const-eval time the actual discriminating /// same type. This is necessary because at const-eval time the actual discriminating

View file

@ -927,14 +927,20 @@ marker_impls! {
/// This is part of [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html), and is /// This is part of [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html), and is
/// tracked by [#125735](https://github.com/rust-lang/rust/issues/125735). /// tracked by [#125735](https://github.com/rust-lang/rust/issues/125735).
#[lang = "unsafe_unpin"] #[lang = "unsafe_unpin"]
pub(crate) unsafe auto trait UnsafeUnpin {} #[unstable(feature = "unsafe_unpin", issue = "125735")]
pub unsafe auto trait UnsafeUnpin {}
#[unstable(feature = "unsafe_unpin", issue = "125735")]
impl<T: ?Sized> !UnsafeUnpin for UnsafePinned<T> {} impl<T: ?Sized> !UnsafeUnpin for UnsafePinned<T> {}
unsafe impl<T: ?Sized> UnsafeUnpin for PhantomData<T> {} marker_impls! {
unsafe impl<T: ?Sized> UnsafeUnpin for *const T {} #[unstable(feature = "unsafe_unpin", issue = "125735")]
unsafe impl<T: ?Sized> UnsafeUnpin for *mut T {} unsafe UnsafeUnpin for
unsafe impl<T: ?Sized> UnsafeUnpin for &T {} {T: ?Sized} PhantomData<T>,
unsafe impl<T: ?Sized> UnsafeUnpin for &mut T {} {T: ?Sized} *const T,
{T: ?Sized} *mut T,
{T: ?Sized} &T,
{T: ?Sized} &mut T,
}
/// Types that do not require any pinning guarantees. /// Types that do not require any pinning guarantees.
/// ///
@ -1027,6 +1033,7 @@ impl !Unpin for PhantomPinned {}
// continue working. Ideally PhantomPinned could just wrap an `UnsafePinned<()>` to get the same // continue working. Ideally PhantomPinned could just wrap an `UnsafePinned<()>` to get the same
// effect, but we can't add a new field to an already stable unit struct -- that would be a breaking // effect, but we can't add a new field to an already stable unit struct -- that would be a breaking
// change. // change.
#[unstable(feature = "unsafe_unpin", issue = "125735")]
impl !UnsafeUnpin for PhantomPinned {} impl !UnsafeUnpin for PhantomPinned {}
marker_impls! { marker_impls! {

View file

@ -2,7 +2,7 @@
//! runtime or const-eval processable way. //! runtime or const-eval processable way.
use crate::any::TypeId; use crate::any::TypeId;
use crate::intrinsics::type_of; use crate::intrinsics::{type_id, type_of};
/// Compile-time type information. /// Compile-time type information.
#[derive(Debug)] #[derive(Debug)]
@ -28,11 +28,17 @@ impl TypeId {
impl Type { impl Type {
/// Returns the type information of the generic type parameter. /// Returns the type information of the generic type parameter.
///
/// Note: Unlike `TypeId`s obtained via `TypeId::of`, the `Type`
/// struct and its fields contain `TypeId`s that are not necessarily
/// derived from types that outlive `'static`. This means that using
/// the `TypeId`s (transitively) obtained from this function will
/// be able to break invariants that other `TypeId` consuming crates
/// may have assumed to hold.
#[unstable(feature = "type_info", issue = "146922")] #[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")]
// FIXME(reflection): don't require the 'static bound pub const fn of<T: ?Sized>() -> Self {
pub const fn of<T: ?Sized + 'static>() -> Self { const { type_id::<T>().info() }
const { TypeId::of::<T>().info() }
} }
} }

View file

@ -31,7 +31,7 @@ use crate::{fmt, intrinsics, ptr, ub_checks};
issue = "none" issue = "none"
)] )]
pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed { pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed {
#[doc(hidden)] /// A type like `Self` but with a niche that includes zero.
type NonZeroInner: Sized + Copy; type NonZeroInner: Sized + Copy;
} }

View file

@ -352,15 +352,6 @@ impl<Idx: Step> RangeInclusive<Idx> {
} }
} }
impl RangeInclusive<usize> {
/// Converts to an exclusive `Range` for `SliceIndex` implementations.
/// The caller is responsible for dealing with `last == usize::MAX`.
#[inline]
pub(crate) const fn into_slice_range(self) -> Range<usize> {
Range { start: self.start, end: self.last + 1 }
}
}
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_range", issue = "none")] #[rustc_const_unstable(feature = "const_range", issue = "none")]
impl<T> const RangeBounds<T> for RangeInclusive<T> { impl<T> const RangeBounds<T> for RangeInclusive<T> {

View file

@ -663,7 +663,6 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeFull {
} }
/// The methods `index` and `index_mut` panic if: /// The methods `index` and `index_mut` panic if:
/// - the end of the range is `usize::MAX` or
/// - the start of the range is greater than the end of the range or /// - the start of the range is greater than the end of the range or
/// - the end of the range is out of bounds. /// - the end of the range is out of bounds.
#[stable(feature = "inclusive_range", since = "1.26.0")] #[stable(feature = "inclusive_range", since = "1.26.0")]
@ -673,12 +672,12 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
#[inline] #[inline]
fn get(self, slice: &[T]) -> Option<&[T]> { fn get(self, slice: &[T]) -> Option<&[T]> {
if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } if *self.end() >= slice.len() { None } else { self.into_slice_range().get(slice) }
} }
#[inline] #[inline]
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } if *self.end() >= slice.len() { None } else { self.into_slice_range().get_mut(slice) }
} }
#[inline] #[inline]
@ -950,8 +949,7 @@ where
R: ops::RangeBounds<usize>, R: ops::RangeBounds<usize>,
{ {
let len = bounds.end; let len = bounds.end;
let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?; try_into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied()))
if r.start > r.end || r.end > len { None } else { Some(r) }
} }
/// Converts a pair of `ops::Bound`s into `ops::Range` without performing any /// Converts a pair of `ops::Bound`s into `ops::Range` without performing any
@ -978,25 +976,31 @@ pub(crate) const fn into_range_unchecked(
/// Returns `None` on overflowing indices. /// Returns `None` on overflowing indices.
#[rustc_const_unstable(feature = "const_range", issue = "none")] #[rustc_const_unstable(feature = "const_range", issue = "none")]
#[inline] #[inline]
pub(crate) const fn into_range( pub(crate) const fn try_into_slice_range(
len: usize, len: usize,
(start, end): (ops::Bound<usize>, ops::Bound<usize>), (start, end): (ops::Bound<usize>, ops::Bound<usize>),
) -> Option<ops::Range<usize>> { ) -> Option<ops::Range<usize>> {
use ops::Bound;
let start = match start {
Bound::Included(start) => start,
Bound::Excluded(start) => start.checked_add(1)?,
Bound::Unbounded => 0,
};
let end = match end { let end = match end {
Bound::Included(end) => end.checked_add(1)?, ops::Bound::Included(end) if end >= len => return None,
Bound::Excluded(end) => end, // Cannot overflow because `end < len` implies `end < usize::MAX`.
Bound::Unbounded => len, ops::Bound::Included(end) => end + 1,
ops::Bound::Excluded(end) if end > len => return None,
ops::Bound::Excluded(end) => end,
ops::Bound::Unbounded => len,
}; };
// Don't bother with checking `start < end` and `end <= len` let start = match start {
// since these checks are handled by `Range` impls ops::Bound::Excluded(start) if start >= end => return None,
// Cannot overflow because `start < end` implies `start < usize::MAX`.
ops::Bound::Excluded(start) => start + 1,
ops::Bound::Included(start) if start > end => return None,
ops::Bound::Included(start) => start,
ops::Bound::Unbounded => 0,
};
Some(start..end) Some(start..end)
} }
@ -1039,12 +1043,12 @@ unsafe impl<T> SliceIndex<[T]> for (ops::Bound<usize>, ops::Bound<usize>) {
#[inline] #[inline]
fn get(self, slice: &[T]) -> Option<&Self::Output> { fn get(self, slice: &[T]) -> Option<&Self::Output> {
into_range(slice.len(), self)?.get(slice) try_into_slice_range(slice.len(), self)?.get(slice)
} }
#[inline] #[inline]
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> { fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
into_range(slice.len(), self)?.get_mut(slice) try_into_slice_range(slice.len(), self)?.get_mut(slice)
} }
#[inline] #[inline]

View file

@ -85,34 +85,50 @@ fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH); let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
let s_trunc = &s[..trunc_len]; let s_trunc = &s[..trunc_len];
let ellipsis = if trunc_len < s.len() { "[...]" } else { "" }; let ellipsis = if trunc_len < s.len() { "[...]" } else { "" };
let len = s.len();
// 1. out of bounds // 1. begin is OOB.
if begin > s.len() || end > s.len() { if begin > len {
let oob_index = if begin > s.len() { begin } else { end }; panic!("start byte index {begin} is out of bounds of `{s_trunc}`{ellipsis}");
panic!("byte index {oob_index} is out of bounds of `{s_trunc}`{ellipsis}");
} }
// 2. begin <= end // 2. end is OOB.
assert!( if end > len {
begin <= end, panic!("end byte index {end} is out of bounds of `{s_trunc}`{ellipsis}");
"begin <= end ({} <= {}) when slicing `{}`{}", }
begin,
end,
s_trunc,
ellipsis
);
// 3. character boundary // 3. range is backwards.
let index = if !s.is_char_boundary(begin) { begin } else { end }; if begin > end {
// find the character panic!("begin > end ({begin} > {end}) when slicing `{s_trunc}`{ellipsis}")
let char_start = s.floor_char_boundary(index); }
// `char_start` must be less than len and a char boundary
let ch = s[char_start..].chars().next().unwrap(); // 4. begin is inside a character.
let char_range = char_start..char_start + ch.len_utf8(); if !s.is_char_boundary(begin) {
panic!( let floor = s.floor_char_boundary(begin);
"byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}", let ceil = s.ceil_char_boundary(begin);
index, ch, char_range, s_trunc, ellipsis let range = floor..ceil;
); let ch = s[floor..ceil].chars().next().unwrap();
panic!(
"start byte index {begin} is not a char boundary; it is inside {ch:?} (bytes {range:?}) of `{s_trunc}`{ellipsis}"
)
}
// 5. end is inside a character.
if !s.is_char_boundary(end) {
let floor = s.floor_char_boundary(end);
let ceil = s.ceil_char_boundary(end);
let range = floor..ceil;
let ch = s[floor..ceil].chars().next().unwrap();
panic!(
"end byte index {end} is not a char boundary; it is inside {ch:?} (bytes {range:?}) of `{s_trunc}`{ellipsis}"
)
}
// 6. end is OOB and range is inclusive (end == len).
// This test cannot be combined with 2. above because for cases like
// `"abcαβγ"[4..9]` the error is that 4 is inside 'α', not that 9 is OOB.
debug_assert_eq!(end, len);
panic!("end byte index {end} is out of bounds of `{s_trunc}`{ellipsis}");
} }
impl str { impl str {

View file

@ -76,13 +76,6 @@ where
} }
} }
#[inline(never)]
#[cold]
#[track_caller]
const fn str_index_overflow_fail() -> ! {
panic!("attempted to index str up to maximum usize");
}
/// Implements substring slicing with syntax `&self[..]` or `&mut self[..]`. /// Implements substring slicing with syntax `&self[..]` or `&mut self[..]`.
/// ///
/// Returns a slice of the whole string, i.e., returns `&self` or `&mut /// Returns a slice of the whole string, i.e., returns `&self` or `&mut
@ -389,12 +382,12 @@ unsafe impl SliceIndex<str> for (ops::Bound<usize>, ops::Bound<usize>) {
#[inline] #[inline]
fn get(self, slice: &str) -> Option<&str> { fn get(self, slice: &str) -> Option<&str> {
crate::slice::index::into_range(slice.len(), self)?.get(slice) crate::slice::index::try_into_slice_range(slice.len(), self)?.get(slice)
} }
#[inline] #[inline]
fn get_mut(self, slice: &mut str) -> Option<&mut str> { fn get_mut(self, slice: &mut str) -> Option<&mut str> {
crate::slice::index::into_range(slice.len(), self)?.get_mut(slice) crate::slice::index::try_into_slice_range(slice.len(), self)?.get_mut(slice)
} }
#[inline] #[inline]
@ -640,11 +633,11 @@ unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
type Output = str; type Output = str;
#[inline] #[inline]
fn get(self, slice: &str) -> Option<&Self::Output> { fn get(self, slice: &str) -> Option<&Self::Output> {
if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } if *self.end() >= slice.len() { None } else { self.into_slice_range().get(slice) }
} }
#[inline] #[inline]
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } if *self.end() >= slice.len() { None } else { self.into_slice_range().get_mut(slice) }
} }
#[inline] #[inline]
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
@ -658,17 +651,37 @@ unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
} }
#[inline] #[inline]
fn index(self, slice: &str) -> &Self::Output { fn index(self, slice: &str) -> &Self::Output {
if *self.end() == usize::MAX { let Self { mut start, mut end, exhausted } = self;
str_index_overflow_fail(); let len = slice.len();
if end < len {
end = end + 1;
start = if exhausted { end } else { start };
if start <= end && slice.is_char_boundary(start) && slice.is_char_boundary(end) {
// SAFETY: just checked that `start` and `end` are on a char boundary,
// and we are passing in a safe reference, so the return value will also be one.
// We also checked char boundaries, so this is valid UTF-8.
unsafe { return &*(start..end).get_unchecked(slice) }
}
} }
self.into_slice_range().index(slice)
super::slice_error_fail(slice, start, end)
} }
#[inline] #[inline]
fn index_mut(self, slice: &mut str) -> &mut Self::Output { fn index_mut(self, slice: &mut str) -> &mut Self::Output {
if *self.end() == usize::MAX { let Self { mut start, mut end, exhausted } = self;
str_index_overflow_fail(); let len = slice.len();
if end < len {
end = end + 1;
start = if exhausted { end } else { start };
if start <= end && slice.is_char_boundary(start) && slice.is_char_boundary(end) {
// SAFETY: just checked that `start` and `end` are on a char boundary,
// and we are passing in a safe reference, so the return value will also be one.
// We also checked char boundaries, so this is valid UTF-8.
unsafe { return &mut *(start..end).get_unchecked_mut(slice) }
}
} }
self.into_slice_range().index_mut(slice)
super::slice_error_fail(slice, start, end)
} }
} }
@ -678,35 +691,29 @@ unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> {
type Output = str; type Output = str;
#[inline] #[inline]
fn get(self, slice: &str) -> Option<&Self::Output> { fn get(self, slice: &str) -> Option<&Self::Output> {
if self.last == usize::MAX { None } else { self.into_slice_range().get(slice) } ops::RangeInclusive::from(self).get(slice)
} }
#[inline] #[inline]
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
if self.last == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } ops::RangeInclusive::from(self).get_mut(slice)
} }
#[inline] #[inline]
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
// SAFETY: the caller must uphold the safety contract for `get_unchecked`. // SAFETY: the caller must uphold the safety contract for `get_unchecked`.
unsafe { self.into_slice_range().get_unchecked(slice) } unsafe { ops::RangeInclusive::from(self).get_unchecked(slice) }
} }
#[inline] #[inline]
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
unsafe { self.into_slice_range().get_unchecked_mut(slice) } unsafe { ops::RangeInclusive::from(self).get_unchecked_mut(slice) }
} }
#[inline] #[inline]
fn index(self, slice: &str) -> &Self::Output { fn index(self, slice: &str) -> &Self::Output {
if self.last == usize::MAX { ops::RangeInclusive::from(self).index(slice)
str_index_overflow_fail();
}
self.into_slice_range().index(slice)
} }
#[inline] #[inline]
fn index_mut(self, slice: &mut str) -> &mut Self::Output { fn index_mut(self, slice: &mut str) -> &mut Self::Output {
if self.last == usize::MAX { ops::RangeInclusive::from(self).index_mut(slice)
str_index_overflow_fail();
}
self.into_slice_range().index_mut(slice)
} }
} }

View file

@ -1565,7 +1565,7 @@ impl Ord for OsStr {
macro_rules! impl_cmp { macro_rules! impl_cmp {
($lhs:ty, $rhs: ty) => { ($lhs:ty, $rhs: ty) => {
#[stable(feature = "cmp_os_str", since = "1.8.0")] #[stable(feature = "cmp_os_str", since = "1.8.0")]
impl<'a, 'b> PartialEq<$rhs> for $lhs { impl PartialEq<$rhs> for $lhs {
#[inline] #[inline]
fn eq(&self, other: &$rhs) -> bool { fn eq(&self, other: &$rhs) -> bool {
<OsStr as PartialEq>::eq(self, other) <OsStr as PartialEq>::eq(self, other)
@ -1573,7 +1573,7 @@ macro_rules! impl_cmp {
} }
#[stable(feature = "cmp_os_str", since = "1.8.0")] #[stable(feature = "cmp_os_str", since = "1.8.0")]
impl<'a, 'b> PartialEq<$lhs> for $rhs { impl PartialEq<$lhs> for $rhs {
#[inline] #[inline]
fn eq(&self, other: &$lhs) -> bool { fn eq(&self, other: &$lhs) -> bool {
<OsStr as PartialEq>::eq(self, other) <OsStr as PartialEq>::eq(self, other)
@ -1581,7 +1581,7 @@ macro_rules! impl_cmp {
} }
#[stable(feature = "cmp_os_str", since = "1.8.0")] #[stable(feature = "cmp_os_str", since = "1.8.0")]
impl<'a, 'b> PartialOrd<$rhs> for $lhs { impl PartialOrd<$rhs> for $lhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
<OsStr as PartialOrd>::partial_cmp(self, other) <OsStr as PartialOrd>::partial_cmp(self, other)
@ -1589,7 +1589,7 @@ macro_rules! impl_cmp {
} }
#[stable(feature = "cmp_os_str", since = "1.8.0")] #[stable(feature = "cmp_os_str", since = "1.8.0")]
impl<'a, 'b> PartialOrd<$lhs> for $rhs { impl PartialOrd<$lhs> for $rhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
<OsStr as PartialOrd>::partial_cmp(self, other) <OsStr as PartialOrd>::partial_cmp(self, other)
@ -1599,10 +1599,10 @@ macro_rules! impl_cmp {
} }
impl_cmp!(OsString, OsStr); impl_cmp!(OsString, OsStr);
impl_cmp!(OsString, &'a OsStr); impl_cmp!(OsString, &OsStr);
impl_cmp!(Cow<'a, OsStr>, OsStr); impl_cmp!(Cow<'_, OsStr>, OsStr);
impl_cmp!(Cow<'a, OsStr>, &'b OsStr); impl_cmp!(Cow<'_, OsStr>, &OsStr);
impl_cmp!(Cow<'a, OsStr>, OsString); impl_cmp!(Cow<'_, OsStr>, OsString);
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl Hash for OsStr { impl Hash for OsStr {

View file

@ -3841,9 +3841,9 @@ impl<'a> IntoIterator for &'a Path {
} }
macro_rules! impl_cmp { macro_rules! impl_cmp {
(<$($life:lifetime),*> $lhs:ty, $rhs: ty) => { ($lhs:ty, $rhs: ty) => {
#[stable(feature = "partialeq_path", since = "1.6.0")] #[stable(feature = "partialeq_path", since = "1.6.0")]
impl<$($life),*> PartialEq<$rhs> for $lhs { impl PartialEq<$rhs> for $lhs {
#[inline] #[inline]
fn eq(&self, other: &$rhs) -> bool { fn eq(&self, other: &$rhs) -> bool {
<Path as PartialEq>::eq(self, other) <Path as PartialEq>::eq(self, other)
@ -3851,7 +3851,7 @@ macro_rules! impl_cmp {
} }
#[stable(feature = "partialeq_path", since = "1.6.0")] #[stable(feature = "partialeq_path", since = "1.6.0")]
impl<$($life),*> PartialEq<$lhs> for $rhs { impl PartialEq<$lhs> for $rhs {
#[inline] #[inline]
fn eq(&self, other: &$lhs) -> bool { fn eq(&self, other: &$lhs) -> bool {
<Path as PartialEq>::eq(self, other) <Path as PartialEq>::eq(self, other)
@ -3859,7 +3859,7 @@ macro_rules! impl_cmp {
} }
#[stable(feature = "cmp_path", since = "1.8.0")] #[stable(feature = "cmp_path", since = "1.8.0")]
impl<$($life),*> PartialOrd<$rhs> for $lhs { impl PartialOrd<$rhs> for $lhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self, other) <Path as PartialOrd>::partial_cmp(self, other)
@ -3867,7 +3867,7 @@ macro_rules! impl_cmp {
} }
#[stable(feature = "cmp_path", since = "1.8.0")] #[stable(feature = "cmp_path", since = "1.8.0")]
impl<$($life),*> PartialOrd<$lhs> for $rhs { impl PartialOrd<$lhs> for $rhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self, other) <Path as PartialOrd>::partial_cmp(self, other)
@ -3876,16 +3876,16 @@ macro_rules! impl_cmp {
}; };
} }
impl_cmp!(<> PathBuf, Path); impl_cmp!(PathBuf, Path);
impl_cmp!(<'a> PathBuf, &'a Path); impl_cmp!(PathBuf, &Path);
impl_cmp!(<'a> Cow<'a, Path>, Path); impl_cmp!(Cow<'_, Path>, Path);
impl_cmp!(<'a, 'b> Cow<'a, Path>, &'b Path); impl_cmp!(Cow<'_, Path>, &Path);
impl_cmp!(<'a> Cow<'a, Path>, PathBuf); impl_cmp!(Cow<'_, Path>, PathBuf);
macro_rules! impl_cmp_os_str { macro_rules! impl_cmp_os_str {
(<$($life:lifetime),*> $lhs:ty, $rhs: ty) => { ($lhs:ty, $rhs: ty) => {
#[stable(feature = "cmp_path", since = "1.8.0")] #[stable(feature = "cmp_path", since = "1.8.0")]
impl<$($life),*> PartialEq<$rhs> for $lhs { impl PartialEq<$rhs> for $lhs {
#[inline] #[inline]
fn eq(&self, other: &$rhs) -> bool { fn eq(&self, other: &$rhs) -> bool {
<Path as PartialEq>::eq(self, other.as_ref()) <Path as PartialEq>::eq(self, other.as_ref())
@ -3893,7 +3893,7 @@ macro_rules! impl_cmp_os_str {
} }
#[stable(feature = "cmp_path", since = "1.8.0")] #[stable(feature = "cmp_path", since = "1.8.0")]
impl<$($life),*> PartialEq<$lhs> for $rhs { impl PartialEq<$lhs> for $rhs {
#[inline] #[inline]
fn eq(&self, other: &$lhs) -> bool { fn eq(&self, other: &$lhs) -> bool {
<Path as PartialEq>::eq(self.as_ref(), other) <Path as PartialEq>::eq(self.as_ref(), other)
@ -3901,7 +3901,7 @@ macro_rules! impl_cmp_os_str {
} }
#[stable(feature = "cmp_path", since = "1.8.0")] #[stable(feature = "cmp_path", since = "1.8.0")]
impl<$($life),*> PartialOrd<$rhs> for $lhs { impl PartialOrd<$rhs> for $lhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self, other.as_ref()) <Path as PartialOrd>::partial_cmp(self, other.as_ref())
@ -3909,7 +3909,7 @@ macro_rules! impl_cmp_os_str {
} }
#[stable(feature = "cmp_path", since = "1.8.0")] #[stable(feature = "cmp_path", since = "1.8.0")]
impl<$($life),*> PartialOrd<$lhs> for $rhs { impl PartialOrd<$lhs> for $rhs {
#[inline] #[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> { fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self.as_ref(), other) <Path as PartialOrd>::partial_cmp(self.as_ref(), other)
@ -3918,20 +3918,20 @@ macro_rules! impl_cmp_os_str {
}; };
} }
impl_cmp_os_str!(<> PathBuf, OsStr); impl_cmp_os_str!(PathBuf, OsStr);
impl_cmp_os_str!(<'a> PathBuf, &'a OsStr); impl_cmp_os_str!(PathBuf, &OsStr);
impl_cmp_os_str!(<'a> PathBuf, Cow<'a, OsStr>); impl_cmp_os_str!(PathBuf, Cow<'_, OsStr>);
impl_cmp_os_str!(<> PathBuf, OsString); impl_cmp_os_str!(PathBuf, OsString);
impl_cmp_os_str!(<> Path, OsStr); impl_cmp_os_str!(Path, OsStr);
impl_cmp_os_str!(<'a> Path, &'a OsStr); impl_cmp_os_str!(Path, &OsStr);
impl_cmp_os_str!(<'a> Path, Cow<'a, OsStr>); impl_cmp_os_str!(Path, Cow<'_, OsStr>);
impl_cmp_os_str!(<> Path, OsString); impl_cmp_os_str!(Path, OsString);
impl_cmp_os_str!(<'a> &'a Path, OsStr); impl_cmp_os_str!(&Path, OsStr);
impl_cmp_os_str!(<'a, 'b> &'a Path, Cow<'b, OsStr>); impl_cmp_os_str!(&Path, Cow<'_, OsStr>);
impl_cmp_os_str!(<'a> &'a Path, OsString); impl_cmp_os_str!(&Path, OsString);
impl_cmp_os_str!(<'a> Cow<'a, Path>, OsStr); impl_cmp_os_str!(Cow<'_, Path>, OsStr);
impl_cmp_os_str!(<'a, 'b> Cow<'a, Path>, &'b OsStr); impl_cmp_os_str!(Cow<'_, Path>, &OsStr);
impl_cmp_os_str!(<'a> Cow<'a, Path>, OsString); impl_cmp_os_str!(Cow<'_, Path>, OsString);
#[stable(since = "1.7.0", feature = "strip_prefix")] #[stable(since = "1.7.0", feature = "strip_prefix")]
impl fmt::Display for StripPrefixError { impl fmt::Display for StripPrefixError {

@ -1 +1 @@
Subproject commit 0081ca6631abdfa02bf42bc85aaf507b8a0e6beb Subproject commit efdd0a7290c22f5438d7c5380105d353ee3e8518

View file

@ -71,7 +71,9 @@ impl NewPermission {
access: None, access: None,
protector: None, protector: None,
} }
} else if pointee.is_unpin(*cx.tcx, cx.typing_env()) { } else if pointee.is_unpin(*cx.tcx, cx.typing_env())
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env())
{
// A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`. // A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`.
NewPermission::Uniform { NewPermission::Uniform {
perm: Permission::Unique, perm: Permission::Unique,
@ -129,7 +131,9 @@ impl NewPermission {
fn from_box_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self { fn from_box_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self {
// `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling). // `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
let pointee = ty.builtin_deref(true).unwrap(); let pointee = ty.builtin_deref(true).unwrap();
if pointee.is_unpin(*cx.tcx, cx.typing_env()) { if pointee.is_unpin(*cx.tcx, cx.typing_env())
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env())
{
// A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only // A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
// a weak protector). // a weak protector).
NewPermission::Uniform { NewPermission::Uniform {

View file

@ -133,7 +133,8 @@ impl<'tcx> NewPermission {
retag_kind: RetagKind, retag_kind: RetagKind,
cx: &crate::MiriInterpCx<'tcx>, cx: &crate::MiriInterpCx<'tcx>,
) -> Option<Self> { ) -> Option<Self> {
let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env()); let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env())
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env());
let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
let is_protected = retag_kind == RetagKind::FnEntry; let is_protected = retag_kind == RetagKind::FnEntry;

View file

@ -9,8 +9,36 @@ fn mutate(x: &UnsafePinned<i32>) {
unsafe { ptr.write(42) }; unsafe { ptr.write(42) };
} }
fn main() { fn mut_alias(x: &mut UnsafePinned<i32>, y: &mut UnsafePinned<i32>) {
let x = UnsafePinned::new(0); unsafe {
mutate(&x); x.get().write(0);
assert_eq!(x.into_inner(), 42); y.get().write(0);
x.get().write(0);
y.get().write(0);
}
}
// Also try this with a type for which we implement `Unpin`, just to be extra mean.
struct MyUnsafePinned<T>(UnsafePinned<T>);
impl<T> Unpin for MyUnsafePinned<T> {}
fn my_mut_alias(x: &mut MyUnsafePinned<i32>, y: &mut MyUnsafePinned<i32>) {
unsafe {
x.0.get().write(0);
y.0.get().write(0);
x.0.get().write(0);
y.0.get().write(0);
}
}
fn main() {
let mut x = UnsafePinned::new(0i32);
mutate(&x);
assert_eq!(unsafe { x.get().read() }, 42);
let ptr = &raw mut x;
unsafe { mut_alias(&mut *ptr, &mut *ptr) };
let ptr = ptr.cast::<MyUnsafePinned<i32>>();
unsafe { my_mut_alias(&mut *ptr, &mut *ptr) };
} }

View file

@ -4,7 +4,7 @@
#![crate_type = "rlib"] #![crate_type = "rlib"]
// CHECK-LABEL: align_offset_byte_ptr // CHECK-LABEL: align_offset_byte_ptr
// CHECK: leaq 31 // CHECK: leaq {{31|28}}
// CHECK: andq $-32 // CHECK: andq $-32
// CHECK: subq // CHECK: subq
#[no_mangle] #[no_mangle]
@ -13,7 +13,7 @@ pub fn align_offset_byte_ptr(ptr: *const u8) -> usize {
} }
// CHECK-LABEL: align_offset_byte_slice // CHECK-LABEL: align_offset_byte_slice
// CHECK: leaq 31 // CHECK: leaq {{31|28}}
// CHECK: andq $-32 // CHECK: andq $-32
// CHECK: subq // CHECK: subq
#[no_mangle] #[no_mangle]
@ -22,7 +22,7 @@ pub fn align_offset_byte_slice(slice: &[u8]) -> usize {
} }
// CHECK-LABEL: align_offset_word_ptr // CHECK-LABEL: align_offset_word_ptr
// CHECK: leaq 31 // CHECK: leaq {{31|28}}
// CHECK: andq $-32 // CHECK: andq $-32
// CHECK: subq // CHECK: subq
// CHECK: shrq // CHECK: shrq
@ -35,7 +35,7 @@ pub fn align_offset_word_ptr(ptr: *const u32) -> usize {
} }
// CHECK-LABEL: align_offset_word_slice // CHECK-LABEL: align_offset_word_slice
// CHECK: leaq 31 // CHECK: leaq {{31|28}}
// CHECK: andq $-32 // CHECK: andq $-32
// CHECK: subq // CHECK: subq
// CHECK: shrq // CHECK: shrq

View file

@ -76,6 +76,9 @@ pub trait BikeshedGuaranteedNoDrop {}
#[lang = "freeze"] #[lang = "freeze"]
pub unsafe auto trait Freeze {} pub unsafe auto trait Freeze {}
#[lang = "unsafe_unpin"]
pub unsafe auto trait UnsafeUnpin {}
#[lang = "unpin"] #[lang = "unpin"]
#[diagnostic::on_unimplemented( #[diagnostic::on_unimplemented(
note = "consider using the `pin!` macro\nconsider using `Box::pin` if you need to access the pinned value outside of the current scope", note = "consider using the `pin!` macro\nconsider using `Box::pin` if you need to access the pinned value outside of the current scope",

View file

@ -24,7 +24,7 @@ pub fn align_to4(x: &[u8]) -> bool {
#[no_mangle] #[no_mangle]
pub fn align_offset_byte_ptr(ptr: *const u8) -> usize { pub fn align_offset_byte_ptr(ptr: *const u8) -> usize {
// CHECK: %[[ADDR:.+]] = ptrtoint ptr %ptr to [[USIZE:i[0-9]+]] // CHECK: %[[ADDR:.+]] = ptrtoint ptr %ptr to [[USIZE:i[0-9]+]]
// CHECK: %[[UP:.+]] = add [[USIZE]] %[[ADDR]], 31 // CHECK: %[[UP:.+]] = add [[USIZE]] %[[ADDR]], {{31|28}}
// CHECK: %[[ALIGNED:.+]] = and [[USIZE]] %[[UP]], -32 // CHECK: %[[ALIGNED:.+]] = and [[USIZE]] %[[UP]], -32
// CHECK: %[[OFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]] // CHECK: %[[OFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]]
@ -41,7 +41,7 @@ pub fn align_offset_byte_ptr(ptr: *const u8) -> usize {
#[no_mangle] #[no_mangle]
pub fn align_offset_word_slice(slice: &[Align4]) -> usize { pub fn align_offset_word_slice(slice: &[Align4]) -> usize {
// CHECK: %[[ADDR:.+]] = ptrtoint ptr %slice.0 to [[USIZE]] // CHECK: %[[ADDR:.+]] = ptrtoint ptr %slice.0 to [[USIZE]]
// CHECK: %[[UP:.+]] = add [[USIZE]] %[[ADDR]], 31 // CHECK: %[[UP:.+]] = add [[USIZE]] %[[ADDR]], {{31|28}}
// CHECK: %[[ALIGNED:.+]] = and [[USIZE]] %[[UP]], -32 // CHECK: %[[ALIGNED:.+]] = and [[USIZE]] %[[UP]], -32
// CHECK: %[[BOFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]] // CHECK: %[[BOFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]]
// CHECK: %[[OFFSET:.+]] = lshr exact [[USIZE]] %[[BOFFSET]], 2 // CHECK: %[[OFFSET:.+]] = lshr exact [[USIZE]] %[[BOFFSET]], 2
@ -57,7 +57,7 @@ pub fn align_offset_word_slice(slice: &[Align4]) -> usize {
#[no_mangle] #[no_mangle]
pub fn align_offset_word_ptr(ptr: *const Align4) -> usize { pub fn align_offset_word_ptr(ptr: *const Align4) -> usize {
// CHECK: %[[ADDR:.+]] = ptrtoint ptr %ptr to [[USIZE]] // CHECK: %[[ADDR:.+]] = ptrtoint ptr %ptr to [[USIZE]]
// CHECK: %[[UP:.+]] = add [[USIZE]] %[[ADDR]], 31 // CHECK: %[[UP:.+]] = add [[USIZE]] %[[ADDR]], {{31|28}}
// CHECK: %[[ALIGNED:.+]] = and [[USIZE]] %[[UP]], -32 // CHECK: %[[ALIGNED:.+]] = and [[USIZE]] %[[UP]], -32
// CHECK: %[[BOFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]] // CHECK: %[[BOFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]]

View file

@ -1,9 +1,9 @@
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes
#![crate_type = "lib"] #![crate_type = "lib"]
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![feature(allocator_api)] #![feature(allocator_api, unsafe_unpin)]
use std::marker::PhantomPinned; use std::marker::{PhantomPinned, UnsafeUnpin};
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::num::NonZero; use std::num::NonZero;
use std::ptr::NonNull; use std::ptr::NonNull;
@ -259,11 +259,21 @@ pub fn trait_raw(_: *const dyn Drop) {}
// CHECK: @trait_box(ptr noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) // CHECK: @trait_box(ptr noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle] #[no_mangle]
pub fn trait_box(_: Box<dyn Drop + Unpin>) {} pub fn trait_box(_: Box<dyn Drop + Unpin + UnsafeUnpin>) {}
// Ensure that removing *either* `Unpin` or `UnsafeUnpin` is enough to lose the attribute.
// CHECK: @trait_box_pin1(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box_pin1(_: Box<dyn Drop + Unpin>) {}
// CHECK: @trait_box_pin2(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box_pin2(_: Box<dyn Drop + UnsafeUnpin>) {}
// CHECK: { ptr, ptr } @trait_option(ptr noalias noundef align 1 %x.0, ptr %x.1) // CHECK: { ptr, ptr } @trait_option(ptr noalias noundef align 1 %x.0, ptr %x.1)
#[no_mangle] #[no_mangle]
pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + Unpin>> { pub fn trait_option(
x: Option<Box<dyn Drop + Unpin + UnsafeUnpin>>,
) -> Option<Box<dyn Drop + Unpin + UnsafeUnpin>> {
x x
} }

View file

@ -0,0 +1,90 @@
//@ compile-flags: -Copt-level=3
//@ min-llvm-version: 21
#![crate_type = "lib"]
use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
macro_rules! tests {
($range_ty:ty, $get_func_name:ident, $index_func_name:ident) => {
#[no_mangle]
pub fn $get_func_name(slice: &[u32], range: $range_ty) -> Option<&[u32]> {
slice.get(range)
}
#[no_mangle]
pub fn $index_func_name(slice: &[u32], range: $range_ty) -> &[u32] {
&slice[range]
}
};
}
// 2 comparisons required:
// end <= len && start <= end
// CHECK-LABEL: @get_range
// CHECK-COUNT-2: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range
// CHECK-COUNT-2: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(Range<usize>, get_range, index_range);
// 2 comparisons required:
// end < len && start <= end + 1
// CHECK-LABEL: @get_range_inclusive
// CHECK-COUNT-2: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_inclusive
// CHECK-COUNT-2: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeInclusive<usize>, get_range_inclusive, index_range_inclusive);
// 1 comparison required:
// end <= len
// CHECK-LABEL: @get_range_to
// CHECK-COUNT-1: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_to
// CHECK-COUNT-1: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeTo<usize>, get_range_to, index_range_to);
// 1 comparison required:
// end < len
// CHECK-LABEL: @get_range_to_inclusive
// CHECK-COUNT-1: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_to_inclusive
// CHECK-COUNT-1: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeToInclusive<usize>, get_range_to_inclusive, index_range_to_inclusive);
// 1 comparison required:
// start <= len
// CHECK-LABEL: @get_range_from
// CHECK-COUNT-1: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_from
// CHECK-COUNT-1: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeFrom<usize>, get_range_from, index_range_from);

View file

@ -0,0 +1,94 @@
//@ compile-flags: -Copt-level=3
//@ min-llvm-version: 21
#![crate_type = "lib"]
use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
macro_rules! tests {
($range_ty:ty, $get_func_name:ident, $index_func_name:ident) => {
#[no_mangle]
pub fn $get_func_name(slice: &str, range: $range_ty) -> Option<&str> {
slice.get(range)
}
#[no_mangle]
pub fn $index_func_name(slice: &str, range: $range_ty) -> &str {
&slice[range]
}
};
}
// 9 comparisons required:
// start <= end
// && (start == 0 || (start >= len && start == len) || bytes[start] >= -0x40)
// && (end == 0 || (end >= len && end == len) || bytes[end] >= -0x40)
// CHECK-LABEL: @get_range
// CHECK-COUNT-9: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range
// CHECK-COUNT-9: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(Range<usize>, get_range, index_range);
// 7 comparisons required:
// end < len && start <= end + 1
// && (start == 0 || start >= len || bytes[start] >= -0x40)
// && ( end + 1 >= len || bytes[end + 1] >= -0x40)
// CHECK-LABEL: @get_range_inclusive
// CHECK-COUNT-7: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_inclusive
// CHECK-COUNT-7: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeInclusive<usize>, get_range_inclusive, index_range_inclusive);
// 4 comparisons required:
// end == 0 || (end >= len && end == len) || bytes[end] >= -0x40
// CHECK-LABEL: @get_range_to
// CHECK-COUNT-4: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_to
// CHECK-COUNT-4: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeTo<usize>, get_range_to, index_range_to);
// 3 comparisons required:
// end < len && (end + 1 >= len || bytes[end + 1] >= -0x40)
// CHECK-LABEL: @get_range_to_inclusive
// CHECK-COUNT-3: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_to_inclusive
// CHECK-COUNT-3: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeToInclusive<usize>, get_range_to_inclusive, index_range_to_inclusive);
// 4 comparisons required:
// start == 0 || (start >= len && start == len) || bytes[start] >= -0x40)
// CHECK-LABEL: @get_range_from
// CHECK-COUNT-4: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
// CHECK-LABEL: @index_range_from
// CHECK-COUNT-4: %{{.+}} = icmp
// CHECK-NOT: %{{.+}} = icmp
// CHECK: ret
tests!(RangeFrom<usize>, get_range_from, index_range_from);

View file

@ -20,7 +20,8 @@ where
//@ has - '//h3[@class="code-header"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send' //@ has - '//h3[@class="code-header"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send'
//@ has - '//h3[@class="code-header"]' 'impl<B> Sync for Switch<B>where <B as Signal>::Item: Sync' //@ has - '//h3[@class="code-header"]' 'impl<B> Sync for Switch<B>where <B as Signal>::Item: Sync'
//@ count - '//*[@id="implementations-list"]//*[@class="impl"]' 0 //@ count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 6 //@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 7
// The number here will need updating when new auto traits are added: ^
pub struct Switch<B: Signal> { pub struct Switch<B: Signal> {
pub inner: <B as Signal2>::Item2, pub inner: <B as Signal2>::Item2,
} }

View file

@ -1,13 +1,15 @@
#![crate_name = "foo"] #![crate_name = "foo"]
#![feature(negative_impls, freeze_impls, freeze)] #![feature(negative_impls, freeze_impls, freeze, unsafe_unpin)]
pub struct Foo; pub struct Foo;
//@ has foo/struct.Foo.html //@ has foo/struct.Foo.html
//@ !hasraw - 'Auto Trait Implementations' //@ !hasraw - 'Auto Trait Implementations'
// Manually un-implement all auto traits for Foo:
impl !Send for Foo {} impl !Send for Foo {}
impl !Sync for Foo {} impl !Sync for Foo {}
impl !std::marker::Freeze for Foo {} impl !std::marker::Freeze for Foo {}
impl !std::marker::UnsafeUnpin for Foo {}
impl !std::marker::Unpin for Foo {} impl !std::marker::Unpin for Foo {}
impl !std::panic::RefUnwindSafe for Foo {} impl !std::panic::RefUnwindSafe for Foo {}
impl !std::panic::UnwindSafe for Foo {} impl !std::panic::UnwindSafe for Foo {}

View file

@ -2,7 +2,8 @@
//@ has - '//h3[@class="code-header"]' 'impl<T> Send for Foo<T>where T: Send' //@ has - '//h3[@class="code-header"]' 'impl<T> Send for Foo<T>where T: Send'
//@ has - '//h3[@class="code-header"]' 'impl<T> Sync for Foo<T>where T: Sync' //@ has - '//h3[@class="code-header"]' 'impl<T> Sync for Foo<T>where T: Sync'
//@ count - '//*[@id="implementations-list"]//*[@class="impl"]' 0 //@ count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 6 //@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 7
// The number here will need updating when new auto traits are added: ^
pub struct Foo<T> { pub struct Foo<T> {
field: T, field: T,
} }

View file

@ -6,7 +6,8 @@
// 'impl<T> Send for Foo<T>' // 'impl<T> Send for Foo<T>'
// //
//@ count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1 //@ count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5 //@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 6
// The number here will need updating when new auto traits are added: ^
pub struct Foo<T> { pub struct Foo<T> {
field: T, field: T,
} }

View file

@ -8,8 +8,11 @@ LL | if String::from("a") == "a".try_into().unwrap() {}
| |
= note: multiple `impl`s satisfying `String: PartialEq<_>` found in the following crates: `alloc`, `std`: = note: multiple `impl`s satisfying `String: PartialEq<_>` found in the following crates: `alloc`, `std`:
- impl PartialEq for String; - impl PartialEq for String;
- impl PartialEq<ByteStr> for String;
- impl PartialEq<ByteString> for String;
- impl PartialEq<Path> for String; - impl PartialEq<Path> for String;
- impl PartialEq<PathBuf> for String; - impl PartialEq<PathBuf> for String;
- impl PartialEq<str> for String;
help: try using a fully qualified path to specify the expected types help: try using a fully qualified path to specify the expected types
| |
LL - if String::from("a") == "a".try_into().unwrap() {} LL - if String::from("a") == "a".try_into().unwrap() {}

View file

@ -1,4 +1,4 @@
thread 'main' ($TID) panicked at $DIR/const-eval-select-backtrace-std.rs:6:8: thread 'main' ($TID) panicked at $DIR/const-eval-select-backtrace-std.rs:6:8:
byte index 1 is out of bounds of `` start byte index 1 is out of bounds of ``
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace