@@ -89,7 +89,14 @@ pub fn inject(
8989impl < ' a > CollectProcMacros < ' a > {
9090 fn check_not_pub_in_root ( & self , vis : & ast:: Visibility , sp : Span ) {
9191 if self . is_proc_macro_crate && self . in_root && vis. kind . is_pub ( ) {
92- self . dcx . emit_err ( diagnostics:: ProcMacro { span : sp } ) ;
92+ // On wasm we end up generating other public exports (though the exact specifics are
93+ // internal). For now omit this check, we'll need to refine it before stabilizing wasm
94+ // proc macros.
95+ if !( self . session . opts . unstable_opts . wasm_proc_macros
96+ && self . session . target . is_like_wasm )
97+ {
98+ self . dcx . emit_err ( diagnostics:: ProcMacro { span : sp } ) ;
99+ }
93100 }
94101 }
95102
@@ -270,6 +277,9 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
270277// // ...
271278// ];
272279// }
280+ //
281+ // If we're targeting wasm32, we also inject a macro call to generate_export!(DECLS). This produces
282+ // the WASI component model ABI exports/imports to support the proc macro's execution.
273283fn mk_decls ( cx : & mut ExtCtxt < ' _ > , macros : & [ ProcMacro ] ) -> Box < ast:: Item > {
274284 let expn_id = cx. resolver . expansion_for_ast_pass (
275285 DUMMY_SP ,
@@ -361,9 +371,33 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box<ast::Item> {
361371 cx. attr_nested_word ( sym:: allow, sym:: deprecated, span) ,
362372 ] ) ;
363373
364- let block = ast:: ConstItemRhsKind :: new_body ( cx. expr_block (
365- cx. block ( span, thin_vec ! [ cx. stmt_item( span, krate) , cx. stmt_item( span, decls_static) ] ) ,
366- ) ) ;
374+ // For wasm targets, exporting from proc-macros requires that we generate additional symbols
375+ // (not just the single #[used] static). We use a macro defined in the proc_macro crate to do
376+ // so.
377+ let block_contents = if cx. sess . target . is_like_wasm {
378+ let mac_call = cx. stmt_semi ( cx. expr_macro_call (
379+ span,
380+ cx. macro_call (
381+ span,
382+ cx. path (
383+ span,
384+ vec ! [ proc_macro, bridge, client, Ident :: new( sym:: generate_export, span) ] ,
385+ ) ,
386+ rustc_ast:: token:: Delimiter :: Parenthesis ,
387+ rustc_ast:: tokenstream:: TokenStream :: new ( vec ! [
388+ rustc_ast:: tokenstream:: TokenTree :: Token (
389+ rustc_ast:: token:: Token :: from_ast_ident( Ident :: new( sym:: _DECLS, span) ) ,
390+ rustc_ast:: tokenstream:: Spacing :: Alone ,
391+ ) ,
392+ ] ) ,
393+ ) ,
394+ ) ) ;
395+ thin_vec ! [ cx. stmt_item( span, krate) , cx. stmt_item( span, decls_static) , mac_call]
396+ } else {
397+ thin_vec ! [ cx. stmt_item( span, krate) , cx. stmt_item( span, decls_static) ]
398+ } ;
399+
400+ let block = ast:: ConstItemRhsKind :: new_body ( cx. expr_block ( cx. block ( span, block_contents) ) ) ;
367401
368402 let anon_constant = cx. item_const (
369403 span,
@@ -376,3 +410,66 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box<ast::Item> {
376410 let items = AstFragment :: Items ( smallvec ! [ anon_constant] ) ;
377411 cx. monotonic_expander ( ) . fully_expand_fragment ( items) . make_items ( ) . pop ( ) . unwrap ( )
378412}
413+
414+ pub ( crate ) struct InternalWitBindgen ;
415+
416+ impl rustc_expand:: base:: BangProcMacro for InternalWitBindgen {
417+ fn expand < ' cx > (
418+ & self ,
419+ ecx : & ' cx mut ExtCtxt < ' _ > ,
420+ span : Span ,
421+ _ts : rustc_ast:: tokenstream:: TokenStream ,
422+ ) -> Result < rustc_ast:: tokenstream:: TokenStream , rustc_span:: ErrorGuaranteed > {
423+ let mut options = wit_bindgen_rust:: Opts :: default ( ) ;
424+ options. pub_export_macro = true ;
425+
426+ let mut resolve = wit_bindgen_core:: wit_parser:: Resolve :: default ( ) ;
427+ let pkg_id = resolve
428+ . push_str (
429+ "proc-macro-wasm.wit" ,
430+ include_str ! ( "../../../library/proc_macro/wasm-interface.wit" ) ,
431+ )
432+ . unwrap ( ) ;
433+ let world = resolve. select_world ( & [ pkg_id] , None ) . unwrap ( ) ;
434+
435+ let mut generator = options. build ( ) ;
436+ let mut files = Default :: default ( ) ;
437+ wit_bindgen_core:: WorldGenerator :: generate ( & mut generator, & mut resolve, world, & mut files)
438+ . expect ( "generation successful" ) ;
439+ let ( _, src) = files. iter ( ) . next ( ) . unwrap ( ) ;
440+ let src = std:: str:: from_utf8 ( src) . unwrap ( ) ;
441+
442+ let expn_data = ecx. current_expansion . id . expn_data ( ) ;
443+ let call_site = ecx. with_call_site_ctxt ( expn_data. call_site ) ;
444+
445+ let needle = "macro_rules! __export_rust_lang_rust_custom_derive_cabi" ;
446+ let output = rustc_parse:: source_str_to_stream (
447+ ecx. psess ( ) ,
448+ rustc_span:: FileName :: proc_macro_source_code ( & src) ,
449+ src. replace (
450+ needle,
451+ // #[allow_internal_unstable(...)] is not transitive on macro expansions and so
452+ // doesn't apply to this inner macro -- which also references unstable types from
453+ // the generated bindings.
454+ //
455+ // This string replacement is obviously a hack, but it works OK in practice. If you
456+ // can remove this and keep wasm proc macro tests passing, go for it.
457+ & format ! ( "#[allow_internal_unstable(proc_macro_internals)] {needle}" ) ,
458+ ) ,
459+ Some ( call_site) ,
460+ ) ;
461+
462+ match output {
463+ Ok ( o) => Ok ( o) ,
464+ Err ( diags) => {
465+ for diag in diags {
466+ diag. emit ( ) ;
467+ }
468+
469+ Err ( ecx
470+ . dcx ( )
471+ . span_delayed_bug ( span, "failed to parse wit-bindgen generated source" ) )
472+ }
473+ }
474+ }
475+ }
0 commit comments