diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
commit | ef24de24a82fe681581cc130f342363c47c0969a (patch) | |
tree | 0d494f7e1a38b95c92426f58fe6eaa877303a86c /compiler/rustc_macros | |
parent | Releasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip |
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_macros')
-rw-r--r-- | compiler/rustc_macros/Cargo.toml | 8 | ||||
-rw-r--r-- | compiler/rustc_macros/src/current_version.rs | 59 | ||||
-rw-r--r-- | compiler/rustc_macros/src/diagnostics/diagnostic.rs | 38 | ||||
-rw-r--r-- | compiler/rustc_macros/src/diagnostics/subdiagnostic.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_macros/src/lib.rs | 7 | ||||
-rw-r--r-- | compiler/rustc_macros/src/query.rs | 18 | ||||
-rw-r--r-- | compiler/rustc_macros/src/serialize.rs | 30 | ||||
-rw-r--r-- | compiler/rustc_macros/src/symbols.rs | 158 | ||||
-rw-r--r-- | compiler/rustc_macros/src/symbols/tests.rs | 2 |
9 files changed, 259 insertions, 65 deletions
diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml index 17651ce95..d8d2bef49 100644 --- a/compiler/rustc_macros/Cargo.toml +++ b/compiler/rustc_macros/Cargo.toml @@ -1,13 +1,15 @@ [package] name = "rustc_macros" -version = "0.1.0" +version = "0.0.0" edition = "2021" [lib] proc-macro = true [dependencies] -synstructure = "0.13.0" -syn = { version = "2.0.9", features = ["full"] } +# tidy-alphabetical-start proc-macro2 = "1" quote = "1" +syn = { version = "2.0.9", features = ["full"] } +synstructure = "0.13.0" +# tidy-alphabetical-end diff --git a/compiler/rustc_macros/src/current_version.rs b/compiler/rustc_macros/src/current_version.rs new file mode 100644 index 000000000..5e3b91c17 --- /dev/null +++ b/compiler/rustc_macros/src/current_version.rs @@ -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 }) + } +} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 04b7c5fee..1a8174bfd 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -42,19 +42,20 @@ impl<'a> DiagnosticDerive<'a> { let init = match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") - .help("specify the slug as the first argument to the `#[diag(...)]` \ - attribute, such as `#[diag(hir_analysis_example_error)]`") + .help( + "specify the slug as the first argument to the `#[diag(...)]` \ + attribute, such as `#[diag(hir_analysis_example_error)]`", + ) .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } - Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { + Some(slug) + if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = + Mismatch::check(slug) => + { span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match") - .note(format!( - "slug is `{slug_name}` but the crate name is `{crate_name}`" - )) - .help(format!( - "expected a slug starting with `{slug_prefix}_...`" - )) + .note(format!("slug is `{slug_name}` but the crate name is `{crate_name}`")) + .help(format!("expected a slug starting with `{slug_prefix}_...`")) .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } @@ -141,19 +142,20 @@ impl<'a> LintDiagnosticDerive<'a> { match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") - .help("specify the slug as the first argument to the attribute, such as \ - `#[diag(compiletest_example)]`") + .help( + "specify the slug as the first argument to the attribute, such as \ + `#[diag(compiletest_example)]`", + ) .emit(); DiagnosticDeriveError::ErrorHandled.to_compile_error() } - Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { + Some(slug) + if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = + Mismatch::check(slug) => + { span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match") - .note(format!( - "slug is `{slug_name}` but the crate name is `{crate_name}`" - )) - .help(format!( - "expected a slug starting with `{slug_prefix}_...`" - )) + .note(format!("slug is `{slug_name}` but the crate name is `{crate_name}`")) + .help(format!("expected a slug starting with `{slug_prefix}_...`")) .emit(); DiagnosticDeriveError::ErrorHandled.to_compile_error() } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 877e97450..877271ff0 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -577,7 +577,9 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } _ => { - if let Some(span) = span_field && !no_span { + if let Some(span) = span_field + && !no_span + { quote! { #diag.#name(#span, #message); } } else { quote! { #diag.#name(#message); } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 85829906f..193dbd75f 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -4,6 +4,7 @@ #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] +#![feature(proc_macro_tracked_env)] #![allow(rustc::default_hash_types)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -14,6 +15,7 @@ use synstructure::decl_derive; use proc_macro::TokenStream; +mod current_version; mod diagnostics; mod hash_stable; mod lift; @@ -25,6 +27,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) } diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index d0d41c614..ad1980136 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -97,6 +97,9 @@ struct QueryModifiers { /// A cycle error results in a delay_bug call cycle_delay_bug: Option<Ident>, + /// A cycle error results in a stashed cycle error that can be unstashed and canceled later + cycle_stash: Option<Ident>, + /// Don't hash the result, instead just mark a query red if it runs no_hash: Option<Ident>, @@ -114,6 +117,11 @@ struct QueryModifiers { /// Generate a `feed` method to set the query's value from another query. feedable: Option<Ident>, + + /// Forward the result on ensure if the query gets recomputed, and + /// return `Ok(())` otherwise. Only applicable to queries returning + /// `Result<(), ErrorGuaranteed>` + ensure_forwards_result_if_red: Option<Ident>, } fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { @@ -122,12 +130,14 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { let mut desc = None; let mut fatal_cycle = None; let mut cycle_delay_bug = None; + let mut cycle_stash = None; let mut no_hash = None; let mut anon = None; let mut eval_always = None; let mut depth_limit = None; let mut separate_provide_extern = None; let mut feedable = None; + let mut ensure_forwards_result_if_red = None; while !input.is_empty() { let modifier: Ident = input.parse()?; @@ -175,6 +185,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { try_insert!(fatal_cycle = modifier); } else if modifier == "cycle_delay_bug" { try_insert!(cycle_delay_bug = modifier); + } else if modifier == "cycle_stash" { + try_insert!(cycle_stash = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); } else if modifier == "anon" { @@ -187,6 +199,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { try_insert!(separate_provide_extern = modifier); } else if modifier == "feedable" { try_insert!(feedable = modifier); + } else if modifier == "ensure_forwards_result_if_red" { + try_insert!(ensure_forwards_result_if_red = modifier); } else { return Err(Error::new(modifier.span(), "unknown query modifier")); } @@ -200,12 +214,14 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { desc, fatal_cycle, cycle_delay_bug, + cycle_stash, no_hash, anon, eval_always, depth_limit, separate_provide_extern, feedable, + ensure_forwards_result_if_red, }) } @@ -320,11 +336,13 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { fatal_cycle, arena_cache, cycle_delay_bug, + cycle_stash, no_hash, anon, eval_always, depth_limit, separate_provide_extern, + ensure_forwards_result_if_red, ); if modifiers.cache.is_some() { diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index ba75517d7..047066ac6 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -5,11 +5,16 @@ use syn::spanned::Spanned; pub fn type_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let decoder_ty = quote! { __D }; - if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - s.add_impl_generic(parse_quote! { 'tcx }); - } - s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder<I = ::rustc_middle::ty::TyCtxt<'tcx>>}); - s.add_bounds(synstructure::AddBounds::Generics); + let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + quote! { <I = ::rustc_middle::ty::TyCtxt<'tcx>> } + } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") { + quote! { <I = I> } + } else { + quote! {} + }; + + s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound }); + s.add_bounds(synstructure::AddBounds::Fields); decodable_body(s, decoder_ty) } @@ -97,12 +102,17 @@ fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream { } pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { - if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - s.add_impl_generic(parse_quote! {'tcx}); - } + let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + quote! { <I = ::rustc_middle::ty::TyCtxt<'tcx>> } + } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") { + quote! { <I = I> } + } else { + quote! {} + }; + let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder<I = ::rustc_middle::ty::TyCtxt<'tcx>>}); - s.add_bounds(synstructure::AddBounds::Generics); + s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound }); + s.add_bounds(synstructure::AddBounds::Fields); encodable_body(s, encoder_ty, false) } diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 04facbf65..4129712a6 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -26,7 +26,7 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use std::collections::HashMap; use syn::parse::{Parse, ParseStream, Result}; -use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token}; +use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Macro, Token}; #[cfg(test)] mod tests; @@ -53,21 +53,46 @@ impl Parse for Keyword { struct Symbol { name: Ident, - value: Option<LitStr>, + value: Value, +} + +enum Value { + SameAsName, + String(LitStr), + Env(LitStr, Macro), + Unsupported(Expr), } impl Parse for Symbol { fn parse(input: ParseStream<'_>) -> Result<Self> { let name = input.parse()?; - let value = match input.parse::<Token![:]>() { - Ok(_) => Some(input.parse()?), - Err(_) => None, - }; + let colon_token: Option<Token![:]> = input.parse()?; + let value = if colon_token.is_some() { input.parse()? } else { Value::SameAsName }; Ok(Symbol { name, value }) } } +impl Parse for Value { + fn parse(input: ParseStream<'_>) -> Result<Self> { + let expr: Expr = input.parse()?; + match &expr { + Expr::Lit(expr) => { + if let Lit::Str(lit) = &expr.lit { + return Ok(Value::String(lit.clone())); + } + } + Expr::Macro(expr) => { + if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() { + return Ok(Value::Env(lit, expr.mac.clone())); + } + } + _ => {} + } + Ok(Value::Unsupported(expr)) + } +} + struct Input { keywords: Punctuated<Keyword, Token![,]>, symbols: Punctuated<Symbol, Token![,]>, @@ -111,6 +136,37 @@ pub fn symbols(input: TokenStream) -> TokenStream { output } +struct Preinterned { + idx: u32, + span_of_name: Span, +} + +struct Entries { + map: HashMap<String, Preinterned>, +} + +impl Entries { + fn with_capacity(capacity: usize) -> Self { + Entries { map: HashMap::with_capacity(capacity) } + } + + fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 { + if let Some(prev) = self.map.get(str) { + errors.error(span, format!("Symbol `{str}` is duplicated")); + errors.error(prev.span_of_name, "location of previous definition".to_string()); + prev.idx + } else { + let idx = self.len(); + self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span }); + idx + } + } + + fn len(&self) -> u32 { + u32::try_from(self.map.len()).expect("way too many symbols") + } +} + fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) { let mut errors = Errors::default(); @@ -127,20 +183,9 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) { let mut keyword_stream = quote! {}; let mut symbols_stream = quote! {}; let mut prefill_stream = quote! {}; - let mut counter = 0u32; - let mut keys = - HashMap::<String, Span>::with_capacity(input.keywords.len() + input.symbols.len() + 10); + let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10); let mut prev_key: Option<(Span, String)> = None; - let mut check_dup = |span: Span, str: &str, errors: &mut Errors| { - if let Some(prev_span) = keys.get(str) { - errors.error(span, format!("Symbol `{str}` is duplicated")); - errors.error(*prev_span, "location of previous definition".to_string()); - } else { - keys.insert(str.to_string(), span); - } - }; - let mut check_order = |span: Span, str: &str, errors: &mut Errors| { if let Some((prev_span, ref prev_str)) = prev_key { if str < prev_str { @@ -156,49 +201,98 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) { let name = &keyword.name; let value = &keyword.value; let value_string = value.value(); - check_dup(keyword.name.span(), &value_string, &mut errors); + let idx = entries.insert(keyword.name.span(), &value_string, &mut errors); prefill_stream.extend(quote! { #value, }); keyword_stream.extend(quote! { - pub const #name: Symbol = Symbol::new(#counter); + pub const #name: Symbol = Symbol::new(#idx); }); - counter += 1; } // Generate the listed symbols. for symbol in input.symbols.iter() { let name = &symbol.name; + check_order(symbol.name.span(), &name.to_string(), &mut errors); + let value = match &symbol.value { - Some(value) => value.value(), - None => name.to_string(), + Value::SameAsName => name.to_string(), + Value::String(lit) => lit.value(), + Value::Env(..) => continue, // in another loop below + Value::Unsupported(expr) => { + errors.list.push(syn::Error::new_spanned( + expr, + concat!( + "unsupported expression for symbol value; implement support for this in ", + file!(), + ), + )); + continue; + } }; - check_dup(symbol.name.span(), &value, &mut errors); - check_order(symbol.name.span(), &name.to_string(), &mut errors); + let idx = entries.insert(symbol.name.span(), &value, &mut errors); prefill_stream.extend(quote! { #value, }); symbols_stream.extend(quote! { - pub const #name: Symbol = Symbol::new(#counter); + pub const #name: Symbol = Symbol::new(#idx); }); - counter += 1; } // Generate symbols for the strings "0", "1", ..., "9". - let digits_base = counter; - counter += 10; for n in 0..10 { let n = n.to_string(); - check_dup(Span::call_site(), &n, &mut errors); + entries.insert(Span::call_site(), &n, &mut errors); prefill_stream.extend(quote! { #n, }); } + // Symbols whose value comes from an environment variable. It's allowed for + // these to have the same value as another symbol. + for symbol in &input.symbols { + let (env_var, expr) = match &symbol.value { + Value::Env(lit, expr) => (lit, expr), + Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue, + }; + + if !proc_macro::is_available() { + errors.error( + Span::call_site(), + "proc_macro::tracked_env is not available in unit test".to_owned(), + ); + break; + } + + let value = match proc_macro::tracked_env::var(env_var.value()) { + Ok(value) => value, + Err(err) => { + errors.list.push(syn::Error::new_spanned(expr, err)); + continue; + } + }; + + let idx = if let Some(prev) = entries.map.get(&value) { + prev.idx + } else { + prefill_stream.extend(quote! { + #value, + }); + entries.insert(symbol.name.span(), &value, &mut errors) + }; + + let name = &symbol.name; + symbols_stream.extend(quote! { + pub const #name: Symbol = Symbol::new(#idx); + }); + } + + let symbol_digits_base = entries.map["0"].idx; + let preinterned_symbols_count = entries.len(); let output = quote! { - const SYMBOL_DIGITS_BASE: u32 = #digits_base; - const PREINTERNED_SYMBOLS_COUNT: u32 = #counter; + const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base; + const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count; #[doc(hidden)] #[allow(non_upper_case_globals)] diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs index bd0c08a53..9c53453df 100644 --- a/compiler/rustc_macros/src/symbols/tests.rs +++ b/compiler/rustc_macros/src/symbols/tests.rs @@ -27,7 +27,7 @@ fn test_symbols() { let body_tokens = m.mac.tokens.clone(); - test_symbols_macro(body_tokens, &[]); + test_symbols_macro(body_tokens, &["proc_macro::tracked_env is not available in unit test"]); } fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) { |