diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index b5d8fc821ae0e..2b264a6843837 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -1,6 +1,5 @@ //! Code related to parsing literals. -use std::fmt::Write as _; use std::{ascii, fmt, str}; use rustc_literal_escaper::{ @@ -38,19 +37,8 @@ pub fn escape_string_symbol(symbol: Symbol) -> Symbol { let mut escaped = String::with_capacity(s.len() + 1); escaped.push_str(prefix); - // Don't use escape_default() here, because using it is slower than escaping manually. - for c in suffix.chars() { - match c { - '\t' => escaped.push_str("\\t"), - '\r' => escaped.push_str("\\r"), - '\n' => escaped.push_str("\\n"), - '\\' => escaped.push_str("\\\\"), - '\'' => escaped.push_str("\\'"), - '\"' => escaped.push_str("\\\""), - '\x20'..='\x7e' => escaped.push(c), - c => write!(escaped, "\\u{{{:x}}}", c as u32).unwrap(), - } - } + escaped.extend(suffix.escape_default()); + Symbol::intern(&escaped) } diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index 0687bc1150907..66362e27ce4da 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml @@ -116,8 +116,6 @@ jobs: - name: Set env run: | echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV - # /tmp is a small tmpfs, and rustdoc keeps every compiled doctest there until the run ends. - echo "TMPDIR=$RUNNER_TEMP" >> $GITHUB_ENV #- name: Cache rust repository ## We only clone the rust repository for rustc tests diff --git a/compiler/rustc_codegen_gcc/.github/workflows/rustup.yml b/compiler/rustc_codegen_gcc/.github/workflows/rustup.yml index 57d7a39de049a..0309ccbad5481 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/rustup.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/rustup.yml @@ -77,8 +77,6 @@ jobs: - name: Set env run: | echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV - # /tmp is a small tmpfs, and rustdoc keeps every compiled doctest there until the run ends. - echo "TMPDIR=$RUNNER_TEMP" >> $GITHUB_ENV - name: Prepare if: ${{ matrix.run_cargo_tests }} diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index d94ce6575ac0e..aec0ef270bf73 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "7.2.0" +version = "7.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27cd340f3c265988de3379689cbbd103c25b1f8a3d0cc73f91a49083e899d3e1" +checksum = "ea1820165dfb376a3bbfe8b69628d9e5a851c423e0480a666f71f560e2d0a924" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2d9d74572f004908f17ec1bd0af071e5db63a12c3335c9651a6125593951c6" +checksum = "a6091291fc118448b91df5b662d20564020198fee46d8445a6aa0c25b6b435be" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 95c4c991297fa..3e279961bfbe0 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -20,7 +20,7 @@ default = ["master"] [dependencies] object = { version = "0.39.0", default-features = false, features = ["std", "read"] } tempfile = "3.20" -gccjit = { version = "7.2.0", features = ["dlopen"] } +gccjit = { version = "7.3.0", features = ["dlopen"] } #gccjit = { git = "https://github.com/rust-lang/gccjit.rs", branch = "error-dlopen", features = ["dlopen"] } # Local copy. diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs index ac076836579ac..cc87c660520d8 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/build.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs @@ -6,7 +6,8 @@ use std::path::{Path, PathBuf}; use crate::config::{Channel, ConfigInfo}; use crate::utils::{ - create_dir, get_sysroot_dir, run_command, run_command_with_output_and_env, walk_dir, + create_dir, get_sysroot_dir, run_command, run_command_with_output_and_env, rustc_version_info, + walk_dir, }; #[derive(Default)] @@ -137,17 +138,16 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu // Builds libs let mut rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default(); - // Record the sysroot sources under the path the `rust-src` component uses, which is where - // rustc looks for them to turn a sysroot span into `/rustc/$hash`. Without this, ui tests - // print the build path where they expect `$SRC_DIR`. - let sysroot_source_dir = lib_path.join("rustlib/src/rust/library"); + // Remap both source spans and embedded panic locations to the standard virtual path. + let sysroot_source_dir = start_dir.join("sysroot_src"); + let rustc_commit = rustc_version_info(env.get("RUSTC").map(String::as_str))? + .commit_hash + .ok_or_else(|| "Couldn't retrieve rustc commit hash".to_string())?; + // Cargo passes workspace-relative filenames, including those used by panic locations. + // Also: the `==` is intentional! The first `=` is "FROM" and the second `=` is for "TO". + rustflags.push_str(&format!(" --remap-path-prefix==/rustc/{rustc_commit}/library")); rustflags.push_str(&format!( - " --remap-path-prefix={library_dir}={sysroot_source_dir}", - library_dir = std::path::absolute(&library_dir) - .map_err(|error| format!( - "Failed to get the absolute path of the sysroot sources: {error:?}" - ))? - .display(), + " --remap-path-prefix={sysroot_source_dir}=/rustc/{rustc_commit}", sysroot_source_dir = std::path::absolute(&sysroot_source_dir) .map_err(|error| format!( "Failed to get the absolute path of the sysroot sources: {error:?}" diff --git a/compiler/rustc_codegen_gcc/doc/subtree.md b/compiler/rustc_codegen_gcc/doc/subtree.md index fcac399e46542..dbdc09499e30d 100644 --- a/compiler/rustc_codegen_gcc/doc/subtree.md +++ b/compiler/rustc_codegen_gcc/doc/subtree.md @@ -1,54 +1,13 @@ -# git subtree sync +# Subtree sync procedure -`rustc_codegen_gcc` is a subtree of the rust compiler. As such, it needs to be -synced from time to time to ensure changes that happened on their side are also -included on our side. +`rustc_codegen_gcc` is using [Josh] to perform subtree synces with the `rust-lang/rust` repository. -### How to install a forked git-subtree +[Josh]: https://josh-project.dev/docs/intro.html -Using git-subtree with `rustc` requires a patched git to make it work. -The PR that is needed is [here](https://github.com/gitgitgadget/git/pull/493). -Use the following instructions to install it: +The procedure for performing subtree synchronizations is documented [here][rdg-josh]. -```bash -git clone git@github.com:tqc/git.git -cd git -git checkout tqc/subtree -make -make install -cd contrib/subtree -make -cp git-subtree ~/bin -``` +[rdg-josh]: https://rustc-dev-guide.rust-lang.org/external-repos.html#synchronizing-a-josh-subtree -### Syncing with rust compiler +## Pushing changes to `rust-lang/rust` -Do a sync with this command: - -```bash -PATH="$HOME/bin:$PATH" ~/bin/git-subtree push -P compiler/rustc_codegen_gcc/ ../rustc_codegen_gcc/ sync_branch_name -cd ../rustc_codegen_gcc -git checkout master -git pull -git checkout sync_branch_name -git merge master -``` - -To send the changes to the rust repo: - -```bash -cd ../rust -git pull origin master -git checkout -b subtree-update_cg_gcc_YYYY-MM-DD -PATH="$HOME/bin:$PATH" ~/bin/git-subtree pull --prefix=compiler/rustc_codegen_gcc/ https://github.com/rust-lang/rustc_codegen_gcc.git master -# Don't forget to update the `gcc` submodule to the same version as the -# one in `rustc_codegen_gcc/libgccjit.version`. -git push - -# Immediately merge the merge commit into cg_gcc to prevent merge conflicts when syncing from rust-lang/rust later. -PATH="$HOME/bin:$PATH" ~/bin/git-subtree push -P compiler/rustc_codegen_gcc/ ../rustc_codegen_gcc/ sync_branch_name -``` - -FIXME: write a script that does the above. - -https://rust-lang.zulipchat.com/#narrow/stream/301329-t-devtools/topic/subtree.20madness/near/258877725 +If the used GCC commit (recorded in `libgccjit.version`) changed in `rustc_codegen_gcc`, then you will have to update the `gcc` submodule in the `rust-lang/rust` repository in the push sync PR. The CI in `rust-lang/rust` should ensure that these two commit references match. You can simply update the `gcc` submodule commit and push it to your push sync PR once it has been created. diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index 0bcc711e4696b..0c6f40148d1e5 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -37ea7cd851f04ee6a313c5067c9fde9328f876aa +e1c6e07b095d091177376a163da44039475e05e3 diff --git a/compiler/rustc_codegen_gcc/rust-toolchain.toml b/compiler/rustc_codegen_gcc/rust-toolchain.toml index 7d0a982195aa0..5750754596ba2 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain.toml +++ b/compiler/rustc_codegen_gcc/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-10-19" +channel = "nightly-2026-10-24" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index b6b0352d2516c..925c600aee936 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -20,7 +20,7 @@ use crate::base; use crate::context::CodegenCx; #[cfg(feature = "master")] use crate::gcc_util::to_gcc_aarch64_extension; -use crate::gcc_util::to_gcc_features; +use crate::gcc_util::{to_gcc_features, to_gcc_target_attribute_feature}; /// Checks if the function `instance` is recursively inline. /// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive. @@ -192,20 +192,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } let target_features = function_features .iter() - .filter_map(|feature| { - // FIXME(antoyo): support soft-float. - if feature.contains("soft-float") { - return None; - } - - if feature.starts_with('-') { - Some(format!("no{}", feature)) - } else if let Some(stripped) = feature.strip_prefix('+') { - Some(stripped.to_string()) - } else { - Some(feature.to_string()) - } - }) + .filter_map(|feature| to_gcc_target_attribute_feature(feature)) .collect::>() .join(","); if !target_features.is_empty() { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 21d92c6cc2936..895bc629d63ce 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -28,12 +28,14 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.const_uint(self.type_u16(), i as u64) } - fn global_string(&self, string: &str) -> LValue<'gcc> { - // FIXME(antoyo): handle non-null-terminated strings. - let string = self.context.new_string_literal(string); + fn global_const_string(&self, string: &str) -> LValue<'gcc> { + // The bytes of a Rust string are not NUL-terminated and can contain NULs. + let string = bytes_in_context(self, string.as_bytes()); let sym = self.generate_local_symbol_name("str"); let global = self.declare_private_global(&sym, self.val_ty(string)); global.global_set_initializer_rvalue(string); + #[cfg(feature = "master")] + global.global_set_readonly(); global // FIXME(antoyo): set linkage. } @@ -126,18 +128,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } } -/// The element type and element count of the array used to represent a run of `len` constant bytes. -/// -/// Larger integers are used where possible: this reduces the number of rvalues, which is a -/// significant memory saving on constant-heavy crates. -fn byte_run_shape<'gcc>(cx: &CodegenCx<'gcc, '_>, len: usize) -> (Type<'gcc>, u64) { - match len % 8 { - 0 => (cx.context.new_type::(), len as u64 / 8), - 4 => (cx.context.new_type::(), len as u64 / 4), - _ => (cx.context.new_type::(), len as u64), - } -} - /// The type [`bytes_in_context`] gives a run of `len` constant bytes. /// /// Exposed separately so that the type of a constant allocation can be computed before any of its @@ -147,65 +137,30 @@ fn byte_run_shape<'gcc>(cx: &CodegenCx<'gcc, '_>, len: usize) -> (Type<'gcc>, u6 /// Two equal-but-distinct array types would key [`CodegenCx::type_struct`] differently and so /// produce two distinct anonymous structs, and libgccjit compares struct types by identity. pub fn bytes_type_in_context<'gcc>(cx: &CodegenCx<'gcc, '_>, len: usize) -> Type<'gcc> { - let (element_type, count) = byte_run_shape(cx, len); - if let Some(&typ) = cx.byte_array_types.borrow().get(&(element_type, count)) { + if let Some(&typ) = cx.byte_array_types.borrow().get(&len) { return typ; } - let typ = new_array_type(cx.context, None, element_type, count); - cx.byte_array_types.borrow_mut().insert((element_type, count), typ); + let typ = new_array_type(cx.context, None, cx.type_u8(), len as u64); + cx.byte_array_types.borrow_mut().insert(len, typ); typ } -// FIXME(FractalFir): Consider using `global_set_initializer` instead. Before this is done, we need to confirm that -// `global_set_initializer` is more memory efficient than the current solution. -// `global_set_initializer` calls `global_set_initializer_rvalue` under the hood - does it generate an array of rvalues, -// or is it using a more efficient representation? pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> { let typ = bytes_type_in_context(cx, bytes.len()); - let (element_type, _) = byte_run_shape(cx, bytes.len()); - let context = &cx.context; - // Since we are representing arbitrary byte runs as integers, we need to follow the target - // endianness. - let endian = cx.sess().target.options.endian; - let elements: Vec<_> = match bytes.len() % 8 { - 0 => { - let (arrays, remainder) = bytes.as_chunks::<8>(); - debug_assert!(remainder.is_empty()); - arrays - .iter() - .map(|&arr| { - context.new_rvalue_from_long( - element_type, - match endian { - rustc_abi::Endian::Little => u64::from_le_bytes(arr) as i64, - rustc_abi::Endian::Big => u64::from_be_bytes(arr) as i64, - }, - ) - }) - .collect() - } - 4 => { - let (arrays, remainder) = bytes.as_chunks::<4>(); - debug_assert!(remainder.is_empty()); - arrays - .iter() - .map(|&arr| { - context.new_rvalue_from_int( - element_type, - match endian { - rustc_abi::Endian::Little => u32::from_le_bytes(arr) as i32, - rustc_abi::Endian::Big => u32::from_be_bytes(arr) as i32, - }, - ) - }) - .collect() - } - _ => bytes + // Raw bytes take far less RAM in libgccjit than one rvalue per array element. + #[cfg(feature = "master")] + { + cx.context.new_array_from_bytes(None, typ, bytes) + } + #[cfg(not(feature = "master"))] + { + let byte_type = cx.type_u8(); + let elements: Vec<_> = bytes .iter() - .map(|&byte| context.new_rvalue_from_int(element_type, byte as i32)) - .collect(), - }; - context.new_array_constructor(None, typ, &elements) + .map(|&byte| cx.context.new_rvalue_from_int(byte_type, byte as i32)) + .collect(); + cx.context.new_array_constructor(None, typ, &elements) + } } pub fn type_is_pointer(typ: Type<'_>) -> bool { @@ -292,7 +247,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { fn const_str(&self, s: &str) -> (RValue<'gcc>, RValue<'gcc>) { let mut const_str_cache = self.const_str_cache.borrow_mut(); let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| { - let g = self.global_string(s); + let g = self.global_const_string(s); const_str_cache.insert(s.to_owned(), g); g }); diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 902f8ba52faaa..6cf765a9b80b7 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -64,14 +64,18 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { let cv = const_alloc_to_gcc(self, alloc); let align = alloc.inner().align; - if let Some(variable) = self.const_globals.borrow().get(&cv) { - if let Some(global_variable) = self.global_lvalues.borrow().get(variable) { - let alignment = align.bits() as i32; - if alignment > global_variable.get_alignment() { - global_variable.set_alignment(alignment); - } + if let Some(&mut (variable, ref mut global_align)) = + self.const_globals.borrow_mut().get_mut(&cv) + { + // `set_alignment` replaces the alignment, which is on the global's type and thus not + // reported by `get_alignment`: only raise it, or a smaller use would lower it. + if align > *global_align + && let Some(global_variable) = self.global_lvalues.borrow().get(&variable) + { + global_variable.set_alignment(align.bytes() as i32); + *global_align = align; } - return *variable; + return variable; } let global_value = self.static_addr_of_mut(cv, align, kind); #[cfg(feature = "master")] @@ -80,7 +84,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { .get(&global_value) .expect("`static_addr_of_mut` did not add the global to `self.global_lvalues`") .global_set_readonly(); - self.const_globals.borrow_mut().insert(cv, global_value); + self.const_globals.borrow_mut().insert(cv, (global_value, align)); global_value } @@ -359,18 +363,30 @@ fn alloc_fields(cx: &CodegenCx<'_, '_>, alloc: &interpret::Allocation) -> Vec next_offset { - fields.push(AllocField::Bytes { range: next_offset..offset }); + push_byte_runs(&mut fields, next_offset..offset); } fields.push(AllocField::Pointer { offset, prov }); next_offset = offset + pointer_size; } if alloc.len() >= next_offset { - fields.push(AllocField::Bytes { range: next_offset..alloc.len() }); + push_byte_runs(&mut fields, next_offset..alloc.len()); } fields } +/// `gcc_jit_context_new_array_from_bytes` rejects runs of `i32::MAX` bytes or more. +const MAX_BYTE_RUN_LEN: usize = i32::MAX as usize - 1; + +fn push_byte_runs(fields: &mut Vec, range: Range) { + let mut start = range.start; + while range.end - start > MAX_BYTE_RUN_LEN { + fields.push(AllocField::Bytes { range: start..start + MAX_BYTE_RUN_LEN }); + start += MAX_BYTE_RUN_LEN; + } + fields.push(AllocField::Bytes { range: start..range.end }); +} + /// The type [`const_alloc_to_gcc`] gives `alloc`, computed without building any rvalue. /// /// This lets `predefine_static` declare a static's global with the type its initializer will have, diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index b57e0fb2cd23a..39a98ede628f2 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -97,11 +97,11 @@ pub struct CodegenCx<'gcc, 'tcx> { /// Cache of the anonymous struct types. pub struct_types: RefCell, Type<'gcc>>>, - /// Cache of the array types used for runs of constant bytes, keyed by element type and count. + /// Cache of the array types used for runs of constant bytes, keyed by length. /// /// libgccjit mints a fresh type on every `new_array_type`, and struct types are keyed on their /// field types, so without this two equal byte runs would yield two distinct anonymous structs. - pub byte_array_types: RefCell, u64), Type<'gcc>>>, + pub byte_array_types: RefCell>>, /// Cache of the return types of the pairs returned in two registers, keyed by the types of the /// two scalars. @@ -126,8 +126,8 @@ pub struct CodegenCx<'gcc, 'tcx> { /// Local holding the return value of the defined functions returning it in memory. pub functions_with_indirect_return: RefCell, LValue<'gcc>>>, - /// Cache of emitted const globals (value -> global) - pub const_globals: RefCell, RValue<'gcc>>>, + /// Cache of emitted const globals (value -> global and its alignment) + pub const_globals: RefCell, (RValue<'gcc>, Align)>>, /// Map from the address of a global variable (rvalue) to the global variable itself (lvalue). /// FIXME(antoyo): remove when the rustc API is fixed. diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 5ecc971830f20..b0442cdfb11cc 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -20,7 +20,10 @@ fn gcc_features_by_flags(sess: &EarlySession, features: &mut Vec) { /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). -pub(crate) fn global_gcc_features(sess: &EarlySession) -> Vec { +/// +/// If `for_cfg` is `true`, only the features implied by `--target` are included, to configure the +/// target info that populates [`rustc_codegen_ssa::TargetConfig`]. +pub(crate) fn global_gcc_features(sess: &EarlySession, for_cfg: bool) -> Vec { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -57,6 +60,12 @@ pub(crate) fn global_gcc_features(sess: &EarlySession) -> Vec { // Features implied by an implicit or explicit `--target`. target_features::target_spec_to_backend_features(sess, &mut extend_backend_features); + // `internal_target_features` applies -Ctarget-features itself, and GCC has no options for the + // features implied by other flags (e.g. retpoline), which would break the target info. + if for_cfg { + return features; + } + // -Ctarget-features target_features::flag_to_backend_features(sess, extend_backend_features); @@ -267,25 +276,7 @@ pub fn new_context<'gcc>(sess: &Session) -> Context<'gcc> { context.add_command_line_option("-mavx"); } - if sess.target.arch == Arch::AArch64 { - // Unlike LLVM, GCC enables outline atomics by default, but their libgcc helpers are - // missing on targets without this feature, like the Linux kernel. Only AArch64 has it. - let outline_atomics = sess - .global_backend_features - .iter() - .rev() - .find_map(|feature| match feature.as_str() { - "outline-atomics" | "+outline-atomics" => Some(true), - "-outline-atomics" => Some(false), - _ => None, - }) - .unwrap_or(false); - context.add_command_line_option(if outline_atomics { - "-moutline-atomics" - } else { - "-mno-outline-atomics" - }); - } + add_feature_options(&context, &sess.target.arch, &sess.global_backend_features); for arg in &sess.opts.cg.llvm_args { context.add_command_line_option(arg); @@ -387,6 +378,80 @@ pub fn new_context<'gcc>(sess: &Session) -> Context<'gcc> { context } +/// Add the options for the global `features` that are not applied with function attributes. +fn add_feature_options(context: &Context<'_>, arch: &Arch, features: &[String]) { + if *arch == Arch::AArch64 { + // Unlike LLVM, GCC enables outline atomics by default, but their libgcc helpers are + // missing on targets without this feature, like the Linux kernel. Only AArch64 has it. + let outline_atomics = + feature_setting(features, "outline-atomics") == FeatureSetting::Enabled; + context.add_command_line_option(if outline_atomics { + "-moutline-atomics" + } else { + "-mno-outline-atomics" + }); + + // Rust's `neon` covers both FP and SIMD, so without it (e.g. on softfloat targets like the + // Linux kernel) GCC must not touch those registers at all. + if feature_setting(features, "neon") == FeatureSetting::Disabled { + context.add_command_line_option("-mgeneral-regs-only"); + } + } +} + +/// Configure the target info `context` with the global `features`, like codegen does. +#[cfg(feature = "master")] +pub fn add_target_info_feature_options(context: &Context<'_>, arch: &Arch, features: &[String]) { + add_feature_options(context, arch, features); + // Codegen applies these as function attributes, which the target info does not see. Of the + // arches using these attributes, libgccjit only has target info for x86, not PowerPC. + if matches!(arch, Arch::X86 | Arch::X86_64) { + for feature in + features.iter().filter_map(|feature| to_gcc_target_attribute_feature(feature)) + { + context.add_command_line_option(format!("-m{}", feature)); + } + } +} + +/// The spelling of a global `feature` in a GCC `target` attribute, e.g. `no-sse` for `-sse`. +pub fn to_gcc_target_attribute_feature(feature: &str) -> Option { + // FIXME(antoyo): support soft-float. + if feature.contains("soft-float") { + return None; + } + + if feature.starts_with('-') { + Some(format!("no{}", feature)) + } else if let Some(stripped) = feature.strip_prefix('+') { + Some(stripped.to_string()) + } else { + Some(feature.to_string()) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum FeatureSetting { + Enabled, + Disabled, + /// No global feature mentions it. + Unspecified, +} + +/// The last setting of `feature` in `features`. +fn feature_setting(features: &[String], feature: &str) -> FeatureSetting { + features + .iter() + .rev() + .find_map(|global_feature| match global_feature.strip_prefix('-') { + Some(disabled) if disabled == feature => Some(FeatureSetting::Disabled), + Some(_) => None, + None => (global_feature.strip_prefix('+').unwrap_or(global_feature) == feature) + .then_some(FeatureSetting::Enabled), + }) + .unwrap_or(FeatureSetting::Unspecified) +} + pub fn add_pic_option<'gcc>(context: &Context<'gcc>, relocation_model: RelocModel) { match relocation_model { rustc_target::spec::RelocModel::Static => { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 414bff06866a8..17034d0c195e7 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -240,7 +240,7 @@ impl CodegenBackend for GccCodegenBackend { panic!("Could not load libgccjit.so. Attempted paths: {:#?}", paths); } } - let global_backend_features = gcc_util::global_gcc_features(sess); + let global_backend_features = gcc_util::global_gcc_features(sess, false); #[cfg(feature = "master")] { @@ -253,6 +253,11 @@ impl CodegenBackend for GccCodegenBackend { if target_cpu != "generic" { context.add_command_line_option(format!("-march={}", target_cpu)); } + gcc_util::add_target_info_feature_options( + &context, + &sess.target.arch, + &gcc_util::global_gcc_features(sess, true), + ); // libgccjit always passes -fPIC, which some targets reject (e.g. MIPS without -mabicalls). gcc_util::add_pic_option(&context, sess.relocation_model()); diff --git a/compiler/rustc_codegen_gcc/tests/asm/const_global_alignment.rs b/compiler/rustc_codegen_gcc/tests/asm/const_global_alignment.rs new file mode 100644 index 0000000000000..e4dfd4bb26dd0 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/const_global_alignment.rs @@ -0,0 +1,44 @@ +//@ assembly-output: emit-asm +//@ only-x86_64-unknown-linux-gnu + +#![crate_type = "lib"] + +// Constants with identical bytes share one global, which needs the largest alignment among +// their uses: lowering it to a later, smaller one or reading it as bits fails these checks. + +// CHECK: "A_WIDE": +// CHECK-NEXT: .quad "[[WIDE_THEN_BYTES:global\.[0-9]+]]" +#[no_mangle] +pub static A_WIDE: &[u128; 4] = &[0x09090909_09090909_09090909_09090909; 4]; + +// CHECK: "B_BYTES": +// CHECK-NEXT: .quad "[[WIDE_THEN_BYTES]]" +#[no_mangle] +pub static B_BYTES: &[u8; 64] = &[9; 64]; + +// CHECK: "C_BYTES": +// CHECK-NEXT: .quad "[[BYTES_THEN_WIDE:global\.[0-9]+]]" +#[no_mangle] +pub static C_BYTES: &[u8; 64] = &[8; 64]; + +// CHECK: "D_WIDE": +// CHECK-NEXT: .quad "[[BYTES_THEN_WIDE]]" +#[no_mangle] +pub static D_WIDE: &[u128; 4] = &[0x08080808_08080808_08080808_08080808; 4]; + +// CHECK: "E_BYTES": +// CHECK-NEXT: .quad "[[BYTES_THEN_HALVES:global\.[0-9]+]]" +#[no_mangle] +pub static E_BYTES: &[u8; 4] = &[3; 4]; + +// CHECK: "F_HALVES": +// CHECK-NEXT: .quad "[[BYTES_THEN_HALVES]]" +#[no_mangle] +pub static F_HALVES: &[u16; 2] = &[0x0303; 2]; + +// CHECK: .section .rodata.[[WIDE_THEN_BYTES]],"a" +// CHECK-NEXT: .align {{16|32|64}}{{$}} +// CHECK: .section .rodata.[[BYTES_THEN_WIDE]],"a" +// CHECK-NEXT: .align 16{{$}} +// CHECK: .section .rodata.[[BYTES_THEN_HALVES]],"a" +// CHECK-NEXT: .align 2{{$}} diff --git a/compiler/rustc_codegen_gcc/tests/asm/const_str.rs b/compiler/rustc_codegen_gcc/tests/asm/const_str.rs new file mode 100644 index 0000000000000..65969a713c996 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/asm/const_str.rs @@ -0,0 +1,24 @@ +//@ assembly-output: emit-asm +//@ only-x86_64-unknown-linux-gnu +//@ compile-flags: -Copt-level=2 + +#![crate_type = "lib"] +#![allow(invalid_value)] + +// The panic messages of the `assert_zero_valid` and `assert_inhabited` intrinsics are built with +// `const_str`, which passes the address of its global as the string data. The global must +// therefore hold the bytes themselves (not a pointer to a string literal), without a trailing +// NUL so that its size matches the length passed along, and it must be read-only. + +// CHECK-LABEL: "zeroed_ref": +// CHECK: mov esi, 57 +// CHECK: lea rdi, "[[REF_STR:str\.[0-9]+]]"[rip] +#[unsafe(no_mangle)] +pub fn zeroed_ref() -> &'static u8 { + unsafe { std::mem::zeroed() } +} + +// CHECK: .section .rodata.[[REF_STR]],"a" +// CHECK: .size "[[REF_STR]]", 57 +// CHECK-NEXT: "[[REF_STR]]": +// CHECK-NEXT: .ascii "attempted to zero-initialize type `&u8`, which is invalid" diff --git a/compiler/rustc_codegen_gcc/tests/compile/large_byte_constant.rs b/compiler/rustc_codegen_gcc/tests/compile/large_byte_constant.rs index b26387ecacdb6..2ca3f4c2fec36 100644 --- a/compiler/rustc_codegen_gcc/tests/compile/large_byte_constant.rs +++ b/compiler/rustc_codegen_gcc/tests/compile/large_byte_constant.rs @@ -1,8 +1,8 @@ -//@ memory-limit: 2G +//@ memory-limit: 1G // Compiler: -// An 8 MiB constant must compile within 2 GiB of address space. A backend that builds one rvalue -// per byte of a constant needs more than 6 GiB for it and runs out of memory. +// An 8 MiB constant must compile within 1 GiB of address space. A backend that builds one rvalue +// per 8 bytes of a constant needs about 1.4 GiB for it and runs out of memory. #![crate_type = "lib"] diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index a18eaf272fd07..e35d9916247f1 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -1,6 +1,5 @@ tests/ui/asm/may_unwind.rs tests/ui/asm/x86_64/may_unwind.rs -tests/ui/intrinsics/panic-uninitialized-zeroed.rs tests/ui/simd/issue-17170.rs tests/ui/simd/issue-39720.rs tests/ui/process/println-with-broken-pipe.rs @@ -12,7 +11,6 @@ tests/ui/process/nofile-limit.rs tests/ui/attributes/fn-align-dyn.rs tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs tests/ui/statics/const_generics.rs -tests/ui/thir-print/offset_of.rs tests/ui/asm/x86_64/global_asm_escape.rs tests/ui/lto/all-crates.rs tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs @@ -20,7 +18,6 @@ tests/ui/explicit-tail-calls/become-indirect-return.rs tests/ui/explicit-tail-calls/indirect.rs tests/ui/abi/rust-tail-cc.rs tests/ui/abi/rust-preserve-none-cc.rs -tests/ui/extern/extern-types-field-offset.rs tests/ui/scalable-vectors/debuginfo-does-not-spill.rs tests/ui/scalable-vectors/debuginfo-no-opt-llvm-ice.rs tests/ui/scalable-vectors/transmute.rs diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 385b785233a75..c2fd8a79407f0 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -2,12 +2,12 @@ //! and related traits for the LLVM codegen backend. use std::any::Any; +use std::cell::OnceCell; use std::ffi::CStr; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::path::PathBuf; use std::ptr; -use std::sync::OnceLock; use rustc_ast::expand::allocator::AllocatorMethod; use rustc_codegen_ssa::back::lto::ThinModule; @@ -451,13 +451,15 @@ pub struct ModuleLlvm { /// Saved ThinLTO buffer for embedding in the object file. /// - /// This is currently a `OnceLock` to avoid aliasing problems when writing to it + /// This is currently a `OnceCell` to avoid aliasing problems when writing to it /// from code that also wants to pass around a shared ref to `ModuleCodegen`. - pub(crate) thin_lto_buffer: OnceLock>, + pub(crate) thin_lto_buffer: OnceCell>, } +// An LLVM context can only be used by one thread at a time. But we can move the entire context and +// all objects inside it to another thread (needed for parallel codegen). This is sound because +// `ModuleLlvm` owns the context and only allows borrowing LLVM objects tied to the context. unsafe impl Send for ModuleLlvm {} -unsafe impl Sync for ModuleLlvm {} /// Shared helper to call [`llvm::LLVMContextCreate`] and then configure /// [`llvm::LLVMContextSetDiscardValueNames`]. @@ -478,7 +480,7 @@ impl ModuleLlvm { llmod_raw, llcx_raw, tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)), - thin_lto_buffer: OnceLock::new(), + thin_lto_buffer: OnceCell::new(), } } } @@ -491,7 +493,7 @@ impl ModuleLlvm { llmod_raw, llcx_raw, tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), - thin_lto_buffer: OnceLock::new(), + thin_lto_buffer: OnceCell::new(), } } } @@ -512,7 +514,7 @@ impl ModuleLlvm { llmod_raw, llcx_raw, tm: ManuallyDrop::new(tm), - thin_lto_buffer: OnceLock::new(), + thin_lto_buffer: OnceCell::new(), } } } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 4c0df97a41985..36bc0a68d882e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -14,7 +14,7 @@ use crate::back::write::{ use crate::{CompiledModule, ModuleCodegen}; pub trait WriteBackendMethods: Clone + 'static { - type Module: Send + Sync; + type Module: Send; type TargetMachine; type ModuleBuffer: ModuleBufferMethods; type ThinData: Send + Sync; diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 94b3c6ee01455..51f5bc189281b 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1480,8 +1480,8 @@ impl VecDeque { // SAFETY: // * Any slice passed to `drop_in_place` is valid; the second case has - // `len <= front.len()` and returning on `len > self.len()` ensures - // `begin <= back.len()` in the first case + // `len <= front.len()` and returning on `len >= self.len()` ensures + // `begin < back.len()` in the first case // * The head of the VecDeque is moved before calling `drop_in_place`, // so no value is dropped twice if `drop_in_place` panics unsafe { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 6c2d5d68d787c..aefe43aa13ef2 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -92,6 +92,7 @@ #![feature(allocator_ext)] #![feature(array_into_iter_constructors)] #![feature(ascii_char)] +#![feature(ascii_char_variants)] #![feature(async_fn_traits)] #![feature(async_iterator)] #![feature(borrowed_buf_init)] @@ -156,6 +157,7 @@ #![feature(pattern)] #![feature(pin_coerce_unsized_trait)] #![feature(ptr_alignment_type)] +#![feature(ptr_cast_array)] #![feature(ptr_metadata)] #![feature(random)] #![feature(raw_os_error_ty)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index bb8301e53d34f..9961c71dc062e 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -42,11 +42,15 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(no_global_oom_handling))] +use core::ascii; use core::error::Error; use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn; #[cfg(not(no_global_oom_handling))] +use core::mem::MaybeUninit; +#[cfg(not(no_global_oom_handling))] use core::num::Saturating; #[cfg(not(no_global_oom_handling))] use core::ops::Add; @@ -1488,8 +1492,8 @@ impl String { /// Shortens this `String` to the specified length. /// - /// If `new_len` is greater than or equal to the string's current length, this has no - /// effect. + /// If `new_len` is greater than or equal to the string's current length, + /// this has no effect. /// /// Note that this method has no effect on the allocated capacity /// of the string @@ -1511,10 +1515,11 @@ impl String { #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] pub fn truncate(&mut self, new_len: usize) { - if new_len <= self.len() { - assert!(self.is_char_boundary(new_len)); - self.vec.truncate(new_len) + if new_len >= self.len() { + return; } + assert!(self.is_char_boundary(new_len)); + self.vec.truncate(new_len) } /// Removes the last character from the string buffer and returns it. @@ -2448,12 +2453,35 @@ impl Clone for String { #[stable(feature = "rust1", since = "1.0.0")] impl FromIterator for String { fn from_iter>(iter: I) -> String { - let mut buf = String::new(); - buf.extend(iter); + FromCharIterSpec::to_string_spec(iter.into_iter()) + } +} + +#[cfg(not(no_global_oom_handling))] +trait FromCharIterSpec: Iterator { + fn to_string_spec(self) -> String; +} + +#[cfg(not(no_global_oom_handling))] +impl FromCharIterSpec for T +where + T: Iterator, +{ + default fn to_string_spec(self) -> String { + let mut buf = String::with_capacity(self.size_hint().0); + buf.extend(self); buf } } +#[cfg(not(no_global_oom_handling))] +impl FromCharIterSpec for core::str::EscapeDefault<'_> { + fn to_string_spec(self) -> String { + // go through the ToString specialization + self.to_string() + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "string_from_iter_by_ref", since = "1.17.0")] impl<'a> FromIterator<&'a char> for String { @@ -2549,10 +2577,7 @@ impl<'a> FromIterator<&'a core::ascii::Char> for String { #[stable(feature = "rust1", since = "1.0.0")] impl Extend for String { fn extend>(&mut self, iter: I) { - let iterator = iter.into_iter(); - let (lower_bound, _) = iterator.size_hint(); - self.reserve(lower_bound); - iterator.for_each(move |c| self.push(c)); + SpecExtendChars::spec_extend_into(iter, self); } #[inline] @@ -2566,6 +2591,28 @@ impl Extend for String { } } +#[cfg(not(no_global_oom_handling))] +trait SpecExtendChars { + fn spec_extend_into(self, s: &mut String); +} + +#[cfg(not(no_global_oom_handling))] +impl> SpecExtendChars for T { + default fn spec_extend_into(self, target: &mut String) { + let iterator = self.into_iter(); + let (lower_bound, _) = iterator.size_hint(); + target.reserve(lower_bound); + iterator.for_each(move |c| target.push(c)); + } +} + +#[cfg(not(no_global_oom_handling))] +impl SpecExtendChars for core::str::EscapeDefault<'_> { + fn spec_extend_into(self, target: &mut String) { + target.extend_from_escape_default(self); + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "extend_ref", since = "1.2.0")] impl<'a> Extend<&'a char> for String { @@ -3160,6 +3207,126 @@ impl SpecToString for fmt::Arguments<'_> { } } +#[cfg(not(no_global_oom_handling))] +impl SpecToString for core::str::EscapeDefault<'_> { + fn spec_to_string(&self) -> String { + let mut s = String::new(); + s.extend_from_escape_default(self.clone()); + s + } +} + +#[cfg(not(no_global_oom_handling))] +impl String { + #[inline] + fn extend_from_escape_default(&mut self, escaper: core::str::EscapeDefault<'_>) { + let (front, middle, tail) = escaper.into_parts(); + let lengths = front.as_ref().map_or_default(|f| f.size_hint().0) + + middle.as_ref().map_or_default(|m| m.size_hint().0) + + tail.as_ref().map_or_default(|t| t.size_hint().0); + self.reserve(lengths); + + if let Some(front) = front { + self.extend(front); + } + if let Some(middle) = middle { + /// Ensures a spare capacity of N bytes and then passes that as MaybeUninit + /// slice to a closure that returns the bytes written, then + /// updates the length. + /// + /// # Safety: + /// + /// The caller must initialize the number of bytes returned by the closure. + #[inline] + unsafe fn with_spare( + bytes: &mut Vec, + then: impl FnOnce(&mut [MaybeUninit; N]) -> usize, + ) { + const { + assert!(core::mem::size_of::() == 1); + } + bytes.reserve(N); + let old_len = bytes.len(); + // SAFETY: We reserved enough space for the requested number of bytes. + unsafe { + let spare: &mut [MaybeUninit; N] = bytes + .spare_capacity_mut() + .as_mut_ptr() + .cast::() + .cast_uninit() + .cast_array::() + .as_mut_unchecked(); + let written = then(spare); + bytes.set_len(old_len + written); + } + } + + for c in middle { + match c { + '\'' | '\"' | '\\' => { + self.push('\\'); + self.push(c); + } + '\x20'..='\x7e' => self.push(c), + '\t' => { + self.push('\\'); + self.push('t'); + } + '\r' => { + self.push('\\'); + self.push('r'); + } + '\n' => { + self.push('\\'); + self.push('n'); + } + c => { + // SAFETY: We only let the function write ascii chars, so it remains valid utf8. + // and it returns the number of bytes written, so so with_spare ensures the rest for us. + unsafe { + with_spare::<10, ascii::Char>(self.as_mut_vec(), |spare| { + escape_unicode_into(spare, c) + }); + } + } + } + } + } + + if let Some(tail) = tail { + self.extend(tail); + } + } +} + +#[cfg(not(no_global_oom_handling))] +#[inline] +// adapted from core::escape::escape_unicode, but it's left-aligned instead of right-aligned. +fn escape_unicode_into(output: &mut [MaybeUninit; 10], c: char) -> usize { + const HEX_DIGITS: [ascii::Char; 16] = *b"0123456789abcdef".as_ascii().unwrap(); + + let c = c as u32; + // OR-ing `1` ensures that for `c == 0` the code computes that + // one digit should be printed. + let prefix_skip = (c | 1).leading_zeros() as usize / 4 - 2; + + // Always write all digits, but for shorter representations the unused + // digits get overwritten by later writes. + output[3usize.saturating_sub(prefix_skip)].write(HEX_DIGITS[((c >> 20) & 15) as usize]); + output[4usize.saturating_sub(prefix_skip)].write(HEX_DIGITS[((c >> 16) & 15) as usize]); + output[5usize.saturating_sub(prefix_skip)].write(HEX_DIGITS[((c >> 12) & 15) as usize]); + output[6usize.saturating_sub(prefix_skip)].write(HEX_DIGITS[((c >> 8) & 15) as usize]); + output[7usize.saturating_sub(prefix_skip)].write(HEX_DIGITS[((c >> 4) & 15) as usize]); + output[8usize.saturating_sub(prefix_skip)].write(HEX_DIGITS[(c & 15) as usize]); + + output[0].write(ascii::Char::ReverseSolidus); + output[1].write(ascii::Char::SmallU); + output[2].write(ascii::Char::LeftCurlyBracket); + output[9 - prefix_skip].write(ascii::Char::RightCurlyBracket); + + 10 - prefix_skip +} + #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for String { #[inline] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index d51eb95e9eeec..03702b19f92c7 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1701,8 +1701,8 @@ impl Vec { /// assert_eq!(vec, [1, 2]); /// ``` /// - /// No truncation occurs when `len` is greater than the vector's current - /// length: + /// No truncation occurs when `len` is greater than or equal to the + /// vector's current length: /// /// ``` /// let mut vec = vec![1, 2, 3]; @@ -1728,16 +1728,13 @@ impl Vec { // This is safe because: // - // * the slice passed to `drop_in_place` is valid; the `len > self.len` + // * the slice passed to `drop_in_place` is valid; the `len >= self.len` // case avoids creating an invalid slice, and // * the `len` of the vector is shrunk before calling `drop_in_place`, // such that no value will be dropped twice in case `drop_in_place` // were to panic once (if it panics twice, the program aborts). unsafe { - // Note: It's intentional that this is `>` and not `>=`. - // Changing it to `>=` has negative performance - // implications in some cases. See #78884 for more. - if len > self.len { + if len >= self.len { return; } let remaining_len = self.len - len; @@ -3163,7 +3160,8 @@ impl Vec { /// calling the closure `f`. The return values from `f` will end up /// in the `Vec` in the order they have been generated. /// - /// If `new_len` is less than `len`, the `Vec` is simply truncated. + /// If `new_len` is less than or equal to `len`, the `Vec` is simply + /// truncated. /// /// This method uses a closure to create new values on every push. If /// you'd rather [`Clone`] a given value, use [`Vec::resize`]. If you @@ -3517,7 +3515,8 @@ impl Vec { /// /// If `new_len` is greater than `len`, the `Vec` is extended by the /// difference, with each additional slot filled with `value`. - /// If `new_len` is less than `len`, the `Vec` is simply truncated. + /// If `new_len` is less than or equal to `len`, the `Vec` is simply + /// truncated. /// /// This method requires `T` to implement [`Clone`], /// in order to be able to clone the passed value. diff --git a/library/alloc/src/wtf8/mod.rs b/library/alloc/src/wtf8/mod.rs index 232fbba622b35..2296806d0cd42 100644 --- a/library/alloc/src/wtf8/mod.rs +++ b/library/alloc/src/wtf8/mod.rs @@ -354,18 +354,19 @@ impl Wtf8Buf { /// Shortens a string to the specified length. /// - /// If `new_len` is greater than the string's current length, this has no - /// effect. + /// If `new_len` is greater than or equal to the string's current length, + /// this has no effect. /// /// # Panics /// /// Panics if `new_len` does not lie on a code point boundary. #[inline] pub fn truncate(&mut self, new_len: usize) { - if new_len <= self.len() { - assert!(self.is_code_point_boundary(new_len)); - self.bytes.truncate(new_len) + if new_len >= self.len() { + return; } + assert!(self.is_code_point_boundary(new_len)); + self.bytes.truncate(new_len) } /// Consumes the WTF-8 string and tries to convert it to a vec of bytes. diff --git a/library/alloctests/benches/str.rs b/library/alloctests/benches/str.rs index 98c7c5413caef..68bdfbed8c8b5 100644 --- a/library/alloctests/benches/str.rs +++ b/library/alloctests/benches/str.rs @@ -1,5 +1,9 @@ use test::{Bencher, black_box}; +#[allow(unused)] // this is a symlink to core benches, we don't use everything here +#[path = "../../coretests/benches/str/corpora.rs"] +pub(super) mod corpora; + #[bench] fn char_iterator(b: &mut Bencher) { let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb"; diff --git a/library/alloctests/benches/string.rs b/library/alloctests/benches/string.rs index 0bbec12e4fdc6..75caf722d723d 100644 --- a/library/alloctests/benches/string.rs +++ b/library/alloctests/benches/string.rs @@ -159,3 +159,15 @@ fn bench_insert_str_long(b: &mut Bencher) { x }) } + +#[bench] +fn bench_from_escape_default_ascii(b: &mut Bencher) { + let s = super::str::corpora::en::LARGE; + b.iter(|| black_box(s).escape_default().to_string()) +} + +#[bench] +fn bench_from_escape_default_multibyte(b: &mut Bencher) { + let s = super::str::corpora::emoji::LARGE; + b.iter(|| black_box(s).escape_default().to_string()) +} diff --git a/library/alloctests/tests/string.rs b/library/alloctests/tests/string.rs index 08eb1855a4824..9c632b4e3a2f9 100644 --- a/library/alloctests/tests/string.rs +++ b/library/alloctests/tests/string.rs @@ -956,3 +956,14 @@ fn test_str_concat() { let s: String = format!("{a}{b}"); assert_eq!(s.as_bytes()[9], 'd' as u8); } + +// alloc has a specialization for EscapeDefault's ToString impl, which isn't covered by core tests. +#[test] +fn test_from_escaped_str() { + assert_eq!("abcABC012".escape_default().to_string(), "abcABC012"); + assert_eq!("\t\n\"".escape_default().to_string(), r#"\t\n\""#); + assert_eq!("\u{10FFFF}".escape_default().to_string(), "\\u{10ffff}"); + assert_eq!("\u{0}".escape_default().to_string(), "\\u{0}"); + assert_eq!("\u{1}".escape_default().to_string(), "\\u{1}"); + assert_eq!("\u{100}".escape_default().to_string(), "\\u{100}"); +} diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index c97e6f4c45395..d296d0ceb12b3 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1506,10 +1506,12 @@ macro_rules! nonzero_integer { #[inline] const fn from_ascii_bytes_radix_impl(src: &[u8], radix: u32) -> Result { + // FIXME(const-hack): This should use `?` again, once it's `const` let n = match <$Int>::from_ascii_bytes_radix_impl(src, radix) { Ok(n) => n, Err(err) => return Err(err), }; + // FIXME(const-hack): This should use `Option::ok_or` again, once it's `const` if let Some(n) = Self::new(n) { Ok(n) } else { diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 60b9e81aaeff9..657d894d38878 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1589,6 +1589,18 @@ pub struct EscapeDefault<'a> { pub(super) inner: FlatMap, char_mod::EscapeDefault, CharEscapeDefault>, } +impl<'a> EscapeDefault<'a> { + /// Disassembles the iterator into its internal state. + /// Used by alloc for an optimized to_string impl. + #[unstable(feature = "std_internals", issue = "none")] + pub fn into_parts( + self, + ) -> (Option>, Option>, Option>) + { + self.inner.into_parts() + } +} + /// The return type of [`str::escape_unicode`]. #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 770cc5f834a11..7a4a7049533ce 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -569,8 +569,8 @@ impl OsString { /// Truncate the `OsString` to the specified length. /// - /// If `new_len` is greater than the string's current length, this has no - /// effect. + /// If `new_len` is greater than or equal to the string's current length, + /// this has no effect. /// /// # Panics /// @@ -579,11 +579,12 @@ impl OsString { #[inline] #[unstable(feature = "os_string_truncate", issue = "133262")] pub fn truncate(&mut self, len: usize) { - if len <= self.len() { - self.as_os_str().inner.check_public_boundary(len); - // SAFETY: The length was just checked to be at a valid boundary. - unsafe { self.inner.truncate_unchecked(len) }; + if len >= self.len() { + return; } + self.as_os_str().inner.check_public_boundary(len); + // SAFETY: The length was just checked to be at a valid boundary. + unsafe { self.inner.truncate_unchecked(len) }; } /// Provides plumbing to `Vec::extend_from_slice` without giving full diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index 311866dab4733..9715411a9a692 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -905,35 +905,36 @@ fn test_rwlock_max_readers() { let mut read_lock_ctr: u32 = 0; let rwlock: RwLock = RwLock::new(0); - const MAX_READERS: u32 = - cfg_select! { - miri => 100, - any( - all(target_os = "windows", not(target_vendor = "win7")), - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "fuchsia", - all(target_family = "wasm", target_feature = "atomics"), - target_os = "hermit", - target_os = "motor", - ) => (1 << 30) - 2, - any( - target_family = "unix", - all(target_os = "windows", target_vendor = "win7", target_pointer_width = "64"), - all(target_vendor = "fortanix", target_env = "sgx"), - target_os = "xous", - target_os = "teeos", - ) => u32::MAX, - // Otherwise a form of deadlock is observed. - all(target_os = "windows", target_vendor = "win7", target_pointer_width = "32") => { - (1 << 28) - 1 + const MAX_READERS: u32 = cfg_select! { + miri => 100, + any( + all(target_os = "windows", not(target_vendor = "win7")), + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "fuchsia", + all(target_family = "wasm", target_feature = "atomics"), + target_os = "hermit", + target_os = "motor", + ) => (1 << 30) - 2, + any( + target_family = "unix", + all(target_os = "windows", target_vendor = "win7"), + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "xous", + target_os = "teeos", + ) => { + if usize::BITS - 4 > u32::BITS { + u32::MAX + } else { + (usize::MAX >> 4) as u32 } - target_os = "solid_asp3" => (1 << 30), - _ => u32::MAX, - }; + } + target_os = "solid_asp3" => (1 << 30), + _ => u32::MAX, + }; while read_lock_ctr < MAX_READERS { let lock = rwlock.read(); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f329f61c100c8..563c38f52fb7a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -4155,9 +4155,6 @@ impl CommandLineStep for Bootstrap { let build_compiler = builder.compiler(0, host); let record_failed_tests = builder.ensure(SetupFailedTestsFile); - // Some tests require cargo submodule to be present. - builder.sess.require_submodule("src/tools/cargo", None); - let mut cargo = tool::prepare_tool_cargo( builder, build_compiler, diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 98cfd58198a6c..27de3ec60b96b 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1026,7 +1026,19 @@ impl Builder<'_> { // // Only clear out the directory if we're running Cargo on std; otherwise, we // should let Cargo take care of things for us (via depdep info) - if !self.config.dry_run() && mode == Mode::Std { + // + // When --keep-stage[-std] is passed, two things can happen: + // - Either this function is not invoked on `std` at all, because the corresponding Std step + // checks the flag and doesn't invoke Cargo for std at all. + // - This function is invoked, in which case it will (most likely) do a no-op stdlib + // rebuild. + // + // In the second situation, we must ensure that we will not clear the standard library + // output directory, otherwise --keep-stage[-std] will of course break. + // Note that the compiler stage corresponds to the standard library stage. + let keep_previously_built_std = self.config.keep_stage.contains(&compiler.stage) + || self.config.keep_stage_std.contains(&compiler.stage); + if !self.config.dry_run() && mode == Mode::Std && !keep_previously_built_std { build_stamp::clear_if_dirty(self, &out_dir, &self.rustc(compiler)); } diff --git a/src/ci/docker/host-x86_64/test-pr-check-1/reuse-requirements.txt b/src/ci/docker/host-x86_64/test-pr-check-1/reuse-requirements.txt index 8784e18864b23..231e06ba0a00c 100644 --- a/src/ci/docker/host-x86_64/test-pr-check-1/reuse-requirements.txt +++ b/src/ci/docker/host-x86_64/test-pr-check-1/reuse-requirements.txt @@ -24,9 +24,9 @@ chardet==5.2.0 \ # via # binaryornot # python-debian -jinja2==3.1.4 \ - --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \ - --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d +Jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # via reuse license-expression==30.3.0 \ --hash=sha256:1295406f736b4f395ff069aec1cebfad53c0fcb3cf57df0f5ec58fc7b905aea5 \ diff --git a/src/gcc b/src/gcc index 37ea7cd851f04..e1c6e07b095d0 160000 --- a/src/gcc +++ b/src/gcc @@ -1 +1 @@ -Subproject commit 37ea7cd851f04ee6a313c5067c9fde9328f876aa +Subproject commit e1c6e07b095d091177376a163da44039475e05e3 diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index 9a432c3bad343..7c68f0091342f 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -12,6 +12,11 @@ pub(super) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) - } let is_json_output = cx.is_json_output(); - ImportStripper { tcx: cx.tcx, is_json_output, document_hidden: cx.document_hidden() } - .fold_crate(krate) + ImportStripper { + tcx: cx.tcx, + is_json_output, + document_hidden: cx.document_hidden(), + document_private: cx.document_private(), + } + .fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 54c8e9daedd17..e99e488ea227c 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -25,9 +25,13 @@ pub(super) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> is_json_output, tcx: cx.tcx, }; - krate = - ImportStripper { tcx: cx.tcx, is_json_output, document_hidden: cx.document_hidden() } - .fold_crate(stripper.fold_crate(krate)); + krate = ImportStripper { + tcx: cx.tcx, + is_json_output, + document_hidden: cx.document_hidden(), + document_private: cx.document_private(), + } + .fold_crate(stripper.fold_crate(krate)); } // strip all impls referencing private items diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 4c6d62917ab24..45bfb519cf4f5 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -268,6 +268,7 @@ pub(crate) struct ImportStripper<'tcx> { pub(crate) tcx: TyCtxt<'tcx>, pub(crate) is_json_output: bool, pub(crate) document_hidden: bool, + pub(crate) document_private: bool, } impl ImportStripper<'_> { @@ -290,6 +291,7 @@ impl DocFolder for ImportStripper<'_> { debug!("ImportStripper: stripping {:?}", i.name); None } + clean::ImportItem(imp) if self.document_private => Some(self.fold_item_recur(i)), // clean::ImportItem(_) if !self.document_hidden && i.is_doc_hidden() => None, clean::ExternCrateItem { .. } | clean::ImportItem(..) if i.visibility(self.tcx) != Some(Visibility::Public) => diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index df55626ffb05d..33b0334e3fd5d 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -490,7 +490,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let tcx = self.cx.tcx; let def_id = item.owner_id.to_def_id(); - let is_pub = tcx.visibility(def_id).is_public(); + // Explanations: if the item is public, nothing more to check. If not, then we check if + // we're documenting private item. If we are, then we want all items, except if it's a + // `use`. In this case, we want to check if it's a reexport. + let is_pub = tcx.visibility(def_id).is_public() + || (self.cx.document_private() + && (!matches!(item.kind, hir::ItemKind::Use(..)) + || def_id.as_local().is_some_and(|def_id| { + tcx.visibility(def_id).is_accessible_from(CRATE_DEF_ID, tcx) + }))); if is_pub { self.store_path(item.owner_id.to_def_id()); @@ -592,7 +600,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // If there was a private module in the current path then don't bother inlining // anything as it will probably be stripped anyway. - if is_pub && self.inside_public_path { + if is_pub && (self.inside_public_path || self.cx.document_private()) { let please_inline = if let Some(res_did) = res.opt_def_id() && matches!(tcx.def_kind(res_did), DefKind::Macro(MacroKinds::BANG)) { diff --git a/tests/rustdoc-html/inline_local/document-private-reexports.rs b/tests/rustdoc-html/inline_local/document-private-reexports.rs new file mode 100644 index 0000000000000..c0953e7678716 --- /dev/null +++ b/tests/rustdoc-html/inline_local/document-private-reexports.rs @@ -0,0 +1,52 @@ +// Test ensures that when we use the `--document-private-items` option, reexports +// work the same as for "public" items. +// Regression test for . + +//@ compile-flags: --document-private-items + +#![crate_name = "foo"] + +//@ has 'foo/index.html' +// The four reexports, the `not_hidden` and `foofoo` (private) modules. +//@ count - '//*[@class="item-table"]/dt' 6 +//@ has - '//*[@class="item-table"]/dt/a[@href="not_hidden/index.html"]' 'not_hidden' +//@ has - '//*[@class="item-table"]/dt/a[@href="struct.Top.html"]' 'Top' +//@ has - '//*[@class="item-table"]/dt/a[@href="struct.NotHidden.html"]' 'NotHidden' +//@ has - '//*[@class="item-table"]/dt/a[@href="struct.Hidden2.html"]' 'Hidden2' +//@ has - '//*[@class="item-table"]/dt/a[@href="struct.X.html"]' 'X' +//@ has - '//*[@class="item-table"]/dt/a[@href="foofoo/index.html"]' 'foofoo' + +#[doc(hidden)] +pub(crate) struct TopHidden; + +#[doc(inline)] +pub(crate) use TopHidden as Top; + +#[doc(hidden)] +mod hidden { + pub(crate) struct NotHidden; +} + +pub(crate) use self::hidden::NotHidden; +// Even though there is no `pub` of any kind, we display it because we're at the +// crate root. +use self::hidden::NotHidden as X; + +mod not_hidden { + #[doc(hidden)] + pub(crate) struct Hidden; +} + +#[doc(inline)] +pub(crate) use self::not_hidden::Hidden as Hidden2; + +//@ has 'foo/foofoo/index.html' +//@ count - '//*[@class="item-table"]/dt' 1 +//@ has - '//*[@class="item-table"]/dt/a[@href="struct.Something.html"]' 'Something' +mod foofoo { + // Since there is no `pub` of any kind, we don't display it, even if there is `doc(inline)`. + #[doc(inline)] + use crate::not_hidden::Hidden; + #[doc(inline)] + pub(crate) use crate::not_hidden::Hidden as Something; +} diff --git a/tests/rustdoc-html/inline_local/glob-private-document-private-items.rs b/tests/rustdoc-html/inline_local/glob-private-document-private-items.rs index 4ad36da843f80..a85ed41fa5ddf 100644 --- a/tests/rustdoc-html/inline_local/glob-private-document-private-items.rs +++ b/tests/rustdoc-html/inline_local/glob-private-document-private-items.rs @@ -17,25 +17,25 @@ pub use mod1::*; //@ has foo/index.html //@ hasraw - "mod1" //@ hasraw - "Mod1Public" -//@ !hasraw - "Mod1Private" -//@ !hasraw - "mod2" +//@ hasraw - "Mod1Private" +//@ hasraw - "mod2" //@ hasraw - "Mod2Public" -//@ !hasraw - "Mod2Private" +//@ hasraw - "Mod2Private" //@ has foo/struct.Mod1Public.html -//@ !has foo/struct.Mod1Private.html +//@ has foo/struct.Mod1Private.html //@ has foo/struct.Mod2Public.html -//@ !has foo/struct.Mod2Private.html +//@ has foo/struct.Mod2Private.html //@ has foo/mod1/index.html //@ hasraw - "mod2" //@ hasraw - "Mod1Public" //@ hasraw - "Mod1Private" -//@ !hasraw - "Mod2Public" -//@ !hasraw - "Mod2Private" +//@ hasraw - "Mod2Public" +//@ hasraw - "Mod2Private" //@ has foo/mod1/struct.Mod1Public.html //@ has foo/mod1/struct.Mod1Private.html -//@ !has foo/mod1/struct.Mod2Public.html -//@ !has foo/mod1/struct.Mod2Private.html +//@ has foo/mod1/struct.Mod2Public.html +//@ has foo/mod1/struct.Mod2Private.html //@ has foo/mod1/mod2/index.html //@ hasraw - "Mod2Public" @@ -43,6 +43,6 @@ pub use mod1::*; //@ has foo/mod1/mod2/struct.Mod2Public.html //@ has foo/mod1/mod2/struct.Mod2Private.html -//@ !has foo/mod2/index.html -//@ !has foo/mod2/struct.Mod2Public.html -//@ !has foo/mod2/struct.Mod2Private.html +//@ has foo/mod2/index.html +//@ has foo/mod2/struct.Mod2Public.html +//@ has foo/mod2/struct.Mod2Private.html diff --git a/tests/ui/coercion/fudge-inference/closure-input-in-fn-ptr-arg-162109.rs b/tests/ui/coercion/fudge-inference/closure-input-in-fn-ptr-arg-162109.rs new file mode 100644 index 0000000000000..3a7faeb4ac4e0 --- /dev/null +++ b/tests/ui/coercion/fudge-inference/closure-input-in-fn-ptr-arg-162109.rs @@ -0,0 +1,18 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// A regression test for https://github.com/rust-lang/rust/issues/162109. + +struct Foo(I, fn(I) -> O); + +impl Foo { + fn new(_: I, _: fn(I) -> O) -> Self { + loop {} + } +} + +fn main() { + let _: Foo<_, u8> = Foo::new(&1u8, |input| *input); +} diff --git a/triagebot.toml b/triagebot.toml index 60786ced590f0..35891af78fbdb 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1104,6 +1104,10 @@ cc = ["@BoxyUwU"] message = "Some changes occurred in `rustc_ty_utils::consts.rs`" cc = ["@BoxyUwU"] +[mentions."compiler/rustc_ty_utils/src/layout/invariant.rs"] +message = "Layout invariants are being changed" +cc = ["@RalfJung"] + [mentions."compiler/rustc_trait_selection/src/solve/"] message = "Some changes occurred to the core trait solver" cc = ["@rust-lang/initiative-trait-system-refactor"]