Auto merge of #147282 - Zalathar:rollup-7wz3k9r, r=Zalathar

Rollup of 6 pull requests

Successful merges:

 - rust-lang/rust#141839 (make rust-analyzer use a dedicated build directory)
 - rust-lang/rust#146166 (Implement range support in `//@ edition`)
 - rust-lang/rust#147259 (cg_llvm: Use helper methods for all calls to `LLVMMDNodeInContext2`)
 - rust-lang/rust#147263 (Disable triagebot auto stable-regression compiler backport nominations pending redesign)
 - rust-lang/rust#147268 (add arm-maintainers to various targets)
 - rust-lang/rust#147270 (Move doc_cfg-specific code into `cfg.rs`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-10-03 00:49:22 +00:00
commit 3b8665c5ab
35 changed files with 839 additions and 425 deletions

View file

@ -538,9 +538,7 @@ pub(crate) fn inline_asm_call<'ll>(
bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)),
)
}));
let md = unsafe { llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()) };
let md = bx.get_metadata_value(md);
llvm::LLVMSetMetadata(call, kind, md);
bx.cx.set_metadata_node(call, kind, &srcloc);
Some(call)
}

View file

@ -1,12 +1,12 @@
use std::borrow::{Borrow, Cow};
use std::iter;
use std::ops::Deref;
use std::{iter, ptr};
use rustc_ast::expand::typetree::FncTree;
pub(crate) mod autodiff;
pub(crate) mod gpu_offload;
use libc::{c_char, c_uint, size_t};
use libc::{c_char, c_uint};
use rustc_abi as abi;
use rustc_abi::{Align, Size, WrappingRange};
use rustc_codegen_ssa::MemFlags;
@ -396,10 +396,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
md.push(weight(is_cold));
}
unsafe {
let md_node = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len() as size_t);
self.cx.set_metadata(switch, llvm::MD_prof, md_node);
}
self.cx.set_metadata_node(switch, llvm::MD_prof, &md);
}
fn invoke(
@ -801,22 +798,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
return;
}
unsafe {
let llty = self.cx.val_ty(load);
let md = [
llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
];
let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
self.set_metadata(load, llvm::MD_range, md);
}
let llty = self.cx.val_ty(load);
let md = [
llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
];
self.set_metadata_node(load, llvm::MD_range, &md);
}
fn nonnull_metadata(&mut self, load: &'ll Value) {
unsafe {
let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
self.set_metadata(load, llvm::MD_nonnull, md);
}
self.set_metadata_node(load, llvm::MD_nonnull, &[]);
}
fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
@ -865,8 +856,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
//
// [1]: https://llvm.org/docs/LangRef.html#store-instruction
let one = llvm::LLVMValueAsMetadata(self.cx.const_i32(1));
let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, &one, 1);
self.set_metadata(store, llvm::MD_nontemporal, md);
self.set_metadata_node(store, llvm::MD_nontemporal, &[one]);
}
}
store
@ -1381,10 +1371,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}
fn set_invariant_load(&mut self, load: &'ll Value) {
unsafe {
let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
self.set_metadata(load, llvm::MD_invariant_load, md);
}
self.set_metadata_node(load, llvm::MD_invariant_load, &[]);
}
fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) {
@ -1528,25 +1515,16 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
}
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
fn align_metadata(&mut self, load: &'ll Value, align: Align) {
unsafe {
let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
self.set_metadata(load, llvm::MD_align, md);
}
let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
self.set_metadata_node(load, llvm::MD_align, &md);
}
fn noundef_metadata(&mut self, load: &'ll Value) {
unsafe {
let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
self.set_metadata(load, llvm::MD_noundef, md);
}
self.set_metadata_node(load, llvm::MD_noundef, &[]);
}
pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) {
unsafe {
let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
self.set_metadata(inst, llvm::MD_unpredictable, md);
}
self.set_metadata_node(inst, llvm::MD_unpredictable, &[]);
}
}
impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {

View file

@ -494,16 +494,7 @@ impl<'ll> CodegenCx<'ll, '_> {
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len());
let alloc = self.create_metadata(bytes);
let data = [section, alloc];
let meta =
unsafe { llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()) };
let val = self.get_metadata_value(meta);
unsafe {
llvm::LLVMAddNamedMetadataOperand(
self.llmod,
c"wasm.custom_sections".as_ptr(),
val,
)
};
self.module_add_named_metadata_node(self.llmod(), c"wasm.custom_sections", &data);
}
} else {
base::set_link_section(g, attrs);

View file

@ -34,7 +34,7 @@ use smallvec::SmallVec;
use crate::back::write::to_llvm_code_model;
use crate::callee::get_fn;
use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
use crate::llvm::{Metadata, MetadataKindId};
use crate::llvm::{Metadata, MetadataKindId, Module};
use crate::type_::Type;
use crate::value::Value;
use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util};
@ -495,14 +495,7 @@ pub(crate) unsafe fn create_module<'ll>(
format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"));
let name_metadata = cx.create_metadata(rustc_producer.as_bytes());
unsafe {
llvm::LLVMAddNamedMetadataOperand(
llmod,
c"llvm.ident".as_ptr(),
&cx.get_metadata_value(llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)),
);
}
cx.module_add_named_metadata_node(llmod, c"llvm.ident", &[name_metadata]);
// Emit RISC-V specific target-abi metadata
// to workaround lld as the LTO plugin not
@ -1002,6 +995,11 @@ impl CodegenCx<'_, '_> {
}
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
/// Wrapper for `LLVMMDNodeInContext2`, i.e. `llvm::MDNode::get`.
pub(crate) fn md_node_in_context(&self, md_list: &[&'ll Metadata]) -> &'ll Metadata {
unsafe { llvm::LLVMMDNodeInContext2(self.llcx(), md_list.as_ptr(), md_list.len()) }
}
/// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`.
pub(crate) fn set_metadata<'a>(
&self,
@ -1012,6 +1010,61 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
let node = self.get_metadata_value(md);
llvm::LLVMSetMetadata(val, kind_id, node);
}
/// Helper method for the sequence of calls:
/// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
/// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
/// - `LLVMSetMetadata` (to set that node as metadata of `kind_id` for `instruction`)
pub(crate) fn set_metadata_node(
&self,
instruction: &'ll Value,
kind_id: MetadataKindId,
md_list: &[&'ll Metadata],
) {
let md = self.md_node_in_context(md_list);
self.set_metadata(instruction, kind_id, md);
}
/// Helper method for the sequence of calls:
/// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
/// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
/// - `LLVMAddNamedMetadataOperand` (to set that node as metadata of `kind_name` for `module`)
pub(crate) fn module_add_named_metadata_node(
&self,
module: &'ll Module,
kind_name: &CStr,
md_list: &[&'ll Metadata],
) {
let md = self.md_node_in_context(md_list);
let md_as_val = self.get_metadata_value(md);
unsafe { llvm::LLVMAddNamedMetadataOperand(module, kind_name.as_ptr(), md_as_val) };
}
/// Helper method for the sequence of calls:
/// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
/// - `LLVMRustGlobalAddMetadata` (to set that node as metadata of `kind_id` for `global`)
pub(crate) fn global_add_metadata_node(
&self,
global: &'ll Value,
kind_id: MetadataKindId,
md_list: &[&'ll Metadata],
) {
let md = self.md_node_in_context(md_list);
unsafe { llvm::LLVMRustGlobalAddMetadata(global, kind_id, md) };
}
/// Helper method for the sequence of calls:
/// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
/// - `LLVMGlobalSetMetadata` (to set that node as metadata of `kind_id` for `global`)
pub(crate) fn global_set_metadata_node(
&self,
global: &'ll Value,
kind_id: MetadataKindId,
md_list: &[&'ll Metadata],
) {
let md = self.md_node_in_context(md_list);
unsafe { llvm::LLVMGlobalSetMetadata(global, kind_id, md) };
}
}
impl HasDataLayout for CodegenCx<'_, '_> {

View file

@ -1607,17 +1607,11 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref);
let typeid = cx.create_metadata(trait_ref_typeid.as_bytes());
unsafe {
let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
llvm::LLVMRustGlobalAddMetadata(
vtable,
llvm::MD_type,
llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()),
);
let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64));
let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1);
llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata);
}
let type_ = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
cx.global_add_metadata_node(vtable, llvm::MD_type, &type_);
let vcall_visibility = [llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64))];
cx.global_set_metadata_node(vtable, llvm::MD_vcall_visibility, &vcall_visibility);
}
/// Creates debug information for the given vtable, which is for the

