diff --git a/Cargo.lock b/Cargo.lock index 7092e498e937e..b0d1c12ebae65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4298,6 +4298,7 @@ dependencies = [ name = "rustc_lint_defs" version = "0.0.0" dependencies = [ + "rustc_ast", "rustc_data_structures", "rustc_error_messages", "rustc_hir_id", diff --git a/compiler/rustc_ast/src/attr/version.rs b/compiler/rustc_ast/src/attr/version.rs index 506a7a291813c..62755a2456ae1 100644 --- a/compiler/rustc_ast/src/attr/version.rs +++ b/compiler/rustc_ast/src/attr/version.rs @@ -24,14 +24,24 @@ impl RustcVersion { } }) } + + /// Parse a [`RustcVersion`] with an optional patch version, ignoring suffixes such as `-dev` or `-nightly`. fn parse_str(value: &str) -> Option { - // Ignore any suffixes such as "-dev" or "-nightly". let mut components = value.split('-').next().unwrap().splitn(3, '.'); let major = components.next()?.parse().ok()?; let minor = components.next()?.parse().ok()?; let patch = components.next().unwrap_or("0").parse().ok()?; Some(RustcVersion { major, minor, patch }) } + + /// Parse a [`RustcVersion`] which is exactly `..`, with no suffix. + pub fn parse_str_strict(value: &str) -> Option { + let mut components = value.splitn(3, '.'); + let major = components.next()?.parse().ok()?; + let minor = components.next()?.parse().ok()?; + let patch = components.next()?.parse().ok()?; + Some(RustcVersion { major, minor, patch }) + } } static CURRENT_OVERRIDABLE: OnceLock = OnceLock::new(); diff --git a/compiler/rustc_attr_parsing/src/template.rs b/compiler/rustc_attr_parsing/src/template.rs index b8de6baa87780..f04f20e2201dc 100644 --- a/compiler/rustc_attr_parsing/src/template.rs +++ b/compiler/rustc_attr_parsing/src/template.rs @@ -2,7 +2,9 @@ use rustc_ast::ast::Safety; use rustc_hir::AttrStyle; use rustc_span::Symbol; -/// A template that the attribute input must match. +/// A template to suggest the correct syntax of an attribute. +/// +/// This is not used to *check* attributes. The attribute's parser is responsible for that. /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. #[derive(Clone, Copy, Default)] pub struct AttributeTemplate { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 2fbb90b770261..9a3fedde3b6b4 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -7,6 +7,7 @@ use std::panic; use std::path::PathBuf; use std::thread::panicking; +use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg}; use rustc_lint_defs::{Applicability, LintExpectationId}; @@ -175,6 +176,8 @@ pub struct IsLint { pub(crate) name: String, /// Indicates whether this lint should show up in cargo's future breakage report. has_future_breakage: bool, + /// Indicates the minimum rust version this lint applies to + rust_version: Option, } #[derive(Debug, PartialEq, Eq)] @@ -308,6 +311,11 @@ impl DiagInner { matches!(self.is_lint, Some(IsLint { has_future_breakage: true, .. })) } + /// Indicates the minimum rust version this lint applies to. + pub(crate) fn rust_version(&self) -> Option { + self.is_lint.as_ref().and_then(|is| is.rust_version) + } + pub(crate) fn is_force_warn(&self) -> bool { match self.level { Level::ForceWarning => { @@ -1152,8 +1160,13 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } - pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self { - self.is_lint = Some(IsLint { name, has_future_breakage }); + pub fn is_lint( + &mut self, + name: String, + has_future_breakage: bool, + rust_version: Option, + ) -> &mut Self { + self.is_lint = Some(IsLint { name, has_future_breakage, rust_version }); self } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index c2d09da3b0e82..76cdb10f429c7 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -46,6 +46,7 @@ pub use diagnostic_impls::{ }; pub use emitter::ColorConfig; use emitter::{DynEmitter, Emitter}; +use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hash::StableHasher; @@ -365,6 +366,9 @@ struct DiagCtxtInner { /// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be /// stored along side the main panic backtrace. ice_file: Option, + + /// Controlled by `-Z lint-rust-version`; this allows avoiding emitting lints which would raise MSRV. + msrv: Option, } /// A key denoting where from a diagnostic was stashed. @@ -479,6 +483,11 @@ impl DiagCtxt { self } + pub fn with_msrv(mut self, msrv: RustcVersion) -> Self { + self.inner.get_mut().msrv = Some(msrv); + self + } + pub fn new(emitter: Box) -> Self { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } @@ -526,6 +535,7 @@ impl DiagCtxt { future_breakage_diagnostics, fulfilled_expectations, ice_file: _, + msrv: _, } = inner.deref_mut(); // For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the @@ -1193,6 +1203,7 @@ impl DiagCtxtInner { future_breakage_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), ice_file: None, + msrv: None, } } @@ -1307,6 +1318,12 @@ impl DiagCtxtInner { } } + if let (Some(msrv), Some(diag_msrv)) = (self.msrv, diagnostic.rust_version()) + && diag_msrv > msrv + { + return None; + } + TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| { if let Some(code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code); diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index 2ca62f7fa8cdc..c8201d5ea8ccc 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } rustc_hir_id = { path = "../rustc_hir_id" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1edc12e1ca7eb..6949090781cf6 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::fmt::Display; +use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hash::{StableCompare, StableHash, StableHashCtxt, StableHasher}; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; @@ -305,6 +306,9 @@ pub struct Lint { /// `true` if this lint is unaffected by `-D warnings` pub ignore_deny_warnings: bool, + + /// Used to avoid lints which would affect MSRV + pub rust_version: Option, } /// Extra information for a future incompatibility lint. @@ -521,7 +525,40 @@ impl Lint { crate_level_only: false, eval_always: false, ignore_deny_warnings: false, + rust_version: None, + } + } + + // FIXME(const-hack): This is used so that `declare_lint` can declare an MSRV statically. + // `RustcVersion::parse_str_strict` should ideally be used instead. + pub const fn parse_rust_version(version: &str) -> RustcVersion { + const fn parse_part(input: &mut &[u8]) -> u16 { + let mut val = 0; + let mut idx = 0; + while idx < input.len() { + let v = input[idx]; + match v { + b'0'..=b'9' => { + val = val * 10 + (v - b'0') as u16; + } + b'.' => { + idx += 1; + break; + } + _ => panic!("invalid character in version"), + } + idx += 1; + } + *input = input.split_at(idx).1; + val } + + let mut bytes = version.as_bytes(); + let major = parse_part(&mut bytes); + let minor = parse_part(&mut bytes); + let patch = parse_part(&mut bytes); + assert!(bytes.is_empty()); + RustcVersion { major, minor, patch } } /// Gets the lint's name, with ASCII letters converted to lowercase. @@ -671,6 +708,7 @@ macro_rules! declare_lint { $($field:ident : $val:expr),* $(,)* }; )? $(@edition $lint_edition:ident => $edition_level:ident;)? + $(@msrv = $msrv:literal;)? $($v:ident),*) => ( $(#[$attr])* $vis static $NAME: &$crate::Lint = &$crate::Lint { @@ -687,6 +725,7 @@ macro_rules! declare_lint { }),)? $(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)? $(eval_always: $eval_always,)? + $(rust_version: Some($crate::Lint::parse_rust_version($msrv)),)? ..$crate::Lint::default_fields_for_macro() }; ); diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index aa4b8f1d12c9f..b852a107d3343 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -514,7 +514,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( err.disable_suggestions(); } - err.is_lint(lint.name_lower(), has_future_breakage); + err.is_lint(lint.name_lower(), has_future_breakage, lint.rust_version); // Lint diagnostics that are covered by the expect level will not be emitted outside // the compiler. It is therefore not necessary to add any information for the user. // This will therefore directly call the decorate function which will in turn emit diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 0b8e84213b5f7..cf62b2c623381 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -895,6 +895,16 @@ impl VariantDef { pub fn fields(&self) -> Vec { with(|cx| cx.variant_fields(*self)) } + + /// Returns the variant index. + pub fn idx(&self) -> VariantIdx { + self.idx + } + + /// Returns the `AdtDef` which this variant comes from. + pub fn adt_def(&self) -> AdtDef { + self.adt_def + } } crate_def_with_ty! { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 003b813124eea..1dba13ce9b4bc 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3059,6 +3059,7 @@ pub(crate) mod dep_tracking { use std::path::PathBuf; use rustc_abi::Align; + use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hash::StableHasher; use rustc_errors::LanguageIdentifier; @@ -3184,7 +3185,8 @@ pub(crate) mod dep_tracking { InliningThreshold, FunctionReturn, Align, - CodegenRetagOptions + CodegenRetagOptions, + RustcVersion, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index bc4c5e98282a5..41b35f5700eca 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -82,7 +82,11 @@ pub fn feature_warn_issue( // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; let future_incompatible = lint.future_incompatible.as_ref().unwrap(); - err.is_lint(lint.name_lower(), /* has_future_breakage */ false); + err.is_lint( + lint.name_lower(), + /* has_future_breakage */ false, + /* rust_version */ None, + ); err.warn(lint.desc); err.note(format!("for more information, see {}", future_incompatible.reason.reference())); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0582791dad005..75f9f73dee948 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use std::str; use rustc_abi::Align; +use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::stable_hash::StableHasher; @@ -846,6 +847,7 @@ mod desc { super::mitigation_coverage::DeniedPartialMitigationKind::KINDS; pub(crate) const parse_deny_partial_mitigations: &str = super::mitigation_coverage::DeniedPartialMitigationKind::KINDS; + pub(crate) const parse_rust_version: &str = "a rust version (`xx.yy.zz`)"; } pub mod parse { @@ -2054,6 +2056,18 @@ pub mod parse { }; true } + + pub(crate) fn parse_rust_version(slot: &mut Option, v: Option<&str>) -> bool { + let Some(v) = v else { + return false; + }; + if let Some(version) = RustcVersion::parse_str_strict(v) { + *slot = Some(version); + true + } else { + false + } + } } options! { @@ -2454,6 +2468,8 @@ options! { "lint LLVM IR (default: no)"), lint_mir: bool = (false, parse_bool, [UNTRACKED], "lint MIR before and after each transformation"), + lint_rust_version: Option = (None, parse_rust_version, [TRACKED], + "control the minimum rust version for lints"), llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED], "a list of module flags to pass to LLVM (space separated)"), llvm_plugins: Vec = (Vec::new(), parse_list, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9f88161073b60..c19a2d71e2078 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1040,6 +1040,10 @@ pub fn build_session( dcx = dcx.with_ice_file(ice_file); } + if let Some(msrv) = sopts.unstable_opts.lint_rust_version { + dcx = dcx.with_msrv(msrv); + } + let host_triple = TargetTuple::from_tuple(config::host_tuple()); let (host, target_warnings) = Target::search(&host_triple, sopts.sysroot.path(), sopts.unstable_opts.unstable_options) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 08781532a0919..838dab90a6de0 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -3002,7 +3002,7 @@ impl Pointer for *const T { // metadata type to reduce the amount of codegen work needed for each distinct type. let ptr: *const T = *self; let ptr_addr = ptr.expose_provenance(); - if <::Metadata as core::unit::IsUnit>::is_unit() { + if <::Metadata as core::unit::IsUnit>::IS_UNIT { pointer_fmt_inner(ptr_addr, f) } else { wide_pointer_fmt_inner(ptr_addr, &core::ptr::metadata(ptr), f) diff --git a/library/core/src/unit.rs b/library/core/src/unit.rs index d54816c444bc4..5d9c79538e371 100644 --- a/library/core/src/unit.rs +++ b/library/core/src/unit.rs @@ -1,3 +1,5 @@ +use crate::intrinsics::type_id; + /// Collapses all unit items from an iterator into one. /// /// This is more useful when combined with higher-level abstractions, like @@ -19,17 +21,10 @@ impl FromIterator<()> for () { } pub(crate) trait IsUnit { - fn is_unit() -> bool; + const IS_UNIT: bool; } impl IsUnit for T { - default fn is_unit() -> bool { - false - } -} - -impl IsUnit for () { - fn is_unit() -> bool { - true - } + // `type_id` erases lifetimes, but that's OK here because "is it ()" never depends on lifetimes + const IS_UNIT: bool = type_id::() == type_id::<()>(); } diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 3b3044484f39d..2ed7d55b00da6 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -1093,7 +1093,7 @@ fn download_http_with_retries( ), ]).run_capture_stdout(exec_ctx); - if powershell.is_failure() { + if powershell.is_success() { return; } diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index b8f8d3145152f..b7130c0870800 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -250,6 +250,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-powerpc", "only-riscv32", "only-riscv64", + "only-riscv64gc-unknown-linux-gnu", "only-rustc_abi-x86-sse2", "only-s390x", "only-sparc", diff --git a/tests/assembly-llvm/carryless-mul.rs b/tests/assembly-llvm/carryless-mul.rs new file mode 100644 index 0000000000000..99eece1d11d8a --- /dev/null +++ b/tests/assembly-llvm/carryless-mul.rs @@ -0,0 +1,126 @@ +// Checks that the `carryless_mul` / `widening_carryless_mul` operations lower to +// the architectures' native carry-less (polynomial) multiply instructions. +// +// - Rust to LLVM IR: https://godbolt.org/z/sM914e4fo +// - LLVM IR to assembly: https://godbolt.org/z/5Y7naa4cY +// +//@revisions: x86_64 aarch64 riscv64 +//@assembly-output: emit-asm +//@min-llvm-version: 23 +//@compile-flags: -C opt-level=3 +// +//@[x86_64] compile-flags: -C target-feature=+pclmulqdq,+avx2 -Cllvm-args=-x86-asm-syntax=intel +//@[x86_64] only-x86_64-unknown-linux-gnu +// +//@[aarch64] compile-flags: -C target-feature=+aes +//@[aarch64] only-aarch64-unknown-linux-gnu +// +//@[riscv64] compile-flags: -C target-feature=+zknd,+zbc +//@[riscv64] only-riscv64gc-unknown-linux-gnu + +#![feature(uint_carryless_mul)] +#![crate_type = "lib"] + +#[unsafe(no_mangle)] +fn carryless_mul_u8(a: u8, b: u8) -> u8 { + // CHECK-LABEL: carryless_mul_u8: + // x86_64: pclmulqdq + // aarch64: pmul + // riscv64: clmul + a.carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn widening_carryless_mul_u8(a: u8, b: u8) -> u16 { + // CHECK-LABEL: widening_carryless_mul_u8: + // x86_64: pclmulqdq + // aarch64: pmull + // riscv64: clmul + a.widening_carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn carryless_mul_u16(a: u16, b: u16) -> u16 { + // CHECK-LABEL: carryless_mul_u16: + // x86_64: pclmulqdq + // aarch64: pmull + // riscv64: clmul + a.carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn widening_carryless_mul_u16(a: u16, b: u16) -> u32 { + // CHECK-LABEL: widening_carryless_mul_u16: + // x86_64: pclmulqdq + // aarch64: pmull + // riscv64: clmul + a.widening_carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn carryless_mul_u32(a: u32, b: u32) -> u32 { + // CHECK-LABEL: carryless_mul_u32: + // x86_64: pclmulqdq + // aarch64: pmull + // riscv64: clmul + a.carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn widening_carryless_mul_u32(a: u32, b: u32) -> u64 { + // CHECK-LABEL: widening_carryless_mul_u32: + // x86_64: pclmulqdq + // aarch64: pmull + // riscv64: slli + // riscv64: slli + // riscv64: clmulh + a.widening_carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn carryless_mul_u64(a: u64, b: u64) -> u64 { + // CHECK-LABEL: carryless_mul_u64: + // x86_64: pclmulqdq + // aarch64: pmull + // riscv64: clmul + a.carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn widening_carryless_mul_u64(a: u64, b: u64) -> u128 { + // CHECK-LABEL: widening_carryless_mul_u64: + // + // x86_64: pclmulqdq + // x86_64: vpextrq + // + // aarch64: rbit + // aarch64: pmull + // aarch64: pmull + // + // riscv64: clmul + // riscv64: clmulh + a.widening_carryless_mul(b) +} + +#[unsafe(no_mangle)] +fn carryless_mul_u128(a: u128, b: u128) -> u128 { + // CHECK-LABEL: carryless_mul_u128: + // + // x86_64: pclmulqdq + // x86_64: pclmulqdq + // x86_64: pclmulqdq + // x86_64: xor + // + // aarch64: pmull + // aarch64: pmull + // aarch64: pmull + // aarch64: eor + // + // riscv64: clmul + // riscv64: clmul + // riscv64: clmulh + // riscv64: xor + // riscv64: clmul + // riscv64: xor + a.carryless_mul(b) +}