Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4012,18 +4012,28 @@ dependencies = [
"rustc_lexer",
"rustc_lint_defs",
"rustc_macros",
"rustc_middle",
"rustc_parse",
"rustc_proc_macro",
"rustc_serialize",
"rustc_session",
"rustc_span",
"scoped-tls",
"smallvec",
"thin-vec",
"tracing",
]

[[package]]
name = "rustc_expand_queries"
version = "0.0.0"
dependencies = [
"rustc_ast",
"rustc_expand",
"rustc_middle",
"rustc_proc_macro",
"rustc_span",
"scoped-tls",
]

[[package]]
name = "rustc_feature"
version = "0.0.0"
Expand Down Expand Up @@ -4224,6 +4234,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_expand",
"rustc_expand_queries",
"rustc_feature",
"rustc_fs_util",
"rustc_hir",
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_expand/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_parse = { path = "../rustc_parse" }
# We must use the proc_macro version that we will compile proc-macros against,
# not the one from our own sysroot.
rustc_proc_macro = { path = "../rustc_proc_macro" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
scoped-tls = "1.0"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
thin-vec = "0.2.19"
tracing = "0.1"
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_expand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,3 @@ pub mod config;
pub mod expand;
pub mod module;
pub mod proc_macro;

pub fn provide(providers: &mut rustc_middle::query::Providers) {
providers.derive_macro_expansion = proc_macro::provide_derive_macro_expansion;
}
11 changes: 5 additions & 6 deletions compiler/rustc_expand/src/mbe/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use rustc_attr_ir::diagnostic::{CustomDiagnostic, Directive, FormatArgs};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage, pluralize};
use rustc_macros::Subdiagnostic;
use rustc_middle::bug;
use rustc_parse::parser::{Parser, Recovery, token_descr};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::SourceMap;
Expand Down Expand Up @@ -203,7 +202,7 @@ impl BestFailure {
impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'matcher> {
fn prepare(&mut self, which_matcher: WhichMatcher, matcher: &'matcher [MatcherLoc]) {
if self.current.is_some() {
bug!("`Self::after_arm()` was not called to clean up context");
panic!("`Self::after_arm()` was not called to clean up context");
}

self.current = Some((which_matcher, matcher));
Expand Down Expand Up @@ -236,12 +235,12 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
}
Failure => {
if self.best_failure.is_none() {
bug!("A matching failure occurred but `Self::failure()` was not called");
panic!("A matching failure occurred but `Self::failure()` was not called");
}
}
Ambiguity => {
if self.result.is_none() {
bug!("An ambiguity error occurred but `Self::ambiguity()` was not called");
panic!("An ambiguity error occurred but `Self::ambiguity()` was not called");
}
}
ErrorReported(guar) => self.result = Some((self.root_span, guar)),
Expand All @@ -253,7 +252,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match

fn failure(&mut self, parser: &Parser<'_>) {
let Some((which_matcher, _)) = self.current else {
bug!("`Self::prepare()` was not called to initialize context");
panic!("`Self::prepare()` was not called to initialize context");
};

let mut token = parser.token;
Expand Down Expand Up @@ -290,7 +289,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match

fn ambiguity(&mut self, parser: &Parser<'_>) {
let Some((_, matcher)) = self.current else {
bug!("`Self::prepare()` was not called to initialize context");
panic!("`Self::prepare()` was not called to initialize context");
};

#[expect(
Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_expand/src/mbe/macro_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ pub(crate) use ParseResult::*;
use rustc_ast::token::{self, DocComment, NonterminalKind, Token, TokenKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_middle::span_bug;
use rustc_parse::parser::{ParseNtResult, Parser, token_descr};
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span};

Expand Down Expand Up @@ -732,17 +731,14 @@ impl TtParser {
// `NamedParseResult`. Otherwise, it's an error.
let mut ret_val = FxHashMap::default();
for loc in matcher {
if let &MatcherLoc::MetaVarDecl { span, bind, .. } = loc
if let &MatcherLoc::MetaVarDecl { bind, .. } = loc
&& ret_val
.insert(MacroRulesNormalizedIdent::new(bind), res.next().unwrap())
.is_some()
{
// Duplicate binds are checked for when the macro definition is processed,
// and should have prevented the definition from ever being used.
span_bug!(
span,
"duplicate meta-variable binding went undetected at macro definition"
)
panic!("duplicate meta-variable binding went undetected at macro definition")
}
}
ret_val
Expand Down
75 changes: 6 additions & 69 deletions compiler/rustc_expand/src/proc_macro.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use rustc_ast as ast;
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::AtomicRef;
use rustc_data_structures::profiling::TimingGuard;
use rustc_errors::ErrorGuaranteed;
use rustc_middle::ty::{self, TyCtxt};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_proc_macro as pm;
use rustc_session::Session;
Expand Down Expand Up @@ -113,14 +113,7 @@ impl MultiItemModifier for DeriveProcMacro {
let res = if ecx.sess.opts.incremental.is_some()
&& ecx.sess.opts.unstable_opts.cache_proc_macros
{
ty::tls::with(|tcx| {
let input = &*tcx.arena.alloc(input);
let key: (LocalExpnId, &TokenStream) = (invoc_id, input);

QueryDeriveExpandCtx::enter(ecx, self.client, move || {
tcx.derive_macro_expansion(key).cloned()
})
})
(*EXPAND_DERIVE_CACHED)(invoc_id, input, ecx, self.client)
} else {
expand_derive_macro(invoc_id, input, ecx, self.client)
};
Expand Down Expand Up @@ -163,24 +156,9 @@ impl MultiItemModifier for DeriveProcMacro {
}
}

/// Provide a query for computing the output of a derive macro.
pub(super) fn provide_derive_macro_expansion<'tcx>(
tcx: TyCtxt<'tcx>,
key: (LocalExpnId, &'tcx TokenStream),
) -> Result<&'tcx TokenStream, ()> {
let (invoc_id, input) = key;

// Make sure that we invalidate the query when the crate defining the proc macro changes
let _ = tcx.crate_hash(invoc_id.expn_data().macro_def_id.unwrap().krate);

QueryDeriveExpandCtx::with(|ecx, client| {
expand_derive_macro(invoc_id, input.clone(), ecx, client).map(|ts| &*tcx.arena.alloc(ts))
})
}

type DeriveClient = pm::bridge::client::Client;

fn expand_derive_macro(
pub fn expand_derive_macro(
invoc_id: LocalExpnId,
input: TokenStream,
ecx: &mut ExtCtxt<'_>,
Expand Down Expand Up @@ -216,47 +194,6 @@ fn expand_derive_macro(
}
}

/// Stores the context necessary to expand a derive proc macro via a query.
struct QueryDeriveExpandCtx {
/// Type-erased version of `&mut ExtCtxt`
expansion_ctx: *mut (),
client: DeriveClient,
}

impl QueryDeriveExpandCtx {
/// Store the extension context and the client into the thread local value.
/// It will be accessible via the `with` method while `f` is active.
fn enter<F, R>(ecx: &mut ExtCtxt<'_>, client: DeriveClient, f: F) -> R
where
F: FnOnce() -> R,
{
// We need erasure to get rid of the lifetime
let ctx = Self { expansion_ctx: ecx as *mut _ as *mut (), client };
DERIVE_EXPAND_CTX.set(&ctx, f)
}

/// Accesses the thread local value of the derive expansion context.
/// Must be called while the `enter` function is active.
fn with<F, R>(f: F) -> R
where
F: for<'a, 'b> FnOnce(&'b mut ExtCtxt<'a>, DeriveClient) -> R,
{
DERIVE_EXPAND_CTX.with(|ctx| {
let ectx = {
let casted = ctx.expansion_ctx.cast::<ExtCtxt<'_>>();
// SAFETY: We can only get the value from `with` while the `enter` function
// is active (on the callstack), and that function's signature ensures that the
// lifetime is valid.
// If `with` is called at some other time, it will panic due to usage of
// `scoped_tls::with`.
unsafe { casted.as_mut().unwrap() }
};

f(ectx, ctx.client)
})
}
}

// When we invoke a query to expand a derive proc macro, we need to provide it with the expansion
// context and derive Client. We do that using a thread-local.
scoped_tls::scoped_thread_local!(static DERIVE_EXPAND_CTX: QueryDeriveExpandCtx);
pub static EXPAND_DERIVE_CACHED: AtomicRef<

@JonathanBrouwer JonathanBrouwer Aug 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we expect this to be replaced by the proper function way before this is ever called, does it make sense to default this to expand_derive_macro? Can we panic instead?

View changes since the review

@JonathanBrouwer JonathanBrouwer Aug 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use EII here instead of an AtomicRef?

View changes since the review

@bjorn3 bjorn3 Aug 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EIIs aren't used anywhere inside rustc yet. It is probably fine given that no default impl is necessary, but maybe do it in a separate PR that moves all callbacks to EII?

Edit: Actually please wait with EII usage until #159843 gets merged.

fn(LocalExpnId, TokenStream, &mut ExtCtxt<'_>, DeriveClient) -> Result<TokenStream, ()>,
> = AtomicRef::new(&(expand_derive_macro as _));
20 changes: 20 additions & 0 deletions compiler/rustc_expand_queries/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "rustc_expand_queries"
version = "0.0.0"
edition = "2024"
build = false

[lib]
doctest = false

[dependencies]
# tidy-alphabetical-start
rustc_ast = { path = "../rustc_ast" }
rustc_expand = { path = "../rustc_expand" }
rustc_middle = { path = "../rustc_middle" }
# We must use the proc_macro version that we will compile proc-macros against,
# not the one from our own sysroot.
rustc_proc_macro = { path = "../rustc_proc_macro" }
rustc_span = { path = "../rustc_span" }
scoped-tls = "1.0"
# tidy-alphabetical-end
82 changes: 82 additions & 0 deletions compiler/rustc_expand_queries/src/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_expand::base::ExtCtxt;
use rustc_middle::ty::{TyCtxt, tls};
use rustc_proc_macro as pm;
use rustc_span::LocalExpnId;

type DeriveClient = pm::bridge::client::Client;

/// Stores the context necessary to expand a derive proc macro via a query.
struct QueryDeriveExpandCtx {
/// Type-erased version of `&mut ExtCtxt`
expansion_ctx: *mut (),
client: DeriveClient,
}

impl QueryDeriveExpandCtx {
/// Store the extension context and the client into the thread local value.
/// It will be accessible via the `with` method while `f` is active.
fn enter<F, R>(ecx: &mut ExtCtxt<'_>, client: DeriveClient, f: F) -> R
where
F: FnOnce() -> R,
{
// We need erasure to get rid of the lifetime
let ctx = Self { expansion_ctx: ecx as *mut _ as *mut (), client };
DERIVE_EXPAND_CTX.set(&ctx, f)
}

/// Accesses the thread local value of the derive expansion context.
/// Must be called while the `enter` function is active.
fn with<F, R>(f: F) -> R
where
F: for<'a, 'b> FnOnce(&'b mut ExtCtxt<'a>, DeriveClient) -> R,
{
DERIVE_EXPAND_CTX.with(|ctx| {
let ectx = {
let casted = ctx.expansion_ctx.cast::<ExtCtxt<'_>>();
// SAFETY: We can only get the value from `with` while the `enter` function
// is active (on the callstack), and that function's signature ensures that the
// lifetime is valid.
// If `with` is called at some other time, it will panic due to usage of
// `scoped_tls::with`.
unsafe { casted.as_mut().unwrap() }
};

f(ectx, ctx.client)
})
}
}

// When we invoke a query to expand a derive proc macro, we need to provide it with the expansion
// context and derive Client. We do that using a thread-local.
scoped_tls::scoped_thread_local!(static DERIVE_EXPAND_CTX: QueryDeriveExpandCtx);

pub(crate) fn expand_derive_macro(
invoc_id: LocalExpnId,
input: TokenStream,
ecx: &mut ExtCtxt<'_>,
client: DeriveClient,
) -> Result<TokenStream, ()> {
tls::with(|tcx| {
let input = &*tcx.arena.alloc(input);
let key: (LocalExpnId, &TokenStream) = (invoc_id, input);

QueryDeriveExpandCtx::enter(ecx, client, move || tcx.derive_macro_expansion(key).cloned())
})
}

/// Provide a query for computing the output of a derive macro.
pub(crate) fn derive_macro_expansion<'tcx>(
tcx: TyCtxt<'tcx>,
key: (LocalExpnId, &'tcx TokenStream),
) -> Result<&'tcx TokenStream, ()> {
let (invoc_id, input) = key;

// Make sure that we invalidate the query when the crate defining the proc macro changes
let _ = tcx.crate_hash(invoc_id.expn_data().macro_def_id.unwrap().krate);

QueryDeriveExpandCtx::with(|ecx, client| {
rustc_expand::proc_macro::expand_derive_macro(invoc_id, input.clone(), ecx, client)
.map(|ts| &*tcx.arena.alloc(ts))
})
}
12 changes: 12 additions & 0 deletions compiler/rustc_expand_queries/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![allow(internal_features, reason = "proc macro internals")]
#![feature(proc_macro_internals)]

mod derive;

pub fn enable() {
rustc_expand::proc_macro::EXPAND_DERIVE_CACHED.swap(&(derive::expand_derive_macro as _));
}

pub fn provide(providers: &mut rustc_middle::query::Providers) {
providers.derive_macro_expansion = derive::derive_macro_expansion;
}
1 change: 1 addition & 0 deletions compiler/rustc_interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ rustc_crate_store = { path = "../rustc_crate_store" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_expand = { path = "../rustc_expand" }
rustc_expand_queries = { path = "../rustc_expand_queries" }
rustc_feature = { path = "../rustc_feature" }
rustc_fs_util = { path = "../rustc_fs_util" }
rustc_hir = { path = "../rustc_hir" }
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@ pub fn setup_callbacks() {
rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
rustc_errors::TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _));
rustc_feature::TRACK_FEATURE.swap(&(track_feature as _));
rustc_expand_queries::enable();
}
2 changes: 1 addition & 1 deletion compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
providers.queries.proc_macro_decls_static = |tcx, _| tcx.hir_crate_items(()).proc_macro_decls();
rustc_ast_lowering::provide(&mut providers.queries);
limits::provide(&mut providers.queries);
rustc_expand::provide(&mut providers.queries);
rustc_expand_queries::provide(&mut providers.queries);
rustc_const_eval::provide(providers);
rustc_middle::hir::provide(&mut providers.queries);
rustc_borrowck::provide(&mut providers.queries);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ expression: bench
- Set({compiler/rustc_error_messages})
- Set({compiler/rustc_errors})
- Set({compiler/rustc_expand})
- Set({compiler/rustc_expand_queries})
- Set({compiler/rustc_feature})
- Set({compiler/rustc_fs_util})
- Set({compiler/rustc_graphviz})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ expression: build compiler
- Set({compiler/rustc_error_messages})
- Set({compiler/rustc_errors})
- Set({compiler/rustc_expand})
- Set({compiler/rustc_expand_queries})
- Set({compiler/rustc_feature})
- Set({compiler/rustc_fs_util})
- Set({compiler/rustc_graphviz})
Expand Down
Loading
Loading