Rollup merge of #117256 - dtolnay:currentversion, r=compiler-errors
Parse rustc version at compile time This PR eliminates a couple awkward codepaths where it was not clear how the compiler should proceed if its own version number is incomprehensible.dab715641e/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs (L385)
dab715641e/compiler/rustc_attr/src/builtin.rs (L630)
We can guarantee that every compiled rustc comes with a working version number, so the ICE codepaths above shouldn't need to be written.
This commit is contained in:
commit
1db8c9d6e2
@ -10,10 +10,9 @@
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||
use rustc_session::parse::{feature_err, ParseSess};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::{RustcVersion, Session};
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{symbol::sym, symbol::Symbol, Span};
|
||||
use std::fmt::{self, Display};
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
||||
@ -24,8 +23,6 @@
|
||||
/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
|
||||
pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
|
||||
|
||||
pub const CURRENT_RUSTC_VERSION: &str = env!("CFG_RELEASE");
|
||||
|
||||
pub fn is_builtin_attr(attr: &Attribute) -> bool {
|
||||
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
@ -153,7 +150,7 @@ pub enum StabilityLevel {
|
||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub enum Since {
|
||||
Version(Version),
|
||||
Version(RustcVersion),
|
||||
/// Stabilized in the upcoming version, whatever number that is.
|
||||
Current,
|
||||
/// Failed to parse a stabilization version.
|
||||
@ -382,7 +379,7 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
|
||||
let since = if let Some(since) = since {
|
||||
if since.as_str() == VERSION_PLACEHOLDER {
|
||||
Since::Current
|
||||
} else if let Some(version) = parse_version(since.as_str(), false) {
|
||||
} else if let Some(version) = parse_version(since) {
|
||||
Since::Version(version)
|
||||
} else {
|
||||
sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
|
||||
@ -567,31 +564,20 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct Version {
|
||||
pub major: u16,
|
||||
pub minor: u16,
|
||||
pub patch: u16,
|
||||
}
|
||||
|
||||
fn parse_version(s: &str, allow_appendix: bool) -> Option<Version> {
|
||||
let mut components = s.split('-');
|
||||
/// Parse a rustc version number written inside string literal in an attribute,
|
||||
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
|
||||
/// not accepted in this position, unlike when parsing CFG_RELEASE.
|
||||
fn parse_version(s: Symbol) -> Option<RustcVersion> {
|
||||
let mut components = s.as_str().split('-');
|
||||
let d = components.next()?;
|
||||
if !allow_appendix && components.next().is_some() {
|
||||
if components.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
let mut digits = d.splitn(3, '.');
|
||||
let major = digits.next()?.parse().ok()?;
|
||||
let minor = digits.next()?.parse().ok()?;
|
||||
let patch = digits.next().unwrap_or("0").parse().ok()?;
|
||||
Some(Version { major, minor, patch })
|
||||
}
|
||||
|
||||
impl Display for Version {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
|
||||
}
|
||||
Some(RustcVersion { major, minor, patch })
|
||||
}
|
||||
|
||||
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
|
||||
@ -623,17 +609,16 @@ pub fn eval_condition(
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let Some(min_version) = parse_version(min_version.as_str(), false) else {
|
||||
let Some(min_version) = parse_version(*min_version) else {
|
||||
sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span });
|
||||
return false;
|
||||
};
|
||||
let rustc_version = parse_version(CURRENT_RUSTC_VERSION, true).unwrap();
|
||||
|
||||
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
|
||||
if sess.assume_incomplete_release {
|
||||
rustc_version > min_version
|
||||
RustcVersion::CURRENT > min_version
|
||||
} else {
|
||||
rustc_version >= min_version
|
||||
RustcVersion::CURRENT >= min_version
|
||||
}
|
||||
}
|
||||
ast::MetaItemKind::List(mis) => {
|
||||
|
@ -27,6 +27,6 @@
|
||||
|
||||
pub use rustc_ast::attr::*;
|
||||
|
||||
pub(crate) use rustc_ast::HashStableContext;
|
||||
pub(crate) use rustc_session::HashStableContext;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
|
59
compiler/rustc_macros/src/current_version.rs
Normal file
59
compiler/rustc_macros/src/current_version.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{parenthesized, parse_macro_input, LitStr, Token};
|
||||
|
||||
pub struct Input {
|
||||
variable: LitStr,
|
||||
}
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(env);
|
||||
}
|
||||
|
||||
impl Parse for Input {
|
||||
// Input syntax is `env!("CFG_RELEASE")` to facilitate grepping.
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let paren;
|
||||
input.parse::<kw::env>()?;
|
||||
input.parse::<Token![!]>()?;
|
||||
parenthesized!(paren in input);
|
||||
let variable: LitStr = paren.parse()?;
|
||||
Ok(Input { variable })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn current_version(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as Input);
|
||||
|
||||
TokenStream::from(match RustcVersion::parse_env_var(&input.variable) {
|
||||
Ok(RustcVersion { major, minor, patch }) => quote!(
|
||||
Self { major: #major, minor: #minor, patch: #patch }
|
||||
),
|
||||
Err(err) => syn::Error::new(Span::call_site(), err).into_compile_error(),
|
||||
})
|
||||
}
|
||||
|
||||
struct RustcVersion {
|
||||
major: u16,
|
||||
minor: u16,
|
||||
patch: u16,
|
||||
}
|
||||
|
||||
impl RustcVersion {
|
||||
fn parse_env_var(env_var: &LitStr) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let value = proc_macro::tracked_env::var(env_var.value())?;
|
||||
Self::parse_str(&value)
|
||||
.ok_or_else(|| format!("failed to parse rustc version: {:?}", value).into())
|
||||
}
|
||||
|
||||
fn parse_str(value: &str) -> Option<Self> {
|
||||
// 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 })
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod current_version;
|
||||
mod diagnostics;
|
||||
mod hash_stable;
|
||||
mod lift;
|
||||
@ -25,6 +26,11 @@
|
||||
mod type_foldable;
|
||||
mod type_visitable;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn current_rustc_version(input: TokenStream) -> TokenStream {
|
||||
current_version::current_version(input)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
||||
query::rustc_queries(input)
|
||||
|
@ -16,7 +16,7 @@
|
||||
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer};
|
||||
use rustc_session::parse::feature_err_issue;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::{RustcVersion, Session};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
use std::num::NonZeroU32;
|
||||
@ -129,11 +129,6 @@ pub fn deprecation_in_effect(depr: &Deprecation) -> bool {
|
||||
let is_since_rustc_version = depr.is_since_rustc_version;
|
||||
let since = depr.since.as_ref().map(Symbol::as_str);
|
||||
|
||||
fn parse_version(ver: &str) -> Vec<u32> {
|
||||
// We ignore non-integer components of the version (e.g., "nightly").
|
||||
ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
|
||||
}
|
||||
|
||||
if !is_since_rustc_version {
|
||||
// The `since` field doesn't have semantic purpose without `#![staged_api]`.
|
||||
return true;
|
||||
@ -144,16 +139,18 @@ fn parse_version(ver: &str) -> Vec<u32> {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(rustc) = option_env!("CFG_RELEASE") {
|
||||
let since: Vec<u32> = parse_version(&since);
|
||||
let rustc: Vec<u32> = parse_version(rustc);
|
||||
// We simply treat invalid `since` attributes as relating to a previous
|
||||
// Rust version, thus always displaying the warning.
|
||||
if since.len() != 3 {
|
||||
return true;
|
||||
}
|
||||
return since <= rustc;
|
||||
// We ignore non-integer components of the version (e.g., "nightly").
|
||||
let since: Vec<u16> =
|
||||
since.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect();
|
||||
|
||||
// We simply treat invalid `since` attributes as relating to a previous
|
||||
// Rust version, thus always displaying the warning.
|
||||
if since.len() != 3 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let rustc = RustcVersion::CURRENT;
|
||||
return since.as_slice() <= &[rustc.major, rustc.minor, rustc.patch];
|
||||
};
|
||||
|
||||
// Assume deprecation is in effect if "since" field is missing
|
||||
|
@ -43,6 +43,9 @@
|
||||
|
||||
pub use getopts;
|
||||
|
||||
mod version;
|
||||
pub use version::RustcVersion;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
|
19
compiler/rustc_session/src/version.rs
Normal file
19
compiler/rustc_session/src/version.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct RustcVersion {
|
||||
pub major: u16,
|
||||
pub minor: u16,
|
||||
pub patch: u16,
|
||||
}
|
||||
|
||||
impl RustcVersion {
|
||||
pub const CURRENT: Self = current_rustc_version!(env!("CFG_RELEASE"));
|
||||
}
|
||||
|
||||
impl Display for RustcVersion {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
|
||||
}
|
||||
}
|
@ -48,13 +48,14 @@
|
||||
use std::string::ToString;
|
||||
|
||||
use askama::Template;
|
||||
use rustc_attr::{ConstStability, Deprecation, Since, StabilityLevel, CURRENT_RUSTC_VERSION};
|
||||
use rustc_attr::{ConstStability, Deprecation, Since, StabilityLevel};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::def_id::{DefId, DefIdSet};
|
||||
use rustc_hir::Mutability;
|
||||
use rustc_middle::middle::stability;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::RustcVersion;
|
||||
use rustc_span::{
|
||||
symbol::{sym, Symbol},
|
||||
BytePos, FileName, RealFileName,
|
||||
@ -979,7 +980,7 @@ fn render_stability_since_raw_with_extra(
|
||||
fn since_to_string(since: &Since) -> Option<String> {
|
||||
match since {
|
||||
Since::Version(since) => Some(since.to_string()),
|
||||
Since::Current => Some(CURRENT_RUSTC_VERSION.to_owned()),
|
||||
Since::Current => Some(RustcVersion::CURRENT.to_string()),
|
||||
Since::Err => None,
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
use crate::msrvs::Msrv;
|
||||
use hir::LangItem;
|
||||
use rustc_attr::{Since, CURRENT_RUSTC_VERSION};
|
||||
use rustc_attr::Since;
|
||||
use rustc_const_eval::transform::check_consts::ConstCx;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -372,23 +372,16 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
|
||||
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
|
||||
|
||||
let const_stab_rust_version = match since {
|
||||
Since::Version(version) => RustcVersion::new(
|
||||
u32::from(version.major),
|
||||
u32::from(version.minor),
|
||||
u32::from(version.patch),
|
||||
),
|
||||
Since::Current => {
|
||||
// HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev.
|
||||
// `rustc-semver` doesn't accept the `-dev` version number so we have to strip it off.
|
||||
let short_version = CURRENT_RUSTC_VERSION.split('-').next().unwrap();
|
||||
RustcVersion::parse(short_version).unwrap_or_else(|err| {
|
||||
panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{CURRENT_RUSTC_VERSION}`, {err:?}")
|
||||
})
|
||||
},
|
||||
Since::Version(version) => version,
|
||||
Since::Current => rustc_session::RustcVersion::CURRENT,
|
||||
Since::Err => return false,
|
||||
};
|
||||
|
||||
msrv.meets(const_stab_rust_version)
|
||||
msrv.meets(RustcVersion::new(
|
||||
u32::from(const_stab_rust_version.major),
|
||||
u32::from(const_stab_rust_version.minor),
|
||||
u32::from(const_stab_rust_version.patch),
|
||||
))
|
||||
} else {
|
||||
// Unstable const fn with the feature enabled.
|
||||
msrv.current().is_none()
|
||||
|
Loading…
Reference in New Issue
Block a user