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:
commit
7bee525095
69 changed files with 2313 additions and 1886 deletions
|
|
@ -18,6 +18,7 @@
|
|||
],
|
||||
"ignorePaths": [
|
||||
"src/intrinsic/archs.rs",
|
||||
"src/intrinsic/old_archs.rs",
|
||||
"src/intrinsic/llvm.rs"
|
||||
],
|
||||
"ignoreRegExpList": [
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@ jobs:
|
|||
git config --global user.name "User"
|
||||
./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
|
||||
run: |
|
||||
./y.sh test --release --clean --build-sysroot ${{ matrix.commands }}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ jobs:
|
|||
run: |
|
||||
./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
|
||||
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
|
||||
|
||||
- name: Build
|
||||
|
|
|
|||
|
|
@ -56,18 +56,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gccjit"
|
||||
version = "3.1.1"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff80f4d6d0749eab3a69122210b3a1fdd52edb6162781aadd7c4842e26983683"
|
||||
checksum = "26b73d18b642ce16378af78f89664841d7eeafa113682ff5d14573424eb0232a"
|
||||
dependencies = [
|
||||
"gccjit_sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gccjit_sys"
|
||||
version = "1.1.2"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f81d901767ddba371a619fa9bba657066a4d3c5607ee69bbb557c1c5ba9bf85"
|
||||
checksum = "ee689456c013616942d5aef9a84d613cefcc3b335340d036f3650fc1a7459e15"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ default = ["master"]
|
|||
[dependencies]
|
||||
object = { version = "0.37.0", default-features = false, features = ["std", "read"] }
|
||||
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"] }
|
||||
|
||||
# Local copy.
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ The default configuration (see below in the [Quick start](#quick-start) section)
|
|||
./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.
|
||||
|
||||
### 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 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 build --sysroot
|
||||
```
|
||||
To check if all is working correctly, run:
|
||||
To check if all is working correctly, run:
|
||||
|
||||
```bash
|
||||
$ ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ seh = "seh"
|
|||
typ = "typ"
|
||||
|
||||
[files]
|
||||
extend-exclude = ["src/intrinsic/archs.rs"]
|
||||
extend-exclude = ["src/intrinsic/archs.rs", "src/intrinsic/old_archs.rs"]
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
if config.target.ends_with(".json") {
|
||||
args.push(&"-Zjson-target-spec");
|
||||
}
|
||||
|
||||
for feature in &config.features {
|
||||
args.push(&"--features");
|
||||
args.push(feature);
|
||||
|
|
|
|||
|
|
@ -679,10 +679,10 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
create_dir(projects_path)?;
|
||||
|
||||
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
|
||||
// one test.
|
||||
let count = projects.len() / nb_parts + 1;
|
||||
let count = count + 1;
|
||||
let current_part = args.current_part.unwrap();
|
||||
let start = current_part * count;
|
||||
// We remove the projects we don't want to test.
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
## 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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
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
|
||||
- /* { dg-do compile { target x86_64-*-* } } */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
`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
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0081ca6631abdfa02bf42bc85aaf507b8a0e6beb
|
||||
efdd0a7290c22f5438d7c5380105d353ee3e8518
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2025-12-20"
|
||||
channel = "nightly-2026-02-14"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
||||
|
|
|
|||
|
|
@ -575,9 +575,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
}
|
||||
if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) {
|
||||
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
|
||||
let builtin_unreachable: RValue<'gcc> =
|
||||
unsafe { std::mem::transmute(builtin_unreachable) };
|
||||
self.call(self.type_void(), None, None, builtin_unreachable, &[], None, None);
|
||||
self.llbb().add_eval(None, self.context.new_call(None, builtin_unreachable, &[]));
|
||||
}
|
||||
|
||||
// Write results to outputs.
|
||||
|
|
|
|||
|
|
@ -1495,6 +1495,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||
|
||||
#[cfg(not(feature = "master"))]
|
||||
fn extract_element(&mut self, vec: RValue<'gcc>, idx: RValue<'gcc>) -> RValue<'gcc> {
|
||||
use crate::context::new_array_type;
|
||||
|
||||
let vector_type = vec
|
||||
.get_type()
|
||||
.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 vec_num_units = vector_type.get_num_units();
|
||||
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();
|
||||
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
|
||||
// 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.
|
||||
let int_max = |signed: bool, int_width: u64| -> u128 {
|
||||
fn int_max(signed: bool, int_width: u64) -> u128 {
|
||||
let shift_amount = 128 - int_width;
|
||||
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 }
|
||||
};
|
||||
}
|
||||
|
||||
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 =
|
||||
ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
assert_eq!(rounded_min.status, Status::OK);
|
||||
ieee::Half::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
//assert_eq!(rounded_min.status, Status::OK);
|
||||
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());
|
||||
(rounded_min.value.to_bits(), rounded_max.value.to_bits())
|
||||
};
|
||||
let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) {
|
||||
let rounded_min =
|
||||
ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
fn compute_clamp_bounds<F: Float>(signed: bool, int_width: u64) -> (u128, u128) {
|
||||
let rounded_min = F::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
assert_eq!(rounded_min.status, Status::OK);
|
||||
let rounded_max =
|
||||
ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero);
|
||||
let rounded_max = F::from_u128_r(int_max(signed, int_width), Round::TowardZero);
|
||||
assert!(rounded_max.value.is_finite());
|
||||
(rounded_min.value.to_bits(), rounded_max.value.to_bits())
|
||||
};
|
||||
}
|
||||
// To implement saturation, we perform the following steps:
|
||||
//
|
||||
// 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 bits_llval = match float_width {
|
||||
16 => bx.cx().const_u16(bits as u16),
|
||||
32 => bx.cx().const_u32(bits as u32),
|
||||
64 => bx.cx().const_u64(bits as u64),
|
||||
128 => bx.cx().const_u128(bits),
|
||||
n => bug!("unsupported float width {}", n),
|
||||
};
|
||||
bx.bitcast(bits_llval, float_ty)
|
||||
};
|
||||
let (f_min, f_max) = match float_width {
|
||||
32 => compute_clamp_bounds_single(signed, int_width),
|
||||
64 => compute_clamp_bounds_double(signed, int_width),
|
||||
16 => compute_clamp_bounds_half(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),
|
||||
};
|
||||
let f_min = float_bits_to_llval(self, f_min);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_middle::mir::Mutability;
|
|||
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, PointerArithmetic, Scalar};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
|
||||
use crate::context::CodegenCx;
|
||||
use crate::context::{CodegenCx, new_array_type};
|
||||
use crate::type_of::LayoutGccExt;
|
||||
|
||||
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||
|
|
@ -20,6 +20,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
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> {
|
||||
// TODO(antoyo): handle non-null-terminated strings.
|
||||
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 => {
|
||||
let context = &cx.context;
|
||||
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
|
||||
.chunks_exact(8)
|
||||
.map(|arr| {
|
||||
|
|
@ -76,7 +80,7 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) ->
|
|||
4 => {
|
||||
let context = &cx.context;
|
||||
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
|
||||
.chunks_exact(4)
|
||||
.map(|arr| {
|
||||
|
|
@ -95,7 +99,7 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) ->
|
|||
_ => {
|
||||
let context = cx.context;
|
||||
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
|
||||
.iter()
|
||||
.map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32))
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ use rustc_middle::ty::layout::{
|
|||
};
|
||||
use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
#[cfg(feature = "master")]
|
||||
use rustc_session::config::DebugInfo;
|
||||
use rustc_span::source_map::respan;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi};
|
||||
|
|
@ -145,6 +147,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
supports_f64_type: bool,
|
||||
supports_f128_type: bool,
|
||||
) -> 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 layout = tcx
|
||||
.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
|
||||
// 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 u128_type = context.new_array_type(None, u64_type, 2)/*.get_aligned(u128_align)*/;
|
||||
let i128_type = new_array_type(context, None, i64_type, 2)/*.get_aligned(i128_align)*/;
|
||||
let u128_type = new_array_type(context, None, u64_type, 2)/*.get_aligned(u128_align)*/;
|
||||
(i128_type, u128_type)
|
||||
};
|
||||
|
||||
|
|
@ -601,3 +608,17 @@ fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
|
|||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -942,7 +942,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
fn float_to_int_cast(
|
||||
&self,
|
||||
signed: bool,
|
||||
value: RValue<'gcc>,
|
||||
mut value: RValue<'gcc>,
|
||||
dest_typ: Type<'gcc>,
|
||||
) -> RValue<'gcc> {
|
||||
let value_type = value.get_type();
|
||||
|
|
@ -951,16 +951,22 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
}
|
||||
|
||||
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) {
|
||||
// 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::FP128 => "tfti",
|
||||
// cSpell:enable
|
||||
kind => panic!("cannot cast a {:?} to non-native integer", kind),
|
||||
};
|
||||
let sign = if signed { "" } else { "uns" };
|
||||
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(
|
||||
None,
|
||||
FunctionType::Extern,
|
||||
|
|
@ -969,6 +975,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||
func_name,
|
||||
false,
|
||||
);
|
||||
if let Some(dest_type) = dest_type {
|
||||
value = self.context.new_cast(None, value, dest_type);
|
||||
}
|
||||
self.context.new_call(None, func, &[value])
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@ use gccjit::{CType, Context, Field, Function, FunctionPtrType, RValue, ToRValue,
|
|||
use rustc_codegen_ssa::traits::BuilderMethods;
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::context::CodegenCx;
|
||||
use crate::context::{CodegenCx, new_array_type};
|
||||
|
||||
fn encode_key_128_type<'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" => {
|
||||
let mut new_args = args.to_vec();
|
||||
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");
|
||||
new_args.push(result.get_address(None));
|
||||
args = new_args.into();
|
||||
|
|
@ -593,7 +593,7 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(
|
|||
"__builtin_ia32_encodekey256_u32" => {
|
||||
let mut new_args = args.to_vec();
|
||||
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");
|
||||
new_args.push(result.get_address(None));
|
||||
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 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");
|
||||
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);
|
||||
let field2 = result.access_field(None, field2);
|
||||
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 field2_ptr =
|
||||
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);
|
||||
let field2 = result.access_field(None, field2);
|
||||
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 field2_ptr =
|
||||
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);
|
||||
let field2 = result.access_field(None, field2);
|
||||
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 field2_ptr =
|
||||
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",
|
||||
// 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",
|
||||
// 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.pmulu.dq.512" => "__builtin_ia32_pmuludq512_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
|
||||
}
|
||||
|
||||
#[cfg(feature = "master")]
|
||||
include!("old_archs.rs");
|
||||
#[cfg(feature = "master")]
|
||||
include!("archs.rs");
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ fn get_simple_function_f128<'gcc, 'tcx>(
|
|||
let f128_type = cx.type_f128();
|
||||
let func_name = match name {
|
||||
sym::ceilf128 => "ceilf128",
|
||||
sym::fabsf128 => "fabsf128",
|
||||
sym::floorf128 => "floorf128",
|
||||
sym::truncf128 => "truncf128",
|
||||
sym::roundf128 => "roundf128",
|
||||
|
|
@ -262,6 +263,7 @@ fn f16_builtin<'gcc, 'tcx>(
|
|||
let builtin_name = match name {
|
||||
sym::ceilf16 => "__builtin_ceilf",
|
||||
sym::copysignf16 => "__builtin_copysignf",
|
||||
sym::fabsf16 => "fabsf",
|
||||
sym::floorf16 => "__builtin_floorf",
|
||||
sym::fmaf16 => "fmaf",
|
||||
sym::maxnumf16 => "__builtin_fmaxf",
|
||||
|
|
@ -328,6 +330,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
}
|
||||
sym::ceilf16
|
||||
| sym::copysignf16
|
||||
| sym::fabsf16
|
||||
| sym::floorf16
|
||||
| sym::fmaf16
|
||||
| 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_ty = fn_ptr.get_type();
|
||||
|
||||
let mut llargs = vec![];
|
||||
let mut call_args = vec![];
|
||||
|
||||
for arg in args {
|
||||
match arg.val {
|
||||
OperandValue::ZeroSized => {}
|
||||
OperandValue::Immediate(_) => llargs.push(arg.immediate()),
|
||||
OperandValue::Immediate(_) => call_args.push(arg.immediate()),
|
||||
OperandValue::Pair(a, b) => {
|
||||
llargs.push(a);
|
||||
llargs.push(b);
|
||||
call_args.push(a);
|
||||
call_args.push(b);
|
||||
}
|
||||
OperandValue::Ref(op_place_val) => {
|
||||
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`.
|
||||
llval = self.to_immediate_scalar(llval, scalar);
|
||||
}
|
||||
llargs.push(llval);
|
||||
call_args.push(llval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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> {
|
||||
unimplemented!();
|
||||
// TODO(antoyo): implement.
|
||||
self.context.new_rvalue_from_int(self.int_type, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
1390
compiler/rustc_codegen_gcc/src/intrinsic/old_archs.rs
Normal file
1390
compiler/rustc_codegen_gcc/src/intrinsic/old_archs.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -234,6 +234,8 @@ impl CodegenBackend for GccCodegenBackend {
|
|||
|
||||
#[cfg(feature = "master")]
|
||||
{
|
||||
gccjit::set_lang_name(c"GNU Rust");
|
||||
|
||||
let target_cpu = target_cpu(sess);
|
||||
|
||||
// Get the second TargetInfo with the correct CPU features by setting the arch.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_middle::ty::layout::TyAndLayout;
|
|||
use rustc_middle::{bug, ty};
|
||||
|
||||
use crate::common::TypeReflection;
|
||||
use crate::context::CodegenCx;
|
||||
use crate::context::{CodegenCx, new_array_type};
|
||||
use crate::type_of::LayoutGccExt;
|
||||
|
||||
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||
|
|
@ -311,7 +311,7 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
|
|||
len = 0;
|
||||
}
|
||||
|
||||
self.context.new_array_type(None, ty, len)
|
||||
new_array_type(self.context, None, ty, len)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ tests/run-make/foreign-exceptions/
|
|||
tests/run-make/glibc-staticlib-args/
|
||||
tests/run-make/lto-smoke-c/
|
||||
tests/run-make/return-non-c-like-enum/
|
||||
|
||||
tests/run-make/short-ice
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
tests/ui/simd/intrinsic/splat.rs
|
||||
|
|
@ -89,11 +89,13 @@ tests/ui/thir-print/offset_of.rs
|
|||
tests/ui/iterators/rangefrom-overflow-debug.rs
|
||||
tests/ui/iterators/rangefrom-overflow-overflow-checks.rs
|
||||
tests/ui/iterators/iter-filter-count-debug-check.rs
|
||||
tests/ui/eii/codegen_single_crate.rs
|
||||
tests/ui/eii/codegen_cross_crate.rs
|
||||
tests/ui/eii/linking/codegen_single_crate.rs
|
||||
tests/ui/eii/linking/codegen_cross_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/same-symbol.rs
|
||||
tests/ui/eii/linking/same-symbol.rs
|
||||
tests/ui/eii/privacy1.rs
|
||||
tests/ui/eii/default/call_impl.rs
|
||||
tests/ui/c-variadic/copy.rs
|
||||
tests/ui/asm/x86_64/global_asm_escape.rs
|
||||
|
|
|
|||
38
compiler/rustc_codegen_gcc/tests/run/call-llvm-intrinsics.rs
Normal file
38
compiler/rustc_codegen_gcc/tests/run/call-llvm-intrinsics.rs
Normal 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();
|
||||
}
|
||||
102
compiler/rustc_codegen_gcc/tests/run/simd-ffi.rs
Normal file
102
compiler/rustc_codegen_gcc/tests/run/simd-ffi.rs
Normal 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() {}
|
||||
27
compiler/rustc_codegen_gcc/tests/run/unreachable-function.rs
Normal file
27
compiler/rustc_codegen_gcc/tests/run/unreachable-function.rs
Normal 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);
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ def run_command(command, cwd=None):
|
|||
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):
|
||||
while True:
|
||||
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
|
||||
elif choice.lower() == "y":
|
||||
print("Updating repository...")
|
||||
run_command(["git", "pull", "origin", branch], cwd=path)
|
||||
run_command(["git", "pull", "origin", "main"], cwd=path)
|
||||
return
|
||||
else:
|
||||
print("Didn't understand answer...")
|
||||
print("Cloning {} repository...".format(repo_name))
|
||||
if sub_paths is None:
|
||||
run_command(["git", "clone", repo_url, "--depth", "1", path])
|
||||
else:
|
||||
run_command(["git", "clone", repo_url, "--filter=tree:0", "--no-checkout", 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)
|
||||
run_command(["git", "clone", repo_url, "--filter=tree:0", "--no-checkout", 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):
|
||||
|
|
@ -45,121 +42,36 @@ def convert_to_string(content):
|
|||
return content
|
||||
|
||||
|
||||
def extract_intrinsics_from_llvm(llvm_path, intrinsics):
|
||||
command = ["llvm-tblgen", "llvm/IR/Intrinsics.td"]
|
||||
def extract_intrinsics_from_llvm(llvm_path):
|
||||
intrinsics = {}
|
||||
command = ["llvm-tblgen", "llvm/IR/Intrinsics.td", "--dump-json"]
|
||||
cwd = os.path.join(llvm_path, "llvm/include")
|
||||
print("=> Running command `{}` from `{}`".format(command, cwd))
|
||||
p = subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE)
|
||||
output, err = p.communicate()
|
||||
lines = convert_to_string(output).splitlines()
|
||||
pos = 0
|
||||
while pos < len(lines):
|
||||
line = lines[pos]
|
||||
if not line.startswith("def "):
|
||||
pos += 1
|
||||
content = json.loads(convert_to_string(output))
|
||||
for intrinsic in content:
|
||||
data = content[intrinsic]
|
||||
if not isinstance(data, dict):
|
||||
continue
|
||||
intrinsic = line.split(" ")[1].strip()
|
||||
content = line
|
||||
while pos < len(lines):
|
||||
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.
|
||||
current_arch = data.get("TargetPrefix")
|
||||
builtin_name = data.get("ClangBuiltinName")
|
||||
if current_arch is None or current_arch == "" or builtin_name is None:
|
||||
continue
|
||||
if it["kind"] != "function":
|
||||
# We're only looking for functions.
|
||||
intrinsic = intrinsic.split("_")
|
||||
if len(intrinsic) < 2 or intrinsic[0] != "int":
|
||||
continue
|
||||
# if len(it["path"]) == 2:
|
||||
# # This is a "general" intrinsic, not bound to a specific arch.
|
||||
# append_translation(json_data, p, general)
|
||||
# continue
|
||||
if len(it["path"]) != 3 or it["path"][1] not in archs:
|
||||
continue
|
||||
arch = it["path"][1]
|
||||
if arch not in intrinsics:
|
||||
intrinsics[arch] = []
|
||||
append_translation(json_data, p, intrinsics[arch])
|
||||
intrinsic[0] = "llvm"
|
||||
intrinsic = ".".join(intrinsic)
|
||||
if current_arch not in intrinsics:
|
||||
intrinsics[current_arch] = []
|
||||
append_intrinsic(intrinsics[current_arch], intrinsic, builtin_name)
|
||||
|
||||
return intrinsics
|
||||
|
||||
|
||||
def fill_intrinsics(intrinsics, from_intrinsics, all_intrinsics):
|
||||
for arch in from_intrinsics:
|
||||
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)
|
||||
def update_intrinsics(llvm_path):
|
||||
intrinsics = extract_intrinsics_from_llvm(llvm_path)
|
||||
|
||||
archs = [arch for arch in intrinsics]
|
||||
archs.sort()
|
||||
|
|
@ -173,33 +85,41 @@ def update_intrinsics(llvm_path, llvmint, llvmint2):
|
|||
# Since all intrinsic names start with "llvm.", we skip that prefix.
|
||||
print("Updating content of `{}`...".format(output_file))
|
||||
with open(output_file, "w", encoding="utf8") as out:
|
||||
out.write("// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`\n")
|
||||
out.write("// DO NOT EDIT IT!\n")
|
||||
out.write("/// Translate a given LLVM intrinsic name to an equivalent GCC one.\n")
|
||||
out.write("fn map_arch_intrinsic(full_name:&str)->&'static str{\n")
|
||||
out.write('let Some(name) = full_name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", full_name) };\n')
|
||||
out.write('let Some((arch, name)) = name.split_once(\'.\') else { unimplemented!("***** unsupported LLVM intrinsic {}", name) };\n')
|
||||
out.write("match arch {\n")
|
||||
out.write("""// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`
|
||||
// DO NOT EDIT IT!
|
||||
/// Translate a given LLVM intrinsic name to an equivalent GCC one.
|
||||
fn map_arch_intrinsic(full_name:&str)-> &'static str {
|
||||
let Some(name) = full_name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", full_name) };
|
||||
let Some((arch, name)) = name.split_once('.') else { unimplemented!("***** unsupported LLVM intrinsic llvm.{}", name) };
|
||||
let old_arch_res = old_archs(arch, name);
|
||||
if let ArchCheckResult::Ok(res) = old_arch_res {
|
||||
return res;
|
||||
}
|
||||
match arch {""")
|
||||
for arch in archs:
|
||||
if len(intrinsics[arch]) == 0:
|
||||
continue
|
||||
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))
|
||||
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))
|
||||
for entry in intrinsics[arch]:
|
||||
llvm_name = entry[0].removeprefix("llvm.");
|
||||
llvm_name = llvm_name.removeprefix(arch);
|
||||
llvm_name = llvm_name.removeprefix(".");
|
||||
if entry[2] is True: # if it is a duplicate
|
||||
out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
|
||||
elif "_round_mask" in entry[1]:
|
||||
if "_round_mask" in entry[1]:
|
||||
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
|
||||
else:
|
||||
out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1]))
|
||||
out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),\n')
|
||||
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}")
|
||||
subprocess.call(["rustfmt", output_file])
|
||||
print("Done!")
|
||||
|
|
@ -210,35 +130,21 @@ def main():
|
|||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"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.
|
||||
clone_repository(
|
||||
"llvm-project",
|
||||
llvm_path,
|
||||
"https://github.com/llvm/llvm-project",
|
||||
branch="main",
|
||||
sub_paths=["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"],
|
||||
["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"],
|
||||
)
|
||||
clone_repository(
|
||||
"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)
|
||||
update_intrinsics(llvm_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__":
|
||||
sys.exit(main())
|
||||
|
|
|
|||
|
|
@ -1693,6 +1693,10 @@ rustc_queries! {
|
|||
query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
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 is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `Unpin`", env.value }
|
||||
|
|
|
|||
|
|
@ -1046,9 +1046,11 @@ where
|
|||
hir::Mutability::Not => {
|
||||
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
|
||||
}
|
||||
hir::Mutability::Mut => {
|
||||
PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, typing_env) }
|
||||
}
|
||||
hir::Mutability::Mut => PointerKind::MutableRef {
|
||||
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 {
|
||||
|
|
@ -1143,7 +1145,9 @@ where
|
|||
debug_assert!(pointee.safe.is_none());
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
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),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
/// 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 {
|
||||
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.
|
||||
fn is_trivially_unpin(self) -> bool {
|
||||
match self.kind() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::io::Write;
|
||||
use std::iter;
|
||||
use std::ops::ControlFlow;
|
||||
use std::sync::Arc;
|
||||
|
||||
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.
|
||||
/// Only waiters in a query are visited.
|
||||
/// `visit` is called for every waiter and is passed a query waiting on `query_ref`
|
||||
/// and a span indicating the reason the query waited on `query_ref`.
|
||||
/// If `visit` returns Some, this function returns.
|
||||
/// `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`.
|
||||
/// 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 resumable waiters it returns Some(Some(Waiter)) which has the
|
||||
/// required information to resume the waiter.
|
||||
/// If all `visit` calls returns None, this function also returns None.
|
||||
fn visit_waiters<'tcx, F>(
|
||||
/// For visits of resumable waiters it returns information required to resume that waiter.
|
||||
fn visit_waiters<'tcx>(
|
||||
job_map: &QueryJobMap<'tcx>,
|
||||
query: QueryJobId,
|
||||
mut visit: F,
|
||||
) -> Option<Option<Waiter>>
|
||||
where
|
||||
F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
|
||||
{
|
||||
mut visit: impl FnMut(Span, QueryJobId) -> ControlFlow<Option<Waiter>>,
|
||||
) -> ControlFlow<Option<Waiter>> {
|
||||
// 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)
|
||||
&& let Some(cycle) = visit(job_map.span_of(query), parent)
|
||||
{
|
||||
return Some(cycle);
|
||||
if let Some(parent) = job_map.parent_of(query) {
|
||||
visit(job_map.span_of(query), parent)?;
|
||||
}
|
||||
|
||||
// Visit the explicit waiters which use condvars and are resumable
|
||||
if let Some(latch) = job_map.latch_of(query) {
|
||||
for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
|
||||
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 Some(Some((query, i)));
|
||||
}
|
||||
// Return a value which indicates that this waiter can be resumed
|
||||
visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Look for query cycles by doing a depth first search starting at `query`.
|
||||
|
|
@ -164,7 +157,7 @@ fn cycle_check<'tcx>(
|
|||
span: Span,
|
||||
stack: &mut Vec<(Span, QueryJobId)>,
|
||||
visited: &mut FxHashSet<QueryJobId>,
|
||||
) -> Option<Option<Waiter>> {
|
||||
) -> ControlFlow<Option<Waiter>> {
|
||||
if !visited.insert(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
|
||||
|
|
@ -173,9 +166,9 @@ fn cycle_check<'tcx>(
|
|||
stack.drain(0..p);
|
||||
// Replace the span for the first query with the cycle cause
|
||||
stack[0].0 = span;
|
||||
Some(None)
|
||||
ControlFlow::Break(None)
|
||||
} 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
|
||||
if r.is_none() {
|
||||
if r.is_continue() {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
|
|
@ -202,21 +195,18 @@ fn connected_to_root<'tcx>(
|
|||
job_map: &QueryJobMap<'tcx>,
|
||||
query: QueryJobId,
|
||||
visited: &mut FxHashSet<QueryJobId>,
|
||||
) -> bool {
|
||||
) -> ControlFlow<Option<Waiter>> {
|
||||
// We already visited this or we're deliberately ignoring it
|
||||
if !visited.insert(query) {
|
||||
return false;
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
// This query is connected to the root (it has no query parent), return true
|
||||
if job_map.parent_of(query).is_none() {
|
||||
return true;
|
||||
return ControlFlow::Break(None);
|
||||
}
|
||||
|
||||
visit_waiters(job_map, query, |_, successor| {
|
||||
connected_to_root(job_map, successor, visited).then_some(None)
|
||||
})
|
||||
.is_some()
|
||||
visit_waiters(job_map, query, |_, successor| connected_to_root(job_map, successor, visited))
|
||||
}
|
||||
|
||||
// Deterministically pick an query from a list
|
||||
|
|
@ -253,7 +243,7 @@ fn remove_cycle<'tcx>(
|
|||
let mut visited = FxHashSet::default();
|
||||
let mut stack = Vec::new();
|
||||
// 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)
|
||||
{
|
||||
// The stack is a vector of pairs of spans and queries; reverse it so that
|
||||
|
|
@ -284,15 +274,15 @@ fn remove_cycle<'tcx>(
|
|||
} else {
|
||||
let mut waiters = Vec::new();
|
||||
// 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
|
||||
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));
|
||||
}
|
||||
|
||||
None
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
if waiters.is_empty() {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -306,8 +306,12 @@ fn arg_attrs_for_rust_scalar<'tcx>(
|
|||
let kind = if let Some(kind) = pointee.safe {
|
||||
Some(kind)
|
||||
} 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.
|
||||
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 {
|
||||
None
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,36 +8,43 @@ use rustc_span::DUMMY_SP;
|
|||
use rustc_trait_selection::traits;
|
||||
|
||||
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>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
|
||||
) -> 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 {
|
||||
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 {
|
||||
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 {
|
||||
is_item_raw(tcx, query, LangItem::Unpin)
|
||||
is_trait_raw(tcx, query, LangItem::Unpin)
|
||||
}
|
||||
|
||||
fn is_async_drop_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
|
||||
) -> 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>,
|
||||
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
|
||||
item: LangItem,
|
||||
|
|
@ -53,6 +60,7 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
is_use_cloned_raw,
|
||||
is_sized_raw,
|
||||
is_freeze_raw,
|
||||
is_unsafe_unpin_raw,
|
||||
is_unpin_raw,
|
||||
is_async_drop_raw,
|
||||
..*providers
|
||||
|
|
|
|||
|
|
@ -477,9 +477,8 @@ impl PartialEq for ByteString {
|
|||
|
||||
macro_rules! impl_partial_eq_ord_cow {
|
||||
($lhs:ty, $rhs:ty) => {
|
||||
#[allow(unused_lifetimes)]
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
impl<'a> PartialEq<$rhs> for $lhs {
|
||||
impl PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
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")]
|
||||
impl<'a> PartialEq<$lhs> for $rhs {
|
||||
impl PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
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")]
|
||||
impl<'a> PartialOrd<$rhs> for $lhs {
|
||||
impl PartialOrd<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
|
||||
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")]
|
||||
impl<'a> PartialOrd<$lhs> for $rhs {
|
||||
impl PartialOrd<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
|
||||
let this: &[u8] = (&**self).as_ref();
|
||||
|
|
@ -667,9 +663,9 @@ impl From<Arc<ByteStr>> for Arc<[u8]> {
|
|||
impl_partial_eq!(ByteStr, Vec<u8>);
|
||||
// PartialOrd with `String` omitted to avoid inference failures
|
||||
impl_partial_eq!(ByteStr, String);
|
||||
impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, ByteStr>);
|
||||
impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, str>);
|
||||
impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, [u8]>);
|
||||
impl_partial_eq_ord_cow!(&ByteStr, Cow<'_, ByteStr>);
|
||||
impl_partial_eq_ord_cow!(&ByteStr, Cow<'_, str>);
|
||||
impl_partial_eq_ord_cow!(&ByteStr, Cow<'_, [u8]>);
|
||||
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
impl<'a> TryFrom<&'a ByteStr> for String {
|
||||
|
|
|
|||
|
|
@ -2661,8 +2661,7 @@ impl<'b> Pattern for &'b String {
|
|||
macro_rules! impl_eq {
|
||||
($lhs:ty, $rhs: ty) => {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(unused_lifetimes)]
|
||||
impl<'a, 'b> PartialEq<$rhs> for $lhs {
|
||||
impl PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
PartialEq::eq(&self[..], &other[..])
|
||||
|
|
@ -2674,8 +2673,7 @@ macro_rules! impl_eq {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(unused_lifetimes)]
|
||||
impl<'a, 'b> PartialEq<$lhs> for $rhs {
|
||||
impl PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
PartialEq::eq(&self[..], &other[..])
|
||||
|
|
@ -2689,13 +2687,13 @@ macro_rules! impl_eq {
|
|||
}
|
||||
|
||||
impl_eq! { String, str }
|
||||
impl_eq! { String, &'a str }
|
||||
impl_eq! { String, &str }
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl_eq! { Cow<'a, str>, str }
|
||||
impl_eq! { Cow<'_, str>, str }
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl_eq! { Cow<'a, str>, &'b str }
|
||||
impl_eq! { Cow<'_, str>, &'_ str }
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
impl_eq! { Cow<'a, str>, String }
|
||||
impl_eq! { Cow<'_, str>, String }
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
|
||||
|
|
|
|||
|
|
@ -612,14 +612,14 @@ mod slice_index {
|
|||
data: "abcdef";
|
||||
good: data[4..4] == "";
|
||||
bad: data[4..3];
|
||||
message: "begin <= end (4 <= 3)";
|
||||
message: "begin > end (4 > 3)";
|
||||
}
|
||||
|
||||
in mod rangeinclusive_neg_width {
|
||||
data: "abcdef";
|
||||
good: data[4..=3] == "";
|
||||
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,
|
||||
// so that `get` doesn't simply return None for the wrong reason.
|
||||
bad: data[0..=usize::MAX];
|
||||
message: "maximum usize";
|
||||
message: "out of bounds";
|
||||
}
|
||||
|
||||
in mod rangetoinclusive {
|
||||
data: "hello";
|
||||
bad: data[..=usize::MAX];
|
||||
message: "maximum usize";
|
||||
message: "out of bounds";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -659,49 +659,49 @@ mod slice_index {
|
|||
data: super::DATA;
|
||||
bad: data[super::BAD_START..super::GOOD_END];
|
||||
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 {
|
||||
data: super::DATA;
|
||||
bad: data[super::GOOD_START..super::BAD_END];
|
||||
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 {
|
||||
data: super::DATA;
|
||||
bad: data[super::BAD_START..];
|
||||
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 {
|
||||
data: super::DATA;
|
||||
bad: data[..super::BAD_END];
|
||||
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 {
|
||||
data: super::DATA;
|
||||
bad: data[super::BAD_START..=super::GOOD_END_INCL];
|
||||
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 {
|
||||
data: super::DATA;
|
||||
bad: data[super::GOOD_START..=super::BAD_END_INCL];
|
||||
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 {
|
||||
data: super::DATA;
|
||||
bad: data[..=super::BAD_END_INCL];
|
||||
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
|
||||
#[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() {
|
||||
let _ = &LOREM_PARAGRAPH[..1024];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ impl hash::Hash for ByteStr {
|
|||
#[unstable(feature = "bstr_internals", issue = "none")]
|
||||
macro_rules! impl_partial_eq {
|
||||
($lhs:ty, $rhs:ty) => {
|
||||
#[allow(unused_lifetimes)]
|
||||
impl<'a> PartialEq<$rhs> for $lhs {
|
||||
impl PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
let other: &[u8] = other.as_ref();
|
||||
|
|
@ -54,8 +53,7 @@ macro_rules! impl_partial_eq {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unused_lifetimes)]
|
||||
impl<'a> PartialEq<$lhs> for $rhs {
|
||||
impl PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
let this: &[u8] = self.as_ref();
|
||||
|
|
@ -76,9 +74,8 @@ macro_rules! impl_partial_eq_ord {
|
|||
($lhs:ty, $rhs:ty) => {
|
||||
$crate::bstr::impl_partial_eq!($lhs, $rhs);
|
||||
|
||||
#[allow(unused_lifetimes)]
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
impl<'a> PartialOrd<$rhs> for $lhs {
|
||||
impl PartialOrd<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
|
||||
let other: &[u8] = other.as_ref();
|
||||
|
|
@ -86,9 +83,8 @@ macro_rules! impl_partial_eq_ord {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unused_lifetimes)]
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
impl<'a> PartialOrd<$lhs> for $rhs {
|
||||
impl PartialOrd<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
|
||||
let this: &[u8] = self.as_ref();
|
||||
|
|
@ -107,7 +103,6 @@ pub use impl_partial_eq_ord;
|
|||
#[unstable(feature = "bstr_internals", issue = "none")]
|
||||
macro_rules! impl_partial_eq_n {
|
||||
($lhs:ty, $rhs:ty) => {
|
||||
#[allow(unused_lifetimes)]
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
impl<const N: usize> PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
|
|
@ -117,7 +112,6 @@ macro_rules! impl_partial_eq_n {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unused_lifetimes)]
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
impl<const N: usize> PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -2886,7 +2886,7 @@ pub const fn type_name<T: ?Sized>() -> &'static str;
|
|||
#[rustc_nounwind]
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[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
|
||||
/// same type. This is necessary because at const-eval time the actual discriminating
|
||||
|
|
|
|||
|
|
@ -927,14 +927,20 @@ marker_impls! {
|
|||
/// 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).
|
||||
#[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> {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for PhantomData<T> {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for *const T {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for *mut T {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for &T {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for &mut T {}
|
||||
marker_impls! {
|
||||
#[unstable(feature = "unsafe_unpin", issue = "125735")]
|
||||
unsafe UnsafeUnpin for
|
||||
{T: ?Sized} PhantomData<T>,
|
||||
{T: ?Sized} *const T,
|
||||
{T: ?Sized} *mut T,
|
||||
{T: ?Sized} &T,
|
||||
{T: ?Sized} &mut T,
|
||||
}
|
||||
|
||||
/// 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
|
||||
// effect, but we can't add a new field to an already stable unit struct -- that would be a breaking
|
||||
// change.
|
||||
#[unstable(feature = "unsafe_unpin", issue = "125735")]
|
||||
impl !UnsafeUnpin for PhantomPinned {}
|
||||
|
||||
marker_impls! {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! runtime or const-eval processable way.
|
||||
|
||||
use crate::any::TypeId;
|
||||
use crate::intrinsics::type_of;
|
||||
use crate::intrinsics::{type_id, type_of};
|
||||
|
||||
/// Compile-time type information.
|
||||
#[derive(Debug)]
|
||||
|
|
@ -28,11 +28,17 @@ impl TypeId {
|
|||
|
||||
impl Type {
|
||||
/// 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")]
|
||||
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
|
||||
// FIXME(reflection): don't require the 'static bound
|
||||
pub const fn of<T: ?Sized + 'static>() -> Self {
|
||||
const { TypeId::of::<T>().info() }
|
||||
pub const fn of<T: ?Sized>() -> Self {
|
||||
const { type_id::<T>().info() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use crate::{fmt, intrinsics, ptr, ub_checks};
|
|||
issue = "none"
|
||||
)]
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
#[rustc_const_unstable(feature = "const_range", issue = "none")]
|
||||
impl<T> const RangeBounds<T> for RangeInclusive<T> {
|
||||
|
|
|
|||
|
|
@ -663,7 +663,6 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeFull {
|
|||
}
|
||||
|
||||
/// 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 end of the range is out of bounds.
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
|
|
@ -673,12 +672,12 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
|
|||
|
||||
#[inline]
|
||||
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]
|
||||
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]
|
||||
|
|
@ -950,8 +949,7 @@ where
|
|||
R: ops::RangeBounds<usize>,
|
||||
{
|
||||
let len = bounds.end;
|
||||
let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?;
|
||||
if r.start > r.end || r.end > len { None } else { Some(r) }
|
||||
try_into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied()))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[rustc_const_unstable(feature = "const_range", issue = "none")]
|
||||
#[inline]
|
||||
pub(crate) const fn into_range(
|
||||
pub(crate) const fn try_into_slice_range(
|
||||
len: usize,
|
||||
(start, end): (ops::Bound<usize>, ops::Bound<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 {
|
||||
Bound::Included(end) => end.checked_add(1)?,
|
||||
Bound::Excluded(end) => end,
|
||||
Bound::Unbounded => len,
|
||||
ops::Bound::Included(end) if end >= len => return None,
|
||||
// Cannot overflow because `end < len` implies `end < usize::MAX`.
|
||||
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`
|
||||
// since these checks are handled by `Range` impls
|
||||
let start = match start {
|
||||
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)
|
||||
}
|
||||
|
|
@ -1039,12 +1043,12 @@ unsafe impl<T> SliceIndex<[T]> for (ops::Bound<usize>, ops::Bound<usize>) {
|
|||
|
||||
#[inline]
|
||||
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]
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -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 s_trunc = &s[..trunc_len];
|
||||
let ellipsis = if trunc_len < s.len() { "[...]" } else { "" };
|
||||
let len = s.len();
|
||||
|
||||
// 1. out of bounds
|
||||
if begin > s.len() || end > s.len() {
|
||||
let oob_index = if begin > s.len() { begin } else { end };
|
||||
panic!("byte index {oob_index} is out of bounds of `{s_trunc}`{ellipsis}");
|
||||
// 1. begin is OOB.
|
||||
if begin > len {
|
||||
panic!("start byte index {begin} is out of bounds of `{s_trunc}`{ellipsis}");
|
||||
}
|
||||
|
||||
// 2. begin <= end
|
||||
assert!(
|
||||
begin <= end,
|
||||
"begin <= end ({} <= {}) when slicing `{}`{}",
|
||||
begin,
|
||||
end,
|
||||
s_trunc,
|
||||
ellipsis
|
||||
);
|
||||
// 2. end is OOB.
|
||||
if end > len {
|
||||
panic!("end byte index {end} is out of bounds of `{s_trunc}`{ellipsis}");
|
||||
}
|
||||
|
||||
// 3. character boundary
|
||||
let index = if !s.is_char_boundary(begin) { begin } else { end };
|
||||
// find the character
|
||||
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();
|
||||
let char_range = char_start..char_start + ch.len_utf8();
|
||||
panic!(
|
||||
"byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}",
|
||||
index, ch, char_range, s_trunc, ellipsis
|
||||
);
|
||||
// 3. range is backwards.
|
||||
if begin > end {
|
||||
panic!("begin > end ({begin} > {end}) when slicing `{s_trunc}`{ellipsis}")
|
||||
}
|
||||
|
||||
// 4. begin is inside a character.
|
||||
if !s.is_char_boundary(begin) {
|
||||
let floor = s.floor_char_boundary(begin);
|
||||
let ceil = s.ceil_char_boundary(begin);
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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[..]`.
|
||||
///
|
||||
/// 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]
|
||||
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]
|
||||
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]
|
||||
|
|
@ -640,11 +633,11 @@ unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
|
|||
type Output = str;
|
||||
#[inline]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
fn index(self, slice: &str) -> &Self::Output {
|
||||
if *self.end() == usize::MAX {
|
||||
str_index_overflow_fail();
|
||||
let Self { mut start, mut end, exhausted } = self;
|
||||
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]
|
||||
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
if *self.end() == usize::MAX {
|
||||
str_index_overflow_fail();
|
||||
let Self { mut start, mut end, exhausted } = self;
|
||||
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;
|
||||
#[inline]
|
||||
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]
|
||||
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]
|
||||
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
|
||||
// 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]
|
||||
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
|
||||
// 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]
|
||||
fn index(self, slice: &str) -> &Self::Output {
|
||||
if self.last == usize::MAX {
|
||||
str_index_overflow_fail();
|
||||
}
|
||||
self.into_slice_range().index(slice)
|
||||
ops::RangeInclusive::from(self).index(slice)
|
||||
}
|
||||
#[inline]
|
||||
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
|
||||
if self.last == usize::MAX {
|
||||
str_index_overflow_fail();
|
||||
}
|
||||
self.into_slice_range().index_mut(slice)
|
||||
ops::RangeInclusive::from(self).index_mut(slice)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1565,7 +1565,7 @@ impl Ord for OsStr {
|
|||
macro_rules! impl_cmp {
|
||||
($lhs:ty, $rhs: ty) => {
|
||||
#[stable(feature = "cmp_os_str", since = "1.8.0")]
|
||||
impl<'a, 'b> PartialEq<$rhs> for $lhs {
|
||||
impl PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
<OsStr as PartialEq>::eq(self, other)
|
||||
|
|
@ -1573,7 +1573,7 @@ macro_rules! impl_cmp {
|
|||
}
|
||||
|
||||
#[stable(feature = "cmp_os_str", since = "1.8.0")]
|
||||
impl<'a, 'b> PartialEq<$lhs> for $rhs {
|
||||
impl PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
<OsStr as PartialEq>::eq(self, other)
|
||||
|
|
@ -1581,7 +1581,7 @@ macro_rules! impl_cmp {
|
|||
}
|
||||
|
||||
#[stable(feature = "cmp_os_str", since = "1.8.0")]
|
||||
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
|
||||
impl PartialOrd<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
|
||||
<OsStr as PartialOrd>::partial_cmp(self, other)
|
||||
|
|
@ -1589,7 +1589,7 @@ macro_rules! impl_cmp {
|
|||
}
|
||||
|
||||
#[stable(feature = "cmp_os_str", since = "1.8.0")]
|
||||
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
|
||||
impl PartialOrd<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
|
||||
<OsStr as PartialOrd>::partial_cmp(self, other)
|
||||
|
|
@ -1599,10 +1599,10 @@ macro_rules! impl_cmp {
|
|||
}
|
||||
|
||||
impl_cmp!(OsString, OsStr);
|
||||
impl_cmp!(OsString, &'a OsStr);
|
||||
impl_cmp!(Cow<'a, OsStr>, OsStr);
|
||||
impl_cmp!(Cow<'a, OsStr>, &'b OsStr);
|
||||
impl_cmp!(Cow<'a, OsStr>, OsString);
|
||||
impl_cmp!(OsString, &OsStr);
|
||||
impl_cmp!(Cow<'_, OsStr>, OsStr);
|
||||
impl_cmp!(Cow<'_, OsStr>, &OsStr);
|
||||
impl_cmp!(Cow<'_, OsStr>, OsString);
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Hash for OsStr {
|
||||
|
|
|
|||
|
|
@ -3841,9 +3841,9 @@ impl<'a> IntoIterator for &'a Path {
|
|||
}
|
||||
|
||||
macro_rules! impl_cmp {
|
||||
(<$($life:lifetime),*> $lhs:ty, $rhs: ty) => {
|
||||
($lhs:ty, $rhs: ty) => {
|
||||
#[stable(feature = "partialeq_path", since = "1.6.0")]
|
||||
impl<$($life),*> PartialEq<$rhs> for $lhs {
|
||||
impl PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
<Path as PartialEq>::eq(self, other)
|
||||
|
|
@ -3851,7 +3851,7 @@ macro_rules! impl_cmp {
|
|||
}
|
||||
|
||||
#[stable(feature = "partialeq_path", since = "1.6.0")]
|
||||
impl<$($life),*> PartialEq<$lhs> for $rhs {
|
||||
impl PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
<Path as PartialEq>::eq(self, other)
|
||||
|
|
@ -3859,7 +3859,7 @@ macro_rules! impl_cmp {
|
|||
}
|
||||
|
||||
#[stable(feature = "cmp_path", since = "1.8.0")]
|
||||
impl<$($life),*> PartialOrd<$rhs> for $lhs {
|
||||
impl PartialOrd<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
|
||||
<Path as PartialOrd>::partial_cmp(self, other)
|
||||
|
|
@ -3867,7 +3867,7 @@ macro_rules! impl_cmp {
|
|||
}
|
||||
|
||||
#[stable(feature = "cmp_path", since = "1.8.0")]
|
||||
impl<$($life),*> PartialOrd<$lhs> for $rhs {
|
||||
impl PartialOrd<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
|
||||
<Path as PartialOrd>::partial_cmp(self, other)
|
||||
|
|
@ -3876,16 +3876,16 @@ macro_rules! impl_cmp {
|
|||
};
|
||||
}
|
||||
|
||||
impl_cmp!(<> PathBuf, Path);
|
||||
impl_cmp!(<'a> PathBuf, &'a Path);
|
||||
impl_cmp!(<'a> Cow<'a, Path>, Path);
|
||||
impl_cmp!(<'a, 'b> Cow<'a, Path>, &'b Path);
|
||||
impl_cmp!(<'a> Cow<'a, Path>, PathBuf);
|
||||
impl_cmp!(PathBuf, Path);
|
||||
impl_cmp!(PathBuf, &Path);
|
||||
impl_cmp!(Cow<'_, Path>, Path);
|
||||
impl_cmp!(Cow<'_, Path>, &Path);
|
||||
impl_cmp!(Cow<'_, Path>, PathBuf);
|
||||
|
||||
macro_rules! impl_cmp_os_str {
|
||||
(<$($life:lifetime),*> $lhs:ty, $rhs: ty) => {
|
||||
($lhs:ty, $rhs: ty) => {
|
||||
#[stable(feature = "cmp_path", since = "1.8.0")]
|
||||
impl<$($life),*> PartialEq<$rhs> for $lhs {
|
||||
impl PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
<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")]
|
||||
impl<$($life),*> PartialEq<$lhs> for $rhs {
|
||||
impl PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
<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")]
|
||||
impl<$($life),*> PartialOrd<$rhs> for $lhs {
|
||||
impl PartialOrd<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
|
||||
<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")]
|
||||
impl<$($life),*> PartialOrd<$lhs> for $rhs {
|
||||
impl PartialOrd<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
|
||||
<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!(<'a> PathBuf, &'a OsStr);
|
||||
impl_cmp_os_str!(<'a> PathBuf, Cow<'a, OsStr>);
|
||||
impl_cmp_os_str!(<> PathBuf, OsString);
|
||||
impl_cmp_os_str!(<> Path, OsStr);
|
||||
impl_cmp_os_str!(<'a> Path, &'a OsStr);
|
||||
impl_cmp_os_str!(<'a> Path, Cow<'a, OsStr>);
|
||||
impl_cmp_os_str!(<> Path, OsString);
|
||||
impl_cmp_os_str!(<'a> &'a Path, OsStr);
|
||||
impl_cmp_os_str!(<'a, 'b> &'a Path, Cow<'b, OsStr>);
|
||||
impl_cmp_os_str!(<'a> &'a Path, OsString);
|
||||
impl_cmp_os_str!(<'a> Cow<'a, Path>, OsStr);
|
||||
impl_cmp_os_str!(<'a, 'b> Cow<'a, Path>, &'b OsStr);
|
||||
impl_cmp_os_str!(<'a> Cow<'a, Path>, OsString);
|
||||
impl_cmp_os_str!(PathBuf, OsStr);
|
||||
impl_cmp_os_str!(PathBuf, &OsStr);
|
||||
impl_cmp_os_str!(PathBuf, Cow<'_, OsStr>);
|
||||
impl_cmp_os_str!(PathBuf, OsString);
|
||||
impl_cmp_os_str!(Path, OsStr);
|
||||
impl_cmp_os_str!(Path, &OsStr);
|
||||
impl_cmp_os_str!(Path, Cow<'_, OsStr>);
|
||||
impl_cmp_os_str!(Path, OsString);
|
||||
impl_cmp_os_str!(&Path, OsStr);
|
||||
impl_cmp_os_str!(&Path, Cow<'_, OsStr>);
|
||||
impl_cmp_os_str!(&Path, OsString);
|
||||
impl_cmp_os_str!(Cow<'_, Path>, OsStr);
|
||||
impl_cmp_os_str!(Cow<'_, Path>, &OsStr);
|
||||
impl_cmp_os_str!(Cow<'_, Path>, OsString);
|
||||
|
||||
#[stable(since = "1.7.0", feature = "strip_prefix")]
|
||||
impl fmt::Display for StripPrefixError {
|
||||
|
|
|
|||
2
src/gcc
2
src/gcc
|
|
@ -1 +1 @@
|
|||
Subproject commit 0081ca6631abdfa02bf42bc85aaf507b8a0e6beb
|
||||
Subproject commit efdd0a7290c22f5438d7c5380105d353ee3e8518
|
||||
|
|
@ -71,7 +71,9 @@ impl NewPermission {
|
|||
access: 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`.
|
||||
NewPermission::Uniform {
|
||||
perm: Permission::Unique,
|
||||
|
|
@ -129,7 +131,9 @@ impl NewPermission {
|
|||
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).
|
||||
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 weak protector).
|
||||
NewPermission::Uniform {
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ impl<'tcx> NewPermission {
|
|||
retag_kind: RetagKind,
|
||||
cx: &crate::MiriInterpCx<'tcx>,
|
||||
) -> 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 is_protected = retag_kind == RetagKind::FnEntry;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,36 @@ fn mutate(x: &UnsafePinned<i32>) {
|
|||
unsafe { ptr.write(42) };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = UnsafePinned::new(0);
|
||||
mutate(&x);
|
||||
assert_eq!(x.into_inner(), 42);
|
||||
fn mut_alias(x: &mut UnsafePinned<i32>, y: &mut UnsafePinned<i32>) {
|
||||
unsafe {
|
||||
x.get().write(0);
|
||||
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) };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#![crate_type = "rlib"]
|
||||
|
||||
// CHECK-LABEL: align_offset_byte_ptr
|
||||
// CHECK: leaq 31
|
||||
// CHECK: leaq {{31|28}}
|
||||
// CHECK: andq $-32
|
||||
// CHECK: subq
|
||||
#[no_mangle]
|
||||
|
|
@ -13,7 +13,7 @@ pub fn align_offset_byte_ptr(ptr: *const u8) -> usize {
|
|||
}
|
||||
|
||||
// CHECK-LABEL: align_offset_byte_slice
|
||||
// CHECK: leaq 31
|
||||
// CHECK: leaq {{31|28}}
|
||||
// CHECK: andq $-32
|
||||
// CHECK: subq
|
||||
#[no_mangle]
|
||||
|
|
@ -22,7 +22,7 @@ pub fn align_offset_byte_slice(slice: &[u8]) -> usize {
|
|||
}
|
||||
|
||||
// CHECK-LABEL: align_offset_word_ptr
|
||||
// CHECK: leaq 31
|
||||
// CHECK: leaq {{31|28}}
|
||||
// CHECK: andq $-32
|
||||
// CHECK: subq
|
||||
// CHECK: shrq
|
||||
|
|
@ -35,7 +35,7 @@ pub fn align_offset_word_ptr(ptr: *const u32) -> usize {
|
|||
}
|
||||
|
||||
// CHECK-LABEL: align_offset_word_slice
|
||||
// CHECK: leaq 31
|
||||
// CHECK: leaq {{31|28}}
|
||||
// CHECK: andq $-32
|
||||
// CHECK: subq
|
||||
// CHECK: shrq
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ pub trait BikeshedGuaranteedNoDrop {}
|
|||
#[lang = "freeze"]
|
||||
pub unsafe auto trait Freeze {}
|
||||
|
||||
#[lang = "unsafe_unpin"]
|
||||
pub unsafe auto trait UnsafeUnpin {}
|
||||
|
||||
#[lang = "unpin"]
|
||||
#[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",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub fn align_to4(x: &[u8]) -> bool {
|
|||
#[no_mangle]
|
||||
pub fn align_offset_byte_ptr(ptr: *const u8) -> usize {
|
||||
// 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: %[[OFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]]
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ pub fn align_offset_byte_ptr(ptr: *const u8) -> usize {
|
|||
#[no_mangle]
|
||||
pub fn align_offset_word_slice(slice: &[Align4]) -> 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: %[[BOFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]]
|
||||
// CHECK: %[[OFFSET:.+]] = lshr exact [[USIZE]] %[[BOFFSET]], 2
|
||||
|
|
@ -57,7 +57,7 @@ pub fn align_offset_word_slice(slice: &[Align4]) -> usize {
|
|||
#[no_mangle]
|
||||
pub fn align_offset_word_ptr(ptr: *const Align4) -> 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: %[[BOFFSET:.+]] = sub [[USIZE]] %[[ALIGNED]], %[[ADDR]]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes
|
||||
#![crate_type = "lib"]
|
||||
#![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::num::NonZero;
|
||||
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)?}})
|
||||
#[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)
|
||||
#[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
|
||||
}
|
||||
|
||||
|
|
|
|||
90
tests/codegen-llvm/slice-range-indexing.rs
Normal file
90
tests/codegen-llvm/slice-range-indexing.rs
Normal 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);
|
||||
94
tests/codegen-llvm/str-range-indexing.rs
Normal file
94
tests/codegen-llvm/str-range-indexing.rs
Normal 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);
|
||||
|
|
@ -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> Sync for Switch<B>where <B as Signal>::Item: Sync'
|
||||
//@ 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 inner: <B as Signal2>::Item2,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
#![crate_name = "foo"]
|
||||
#![feature(negative_impls, freeze_impls, freeze)]
|
||||
#![feature(negative_impls, freeze_impls, freeze, unsafe_unpin)]
|
||||
|
||||
pub struct Foo;
|
||||
|
||||
//@ has foo/struct.Foo.html
|
||||
//@ !hasraw - 'Auto Trait Implementations'
|
||||
// Manually un-implement all auto traits for Foo:
|
||||
impl !Send for Foo {}
|
||||
impl !Sync for Foo {}
|
||||
impl !std::marker::Freeze for Foo {}
|
||||
impl !std::marker::UnsafeUnpin for Foo {}
|
||||
impl !std::marker::Unpin for Foo {}
|
||||
impl !std::panic::RefUnwindSafe for Foo {}
|
||||
impl !std::panic::UnwindSafe for Foo {}
|
||||
|
|
|
|||
|
|
@ -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> Sync for Foo<T>where T: Sync'
|
||||
//@ 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> {
|
||||
field: T,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
// 'impl<T> Send for Foo<T>'
|
||||
//
|
||||
//@ 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> {
|
||||
field: T,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`:
|
||||
- impl PartialEq for String;
|
||||
- impl PartialEq<ByteStr> for String;
|
||||
- impl PartialEq<ByteString> for String;
|
||||
- impl PartialEq<Path> for String;
|
||||
- impl PartialEq<PathBuf> for String;
|
||||
- impl PartialEq<str> for String;
|
||||
help: try using a fully qualified path to specify the expected types
|
||||
|
|
||||
LL - if String::from("a") == "a".try_into().unwrap() {}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue