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

Rollup of 7 pull requests

Successful merges:

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

View file

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

View file

@ -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 }}

View file

@ -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

View file

@ -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",
]

View file

@ -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.

View file

@ -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):

View file

@ -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"]

View file

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

View file

@ -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.

View file

@ -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:

View file

@ -15,7 +15,7 @@ That can be caused by the fact that you try to compile with `lto = "fat"`, but y
### ld: cannot find crtbegin.o
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

View file

@ -3,7 +3,7 @@
You can see the full documentation about what GIMPLE is [here](https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html). In this document we will explain how to generate it.
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-*-* } } */

View file

@ -53,7 +53,7 @@ If you wish to build a custom sysroot, pass the path of your sysroot source to `
### How to use [mem-trace](https://github.com/antoyo/mem-trace)
`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

View file

@ -1 +1 @@
0081ca6631abdfa02bf42bc85aaf507b8a0e6beb
efdd0a7290c22f5438d7c5380105d353ee3e8518

View file

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

View file

@ -575,9 +575,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
}
if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) {
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.

View file

@ -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);

View file

@ -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))

View file

@ -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)
}

View file

@ -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

View file

@ -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");

View file

@ -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)
}
}

File diff suppressed because it is too large Load diff

View file

@ -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.

View file

@ -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)
}
}

View file

@ -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

View file

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

View file

@ -89,11 +89,13 @@ tests/ui/thir-print/offset_of.rs
tests/ui/iterators/rangefrom-overflow-debug.rs
tests/ui/iterators/rangefrom-overflow-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

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@ def run_command(command, cwd=None):
sys.exit(1)
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,14 +21,11 @@ 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)
@ -45,31 +42,22 @@ 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
current_arch = data.get("TargetPrefix")
builtin_name = data.get("ClangBuiltinName")
if current_arch is None or current_arch == "" or builtin_name is None:
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
@ -77,89 +65,13 @@ def extract_intrinsics_from_llvm(llvm_path, intrinsics):
intrinsic = ".".join(intrinsic)
if current_arch not in intrinsics:
intrinsics[current_arch] = []
append_intrinsic(intrinsics[current_arch], intrinsic, entries[0])
append_intrinsic(intrinsics[current_arch], intrinsic, builtin_name)
return intrinsics
def append_translation(json_data, p, array):
it = json_data["index"][p]
content = it["docs"].split('`')
if len(content) != 5:
return
append_intrinsic(array, content[1], content[3])
def extract_intrinsics_from_llvmint(llvmint, intrinsics):
archs = [
"AMDGPU",
"aarch64",
"arm",
"cuda",
"hexagon",
"mips",
"nvvm",
"ppc",
"ptx",
"x86",
"xcore",
]
json_file = os.path.join(llvmint, "target/doc/llvmint.json")
# We need to regenerate the documentation!
run_command(
["cargo", "rustdoc", "--", "-Zunstable-options", "--output-format", "json"],
cwd=llvmint,
)
with open(json_file, "r", encoding="utf8") as f:
json_data = json.loads(f.read())
for p in json_data["paths"]:
it = json_data["paths"][p]
if it["crate_id"] != 0:
# This is from an external crate.
continue
if it["kind"] != "function":
# We're only looking for functions.
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])
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())

View file

@ -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 }

View file

@ -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),
});
}

View file

@ -1189,14 +1189,23 @@ impl<'tcx> Ty<'tcx> {
}
}
/// Checks whether values of this type `T` implement the `UnsafeUnpin` trait.
pub fn is_unsafe_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
self.is_trivially_unpin() || tcx.is_unsafe_unpin_raw(typing_env.as_query_input(self))
}
/// Checks whether values of this type `T` implement the `Unpin` trait.
///
/// 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() {

View file

@ -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)));
}
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

View file

@ -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
};

View file

@ -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

View file

@ -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 {

View file

@ -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")]

View file

@ -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];
}

View file

@ -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]

View file

@ -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

View file

@ -927,14 +927,20 @@ marker_impls! {
/// This is part of [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html), and is
/// 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! {

View file

@ -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() }
}
}

View file

@ -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;
}

View file

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

View file

@ -663,7 +663,6 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeFull {
}
/// The methods `index` and `index_mut` panic if:
/// - the 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]

View file

@ -85,34 +85,50 @@ fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
let 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();
// 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!(
"byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}",
index, ch, char_range, s_trunc, ellipsis
);
"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 {

View file

@ -76,13 +76,6 @@ where
}
}
#[inline(never)]
#[cold]
#[track_caller]
const fn str_index_overflow_fail() -> ! {
panic!("attempted to index str up to maximum usize");
}
/// Implements substring slicing with syntax `&self[..]` or `&mut self[..]`.
///
/// 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)
}
}

View file

@ -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 {

View file

@ -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 {

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

View file

@ -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 {

View file

@ -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;

View file

@ -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) };
}

View file

@ -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

View file

@ -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",

View file

@ -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]]

View file

@ -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
}

View file

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

View file

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

View file

@ -20,7 +20,8 @@ where
//@ has - '//h3[@class="code-header"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send'
//@ has - '//h3[@class="code-header"]' 'impl<B> 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,
}

View file

@ -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 {}

View file

@ -2,7 +2,8 @@
//@ has - '//h3[@class="code-header"]' 'impl<T> Send for Foo<T>where T: Send'
//@ has - '//h3[@class="code-header"]' 'impl<T> 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,
}

View file

@ -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,
}

View file

@ -8,8 +8,11 @@ LL | if String::from("a") == "a".try_into().unwrap() {}
|
= note: multiple `impl`s satisfying `String: PartialEq<_>` found in the following crates: `alloc`, `std`:
- 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() {}

View file

@ -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