View file

@ -302,26 +302,14 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
let typeid_metadata = self.create_metadata(typeid);
unsafe {
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
llvm::LLVMRustGlobalAddMetadata(
function,
llvm::MD_type,
llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
)
}
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
self.global_add_metadata_node(function, llvm::MD_type, &v);
}
fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
let typeid_metadata = self.create_metadata(typeid);
unsafe {
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
llvm::LLVMGlobalSetMetadata(
function,
llvm::MD_type,
llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
)
}
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
self.global_set_metadata_node(function, llvm::MD_type, &v);
}
fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> {
@ -329,32 +317,12 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
let kcfi_type_metadata = self.const_u32(kcfi_typeid);
unsafe {
llvm::LLVMRustGlobalAddMetadata(
function,
llvm::MD_kcfi_type,
llvm::LLVMMDNodeInContext2(
self.llcx,
&llvm::LLVMValueAsMetadata(kcfi_type_metadata),
1,
),
)
}
let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
self.global_add_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
}
fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
let kcfi_type_metadata = self.const_u32(kcfi_typeid);
unsafe {
llvm::LLVMGlobalSetMetadata(
function,
llvm::MD_kcfi_type,
llvm::LLVMMDNodeInContext2(
self.llcx,
&llvm::LLVMValueAsMetadata(kcfi_type_metadata),
1,
),
)
}
let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
self.global_set_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
}
}

View file

@ -587,6 +587,7 @@ Select which editor you would like to set up [default: None]: ";
"631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9",
"080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce",
"f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1",
"e260553b71e4773c30a63c4b23b42b279fc73e72f95b775c47b7b7c511c51595",
],
EditorKind::Helix => &[
"2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233",
@ -594,6 +595,7 @@ Select which editor you would like to set up [default: None]: ";
"f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5",
"198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5",
"1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1",
"eec09a09452682060afd23dd5d3536ccac5615b3cdbf427366446901215fb9f6",
],
EditorKind::Vim | EditorKind::VsCode => &[
"ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
@ -610,6 +612,7 @@ Select which editor you would like to set up [default: None]: ";
"f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893",
"701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12",
"a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054",
"02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47",
],
EditorKind::Zed => &[
"bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c",
@ -617,6 +620,7 @@ Select which editor you would like to set up [default: None]: ";
"2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26",
"4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6",
"f0bb3d23ab1a49175ab0ef5c4071af95bb03d01d460776cdb716d91333443382",
"5ef83292111d9a8bb63b6afc3abf42d0bc78fe24985f0d2e039e73258b5dab8f",
],
}
}

View file

@ -262,6 +262,20 @@ Consider writing the test as a proper incremental test instead.
</div>
#### The edition directive
The `//@ edition` directive can take an exact edition, a bounded half-open range of editions or a left-bounded half-open range of editions, this affects which edition is used by `./x test` to run the test. For example:
- A test with the `//@ edition: 2018` directive will only run under the 2018 edition.
- A test with the `//@ edition: 2015..2021` directive can be run under both the 2015 and 2018 editions. However, CI will only run the test with the lowest edition possible (2015 in this case).
- A test with the `//@ edition: 2018..` directive will run under any edition greater or equal than 2018. However, CI will only run the test with the lowest edition possible (2018 in this case).
You can also force `./x test` to use a specific edition by passing the `-- --edition=` argument. However, tests with the `//@ edition` directive will clamp the value passed to the argument. For example, if we run `./x test -- --edition=2015`:
- A test with the `//@ edition: 2018` will run with the 2018 edition.
- A test with the `//@ edition: 2015..2021` will be run with the 2015 edition.
- A test with the `//@ edition: 2018..` will run with the 2018 edition.
### Rustdoc
| Directive | Explanation | Supported test suites | Possible values |

View file

@ -47,6 +47,7 @@
- [\*-apple-watchos](platform-support/apple-watchos.md)
- [\*-apple-visionos](platform-support/apple-visionos.md)
- [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md)
- [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md)
- [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md)
- [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md)
- [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md)
@ -67,6 +68,7 @@
- [arm\*-unknown-linux-\*](./platform-support/arm-linux.md)
- [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
- [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md)
- [armv7-unknown-linux-gnueabi](platform-support/armv7-unknown-linux-gnueabi.md)
- [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
- [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
- [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md)

View file

@ -34,7 +34,7 @@ target | notes
-------|-------
[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+)
[`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC
`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1+, glibc 2.17+)
[`aarch64-unknown-linux-gnu`](platform-support/aarch64-unknown-linux-gnu.md) | ARM64 Linux (kernel 4.1+, glibc 2.17+)
[`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI]
[`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+)
@ -93,7 +93,7 @@ target | notes
[`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony
`arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17)
`arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17)
`armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17)
[`armv7-unknown-linux-gnueabihf`](platform-support/armv7-unknown-linux-gnueabi.md) | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17)
[`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony
[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36), LSX required
[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5), LSX required
@ -159,7 +159,7 @@ target | std | notes
[`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23)
`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3
[`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android
`armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27)
[`armv7-unknown-linux-gnueabi`](platform-support/armv7-unknown-linux-gnueabi.md) | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27)
`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3
`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat
[`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A

View file

@ -0,0 +1,50 @@
# `aarch64-unknown-linux-gnu`
**Tier: 1 (with Host Tools)**
Target for 64-bit little endian ARMv8-A Linux 4.1+ programs using glibc 2.17+.
## Target maintainers
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Requirements
Building the target itself requires a 64-bit little endian ARMv8-A compiler that is supported by
`cc-rs`.
## Building the target
The target can be built by enabling it for a `rustc` build:
```toml
[build]
target = ["aarch64-unknown-linux-gnu"]
```
If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the
`bootstrap.toml`:
```toml
[target.aarch64-unknown-linux-musl]
cc = "aarch64-linux-gnu-gcc"
cxx = "aarch64-linux-gnu-g++"
ar = "aarch64-linux-gnu-ar"
linker = "aarch64-linux-gnu-gcc"
```
## Building Rust programs
This target is distributed through `rustup`, and otherwise requires no special configuration.
## Cross-compilation
This target can be cross-compiled from any host.
## Testing
This target can be tested as normal with `x.py` on a 64-bit little endian ARMv8-A host or via QEMU
emulation.

View file

@ -14,9 +14,12 @@ Processors in this family include the [Arm Cortex-A35, 53, 76, etc][aarch64-cpus
## Target maintainers
[Rust Embedded Devices Working Group Arm Team]
- [Rust Embedded Devices Working Group Arm Team]
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Target CPU and Target Feature options

View file

@ -14,8 +14,8 @@ Linux (but not Android). Those targets are:
* [`armv5te-unknown-linux-gnueabi`](armv5te-unknown-linux-gnueabi.md)
* `armv5te-unknown-linux-musleabi`
* `armv5te-unknown-linux-uclibceabi`
* `armv7-unknown-linux-gnueabi`
* `armv7-unknown-linux-gnueabihf`
* [`armv7-unknown-linux-gnueabi`](armv7-unknown-linux-gnueabi.md)
* [`armv7-unknown-linux-gnueabihf`](armv7-unknown-linux-gnueabi.md)
* `armv7-unknown-linux-musleabi`
* `armv7-unknown-linux-musleabihf`
* `armv7-unknown-linux-ohos`

View file

@ -0,0 +1,51 @@
# `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-gnueabihf`
* **Tier: 2 (with Host Tools)** for `armv7-unknown-linux-gnueabihf`
* **Tier: 2** for `armv7-unknown-linux-gnueabi`
Target for 32-bit little endian ARMv7-A Linux 3.2+ programs using glibc 2.17+.
## Target maintainers
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Requirements
Building the targets themselves requires a 32-bit little endian ARMv7-A compiler that is supported
by `cc-rs`.
## Building the target
These targets can be built by enabling it for a `rustc` build:
```toml
[build]
target = ["armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabi"]
```
If cross-compiling, make sure your C compiler is included in `$PATH`, then add it to the
`bootstrap.toml`:
```toml
[target.aarch64-unknown-linux-musl]
cc = "arm-linux-gnu-gcc"
cxx = "arm-linux-gnu-g++"
ar = "arm-linux-gnu-ar"
linker = "arm-linux-gnu-gcc"
```
## Building Rust programs
These targets is distributed through `rustup`, and otherwise requires no special configuration.
## Cross-compilation
These targets can be cross-compiled from any host.
## Testing
These targets can be tested as normal with `x.py` on a 32-bit little endian ARMv7-A host or via
QEMU emulation.

View file

@ -19,9 +19,12 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
## Target maintainers
[Rust Embedded Devices Working Group Arm Team]
- [Rust Embedded Devices Working Group Arm Team]
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Requirements

View file

@ -15,10 +15,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
## Target maintainers
[@chrisnc](https://github.com/chrisnc)
[Rust Embedded Devices Working Group Arm Team]
- [@chrisnc](https://github.com/chrisnc)
- [Rust Embedded Devices Working Group Arm Team]
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Requirements

View file

@ -17,10 +17,13 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
## Target maintainers
[@chrisnc](https://github.com/chrisnc)
[Rust Embedded Devices Working Group Arm Team]
- [@chrisnc](https://github.com/chrisnc)
- [Rust Embedded Devices Working Group Arm Team]
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Requirements

View file

@ -22,7 +22,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
## Target maintainers
[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Target CPU and Target Feature options

View file

@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv7-M].
## Target maintainers
[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Target CPU and Target Feature options

View file

@ -23,7 +23,11 @@ only option because there is no FPU support in [Armv8-M] Baseline.
## Target maintainers
[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Target CPU and Target Feature options

View file

@ -26,7 +26,11 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
## Target maintainers
[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team)
- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Target CPU and Target Feature options

View file

@ -13,8 +13,12 @@ Available targets:
## Target maintainers
[@dvdhrm](https://github.com/dvdhrm)
[@nicholasbishop](https://github.com/nicholasbishop)
- [@dvdhrm](https://github.com/dvdhrm)
- [@nicholasbishop](https://github.com/nicholasbishop)
- (for `aarch64-unknown-uefi` only) [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
[arm_email]: mailto:rust@arm.com
## Requirements

View file

@ -6,6 +6,8 @@
:overrideCommand ["python3"
"x.py"
"check"
"--build-dir"
"build-rust-analyzer"
"--json-output"])
:linkedProjects ["Cargo.toml"
"compiler/rustc_codegen_cranelift/Cargo.toml"
@ -13,9 +15,9 @@
"library/Cargo.toml"
"src/bootstrap/Cargo.toml"
"src/tools/rust-analyzer/Cargo.toml"]
:rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
:rustfmt ( :overrideCommand ["build-rust-analyzer/host/rustfmt/bin/rustfmt"
"--edition=2024"])
:procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
:procMacro ( :server "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
:enable t)
:cargo ( :buildScripts ( :enable t
:invocationLocation "root"
@ -23,6 +25,8 @@
:overrideCommand ["python3"
"x.py"
"check"
"--build-dir"
"build-rust-analyzer"
"--json-output"
"--compile-time-deps"])]
:sysrootSrc "./library"

View file

@ -1,10 +1,10 @@
# This config uses a separate build directory for rust-analyzer,
# so that r-a's checks don't block user `x` commands and vice-verse.
# R-a's build directory is located in `build/rust-analyzer`.
# R-a's build directory is located in `build-rust-analyzer`.
#
# To build rustfmt and proc macro server for r-a run the following command:
# ```
# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build/rust-analyzer
# x b proc-macro-srv-cli rustfmt --stage 0 --build-dir build-rust-analyzer
# ```
[language-server.rust-analyzer.config]
@ -26,17 +26,17 @@ overrideCommand = [
"check",
"--json-output",
"--build-dir",
"build/rust-analyzer",
"build-rust-analyzer",
]
[language-server.rust-analyzer.config.rustfmt]
overrideCommand = [
"build/rust-analyzer/host/rustfmt/bin/rustfmt",
"build-rust-analyzer/host/rustfmt/bin/rustfmt",
"--edition=2024"
]
[language-server.rust-analyzer.config.procMacro]
server = "build/rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
server = "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
enable = true
[language-server.rust-analyzer.config.rustc]
@ -58,6 +58,6 @@ overrideCommand = [
"check",
"--json-output",
"--build-dir",
"build/rust-analyzer",
"--compile-time-deps"
"build-rust-analyzer",
"--compile-time-deps",
]

View file

@ -5,6 +5,8 @@
"python3",
"x.py",
"check",
"--build-dir",
"build-rust-analyzer",
"--json-output"
],
"rust-analyzer.linkedProjects": [
@ -16,10 +18,10 @@
"src/tools/rust-analyzer/Cargo.toml"
],
"rust-analyzer.rustfmt.overrideCommand": [
"${workspaceFolder}/build/host/rustfmt/bin/rustfmt",
"${workspaceFolder}/build-rust-analyzer/host/rustfmt/bin/rustfmt",
"--edition=2024"
],
"rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv",
"rust-analyzer.procMacro.server": "${workspaceFolder}/build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv",
"rust-analyzer.procMacro.enable": true,
"rust-analyzer.cargo.buildScripts.enable": true,
"rust-analyzer.cargo.buildScripts.invocationStrategy": "once",
@ -27,6 +29,8 @@
"python3",
"x.py",
"check",
"--build-dir",
"build-rust-analyzer",
"--json-output",
"--compile-time-deps"
],

View file

@ -7,7 +7,15 @@
"enable": true,
"invocationLocation": "root",
"invocationStrategy": "once",
"overrideCommand": ["python3", "x.py", "check", "--json-output", "--compile-time-deps"]
"overrideCommand": [
"python3",
"x.py",
"check",
"--build-dir",
"build-rust-analyzer",
"--compile-time-deps",
"--json-output"
]
},
"extraEnv": {
"RUSTC_BOOTSTRAP": "1"
@ -17,7 +25,14 @@
"check": {
"invocationLocation": "root",
"invocationStrategy": "once",
"overrideCommand": ["python3", "x.py", "check", "--json-output"]
"overrideCommand": [
"python3",
"x.py",
"check",
"--json-output",
"--build-dir",
"build-rust-analyzer"
]
},
"linkedProjects": [
"Cargo.toml",
@ -29,14 +44,14 @@
],
"procMacro": {
"enable": true,
"server": "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
"server": "build-rust-analyzer/host/stage0/libexec/rust-analyzer-proc-macro-srv"
},
"rustc": {
"source": "./Cargo.toml"
},
"rustfmt": {
"overrideCommand": [
"build/host/rustfmt/bin/rustfmt",
"build-rust-analyzer/host/rustfmt/bin/rustfmt",
"--edition=2024"
]
},

View file

@ -3,14 +3,18 @@
// FIXME: Once the portability lint RFC is implemented (see tracking issue #41619),
// switch to use those structures instead.
use std::sync::Arc;
use std::{fmt, mem, ops};
use itertools::Either;
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::attrs::AttributeKind;
use rustc_middle::ty::TyCtxt;
use rustc_session::parse::ParseSess;
use rustc_span::Span;
use rustc_span::symbol::{Symbol, sym};
use {rustc_ast as ast, rustc_hir as hir};
use crate::display::{Joined as _, MaybeDisplay, Wrapped};
use crate::html::escape::Escape;
@ -600,3 +604,264 @@ impl fmt::Display for Display<'_> {
}
}
}
/// This type keeps track of (doc) cfg information as we go down the item tree.
#[derive(Clone, Debug)]
pub(crate) struct CfgInfo {
/// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active
/// `doc(auto_cfg(show(...)))` cfgs.
hidden_cfg: FxHashSet<Cfg>,
/// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
/// taking into account the `hidden_cfg` information.
current_cfg: Cfg,
/// Whether the `doc(auto_cfg())` feature is enabled or not at this point.
auto_cfg_active: bool,
/// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`,
/// instead we will concatenate with it. However, if it's not the case, we need to overwrite
/// `current_cfg`.
parent_is_doc_cfg: bool,
}
impl Default for CfgInfo {
fn default() -> Self {
Self {
hidden_cfg: FxHashSet::from_iter([
Cfg::Cfg(sym::test, None),
Cfg::Cfg(sym::doc, None),
Cfg::Cfg(sym::doctest, None),
]),
current_cfg: Cfg::True,
auto_cfg_active: true,
parent_is_doc_cfg: false,
}
}
}
fn show_hide_show_conflict_error(
tcx: TyCtxt<'_>,
item_span: rustc_span::Span,
previous: rustc_span::Span,
) {
let mut diag = tcx.sess.dcx().struct_span_err(
item_span,
format!(
"same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
),
);
diag.span_note(previous, "first change was here");
diag.emit();
}
/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument.
///
/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
/// `auto_cfg(show(...))` on the same item and emits an error if it's the case.
///
/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs`
/// and in `new_hide_attrs` arguments.
fn handle_auto_cfg_hide_show(
tcx: TyCtxt<'_>,
cfg_info: &mut CfgInfo,
sub_attr: &MetaItemInner,
is_show: bool,
new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
) {
if let MetaItemInner::MetaItem(item) = sub_attr
&& let MetaItemKind::List(items) = &item.kind
{
for item in items {
// FIXME: Report in case `Cfg::parse` reports an error?
if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
if is_show {
if let Some(span) = new_hide_attrs.get(&(key, value)) {
show_hide_show_conflict_error(tcx, item.span(), *span);
} else {
new_show_attrs.insert((key, value), item.span());
}
cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
} else {
if let Some(span) = new_show_attrs.get(&(key, value)) {
show_hide_show_conflict_error(tcx, item.span(), *span);
} else {
new_hide_attrs.insert((key, value), item.span());
}
cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
}
}
}
}
}
pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
attrs: I,
tcx: TyCtxt<'_>,
cfg_info: &mut CfgInfo,
) -> Option<Arc<Cfg>> {
fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
let mut iter = it.into_iter();
let item = iter.next()?;
if iter.next().is_some() {
return None;
}
Some(item)
}
fn check_changed_auto_active_status(
changed_auto_active_status: &mut Option<rustc_span::Span>,
attr: &ast::MetaItem,
cfg_info: &mut CfgInfo,
tcx: TyCtxt<'_>,
new_value: bool,
) -> bool {
if let Some(first_change) = changed_auto_active_status {
if cfg_info.auto_cfg_active != new_value {
tcx.sess
.dcx()
.struct_span_err(
vec![*first_change, attr.span],
"`auto_cfg` was disabled and enabled more than once on the same item",
)
.emit();
return true;
}
} else {
*changed_auto_active_status = Some(attr.span);
}
cfg_info.auto_cfg_active = new_value;
false
}
let mut new_show_attrs = FxHashMap::default();
let mut new_hide_attrs = FxHashMap::default();
let mut doc_cfg = attrs
.clone()
.filter(|attr| attr.has_name(sym::doc))
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
.filter(|attr| attr.has_name(sym::cfg))
.peekable();
// If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes.
if doc_cfg.peek().is_some() {
let sess = tcx.sess;
// We overwrite existing `cfg`.
if !cfg_info.parent_is_doc_cfg {
cfg_info.current_cfg = Cfg::True;
cfg_info.parent_is_doc_cfg = true;
}
for attr in doc_cfg {
if let Some(cfg_mi) =
attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess))
{
match Cfg::parse(cfg_mi) {
Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
Err(e) => {
sess.dcx().span_err(e.span, e.msg);
}
}
}
}
} else {
cfg_info.parent_is_doc_cfg = false;
}
let mut changed_auto_active_status = None;
// We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
for attr in attrs {
if let Some(ident) = attr.ident()
&& ident.name == sym::doc
&& let Some(attrs) = attr.meta_item_list()
{
for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
let MetaItemInner::MetaItem(attr) = attr else {
continue;
};
match &attr.kind {
MetaItemKind::Word => {
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
true,
) {
return None;
}
}
MetaItemKind::NameValue(lit) => {
if let LitKind::Bool(value) = lit.kind {
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
value,
) {
return None;
}
}
}
MetaItemKind::List(sub_attrs) => {
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
true,
) {
return None;
}
for sub_attr in sub_attrs.iter() {
if let Some(ident) = sub_attr.ident()
&& (ident.name == sym::show || ident.name == sym::hide)
{
handle_auto_cfg_hide_show(
tcx,
cfg_info,
&sub_attr,
ident.name == sym::show,
&mut new_show_attrs,
&mut new_hide_attrs,
);
}
}
}
}
}
} else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
// Treat `#[target_feature(enable = "feat")]` attributes as if they were
// `#[doc(cfg(target_feature = "feat"))]` attributes as well.
for (feature, _) in features {
cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
}
continue;
} else if !cfg_info.parent_is_doc_cfg
&& let Some(ident) = attr.ident()
&& matches!(ident.name, sym::cfg | sym::cfg_trace)
&& let Some(attr) = single(attr.meta_item_list()?)
&& let Ok(new_cfg) = Cfg::parse(&attr)
{
cfg_info.current_cfg &= new_cfg;
}
}
// If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing
// to be done here.
if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
None
} else if cfg_info.parent_is_doc_cfg {
if cfg_info.current_cfg == Cfg::True {
None
} else {
Some(Arc::new(cfg_info.current_cfg.clone()))
}
} else {
// If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
// hidden ones afterward.
match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
None | Some(Cfg::True) => None,
Some(cfg) => Some(Arc::new(cfg)),
}
}
}

View file

@ -58,6 +58,7 @@ use tracing::{debug, instrument};
use utils::*;
use {rustc_ast as ast, rustc_hir as hir};
pub(crate) use self::cfg::{CfgInfo, extract_cfg_from_attrs};
pub(crate) use self::types::*;
pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls};
use crate::core::DocContext;

View file

@ -6,8 +6,7 @@ use std::{fmt, iter};
use arrayvec::ArrayVec;
use itertools::Either;
use rustc_abi::{ExternAbi, VariantIdx};
use rustc_ast::ast::{LitKind, MetaItemInner, MetaItemKind};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@ -922,267 +921,6 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>(
.flatten()
}
/// This type keeps track of (doc) cfg information as we go down the item tree.
#[derive(Clone, Debug)]
pub(crate) struct CfgInfo {
/// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active
/// `doc(auto_cfg(show(...)))` cfgs.
hidden_cfg: FxHashSet<Cfg>,
/// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
/// taking into account the `hidden_cfg` information.
current_cfg: Cfg,
/// Whether the `doc(auto_cfg())` feature is enabled or not at this point.
auto_cfg_active: bool,
/// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`,
/// instead we will concatenate with it. However, if it's not the case, we need to overwrite
/// `current_cfg`.
parent_is_doc_cfg: bool,
}
impl Default for CfgInfo {
fn default() -> Self {
Self {
hidden_cfg: FxHashSet::from_iter([
Cfg::Cfg(sym::test, None),
Cfg::Cfg(sym::doc, None),
Cfg::Cfg(sym::doctest, None),
]),
current_cfg: Cfg::True,
auto_cfg_active: true,
parent_is_doc_cfg: false,
}
}
}
fn show_hide_show_conflict_error(
tcx: TyCtxt<'_>,
item_span: rustc_span::Span,
previous: rustc_span::Span,
) {
let mut diag = tcx.sess.dcx().struct_span_err(
item_span,
format!(
"same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
),
);
diag.span_note(previous, "first change was here");
diag.emit();
}
/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument.
///
/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
/// `auto_cfg(show(...))` on the same item and emits an error if it's the case.
///
/// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs`
/// and in `new_hide_attrs` arguments.
fn handle_auto_cfg_hide_show(
tcx: TyCtxt<'_>,
cfg_info: &mut CfgInfo,
sub_attr: &MetaItemInner,
is_show: bool,
new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
) {
if let MetaItemInner::MetaItem(item) = sub_attr
&& let MetaItemKind::List(items) = &item.kind
{
for item in items {
// FIXME: Report in case `Cfg::parse` reports an error?
if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
if is_show {
if let Some(span) = new_hide_attrs.get(&(key, value)) {
show_hide_show_conflict_error(tcx, item.span(), *span);
} else {
new_show_attrs.insert((key, value), item.span());
}
cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
} else {
if let Some(span) = new_show_attrs.get(&(key, value)) {
show_hide_show_conflict_error(tcx, item.span(), *span);
} else {
new_hide_attrs.insert((key, value), item.span());
}
cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
}
}
}
}
}
pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
attrs: I,
tcx: TyCtxt<'_>,
cfg_info: &mut CfgInfo,
) -> Option<Arc<Cfg>> {
fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
let mut iter = it.into_iter();
let item = iter.next()?;
if iter.next().is_some() {
return None;
}
Some(item)
}
fn check_changed_auto_active_status(
changed_auto_active_status: &mut Option<rustc_span::Span>,
attr: &ast::MetaItem,
cfg_info: &mut CfgInfo,
tcx: TyCtxt<'_>,
new_value: bool,
) -> bool {
if let Some(first_change) = changed_auto_active_status {
if cfg_info.auto_cfg_active != new_value {
tcx.sess
.dcx()
.struct_span_err(
vec![*first_change, attr.span],
"`auto_cfg` was disabled and enabled more than once on the same item",
)
.emit();
return true;
}
} else {
*changed_auto_active_status = Some(attr.span);
}
cfg_info.auto_cfg_active = new_value;
false
}
let mut new_show_attrs = FxHashMap::default();
let mut new_hide_attrs = FxHashMap::default();
let mut doc_cfg = attrs
.clone()
.filter(|attr| attr.has_name(sym::doc))
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
.filter(|attr| attr.has_name(sym::cfg))
.peekable();
// If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes.
if doc_cfg.peek().is_some() {
let sess = tcx.sess;
// We overwrite existing `cfg`.
if !cfg_info.parent_is_doc_cfg {
cfg_info.current_cfg = Cfg::True;
cfg_info.parent_is_doc_cfg = true;
}
for attr in doc_cfg {
if let Some(cfg_mi) =
attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess))
{
match Cfg::parse(cfg_mi) {
Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
Err(e) => {
sess.dcx().span_err(e.span, e.msg);
}
}
}
}
} else {
cfg_info.parent_is_doc_cfg = false;
}
let mut changed_auto_active_status = None;
// We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
for attr in attrs {
if let Some(ident) = attr.ident()
&& ident.name == sym::doc
&& let Some(attrs) = attr.meta_item_list()
{
for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
let MetaItemInner::MetaItem(attr) = attr else {
continue;
};
match &attr.kind {
MetaItemKind::Word => {
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
true,
) {
return None;
}
}
MetaItemKind::NameValue(lit) => {
if let LitKind::Bool(value) = lit.kind {
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
value,
) {
return None;
}
}
}
MetaItemKind::List(sub_attrs) => {
if check_changed_auto_active_status(
&mut changed_auto_active_status,
attr,
cfg_info,
tcx,
true,
) {
return None;
}
for sub_attr in sub_attrs.iter() {
if let Some(ident) = sub_attr.ident()
&& (ident.name == sym::show || ident.name == sym::hide)
{
handle_auto_cfg_hide_show(
tcx,
cfg_info,
&sub_attr,
ident.name == sym::show,
&mut new_show_attrs,
&mut new_hide_attrs,
);
}
}
}
}
}
} else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
// Treat `#[target_feature(enable = "feat")]` attributes as if they were
// `#[doc(cfg(target_feature = "feat"))]` attributes as well.
for (feature, _) in features {
cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
}
continue;
} else if !cfg_info.parent_is_doc_cfg
&& let Some(ident) = attr.ident()
&& matches!(ident.name, sym::cfg | sym::cfg_trace)
&& let Some(attr) = single(attr.meta_item_list()?)
&& let Ok(new_cfg) = Cfg::parse(&attr)
{
cfg_info.current_cfg &= new_cfg;
}
}
// If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing
// to be done here.
if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
None
} else if cfg_info.parent_is_doc_cfg {
if cfg_info.current_cfg == Cfg::True {
None
} else {
Some(Arc::new(cfg_info.current_cfg.clone()))
}
} else {
// If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
// hidden ones afterward.
match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
None | Some(Cfg::True) => None,
Some(cfg) => Some(Arc::new(cfg)),
}
}
}
pub(crate) trait NestedAttributesExt {
/// Returns `true` if the attribute list contains a specific `word`
fn has_word(self, word: Symbol) -> bool

View file

@ -7,6 +7,7 @@ use build_helper::git::GitConfig;
use camino::{Utf8Path, Utf8PathBuf};
use semver::Version;
use crate::edition::Edition;
use crate::executor::ColorConfig;
use crate::fatal;
use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum};
@ -612,10 +613,7 @@ pub struct Config {
pub git_hash: bool,
/// The default Rust edition.
///
/// FIXME: perform stronger validation for this. There are editions that *definitely* exists,
/// but there might also be "future" edition.
pub edition: Option<String>,
pub edition: Option<Edition>,
// Configuration for various run-make tests frobbing things like C compilers or querying about
// various LLVM component information.

View file

@ -16,10 +16,11 @@ use crate::directives::directive_names::{
KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES,
};
use crate::directives::needs::CachedNeedsConditions;
use crate::edition::{Edition, parse_edition};
use crate::errors::ErrorKind;
use crate::executor::{CollectedTestDesc, ShouldPanic};
use crate::help;
use crate::util::static_regex;
use crate::{fatal, help};
pub(crate) mod auxiliary;
mod cfg;
@ -437,10 +438,13 @@ impl TestProps {
panic!("`compiler-flags` directive should be spelled `compile-flags`");
}
if let Some(edition) = config.parse_edition(ln, testfile) {
if let Some(range) = parse_edition_range(config, ln, testfile) {
// The edition is added at the start, since flags from //@compile-flags must
// be passed to rustc last.
self.compile_flags.insert(0, format!("--edition={}", edition.trim()));
self.compile_flags.insert(
0,
format!("--edition={}", range.edition_to_test(config.edition)),
);
has_edition = true;
}
@ -1124,10 +1128,6 @@ impl Config {
}
}
fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<String> {
self.parse_name_value_directive(line, "edition", testfile)
}
fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) {
// If the flag is already true, don't bother looking at the directive.
*value = *value || self.parse_name_directive(line, directive);
@ -1769,3 +1769,86 @@ enum IgnoreDecision {
Continue,
Error { message: String },
}
fn parse_edition_range(
config: &Config,
line: &DirectiveLine<'_>,
testfile: &Utf8Path,
) -> Option<EditionRange> {
let raw = config.parse_name_value_directive(line, "edition", testfile)?;
let line_number = line.line_number;
// Edition range is half-open: `[lower_bound, upper_bound)`
if let Some((lower_bound, upper_bound)) = raw.split_once("..") {
Some(match (maybe_parse_edition(lower_bound), maybe_parse_edition(upper_bound)) {
(Some(lower_bound), Some(upper_bound)) if upper_bound <= lower_bound => {
fatal!(
"{testfile}:{line_number}: the left side of `//@ edition` cannot be greater than or equal to the right side"
);
}
(Some(lower_bound), Some(upper_bound)) => {
EditionRange::Range { lower_bound, upper_bound }
}
(Some(lower_bound), None) => EditionRange::RangeFrom(lower_bound),
(None, Some(_)) => {
fatal!(
"{testfile}:{line_number}: `..edition` is not a supported range in `//@ edition`"
);
}
(None, None) => {
fatal!("{testfile}:{line_number}: `..` is not a supported range in `//@ edition`");
}
})
} else {
match maybe_parse_edition(&raw) {
Some(edition) => Some(EditionRange::Exact(edition)),
None => {
fatal!("{testfile}:{line_number}: empty value for `//@ edition`");
}
}
}
}
fn maybe_parse_edition(mut input: &str) -> Option<Edition> {
input = input.trim();
if input.is_empty() {
return None;
}
Some(parse_edition(input))
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum EditionRange {
Exact(Edition),
RangeFrom(Edition),
/// Half-open range: `[lower_bound, upper_bound)`
Range {
lower_bound: Edition,
upper_bound: Edition,
},
}
impl EditionRange {
fn edition_to_test(&self, requested: impl Into<Option<Edition>>) -> Edition {
let min_edition = Edition::Year(2015);
let requested = requested.into().unwrap_or(min_edition);
match *self {
EditionRange::Exact(exact) => exact,
EditionRange::RangeFrom(lower_bound) => {
if requested >= lower_bound {
requested
} else {
lower_bound
}
}
EditionRange::Range { lower_bound, upper_bound } => {
if requested >= lower_bound && requested < upper_bound {
requested
} else {
lower_bound
}
}
}
}
}

View file

@ -4,10 +4,11 @@ use camino::Utf8Path;
use semver::Version;
use super::{
DirectivesCache, EarlyProps, extract_llvm_version, extract_version_range, iter_directives,
parse_normalize_rule,
DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version,
extract_version_range, iter_directives, parse_normalize_rule,
};
use crate::common::{Config, Debugger, TestMode};
use crate::directives::parse_edition;
use crate::executor::{CollectedTestDesc, ShouldPanic};
fn make_test_description<R: Read>(
@ -73,6 +74,7 @@ fn test_parse_normalize_rule() {
struct ConfigBuilder {
mode: Option<String>,
channel: Option<String>,
edition: Option<Edition>,
host: Option<String>,
target: Option<String>,
stage: Option<u32>,
@ -96,6 +98,11 @@ impl ConfigBuilder {
self
}
fn edition(&mut self, e: Edition) -> &mut Self {
self.edition = Some(e);
self
}
fn host(&mut self, s: &str) -> &mut Self {
self.host = Some(s.to_owned());
self
@ -183,6 +190,10 @@ impl ConfigBuilder {
];
let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
if let Some(edition) = &self.edition {
args.push(format!("--edition={edition}"));
}
if let Some(ref llvm_version) = self.llvm_version {
args.push("--llvm-version".to_owned());
args.push(llvm_version.clone());
@ -941,3 +952,130 @@ fn test_needs_target_std() {
let config = cfg().target("x86_64-unknown-linux-gnu").build();
assert!(!check_ignore(&config, "//@ needs-target-std"));
}
fn parse_edition_range(line: &str) -> Option<EditionRange> {
let config = cfg().build();
let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line };
super::parse_edition_range(&config, &line, "tmp.rs".into())
}
#[test]
fn test_parse_edition_range() {
assert_eq!(None, parse_edition_range("hello-world"));
assert_eq!(None, parse_edition_range("edition"));
assert_eq!(Some(EditionRange::Exact(2018.into())), parse_edition_range("edition: 2018"));
assert_eq!(Some(EditionRange::Exact(2021.into())), parse_edition_range("edition:2021"));
assert_eq!(Some(EditionRange::Exact(2024.into())), parse_edition_range("edition: 2024 "));
assert_eq!(Some(EditionRange::Exact(Edition::Future)), parse_edition_range("edition: future"));
assert_eq!(Some(EditionRange::RangeFrom(2018.into())), parse_edition_range("edition: 2018.."));
assert_eq!(Some(EditionRange::RangeFrom(2021.into())), parse_edition_range("edition:2021 .."));
assert_eq!(
Some(EditionRange::RangeFrom(2024.into())),
parse_edition_range("edition: 2024 .. ")
);
assert_eq!(
Some(EditionRange::RangeFrom(Edition::Future)),
parse_edition_range("edition: future.. ")
);
assert_eq!(
Some(EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() }),
parse_edition_range("edition: 2018..2024")
);
assert_eq!(
Some(EditionRange::Range { lower_bound: 2015.into(), upper_bound: 2021.into() }),
parse_edition_range("edition:2015 .. 2021 ")
);
assert_eq!(
Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: 2027.into() }),
parse_edition_range("edition: 2021 .. 2027 ")
);
assert_eq!(
Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: Edition::Future }),
parse_edition_range("edition: 2021..future")
);
}
#[test]
#[should_panic]
fn test_parse_edition_range_empty() {
parse_edition_range("edition:");
}
#[test]
#[should_panic]
fn test_parse_edition_range_invalid_edition() {
parse_edition_range("edition: hello");
}
#[test]
#[should_panic]
fn test_parse_edition_range_double_dots() {
parse_edition_range("edition: ..");
}
#[test]
#[should_panic]
fn test_parse_edition_range_inverted_range() {
parse_edition_range("edition: 2021..2015");
}
#[test]
#[should_panic]
fn test_parse_edition_range_inverted_range_future() {
parse_edition_range("edition: future..2015");
}
#[test]
#[should_panic]
fn test_parse_edition_range_empty_range() {
parse_edition_range("edition: 2021..2021");
}
#[track_caller]
fn assert_edition_to_test(
expected: impl Into<Edition>,
range: EditionRange,
default: Option<Edition>,
) {
let mut cfg = cfg();
if let Some(default) = default {
cfg.edition(default);
}
assert_eq!(expected.into(), range.edition_to_test(cfg.build().edition));
}
#[test]
fn test_edition_range_edition_to_test() {
let e2015 = parse_edition("2015");
let e2018 = parse_edition("2018");
let e2021 = parse_edition("2021");
let e2024 = parse_edition("2024");
let efuture = parse_edition("future");
let exact = EditionRange::Exact(2021.into());
assert_edition_to_test(2021, exact, None);
assert_edition_to_test(2021, exact, Some(e2018));
assert_edition_to_test(2021, exact, Some(efuture));
assert_edition_to_test(Edition::Future, EditionRange::Exact(Edition::Future), None);
let greater_equal_than = EditionRange::RangeFrom(2021.into());
assert_edition_to_test(2021, greater_equal_than, None);
assert_edition_to_test(2021, greater_equal_than, Some(e2015));
assert_edition_to_test(2021, greater_equal_than, Some(e2018));
assert_edition_to_test(2021, greater_equal_than, Some(e2021));
assert_edition_to_test(2024, greater_equal_than, Some(e2024));
assert_edition_to_test(Edition::Future, greater_equal_than, Some(efuture));
let range = EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() };
assert_edition_to_test(2018, range, None);
assert_edition_to_test(2018, range, Some(e2015));
assert_edition_to_test(2018, range, Some(e2018));
assert_edition_to_test(2021, range, Some(e2021));
assert_edition_to_test(2018, range, Some(e2024));
assert_edition_to_test(2018, range, Some(efuture));
}

View file

@ -0,0 +1,35 @@
use crate::fatal;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Edition {
// Note that the ordering here is load-bearing, as we want the future edition to be greater than
// any year-based edition.
Year(u32),
Future,
}
impl std::fmt::Display for Edition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Edition::Year(year) => write!(f, "{year}"),
Edition::Future => f.write_str("future"),
}
}
}
impl From<u32> for Edition {
fn from(value: u32) -> Self {
Edition::Year(value)
}
}
pub fn parse_edition(mut input: &str) -> Edition {
input = input.trim();
if input == "future" {
Edition::Future
} else {
Edition::Year(input.parse().unwrap_or_else(|_| {
fatal!("`{input}` doesn't look like an edition");
}))
}
}

View file

@ -7,6 +7,7 @@ pub mod common;
mod debuggers;
pub mod diagnostics;
pub mod directives;
pub mod edition;
pub mod errors;
mod executor;
mod json;
@ -39,6 +40,7 @@ use crate::common::{
expected_output_path, output_base_dir, output_relative_path,
};
use crate::directives::DirectivesCache;
use crate::edition::parse_edition;
use crate::executor::{CollectedTest, ColorConfig};
/// Creates the `Config` instance for this invocation of compiletest.
@ -449,7 +451,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
has_enzyme,
channel: matches.opt_str("channel").unwrap(),
git_hash: matches.opt_present("git-hash"),
edition: matches.opt_str("edition"),
edition: matches.opt_str("edition").as_deref().map(parse_edition),
cc: matches.opt_str("cc").unwrap(),
cxx: matches.opt_str("cxx").unwrap(),

View file

@ -61,10 +61,6 @@ required-issue-label = "regression-from-stable-to-beta"
# if the above conditions matches, the PR will receive these labels
add-labels = ["beta-nominated"]
[backport.t-compiler-stable-backport]
required-pr-labels = ["T-compiler"]
required-issue-label = "regression-from-stable-to-stable"
add-labels = ["stable-nominated"]
# ------------------------------------------------------------------------------
# Ping groups