From 838c8c713e9584a7b8261f3f613682256998138b Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 31 May 2026 11:03:35 -0400 Subject: [PATCH] Implement support for proc macros on wasm32-wasip2 --- Cargo.lock | 920 +++++++++++++++++- bootstrap.example.toml | 9 + compiler/rustc/Cargo.toml | 2 + compiler/rustc_builtin_macros/Cargo.toml | 2 + compiler/rustc_builtin_macros/src/lib.rs | 5 + .../src/proc_macro_harness.rs | 108 +- compiler/rustc_codegen_ssa/src/back/linker.rs | 20 +- compiler/rustc_driver_impl/Cargo.toml | 3 + compiler/rustc_driver_impl/src/lib.rs | 2 + compiler/rustc_expand/Cargo.toml | 6 + compiler/rustc_expand/src/lib.rs | 1 + compiler/rustc_expand/src/proc_macro.rs | 195 +++- .../rustc_expand/src/proc_macro_server.rs | 8 +- compiler/rustc_expand/src/wasm_proc_macro.rs | 678 +++++++++++++ compiler/rustc_interface/src/diagnostics.rs | 4 + compiler/rustc_interface/src/passes.rs | 9 +- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_metadata/Cargo.toml | 1 + compiler/rustc_metadata/src/creader.rs | 62 +- compiler/rustc_metadata/src/host_dylib.rs | 7 +- compiler/rustc_metadata/src/locator.rs | 120 ++- compiler/rustc_metadata/src/rmeta/decoder.rs | 70 +- compiler/rustc_session/src/cstore.rs | 1 + compiler/rustc_session/src/options.rs | 2 + compiler/rustc_session/src/output.rs | 18 +- compiler/rustc_session/src/session.rs | 19 + compiler/rustc_span/src/span_encoding.rs | 11 + compiler/rustc_span/src/symbol.rs | 2 + library/Cargo.lock | 2 + library/proc_macro/Cargo.toml | 6 + library/proc_macro/src/bridge/client.rs | 2 + library/proc_macro/src/bridge/client_wasi.rs | 481 +++++++++ library/proc_macro/src/bridge/mod.rs | 23 + .../proc_macro/src/bridge/panic_message.rs | 3 + library/proc_macro/src/bridge/server.rs | 6 +- library/proc_macro/src/bridge/symbol.rs | 10 +- library/proc_macro/src/lib.rs | 32 +- library/proc_macro/wasm-interface.wit | 176 ++++ src/bootstrap/src/core/build_steps/test.rs | 12 + src/bootstrap/src/core/config/config.rs | 4 + src/bootstrap/src/core/config/toml/rust.rs | 2 + src/bootstrap/src/utils/cc_detect.rs | 11 +- src/bootstrap/src/utils/proc_macro_deps.rs | 2 + .../src/compiler-flags/wasm-proc-macros.md | 8 + src/tools/compiletest/src/common.rs | 2 + src/tools/compiletest/src/directives/cfg.rs | 6 + .../src/directives/directive_names.rs | 2 + src/tools/compiletest/src/directives/tests.rs | 2 +- src/tools/compiletest/src/lib.rs | 4 +- src/tools/compiletest/src/runtest.rs | 39 +- src/tools/compiletest/src/rustdoc_gui_test.rs | 1 + .../proc-macro-srv/src/dylib/proc_macros.rs | 10 +- .../crates/proc-macro-srv/src/lib.rs | 1 + src/tools/tidy/src/deps.rs | 107 ++ src/tools/tidy/src/pal.rs | 1 + .../supported-crate-types.wasm.stdout | 1 + tests/ui/proc-macro/auxiliary/make-macro.rs | 5 +- ...tdout => meta-macro-hygiene.native.stdout} | 9 +- tests/ui/proc-macro/meta-macro-hygiene.rs | 3 + .../proc-macro/meta-macro-hygiene.wasm.stdout | 68 ++ tests/ui/proc-macro/meta-macro.native.stdout | 3 + tests/ui/proc-macro/meta-macro.rs | 3 + tests/ui/proc-macro/meta-macro.wasm.stdout | 3 + 63 files changed, 3200 insertions(+), 136 deletions(-) create mode 100644 compiler/rustc_expand/src/wasm_proc_macro.rs create mode 100644 library/proc_macro/src/bridge/client_wasi.rs create mode 100644 library/proc_macro/wasm-interface.wit create mode 100644 src/doc/unstable-book/src/compiler-flags/wasm-proc-macros.md rename tests/ui/proc-macro/{meta-macro-hygiene.stdout => meta-macro-hygiene.native.stdout} (71%) create mode 100644 tests/ui/proc-macro/meta-macro-hygiene.wasm.stdout create mode 100644 tests/ui/proc-macro/meta-macro.native.stdout create mode 100644 tests/ui/proc-macro/meta-macro.wasm.stdout diff --git a/Cargo.lock b/Cargo.lock index 4f65c39bc204c..3b4f1307aa3a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "gimli 0.31.1", ] +[[package]] +name = "addr2line" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a" +dependencies = [ + "gimli 0.33.0", +] + [[package]] name = "adler2" version = "2.0.1" @@ -43,6 +52,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -184,6 +199,12 @@ dependencies = [ "object 0.37.3", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arrayref" version = "0.3.9" @@ -264,6 +285,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -276,7 +308,7 @@ version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ - "addr2line", + "addr2line 0.24.2", "cfg-if", "libc", "miniz_oxide", @@ -397,6 +429,9 @@ name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +dependencies = [ + "allocator-api2", +] [[package]] name = "bytecheck" @@ -442,6 +477,74 @@ dependencies = [ "serde_core", ] +[[package]] +name = "cap-fs-ext" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5528f85b1e134ae811704e41ef80930f56e795923f866813255bc342cc20654" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.59.0", +] + +[[package]] +name = "cap-net-ext" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20a158160765c6a7d0d8c072a53d772e4cb243f38b04bfcf6b4939cfbe7482e7" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix", + "smallvec", +] + +[[package]] +name = "cap-primitives" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cf3aea8a5081171859ef57bc1606b1df6999df4f1110f8eef68b30098d1d3a" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "rustix-linux-procfs", + "windows-sys 0.59.0", + "winx", +] + +[[package]] +name = "cap-std" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6dc3090992a735d23219de5c204927163d922f42f575a0189b005c62d37549a" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def102506ce40c11710a9b16e614af0cde8e76ae51b1f48c04b8d79f4b671a80" +dependencies = [ + "ambient-authority", + "cap-primitives", + "iana-time-zone", + "once_cell", + "rustix", + "winx", +] + [[package]] name = "capstone" version = "0.14.0" @@ -947,6 +1050,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "cpp_demangle" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpubits" version = "0.1.1" @@ -971,6 +1083,152 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-assembler-x64" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e06aeba2c965fc446d13c56a6ccb2631b78445d7544543dd9a25289977630914" +dependencies = [ + "cranelift-assembler-x64-meta", +] + +[[package]] +name = "cranelift-assembler-x64-meta" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2d2dde4ec1352715595b5cfa6fe2e5b8ebb9da3457b3ee8db0aa2808c069aa" +dependencies = [ + "cranelift-srcgen", +] + +[[package]] +name = "cranelift-bforest" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4982ef9fa54ec9eee841e891e7ddc5434be1250e88de31572e000c888f30b" +dependencies = [ + "cranelift-entity", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-bitset" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529143118c4eeb58c39ecb02319557d512be6c61348486422974ab8e3906b8a8" +dependencies = [ + "serde", + "serde_derive", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-codegen" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7780677247ad3577e3a6a3ebf43f39b325a11d6393db72b2c9968a910d4d13d" +dependencies = [ + "bumpalo", + "cranelift-assembler-x64", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.33.0", + "hashbrown 0.17.0", + "libm", + "log", + "postcard", + "pulley-interpreter", + "regalloc2", + "rustc-hash 2.1.1", + "serde", + "serde_derive", + "sha2", + "smallvec", + "target-lexicon", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9645250416cbf92454fe61160e17e026e0ce405906a54500b114f923ddffc9" +dependencies = [ + "cranelift-assembler-x64-meta", + "cranelift-codegen-shared", + "cranelift-srcgen", + "heck", + "pulley-interpreter", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ee8d222ff0fd3681791979afbf88586ac9f49010d3db96b3cbe4c96759aee3" + +[[package]] +name = "cranelift-control" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591abe6f5312bd2c4220f1b3bead56c2ad00257c52668015ba013b85dcf2a17a" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5300c49cf940526fe771517b3b3eabd5d0ff164ee61698579cf403fe8d3af3c" +dependencies = [ + "cranelift-bitset", + "serde", + "serde_derive", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-frontend" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4adbf760207fdbbe130f1191cce01cdef66831a9f648b1f39ff2800d126d45" +dependencies = [ + "cranelift-codegen", + "hashbrown 0.17.0", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8315b21ff018226a42a60a4702c2dd75f6447cac26e9bca622e14c22088c2ff5" + +[[package]] +name = "cranelift-native" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d506ef23a60715bde451b06620b14402166ded3b648454fccbf04f3e46a4aa70" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-srcgen" +version = "0.133.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ed47e602652e3410f9387fc0db70fefadcee4d78a78881421aabcab4e26b89" + [[package]] name = "crc32fast" version = "1.5.0" @@ -1364,6 +1622,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_filter" version = "0.1.4" @@ -1556,12 +1823,87 @@ dependencies = [ "autocfg", ] +[[package]] +name = "fs-set-times" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" +dependencies = [ + "io-lifetimes", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "fs_extra" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + [[package]] name = "generate-copyright" version = "0.1.0" @@ -1670,6 +2012,18 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c" +dependencies = [ + "fnv", + "hashbrown 0.16.1", + "indexmap", + "stable_deref_trait", +] + [[package]] name = "git2" version = "0.20.2" @@ -2095,6 +2449,22 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "io-extras" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" +dependencies = [ + "io-lifetimes", + "windows-sys 0.59.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" + [[package]] name = "ipc-channel" version = "0.22.0" @@ -2114,6 +2484,12 @@ dependencies = [ "windows 0.61.3", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -2129,6 +2505,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.15.0" @@ -2326,9 +2711,9 @@ checksum = "803ec87c9cfb29b9d2633f20cba1f488db3fd53f2158b1024cbefb47ba05d413" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libffi" @@ -2384,9 +2769,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" @@ -2441,9 +2826,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -2492,6 +2877,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "mach2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + [[package]] name = "markup5ever" version = "0.39.0" @@ -2512,6 +2903,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + [[package]] name = "md-5" version = "0.10.6" @@ -2542,6 +2939,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memfd" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +dependencies = [ + "rustix", +] + [[package]] name = "memmap2" version = "0.2.3" @@ -2575,9 +2981,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -2840,6 +3246,18 @@ dependencies = [ "wasmparser 0.236.1", ] +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "crc32fast", + "hashbrown 0.17.0", + "indexmap", + "memchr", +] + [[package]] name = "odht" version = "0.3.1" @@ -3274,12 +3692,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" [[package]] -name = "punycode" -version = "0.4.1" +name = "pulley-interpreter" +version = "46.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e1dcb320d6839f6edb64f7a4a59d39b30480d4d1765b56873f7c858538a5fe" - -[[package]] +checksum = "38b92604caae1a1899b6a5b54967289dd538177c626004c91accf9d0ec7e4a12" +dependencies = [ + "cranelift-bitset", + "log", + "pulley-macros", + "wasmtime-internal-core", +] + +[[package]] +name = "pulley-macros" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7ac85c0bb3fb351f10d531230aaa5e366b46d7c4e5328e5f02801d6dac1165" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "punycode" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e1dcb320d6839f6edb64f7a4a59d39b30480d4d1765b56873f7c858538a5fe" + +[[package]] name = "quine-mc_cluskey" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3439,6 +3880,21 @@ dependencies = [ "syn", ] +[[package]] +name = "regalloc2" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2c52737737f8609e94f975dee22854a2d5c125772d4b1cf292120f4d45c186" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.17.0", + "log", + "rustc-hash 2.1.1", + "serde", + "smallvec", +] + [[package]] name = "regex" version = "1.12.2" @@ -3597,6 +4053,7 @@ name = "rustc-main" version = "0.0.0" dependencies = [ "getrandom 0.3.3", + "proc-macro2", "rustc_codegen_ssa", "rustc_driver", "rustc_driver_impl", @@ -3829,6 +4286,8 @@ dependencies = [ "smallvec", "thin-vec", "tracing", + "wit-bindgen-core 0.58.0", + "wit-bindgen-rust 0.58.0", ] [[package]] @@ -3981,6 +4440,7 @@ dependencies = [ "ctrlc", "jiff", "libc", + "quote", "rustc_abi", "rustc_ast", "rustc_ast_pretty", @@ -4009,6 +4469,7 @@ dependencies = [ "rustc_target", "serde_json", "shlex", + "syn", "tracing", "windows 0.61.3", ] @@ -4083,6 +4544,8 @@ dependencies = [ "smallvec", "thin-vec", "tracing", + "wasmtime", + "wasmtime-wasi", ] [[package]] @@ -4426,6 +4889,7 @@ dependencies = [ "rustc_target", "tempfile", "tracing", + "wasmparser 0.252.0", ] [[package]] @@ -5111,9 +5575,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -5122,6 +5586,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustix-linux-procfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056" +dependencies = [ + "once_cell", + "rustix", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -5395,11 +5869,20 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -5541,6 +6024,12 @@ dependencies = [ "xattr", ] +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + [[package]] name = "tempfile" version = "3.23.0" @@ -5746,6 +6235,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + [[package]] name = "toml" version = "0.7.8" @@ -6419,6 +6922,16 @@ dependencies = [ "wasmparser 0.244.0", ] +[[package]] +name = "wasm-encoder" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a879a421bd17c528b74721b2abf4c62e8f1d1889c2ba8c3c50d02deaf2ce395" +dependencies = [ + "leb128fmt", + "wasmparser 0.251.0", +] + [[package]] name = "wasm-encoder" version = "0.253.0" @@ -6441,6 +6954,18 @@ dependencies = [ "wasmparser 0.244.0", ] +[[package]] +name = "wasm-metadata" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f998ccc6e012f7b86865eb2a106c8a0422017a1a88977ce01a69f2244be2e57" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.251.0", + "wasmparser 0.251.0", +] + [[package]] name = "wasm-metadata" version = "0.253.0" @@ -6485,6 +7010,30 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437970b35b1a85cfde9c74b2398352d8d653f3bd8e3a3db0c063ea8f5b4b36ff" +dependencies = [ + "bitflags", + "hashbrown 0.17.0", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wasmparser" +version = "0.252.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + [[package]] name = "wasmparser" version = "0.253.0" @@ -6498,6 +7047,264 @@ dependencies = [ "serde", ] +[[package]] +name = "wasmprinter" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8798c1a699bd25648b6708eefe94d97c6f9891febb94b42cca1f7a4b086ea64e" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser 0.251.0", +] + +[[package]] +name = "wasmtime" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4213d2f019a5e44aa8a61d8826dd33a505bff79f749b14a8bafd67321cb9351" +dependencies = [ + "addr2line 0.26.1", + "async-trait", + "bitflags", + "bumpalo", + "cc", + "cfg-if", + "encoding_rs", + "futures", + "libc", + "log", + "mach2", + "memfd", + "object 0.39.1", + "once_cell", + "postcard", + "pulley-interpreter", + "rustix", + "semver", + "serde", + "serde_derive", + "smallvec", + "target-lexicon", + "wasmparser 0.251.0", + "wasmtime-environ", + "wasmtime-internal-component-macro", + "wasmtime-internal-component-util", + "wasmtime-internal-core", + "wasmtime-internal-cranelift", + "wasmtime-internal-fiber", + "wasmtime-internal-jit-debug", + "wasmtime-internal-jit-icache-coherence", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-environ" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d45863de41977ec6453e859cf843d456fa3fcb45a659b66d16e794f90ec4f5b7" +dependencies = [ + "anyhow", + "cpp_demangle", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-entity", + "gimli 0.33.0", + "hashbrown 0.17.0", + "indexmap", + "log", + "object 0.39.1", + "postcard", + "rustc-demangle", + "semver", + "serde", + "serde_derive", + "sha2", + "smallvec", + "target-lexicon", + "wasm-encoder 0.251.0", + "wasmparser 0.251.0", + "wasmprinter", + "wasmtime-internal-component-util", + "wasmtime-internal-core", +] + +[[package]] +name = "wasmtime-internal-component-macro" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e48f8d4966d62a10b6d70722bc432c1e163890be2801d3b5784589ad36ffc3" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wasmtime-internal-component-util", + "wasmtime-internal-wit-bindgen", + "wit-parser 0.251.0", +] + +[[package]] +name = "wasmtime-internal-component-util" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819ad5abd5822a22dbf4014475cdfd1fe790707761cd732d74aaa3ba4d5ba489" + +[[package]] +name = "wasmtime-internal-core" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc28372e36eaf8cf70faa83b5779137f7e99c8d18569a125d1580e735cc9e4d" +dependencies = [ + "hashbrown 0.17.0", + "libm", + "serde", +] + +[[package]] +name = "wasmtime-internal-cranelift" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a433efc6e35112a5457e1dc8bc4d8d39820ac7722267e89bc04e5df641f32124" +dependencies = [ + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "gimli 0.33.0", + "itertools 0.14.0", + "log", + "object 0.39.1", + "pulley-interpreter", + "smallvec", + "target-lexicon", + "thiserror 2.0.17", + "wasmparser 0.251.0", + "wasmtime-environ", + "wasmtime-internal-core", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", +] + +[[package]] +name = "wasmtime-internal-fiber" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a1d3a39d0d210f6b8574ee96a4315e0a14c67f3a1fc3cd5372cb10d2fb4422" +dependencies = [ + "cc", + "cfg-if", + "libc", + "rustix", + "wasmtime-environ", + "wasmtime-internal-versioned-export-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-internal-jit-debug" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f667288cb4dfa68a4639ffac4d5628535dda64ebdc2b990526efb12b30ba803" +dependencies = [ + "cc", + "wasmtime-internal-versioned-export-macros", +] + +[[package]] +name = "wasmtime-internal-jit-icache-coherence" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba651d44ab0faad4c58106b3adb45068189fb65ef50f0c404b6d9e3bf81a357" +dependencies = [ + "cfg-if", + "libc", + "wasmtime-internal-core", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-internal-unwinder" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ecc52563b0558af2a7487eb710de07cc4532564b55528876129238e83118cb1" +dependencies = [ + "cfg-if", + "cranelift-codegen", + "log", + "object 0.39.1", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-internal-versioned-export-macros" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e747f4a074699ba1b4e4d841fb263f9b7df5bd1555181c4752bf5990d21ba676" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmtime-internal-wit-bindgen" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80009f46991622814196d96fac6fc0a938f46b5cba737a8f4e21e24e5a03856f" +dependencies = [ + "anyhow", + "bitflags", + "heck", + "indexmap", + "wit-parser 0.251.0", +] + +[[package]] +name = "wasmtime-wasi" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f65ef30a2c5478873cdb619085a7a649d3ce41cc3eaf298a7ce3dee96a8e11" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-std", + "cap-time-ext", + "cfg-if", + "futures", + "io-extras", + "io-lifetimes", + "rand 0.10.1", + "rustix", + "thiserror 2.0.17", + "tokio", + "tracing", + "url", + "wasmtime", + "wasmtime-wasi-io", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-wasi-io" +version = "46.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee57d5fef4976b1ab542615f4cef2c43278eb549d8078939668ea0f13d5c696" +dependencies = [ + "async-trait", + "bytes", + "futures", + "tracing", + "wasmtime", +] + [[package]] name = "wast" version = "253.0.0" @@ -6876,6 +7683,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" +[[package]] +name = "winx" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" +dependencies = [ + "bitflags", + "windows-sys 0.59.0", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -6902,6 +7719,17 @@ dependencies = [ "wit-parser 0.244.0", ] +[[package]] +name = "wit-bindgen-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4738d1c9a78e97bc7f664bfafd5d8e67d7bb26faa5c41e6d628e8bbdad3ec351" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.251.0", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -6923,10 +7751,26 @@ dependencies = [ "prettyplease", "syn", "wasm-metadata 0.244.0", - "wit-bindgen-core", + "wit-bindgen-core 0.51.0", "wit-component 0.244.0", ] +[[package]] +name = "wit-bindgen-rust" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1130ce1f531bc9f9a75922244aa773bf5e2117fda1ef4a86b9f98d6b8135eb46" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata 0.251.0", + "wit-bindgen-core 0.58.0", + "wit-component 0.251.0", +] + [[package]] name = "wit-bindgen-rust-macro" version = "0.51.0" @@ -6938,8 +7782,8 @@ dependencies = [ "proc-macro2", "quote", "syn", - "wit-bindgen-core", - "wit-bindgen-rust", + "wit-bindgen-core 0.51.0", + "wit-bindgen-rust 0.51.0", ] [[package]] @@ -6961,6 +7805,25 @@ dependencies = [ "wit-parser 0.244.0", ] +[[package]] +name = "wit-component" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a5e60173c413659c689f0581b0cf5d1a2404077568f9ffdce748a9eb2fc913" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.251.0", + "wasm-metadata 0.251.0", + "wasmparser 0.251.0", + "wit-parser 0.251.0", +] + [[package]] name = "wit-component" version = "0.253.0" @@ -6998,6 +7861,25 @@ dependencies = [ "wasmparser 0.244.0", ] +[[package]] +name = "wit-parser" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e960732e824fab95099971a09e638979347c94ca48568d3c854c945729196947" +dependencies = [ + "anyhow", + "hashbrown 0.17.0", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.251.0", +] + [[package]] name = "wit-parser" version = "0.253.0" diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 2c9a5ab1a152c..f9642f4faf334 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -923,6 +923,15 @@ # #rust.parallel-frontend-threads = 1 +# Enables building a wasm proc macro compatible toolchain. +# +# This requires building an additional standard library for a different target and adding it +# to the sysroot before running tests, and so needs special handling in bootstrap. Currently +# off by default. +# +# This currently opts compiletest into running/building proc-macro tests via wasm. +#rust.wasm-proc-macro = false + # ============================================================================= # Distribution options # diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 8cef9e0644bb0..ce569dc1bda35 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -20,6 +20,8 @@ rustc_public = { path = "../rustc_public" } rustc_public_bridge = { path = "../rustc_public_bridge" } # tidy-alphabetical-end +proc-macro2 = { version = "1", features = ["proc-macro"] } + # Pin these to avoid pulling in a package with a binary blob # [target.'cfg(target_os = "wasi")'.dependencies] diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index f5dbc4a0d7cbe..ab6df94eb8f48 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -31,6 +31,8 @@ rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.18" tracing = "0.1" +wit-bindgen-core = "0.58" +wit-bindgen-rust = "0.58" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 78bf7d97bd7b8..fbde333aacb32 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -149,4 +149,9 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { register(sym::contracts_requires, requires); let ensures = SyntaxExtensionKind::Attr(Arc::new(contracts::ExpandEnsures)); register(sym::contracts_ensures, ensures); + + register( + sym::internal_wit_bindgen, + SyntaxExtensionKind::Bang(Arc::new(proc_macro_harness::InternalWitBindgen)), + ); } diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 32865f234ef44..6e5fe8a5d1d7d 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -89,7 +89,14 @@ pub fn inject( impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { - self.dcx.emit_err(diagnostics::ProcMacro { span: sp }); + // On wasm we end up generating other public exports (though the exact specifics are + // internal). For now omit this check, we'll need to refine it before stabilizing wasm + // proc macros. + if !(self.session.opts.unstable_opts.wasm_proc_macros + && self.session.target.is_like_wasm) + { + self.dcx.emit_err(diagnostics::ProcMacro { span: sp }); + } } } @@ -270,6 +277,9 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { // // ... // ]; // } +// +// If we're targeting wasm32, we also inject a macro call to generate_export!(DECLS). This produces +// the WASI component model ABI exports/imports to support the proc macro's execution. fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box { let expn_id = cx.resolver.expansion_for_ast_pass( DUMMY_SP, @@ -361,9 +371,33 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box { cx.attr_nested_word(sym::allow, sym::deprecated, span), ]); - let block = ast::ConstItemRhsKind::new_body(cx.expr_block( - cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]), - )); + // For wasm targets, exporting from proc-macros requires that we generate additional symbols + // (not just the single #[used] static). We use a macro defined in the proc_macro crate to do + // so. + let block_contents = if cx.sess.target.is_like_wasm { + let mac_call = cx.stmt_semi(cx.expr_macro_call( + span, + cx.macro_call( + span, + cx.path( + span, + vec![proc_macro, bridge, client, Ident::new(sym::generate_export, span)], + ), + rustc_ast::token::Delimiter::Parenthesis, + rustc_ast::tokenstream::TokenStream::new(vec![ + rustc_ast::tokenstream::TokenTree::Token( + rustc_ast::token::Token::from_ast_ident(Ident::new(sym::_DECLS, span)), + rustc_ast::tokenstream::Spacing::Alone, + ), + ]), + ), + )); + thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static), mac_call] + } else { + thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)] + }; + + let block = ast::ConstItemRhsKind::new_body(cx.expr_block(cx.block(span, block_contents))); let anon_constant = cx.item_const( span, @@ -376,3 +410,69 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box { let items = AstFragment::Items(smallvec![anon_constant]); cx.monotonic_expander().fully_expand_fragment(items).make_items().pop().unwrap() } + +pub(crate) struct InternalWitBindgen; + +impl rustc_expand::base::BangProcMacro for InternalWitBindgen { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + _ts: rustc_ast::tokenstream::TokenStream, + ) -> Result { + let mut options = wit_bindgen_rust::Opts::default(); + options.pub_export_macro = true; + options.format = false; + + let mut resolve = wit_bindgen_core::wit_parser::Resolve::default(); + let pkg_id = resolve + .push_str( + "proc-macro-wasm.wit", + include_str!("../../../library/proc_macro/wasm-interface.wit"), + ) + .unwrap(); + let world = resolve.select_world(&[pkg_id], None).unwrap(); + + let mut generator = options.build(); + let mut files = Default::default(); + wit_bindgen_core::WorldGenerator::generate(&mut generator, &mut resolve, world, &mut files) + .expect("generation successful"); + let (_, src) = files.iter().next().unwrap(); + let src = std::str::from_utf8(src).unwrap(); + + let expn_data = ecx.current_expansion.id.expn_data(); + let call_site = ecx.with_call_site_ctxt(expn_data.call_site); + + let needle = "macro_rules! "; + let src = src.replace( + needle, + // #[allow_internal_unstable(...)] is not transitive on macro expansions and so + // doesn't apply to this inner macro -- which also references unstable types from + // the generated bindings. + // + // This string replacement is obviously a hack, but it works OK in practice. If you + // can remove this and keep wasm proc macro tests passing, go for it. + &format!("#[allow_internal_unstable(proc_macro_internals)] {needle}"), + ); + assert!(src.contains(needle), "{}", src); + let output = rustc_parse::source_str_to_stream( + ecx.psess(), + rustc_span::FileName::proc_macro_source_code(&src), + src, + Some(call_site), + ); + + match output { + Ok(o) => Ok(o), + Err(diags) => { + for diag in diags { + diag.emit(); + } + + Err(ecx + .dcx() + .span_delayed_bug(span, "failed to parse wit-bindgen generated source")) + } + } + } +} diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e24e7bad2b945..f200f5fb3869e 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1800,11 +1800,7 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec, crate_type: CrateType) -> Vec, + span: Span, + input: TokenStream, + ) -> Result { + let _timer = record_expand_proc_macro(ecx, "expand_proc_macro", span); + + (self.client)(ecx, ecx.current_expansion.id, input).map_err(|e| { + ecx.dcx().emit_err(diagnostics::ProcMacroPanicked { + span, + message: e.map(|message| diagnostics::ProcMacroPanickedHelp { message }), + }) + }) + } +} + pub struct AttrProcMacro { pub client: pm::bridge::client::Client, } @@ -87,8 +111,31 @@ impl base::AttrProcMacro for AttrProcMacro { } } +pub struct WasmAttrProcMacro { + pub client: WasmExpand2, +} + +impl base::AttrProcMacro for WasmAttrProcMacro { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + let _timer = record_expand_proc_macro(ecx, "expand_proc_macro", span); + + (self.client)(ecx, ecx.current_expansion.id, annotation, annotated).map_err(|e| { + ecx.dcx().emit_err(diagnostics::CustomAttributePanicked { + span, + message: e.map(|message| diagnostics::CustomAttributePanickedHelp { message }), + }) + }) + } +} + pub struct DeriveProcMacro { - pub client: DeriveClient, + pub client: pm::bridge::client::Client, } impl MultiItemModifier for DeriveProcMacro { @@ -122,45 +169,89 @@ impl MultiItemModifier for DeriveProcMacro { }) }) } else { - expand_derive_macro(invoc_id, input, ecx, self.client) + expand_derive_macro(invoc_id, input, ecx, &self.client) }; - let Ok(output) = res else { - // error will already have been emitted - return ExpandResult::Ready(vec![]); + finish_derive_expansion(ecx, span, is_stmt, res) + } +} + +pub struct WasmDeriveProcMacro { + pub client: WasmExpand1, +} + +impl MultiItemModifier for WasmDeriveProcMacro { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + span: Span, + _meta_item: &ast::MetaItem, + item: Annotatable, + _is_derive_const: bool, + ) -> ExpandResult, Annotatable> { + let _timer = record_expand_proc_macro(ecx, "expand_derive_proc_macro_outer", span); + + // We need special handling for statement items + // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) + let is_stmt = matches!(item, Annotatable::Stmt(..)); + + let input = item.to_tokens(); + + let invoc_id = ecx.current_expansion.id; + + let res = if ecx.sess.opts.incremental.is_some() + && ecx.sess.opts.unstable_opts.cache_proc_macros + { + unimplemented!("wasm proc macros don't yet support -Zcache-proc-macros") + } else { + expand_wasm_derive_macro(invoc_id, input, ecx, &self.client) }; - let error_count_before = ecx.dcx().err_count(); - let mut parser = Parser::new(&ecx.sess.psess, output, Some("proc-macro derive")); - let mut items = vec![]; - - loop { - match parser.parse_item( - ForceCollect::No, - if is_stmt { AllowConstBlockItems::No } else { AllowConstBlockItems::Yes }, - ) { - Ok(None) => break, - Ok(Some(item)) => { - if is_stmt { - items.push(Annotatable::Stmt(Box::new(ecx.stmt_item(span, item)))); - } else { - items.push(Annotatable::Item(item)); - } - } - Err(err) => { - err.emit(); - break; + finish_derive_expansion(ecx, span, is_stmt, res) + } +} + +fn finish_derive_expansion( + ecx: &mut ExtCtxt<'_>, + span: Span, + is_stmt: bool, + res: Result, +) -> ExpandResult, Annotatable> { + let Ok(output) = res else { + // error will already have been emitted + return ExpandResult::Ready(vec![]); + }; + + let error_count_before = ecx.dcx().err_count(); + let mut parser = Parser::new(&ecx.sess.psess, output, Some("proc-macro derive")); + let mut items = vec![]; + + loop { + match parser.parse_item( + ForceCollect::No, + if is_stmt { AllowConstBlockItems::No } else { AllowConstBlockItems::Yes }, + ) { + Ok(None) => break, + Ok(Some(item)) => { + if is_stmt { + items.push(Annotatable::Stmt(Box::new(ecx.stmt_item(span, item)))); + } else { + items.push(Annotatable::Item(item)); } } + Err(err) => { + err.emit(); + break; + } } // fail if there have been errors emitted if ecx.dcx().err_count() > error_count_before { ecx.dcx().emit_err(diagnostics::ProcMacroDeriveTokens { span }); } - - ExpandResult::Ready(items) } + + ExpandResult::Ready(items) } /// Provide a query for computing the output of a derive macro. @@ -174,17 +265,37 @@ pub(super) fn provide_derive_macro_expansion<'tcx>( 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)) + expand_derive_macro(invoc_id, input.clone(), ecx, &client).map(|ts| &*tcx.arena.alloc(ts)) }) } type DeriveClient = pm::bridge::client::Client; +pub type WasmExpand1 = rustc_data_structures::sync::IntoDynSyncSend< + Arc< + dyn Fn(&mut ExtCtxt<'_>, LocalExpnId, TokenStream) -> Result> + + Send + + Sync, + >, +>; +pub type WasmExpand2 = rustc_data_structures::sync::IntoDynSyncSend< + Arc< + dyn Fn( + &mut ExtCtxt<'_>, + LocalExpnId, + TokenStream, + TokenStream, + ) -> Result> + + Send + + Sync, + >, +>; + fn expand_derive_macro( invoc_id: LocalExpnId, input: TokenStream, ecx: &mut ExtCtxt<'_>, - client: DeriveClient, + client: &DeriveClient, ) -> Result { let _timer = ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| { @@ -216,6 +327,32 @@ fn expand_derive_macro( } } +fn expand_wasm_derive_macro( + invoc_id: LocalExpnId, + input: TokenStream, + ecx: &mut ExtCtxt<'_>, + client: &WasmExpand1, +) -> Result { + let _timer = + ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| { + let invoc_expn_data = invoc_id.expn_data(); + let span = invoc_expn_data.call_site; + let event_arg = invoc_expn_data.kind.descr(); + recorder.record_arg_with_span(ecx.sess.source_map(), event_arg, span); + }); + + client(ecx, invoc_id, input).map_err(|e| { + let invoc_expn_data = invoc_id.expn_data(); + let span = invoc_expn_data.call_site; + ecx.dcx().emit_err({ + diagnostics::ProcMacroDerivePanicked { + span, + message: e.map(|message| diagnostics::ProcMacroDerivePanickedHelp { message }), + } + }); + }) +} + /// Stores the context necessary to expand a derive proc macro via a query. struct QueryDeriveExpandCtx { /// Type-erased version of `&mut ExtCtxt` diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 65af690611919..617dbae1dfdb2 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -420,10 +420,10 @@ fn cancel_diags_into_string(diags: Vec>) -> String { } pub(crate) struct Rustc<'a, 'b> { - ecx: &'a mut ExtCtxt<'b>, - def_site: Span, - call_site: Span, - mixed_site: Span, + pub(crate) ecx: &'a mut ExtCtxt<'b>, + pub(crate) def_site: Span, + pub(crate) call_site: Span, + pub(crate) mixed_site: Span, krate: CrateNum, rebased_spans: FxHashMap, } diff --git a/compiler/rustc_expand/src/wasm_proc_macro.rs b/compiler/rustc_expand/src/wasm_proc_macro.rs new file mode 100644 index 0000000000000..7f85cfbdc6fd3 --- /dev/null +++ b/compiler/rustc_expand/src/wasm_proc_macro.rs @@ -0,0 +1,678 @@ +use std::sync::Arc; + +use rustc_ast::tokenstream; +use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::sync::IntoDynSyncSend; +use rustc_proc_macro::bridge; +use rustc_proc_macro::bridge::server::Server as _; +use rustc_session::Session; +use rustc_span::{Span, Symbol}; + +use crate::base::ExtCtxt; +use crate::proc_macro_server::Rustc; +use crate::wasm_proc_macro::generated::exports::rust_lang::rust::custom_derive::DeriveKind; +use crate::wasm_proc_macro::generated::rust_lang::rust::host; + +#[allow(unreachable_pub)] +mod generated { + wasmtime::component::bindgen!({ + path: "../../library/proc_macro/wasm-interface.wit", + + with: { + "rust-lang:rust/host.token-stream": rustc_ast::tokenstream::TokenStream, + "rust-lang:rust/host.diagnostic": rust_lang::rust::host::DiagnosticInner, + }, + }); +} + +pub struct Ctx { + ctx: wasmtime_wasi::WasiCtx, + pub table: wasmtime_wasi::ResourceTable, + + // Outer Option is tracking whether a panic was stored during wasm execution + // (from a panic hook). + // Inner Option is the panic message (which may not be available). + stored_panic: Option>, +} + +impl Default for Ctx { + fn default() -> Self { + Ctx { + ctx: wasmtime_wasi::WasiCtx::builder().inherit_stdout().inherit_stderr().build(), + table: Default::default(), + stored_panic: None, + } + } +} + +impl wasmtime_wasi::WasiView for Ctx { + fn ctx(&mut self) -> wasmtime_wasi::WasiCtxView<'_> { + wasmtime_wasi::WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } + } +} + +trait ToInternal { + fn to_internal(self) -> T; +} + +impl ToInternal for generated::rust_lang::rust::host::Level { + fn to_internal(self) -> rustc_errors::Level { + match self { + Self::Error => rustc_errors::Level::Error, + Self::Warning => rustc_errors::Level::Warning, + Self::Help => rustc_errors::Level::Help, + Self::Note => rustc_errors::Level::Note, + } + } +} + +impl ToInternal for generated::rust_lang::rust::host::Level { + fn to_internal(self) -> rustc_proc_macro::Level { + match self { + Self::Error => rustc_proc_macro::Level::Error, + Self::Warning => rustc_proc_macro::Level::Warning, + Self::Help => rustc_proc_macro::Level::Help, + Self::Note => rustc_proc_macro::Level::Note, + } + } +} + +impl host::DiagnosticInner { + fn to_internal( + self, + table: &mut wasmtime_wasi::ResourceTable, + ) -> rustc_proc_macro::bridge::Diagnostic { + rustc_proc_macro::bridge::Diagnostic { + level: self.level.to_internal(), + message: self.message, + spans: self.spans.into_iter().map(|s| s.decode()).collect(), + children: self + .children + .into_iter() + .map(|c| { + let child = table.delete(c).unwrap(); + child.to_internal(table) + }) + .collect(), + } + } +} + +impl ToInternal for rustc_proc_macro::bridge::LitKind { + fn to_internal(self) -> host::LiteralKind { + use host::LiteralKind; + use rustc_proc_macro::bridge::LitKind; + match self { + LitKind::Byte => LiteralKind::Byte, + LitKind::Char => LiteralKind::Char, + LitKind::Integer => LiteralKind::Integer, + LitKind::Float => LiteralKind::Float, + LitKind::Str => LiteralKind::Str, + LitKind::StrRaw(n) => LiteralKind::StrRaw(n), + LitKind::ByteStr => LiteralKind::ByteStr, + LitKind::ByteStrRaw(n) => LiteralKind::ByteStrRaw(n), + LitKind::CStr => LiteralKind::CStr, + LitKind::CStrRaw(n) => LiteralKind::CStrRaw(n), + LitKind::ErrWithGuar => LiteralKind::ErrWithGuar, + } + } +} + +impl ToInternal for host::LiteralKind { + fn to_internal(self) -> rustc_proc_macro::bridge::LitKind { + use host::LiteralKind; + use rustc_proc_macro::bridge::LitKind; + match self { + LiteralKind::Byte => LitKind::Byte, + LiteralKind::Char => LitKind::Char, + LiteralKind::Integer => LitKind::Integer, + LiteralKind::Float => LitKind::Float, + LiteralKind::Str => LitKind::Str, + LiteralKind::StrRaw(n) => LitKind::StrRaw(n), + LiteralKind::ByteStr => LitKind::ByteStr, + LiteralKind::ByteStrRaw(n) => LitKind::ByteStrRaw(n), + LiteralKind::CStr => LitKind::CStr, + LiteralKind::CStrRaw(n) => LitKind::CStrRaw(n), + LiteralKind::ErrWithGuar => LitKind::ErrWithGuar, + } + } +} + +trait ToWasmSpan { + fn encode(self) -> WasmSpan; +} + +impl ToWasmSpan for Span { + fn encode(self) -> WasmSpan { + WasmSpan { inner: self.encode_raw() } + } +} + +impl WasmSpan { + fn decode(self) -> Span { + Span::decode_raw(self.inner) + } +} + +impl generated::rust_lang::rust::host::Host for Ctx { + fn emit_diagnostic(&mut self, diag: wasmtime::component::Resource) { + let diagnostic = self.table.delete(diag).unwrap(); + DeriveExpandCtx::with(|(ecx, _)| { + ecx.emit_diagnostic(diagnostic.to_internal(&mut self.table)) + }) + } + + fn literal_from_str(&mut self, s: String) -> Result { + DeriveExpandCtx::with(|(ecx, _)| { + let literal = ecx.literal_from_str(&s)?; + Ok(host::Literal { + kind: literal.kind.to_internal(), + symbol: literal.symbol.to_string(), + suffix: literal.suffix.map(|s| s.to_string()), + span: self.span_call_site(), + }) + }) + } + + fn symbol_normalize_and_validate_ident(&mut self, string: String) -> Result { + DeriveExpandCtx::with(|(ecx, _)| { + let sym = ecx.symbol_normalize_and_validate_ident(&string)?; + Ok(sym.to_string()) + }) + } + + fn injected_env_var(&mut self, var: String) -> Option { + DeriveExpandCtx::with(|(ecx, _)| ecx.injected_env_var(&var)) + } + + fn track_env_var(&mut self, var: String, value: Option) { + DeriveExpandCtx::with(|(ecx, _)| ecx.track_env_var(&var, value.as_deref())) + } + + fn track_path(&mut self, path: String) { + DeriveExpandCtx::with(|(ecx, _)| ecx.track_path(&path)) + } + + fn report_panic(&mut self, msg: Option) -> bool { + self.stored_panic = Some(msg); + + DeriveExpandCtx::with(|(ecx, _)| ecx.ecx.ecfg.proc_macro_backtrace) + } + + fn span_is_same(&mut self, a: WasmSpan, b: WasmSpan) -> bool { + a.decode() == b.decode() + } + + fn span_subspan( + &mut self, + a: WasmSpan, + start: host::RangeBound, + end: host::RangeBound, + ) -> Option { + let span = a.decode(); + + let start = match start.bound { + host::Bound::Included => std::ops::Bound::Included(start.value as usize), + host::Bound::Excluded => std::ops::Bound::Excluded(start.value as usize), + host::Bound::Unbounded => std::ops::Bound::Unbounded, + }; + let end = match end.bound { + host::Bound::Included => std::ops::Bound::Included(end.value as usize), + host::Bound::Excluded => std::ops::Bound::Excluded(end.value as usize), + host::Bound::Unbounded => std::ops::Bound::Unbounded, + }; + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.span_subspan(span, start, end))?; + Some(ret.encode()) + } + + fn span_def_site(&mut self) -> WasmSpan { + DeriveExpandCtx::with(|(ecx, _)| ecx.def_site.encode()) + } + + fn span_call_site(&mut self) -> WasmSpan { + DeriveExpandCtx::with(|(ecx, _)| ecx.call_site.encode()) + } + + fn span_mixed_site(&mut self) -> WasmSpan { + DeriveExpandCtx::with(|(ecx, _)| ecx.mixed_site.encode()) + } + + fn span_line(&mut self, sp: WasmSpan) -> u64 { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| ecx.span_line(span)).try_into().unwrap() + } + + fn span_column(&mut self, sp: WasmSpan) -> u64 { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| ecx.span_column(span).try_into().unwrap()) + } + + fn span_file(&mut self, sp: WasmSpan) -> String { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| ecx.span_file(span)) + } + + fn span_local_file(&mut self, sp: WasmSpan) -> Option { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| ecx.span_local_file(span)) + } + + fn span_start(&mut self, sp: WasmSpan) -> WasmSpan { + let span = sp.decode(); + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.span_start(span)); + ret.encode() + } + + fn span_end(&mut self, sp: WasmSpan) -> WasmSpan { + let span = sp.decode(); + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.span_end(span)); + ret.encode() + } + + fn span_join(&mut self, this: WasmSpan, other: WasmSpan) -> Option { + let this = this.decode(); + let other = other.decode(); + + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.span_join(this, other))?; + Some(ret.encode()) + } + + fn span_byte_range(&mut self, sp: WasmSpan) -> (u64, u64) { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| { + let ret = ecx.span_byte_range(span); + (ret.start as u64, ret.end as u64) + }) + } + + fn span_parent(&mut self, sp: WasmSpan) -> Option { + let span = sp.decode(); + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.span_parent(span))?; + Some(ret.encode()) + } + + fn span_source(&mut self, sp: WasmSpan) -> WasmSpan { + let span = sp.decode(); + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.span_source(span)); + ret.encode() + } + + fn span_debug(&mut self, sp: WasmSpan) -> String { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| ecx.span_debug(span)) + } + + fn span_source_text(&mut self, sp: WasmSpan) -> Option { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| ecx.span_source_text(span)) + } + + fn span_resolved_at(&mut self, this: WasmSpan, at: WasmSpan) -> WasmSpan { + let this = this.decode(); + let at = at.decode(); + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.span_resolved_at(this, at)); + ret.encode() + } + + fn span_save_span(&mut self, sp: WasmSpan) -> u64 { + let span = sp.decode(); + DeriveExpandCtx::with(|(ecx, _)| ecx.span_save_span(span)).try_into().unwrap() + } + + fn span_recover_proc_macro_span(&mut self, id: u64) -> WasmSpan { + let span = DeriveExpandCtx::with(|(ecx, _)| ecx.span_recover_proc_macro_span(id as usize)); + span.encode() + } + + fn ts_drop(&mut self, ts: TokenStreamResource) { + self.table.delete(ts).unwrap(); + } + + fn ts_is_empty(&mut self, input: TokenStreamResource) -> bool { + let input = self.table.get(&input).unwrap(); + input.is_empty() + } + + fn ts_concat_streams( + &mut self, + base: Option, + streams: Vec, + ) -> TokenStreamResource { + let base = base.map(|b| self.table.delete(b).unwrap()); + let ret = DeriveExpandCtx::with(|(ecx, _)| { + ecx.ts_concat_streams( + base, + streams.into_iter().map(|s| self.table.delete(s).unwrap()).collect(), + ) + }); + self.table.push(ret).unwrap() + } + + fn ts_from_token_tree(&mut self, tree: host::TokenTree) -> TokenStreamResource { + let ret = DeriveExpandCtx::with(|(ecx, _)| { + ecx.ts_from_token_tree(tree.to_internal(&mut self.table)) + }); + self.table.push(ret).unwrap() + } + + fn ts_concat_trees( + &mut self, + base: Option, + trees: Vec, + ) -> TokenStreamResource { + let base = base.map(|b| self.table.delete(b).unwrap()); + let ret = DeriveExpandCtx::with(|(ecx, _)| { + ecx.ts_concat_trees( + base, + trees.into_iter().map(|t| t.to_internal(&mut self.table)).collect(), + ) + }); + self.table.push(ret).unwrap() + } + + fn ts_clone(&mut self, input: TokenStreamResource) -> TokenStreamResource { + let input = self.table.get(&input).unwrap(); + self.table.push(input.clone()).unwrap() + } + + fn ts_expand_expr(&mut self, stream: TokenStreamResource) -> Result { + let stream = self.table.get(&stream).unwrap(); + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.ts_expand_expr(stream))?; + Ok(self.table.push(ret).unwrap()) + } + + fn ts_from_str(&mut self, src: String) -> Result { + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.ts_from_str(&src))?; + Ok(self.table.push(ret).unwrap()) + } + + fn ts_to_string(&mut self, input: TokenStreamResource) -> String { + let input = self.table.get(&input).unwrap(); + DeriveExpandCtx::with(|(ecx, _)| ecx.ts_to_string(&input)) + } + + fn ts_into_trees(&mut self, stream: TokenStreamResource) -> Vec { + let stream = self.table.get(&stream).expect("valid resource"); + let stream = stream.clone(); + + let ret = DeriveExpandCtx::with(|(ecx, _)| ecx.ts_into_trees(stream)); + ret.into_iter() + .map(|tt| match tt { + bridge::TokenTree::Group(group) => host::TokenTree::Group(host::Group { + delimiter: match group.delimiter { + rustc_proc_macro::Delimiter::Parenthesis => host::Delimiter::Parenthesis, + rustc_proc_macro::Delimiter::Brace => host::Delimiter::Brace, + rustc_proc_macro::Delimiter::Bracket => host::Delimiter::Bracket, + rustc_proc_macro::Delimiter::None => host::Delimiter::None, + }, + stream: group.stream.map(|ts| self.table.push(ts).unwrap()), + span: host::DelimSpan { + open: group.span.open.encode(), + close: group.span.close.encode(), + entire: group.span.entire.encode(), + }, + }), + bridge::TokenTree::Punct(punct) => host::TokenTree::Punct(host::Punct { + ch: punct.ch, + joint: punct.joint, + span: punct.span.encode(), + }), + bridge::TokenTree::Ident(ident) => host::TokenTree::Ident(host::Ident { + sym: ident.sym.to_string(), + is_raw: ident.is_raw, + span: ident.span.encode(), + }), + bridge::TokenTree::Literal(literal) => host::TokenTree::Literal(host::Literal { + kind: literal.kind.to_internal(), + symbol: literal.symbol.to_string(), + suffix: literal.suffix.map(|s| s.to_string()), + span: literal.span.encode(), + }), + }) + .collect() + } +} + +type TokenStreamResource = wasmtime::component::Resource; +type WasmSpan = generated::rust_lang::rust::host::Span; +type Diagnostic = generated::rust_lang::rust::host::DiagnosticInner; + +impl generated::rust_lang::rust::host::HostDiagnostic for Ctx { + fn new(&mut self, diagnostic: Diagnostic) -> wasmtime::component::Resource { + self.table.push(diagnostic).unwrap() + } + + fn drop(&mut self, rep: wasmtime::component::Resource) -> wasmtime::Result<()> { + self.table.delete(rep)?; + Ok(()) + } +} + +impl host::DelimSpan { + fn to_internal(self) -> rustc_proc_macro::bridge::DelimSpan { + rustc_proc_macro::bridge::DelimSpan { + open: self.open.decode(), + close: self.close.decode(), + entire: self.entire.decode(), + } + } +} + +impl host::Delimiter { + fn to_internal(self) -> rustc_proc_macro::Delimiter { + match self { + host::Delimiter::Parenthesis => rustc_proc_macro::Delimiter::Parenthesis, + host::Delimiter::Brace => rustc_proc_macro::Delimiter::Brace, + host::Delimiter::Bracket => rustc_proc_macro::Delimiter::Bracket, + host::Delimiter::None => rustc_proc_macro::Delimiter::None, + } + } +} + +impl host::TokenTree { + fn to_internal( + self, + table: &mut wasmtime_wasi::ResourceTable, + ) -> bridge::TokenTree { + match self { + host::TokenTree::Group(group) => bridge::TokenTree::Group(bridge::Group { + delimiter: group.delimiter.to_internal(), + stream: group.stream.map(|s| table.delete(s).unwrap()), + span: group.span.to_internal(), + }), + host::TokenTree::Punct(punct) => bridge::TokenTree::Punct(bridge::Punct { + ch: punct.ch, + joint: punct.joint, + span: punct.span.decode(), + }), + host::TokenTree::Ident(ident) => bridge::TokenTree::Ident(bridge::Ident { + sym: Symbol::intern(&ident.sym), + is_raw: ident.is_raw, + span: ident.span.decode(), + }), + host::TokenTree::Literal(literal) => bridge::TokenTree::Literal(bridge::Literal { + kind: literal.kind.to_internal(), + symbol: Symbol::intern(&literal.symbol), + suffix: literal.suffix.map(|s| Symbol::intern(&s)), + span: literal.span.decode(), + }), + } + } +} + +impl generated::rust_lang::rust::host::HostTokenStream for Ctx { + fn new(&mut self) -> TokenStreamResource { + self.table.push(TokenStream::default()).unwrap() + } + + fn drop(&mut self, r: TokenStreamResource) -> wasmtime::Result<()> { + self.table.delete(r)?; + Ok(()) + } +} + +#[derive(Clone)] +pub enum RustcProcMacro { + Dylib { client: rustc_proc_macro::bridge::client::Client }, + + WasmExpand1 { client: crate::proc_macro::WasmExpand1 }, + + WasmExpand2 { client: crate::proc_macro::WasmExpand2 }, +} + +type ErasedCtx<'a, 'b> = (Rustc<'a, 'b>, rustc_span::LocalExpnId); + +/// Stores the context necessary to expand a derive proc macro via a query. +struct DeriveExpandCtx { + /// Type-erased version of `&mut ExtCtxt` + ctx: *mut (), +} + +impl DeriveExpandCtx { + /// 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(ecx: &mut ErasedCtx<'_, '_>, f: F) -> R + where + F: FnOnce() -> R, + { + // We need erasure to get rid of the lifetime + let ctx = Self { ctx: ecx as *mut _ as *mut () }; + 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: F) -> R + where + F: for<'a, 'b, 'c> FnOnce(&'a mut ErasedCtx<'b, 'c>) -> R, + { + DERIVE_EXPAND_CTX.with(|ctx| { + let ectx = { + let casted = ctx.ctx.cast::>(); + // 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) + }) + } +} + +// 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: DeriveExpandCtx); + +pub fn expand( + engine: &wasmtime::Engine, + component: &wasmtime::component::Component, + ecx: &mut ExtCtxt<'_>, + expn_id: rustc_span::LocalExpnId, + derive_idx: usize, + cb: impl FnOnce( + &mut wasmtime::Store, + wasmtime::component::ResourceAny, + generated::exports::rust_lang::rust::custom_derive::GuestCustomDerive<'_>, + ) -> wasmtime::Result, +) -> Result> { + let mut linker = wasmtime::component::Linker::new(&engine); + wasmtime_wasi::p2::add_to_linker_sync(&mut linker).unwrap(); + generated::ProcMacro::add_to_linker::<_, wasmtime::component::HasSelf<_>>( + &mut linker, + |state| state, + ) + .unwrap(); + + let ctx = Ctx::default(); + + let mut store = wasmtime::Store::new(&engine, ctx); + let bindings = generated::ProcMacro::instantiate(&mut store, component, &linker).unwrap(); + + let mut derives = + bindings.rust_lang_rust_custom_derive().call_get_custom_derives(&mut store).unwrap(); + let derive = derives.remove(derive_idx); + + let ecx = Rustc::new(ecx); + + DeriveExpandCtx::enter(&mut (ecx, expn_id), || { + match cb(&mut store, derive, bindings.rust_lang_rust_custom_derive().custom_derive()) { + Ok(expanded) => Ok(store.data_mut().table.delete::(expanded).unwrap()), + Err(e) => { + if let Some(msg) = store.data_mut().stored_panic.take() { + return Err(msg); + } + + // If we didn't store a panic during execution then this must be something else. + panic!("Failed to run wasm proc-macro: {:?}", e); + } + } + }) +} + +pub enum WasmLoadError { + FileRead(std::io::Error), + Parse(wasmtime::Error), + RuntimeFailed(wasmtime::Error), +} + +pub fn load_wasm_macro( + _sess: &Session, + path: &std::path::Path, +) -> Result, WasmLoadError> { + let code = std::fs::read(path).map_err(WasmLoadError::FileRead)?; + let engine = wasmtime::Engine::default(); + let component = + wasmtime::component::Component::new(&engine, code).map_err(WasmLoadError::Parse)?; + let mut linker = wasmtime::component::Linker::new(&engine); + wasmtime_wasi::p2::add_to_linker_sync(&mut linker).unwrap(); + generated::ProcMacro::add_to_linker::<_, wasmtime::component::HasSelf<_>>( + &mut linker, + |state| state, + ) + .unwrap(); + + let ctx = Ctx::default(); + let mut store = wasmtime::Store::new(&engine, ctx); + let bindings = generated::ProcMacro::instantiate(&mut store, &component, &linker).unwrap(); + let mut provided = vec![]; + + let derives = + bindings.rust_lang_rust_custom_derive().call_get_custom_derives(&mut store).unwrap(); + for (idx, derive) in derives.into_iter().enumerate() { + let kind = bindings + .rust_lang_rust_custom_derive() + .custom_derive() + .call_get_kind(&mut store, derive) + .unwrap(); + let component = component.clone(); + let engine = engine.clone(); + match kind { + DeriveKind::Expand1 => { + provided.push(RustcProcMacro::WasmExpand1 { + client: IntoDynSyncSend(Arc::new(move |ecx, expn_id, stream| { + expand(&engine, &component, ecx, expn_id, idx, |store, derive, bindings| { + let input_handle = store.data_mut().table.push(stream).unwrap(); + bindings.call_expand1(store, derive, input_handle) + }) + })), + }); + } + + DeriveKind::Expand2 => { + provided.push(RustcProcMacro::WasmExpand2 { + client: IntoDynSyncSend(Arc::new(move |ecx, expn_id, stream_a, stream_b| { + expand(&engine, &component, ecx, expn_id, idx, |store, derive, bindings| { + let input_handle_a = store.data_mut().table.push(stream_a).unwrap(); + let input_handle_b = store.data_mut().table.push(stream_b).unwrap(); + bindings.call_expand2(store, derive, input_handle_a, input_handle_b) + }) + })), + }); + } + } + } + Ok(provided) +} diff --git a/compiler/rustc_interface/src/diagnostics.rs b/compiler/rustc_interface/src/diagnostics.rs index 7cae1aa54d2e7..3a2386ab7dea4 100644 --- a/compiler/rustc_interface/src/diagnostics.rs +++ b/compiler/rustc_interface/src/diagnostics.rs @@ -53,6 +53,10 @@ pub(crate) struct MixedBinCrate; #[diag("cannot mix `proc-macro` crate type with others")] pub(crate) struct MixedProcMacroCrate; +#[derive(Diagnostic)] +#[diag("cannot compile `proc-macro` crate to wasm targets without -Zwasm-proc-macros")] +pub(crate) struct UnstableWasmProcMacro; + #[derive(Diagnostic)] #[diag("error writing dependencies to `{$path}`: {$error}")] pub(crate) struct ErrorWritingDependencies<'a> { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ca983db29bcf7..2701aa5ddb49d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -274,13 +274,20 @@ fn configure_and_expand( } if is_proc_macro_crate { sess.dcx().emit_err(diagnostics::MixedProcMacroCrate); + + if sess.target.is_like_wasm && !sess.opts.unstable_opts.wasm_proc_macros { + sess.dcx().emit_err(diagnostics::UnstableWasmProcMacro); + } } } if crate_types.contains(&CrateType::Sdylib) && !tcx.features().export_stable() { feature_err(sess, sym::export_stable, DUMMY_SP, "`sdylib` crate type is unstable").emit(); } - if is_proc_macro_crate && !sess.panic_strategy().unwinds() { + // Wasm targets today don't unwind, but they also generally don't crash the compiler since + // at least today proc-macros are run in a separate wasm runtime than the compiler. (The + // compiler probably won't run in wasm itself anyway). + if is_proc_macro_crate && !sess.panic_strategy().unwinds() && !sess.target.is_like_wasm { sess.dcx().emit_warn(diagnostics::ProcMacroCratePanicAbort); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 71d1829f76584..6e6647b190b97 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -911,6 +911,7 @@ fn test_unstable_options_tracking_hash() { tracked!(verify_llvm_ir, true); tracked!(virtual_function_elimination, true); tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); + tracked!(wasm_proc_macros, true); // tidy-alphabetical-end macro_rules! tracked_no_crate_hash { diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 2fce131e08b6a..e77d82ceb3dec 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -30,6 +30,7 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } tempfile = "3.7.1" tracing = "0.1" +wasmparser = { version = "0.252.0", default-features = false, features = ["std", "component-model", "simd"] } # tidy-alphabetical-end [target.'cfg(target_os = "aix")'.dependencies] diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index dd063f5425511..543b9f1e60d6a 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -13,6 +13,7 @@ use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; use rustc_data_structures::unord::UnordMap; use rustc_expand::base::SyntaxExtension; +use rustc_expand::wasm_proc_macro::{RustcProcMacro, WasmLoadError}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_hir::definitions::Definitions; @@ -20,7 +21,6 @@ use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::ty::data_structures::IndexSet; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; -use rustc_proc_macro::bridge::client::Client as ProcMacroClient; use rustc_session::config::mitigation_coverage::DeniedPartialMitigationLevel; use rustc_session::config::{ CrateType, ExtendedTargetModifierInfo, ExternLocation, Externs, OptionsTargetModifiers, @@ -119,10 +119,13 @@ impl<'a> std::fmt::Debug for CrateDump<'a> { writeln!(fmt, " hash: {}", data.hash())?; writeln!(fmt, " reqd: {:?}", data.dep_kind())?; writeln!(fmt, " priv: {:?}", data.is_private_dep())?; - let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source(); + let CrateSource { dylib, rlib, wasm, rmeta, sdylib_interface } = data.source(); if let Some(dylib) = dylib { writeln!(fmt, " dylib: {}", dylib.display())?; } + if let Some(wasm) = wasm { + writeln!(fmt, " wasm: {}", wasm.display())?; + } if let Some(rlib) = rlib { writeln!(fmt, " rlib: {}", rlib.display())?; } @@ -649,8 +652,15 @@ impl CStore { }), None => (&source, &crate_root), }; - let dlsym_dylib = dlsym_source.dylib.as_ref().expect("no dylib for a proc-macro crate"); - Some(self.dlsym_proc_macros(dlsym_dylib, dlsym_root.stable_crate_id())?) + if tcx.sess.opts.unstable_opts.wasm_proc_macros && dlsym_source.wasm.is_some() { + let wasm_source = + dlsym_source.wasm.as_ref().expect("no wasm for a proc-macro crate"); + Some(self.load_wasm_macro(tcx.sess, wasm_source)?) + } else { + let dlsym_dylib = + dlsym_source.dylib.as_ref().expect("no dylib for a proc-macro crate"); + Some(self.dlsym_proc_macros(dlsym_dylib, dlsym_root.stable_crate_id())?) + } } else { None }; @@ -730,13 +740,21 @@ impl CStore { // Load the proc macro crate for the host proc_macro_locator.for_proc_macro(sess, path_kind); - let Some(host_result) = + if let Some(host_result) = self.load(&mut proc_macro_locator, &mut CrateRejections::default())? - else { - return Ok(None); - }; + { + return Ok(Some((host_result, None))); + } + + proc_macro_locator.for_wasm_proc_macro(sess, path_kind); + + if let Some(wasm_result) = + self.load(&mut proc_macro_locator, &mut CrateRejections::default())? + { + return Ok(Some((wasm_result, None))); + } - Ok(Some((host_result, None))) + Ok(None) } } @@ -817,7 +835,7 @@ impl CStore { match self.load(&mut locator, &mut crate_rejections)? { Some(res) => (res, None), None => { - info!("falling back to loading proc_macro"); + info!("falling back to loading proc_macro for {}", name); dep_kind = CrateDepKind::MacrosOnly; match self.load_proc_macro( tcx.sess, @@ -947,10 +965,32 @@ impl CStore { &self, path: &Path, stable_crate_id: StableCrateId, - ) -> Result<&'static [ProcMacroClient], CrateError> { + ) -> Result, CrateError> { Ok(crate::host_dylib::dlsym_proc_macros(path, stable_crate_id)?) } + fn load_wasm_macro( + &self, + sess: &Session, + path: &Path, + ) -> Result, CrateError> { + match rustc_expand::wasm_proc_macro::load_wasm_macro(sess, path) { + Ok(p) => return Ok(p), + Err(WasmLoadError::FileRead(err)) => { + return Err(CrateError::DlOpen(path.to_string_lossy().into(), err.to_string())); + } + Err(WasmLoadError::Parse(err)) => { + return Err(CrateError::DlOpen( + path.to_string_lossy().into(), + format!("wasm parse failed: {}", err), + )); + } + Err(WasmLoadError::RuntimeFailed(err)) => { + bug!("failed to load wasm macro, wasm runtime error: {:?}", err) + } + } + } + fn inject_panic_runtime(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) { // If we're only compiling an rlib, then there's no need to select a // panic runtime, so we just skip this section entirely. diff --git a/compiler/rustc_metadata/src/host_dylib.rs b/compiler/rustc_metadata/src/host_dylib.rs index 9bd2a57fcd285..d4bbd5be7f0d4 100644 --- a/compiler/rustc_metadata/src/host_dylib.rs +++ b/compiler/rustc_metadata/src/host_dylib.rs @@ -2,6 +2,7 @@ use std::error::Error; use std::path::Path; use std::time::Duration; +use rustc_expand::wasm_proc_macro::RustcProcMacro; use rustc_fs_util::try_canonicalize; use rustc_proc_macro::bridge::client::Client as ProcMacroClient; use rustc_session::StableCrateId; @@ -126,17 +127,17 @@ pub unsafe fn load_symbol_from_dylib( pub(crate) fn dlsym_proc_macros( path: &Path, stable_crate_id: StableCrateId, -) -> Result<&'static [ProcMacroClient], DylibError> { +) -> Result, DylibError> { let sym_name = rustc_session::generate_proc_macro_decls_symbol(stable_crate_id); debug!("trying to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name); unsafe { // FIXME(bjorn3) this depends on the unstable slice memory layout - let result = crate::load_symbol_from_dylib::<*const &[ProcMacroClient]>(path, &sym_name); + let result = load_symbol_from_dylib::<*const &[ProcMacroClient]>(path, &sym_name); match result { Ok(result) => { debug!("loaded dlsym proc_macros {} for symbol `{}`", path.display(), sym_name); - Ok(*result) + Ok((*result).iter().map(|pm| RustcProcMacro::Dylib { client: *pm }).collect()) } Err(err) => { debug!("failed to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 83a0977296b30..fca051da04b4f 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -223,8 +223,8 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned}; use rustc_data_structures::svh::Svh; use rustc_errors::{DiagArgValue, IntoDiagArg}; +use rustc_expand::wasm_proc_macro::RustcProcMacro; use rustc_fs_util::try_canonicalize; -use rustc_proc_macro::bridge::client::Client as ProcMacroClient; use rustc_session::cstore::CrateSource; use rustc_session::filesearch::FileSearch; use rustc_session::search_paths::PathKind; @@ -276,6 +276,7 @@ pub(crate) enum CrateFlavor { Rmeta, Dylib, SDylib, + Wasm, } impl fmt::Display for CrateFlavor { @@ -284,6 +285,7 @@ impl fmt::Display for CrateFlavor { CrateFlavor::Rlib => "rlib", CrateFlavor::Rmeta => "rmeta", CrateFlavor::Dylib => "dylib", + CrateFlavor::Wasm => "wasm", CrateFlavor::SDylib => "sdylib", }) } @@ -295,6 +297,7 @@ impl IntoDiagArg for CrateFlavor { CrateFlavor::Rlib => DiagArgValue::Str(Cow::Borrowed("rlib")), CrateFlavor::Rmeta => DiagArgValue::Str(Cow::Borrowed("rmeta")), CrateFlavor::Dylib => DiagArgValue::Str(Cow::Borrowed("dylib")), + CrateFlavor::Wasm => DiagArgValue::Str(Cow::Borrowed("wasm")), CrateFlavor::SDylib => DiagArgValue::Str(Cow::Borrowed("sdylib")), } } @@ -360,6 +363,14 @@ impl<'a> CrateLocator<'a> { self.path_kind = path_kind; } + pub(crate) fn for_wasm_proc_macro(&mut self, sess: &'a Session, path_kind: PathKind) { + self.is_proc_macro = true; + self.target = &sess.host; + self.tuple = TargetTuple::from_tuple("wasm32-wasip2"); + self.filesearch = sess.wasm_filesearch(); + self.path_kind = path_kind; + } + pub(crate) fn maybe_load_library_crate( &self, crate_rejections: &mut CrateRejections, @@ -390,16 +401,20 @@ impl<'a> CrateLocator<'a> { let staticlib_prefix = &format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix); let interface_prefix = rmeta_prefix; + // Right now CrateFlavor::Wasm is only supported for proc-macros, where we inject `lib` + // artificially for the output artifact despite the target not having it normally. + let wasm_prefix = &format!("lib{}", self.crate_name); let rmeta_suffix = ".rmeta"; let rlib_suffix = ".rlib"; let dylib_suffix = &self.target.dll_suffix; let staticlib_suffix = &self.target.staticlib_suffix; let interface_suffix = ".rs"; + let wasm_suffix = ".wasm"; let mut candidates: FxIndexMap< _, - (FxIndexSet<_>, FxIndexSet<_>, FxIndexSet<_>, FxIndexSet<_>), + (FxIndexSet<_>, FxIndexSet<_>, FxIndexSet<_>, FxIndexSet<_>, FxIndexSet<_>), > = Default::default(); // First, find all possible candidate rlibs and dylibs purely based on @@ -422,26 +437,32 @@ impl<'a> CrateLocator<'a> { // given that `extra_filename` comes from the `-C extra-filename` // option and thus can be anything, and the incorrect match will be // handled safely in `extract_one`. + let search_params = [ + (rlib_prefix.as_str(), rlib_suffix, CrateFlavor::Rlib), + (rmeta_prefix.as_str(), rmeta_suffix, CrateFlavor::Rmeta), + (wasm_prefix, wasm_suffix, CrateFlavor::Wasm), + (dylib_prefix, dylib_suffix, CrateFlavor::Dylib), + (interface_prefix, interface_suffix, CrateFlavor::SDylib), + ]; + debug!("searching with params={:?}", search_params); for search_path in self.filesearch.search_paths(self.path_kind) { - debug!("searching {}", search_path.dir.display()); + debug!("searching {} {:?}", search_path.dir.display(), search_path); let spf = &search_path.files; let mut should_check_staticlibs = true; - for (prefix, suffix, kind) in [ - (rlib_prefix.as_str(), rlib_suffix, CrateFlavor::Rlib), - (rmeta_prefix.as_str(), rmeta_suffix, CrateFlavor::Rmeta), - (dylib_prefix, dylib_suffix, CrateFlavor::Dylib), - (interface_prefix, interface_suffix, CrateFlavor::SDylib), - ] { + for (prefix, suffix, kind) in search_params.iter().copied() { if prefix == staticlib_prefix && suffix == staticlib_suffix { should_check_staticlibs = false; } if let Some(matches) = spf.query(prefix, suffix) { for (hash, spf) in matches { let spf_path = spf.path(&search_path.dir); - info!("lib candidate: {}", spf_path.display()); + info!( + "lib candidate: {} (kind={kind:?} hash={hash:?})", + spf_path.display() + ); - let (rlibs, rmetas, dylibs, interfaces) = + let (rlibs, rmetas, dylibs, interfaces, wasms) = candidates.entry(hash).or_default(); { // As a performance optimisation we canonicalize the path and skip @@ -462,6 +483,7 @@ impl<'a> CrateLocator<'a> { CrateFlavor::Rmeta => rmetas.insert(spf_path), CrateFlavor::Dylib => dylibs.insert(spf_path), CrateFlavor::SDylib => interfaces.insert(spf_path), + CrateFlavor::Wasm => wasms.insert(spf_path), }; } } @@ -488,9 +510,9 @@ impl<'a> CrateLocator<'a> { // libraries corresponds to the crate id and hash criteria that this // search is being performed for. let mut libraries = FxIndexMap::default(); - for (_hash, (rlibs, rmetas, dylibs, interfaces)) in candidates { + for (_hash, (rlibs, rmetas, dylibs, interfaces, wasms)) in candidates { if let Some((svh, lib)) = - self.extract_lib(crate_rejections, rlibs, rmetas, dylibs, interfaces)? + self.extract_lib(crate_rejections, rlibs, rmetas, dylibs, interfaces, wasms)? { libraries.insert(svh, lib); } @@ -526,6 +548,7 @@ impl<'a> CrateLocator<'a> { rmetas: FxIndexSet, dylibs: FxIndexSet, interfaces: FxIndexSet, + wasm_proc_macros: FxIndexSet, ) -> Result, CrateError> { let mut slot = None; // Order here matters, rmeta should come first. @@ -538,12 +561,14 @@ impl<'a> CrateLocator<'a> { let sdylib_interface = self.extract_one(crate_rejections, interfaces, CrateFlavor::SDylib, &mut slot)?; let dylib = self.extract_one(crate_rejections, dylibs, CrateFlavor::Dylib, &mut slot)?; + let wasm = + self.extract_one(crate_rejections, wasm_proc_macros, CrateFlavor::Wasm, &mut slot)?; if sdylib_interface.is_some() && dylib.is_none() { return Err(CrateError::FullMetadataNotFound(self.crate_name, CrateFlavor::SDylib)); } - let source = CrateSource { rmeta, rlib, dylib, sdylib_interface }; + let source = CrateSource { rmeta, rlib, dylib, wasm, sdylib_interface }; Ok(slot.map(|(svh, metadata, _, _)| (svh, Library { source, metadata }))) } @@ -552,6 +577,10 @@ impl<'a> CrateLocator<'a> { return true; } + if flavor == CrateFlavor::Wasm && self.is_proc_macro { + return true; + } + if self.only_needs_metadata { flavor == CrateFlavor::Rmeta } else { @@ -749,6 +778,7 @@ impl<'a> CrateLocator<'a> { let mut rmetas = FxIndexSet::default(); let mut dylibs = FxIndexSet::default(); let mut sdylib_interfaces = FxIndexSet::default(); + let mut wasm_proc_macros = FxIndexSet::default(); for loc in &self.exact_paths { let loc_canon = loc.canonicalized(); let loc_orig = loc.original(); @@ -786,14 +816,25 @@ impl<'a> CrateLocator<'a> { dylibs.insert(loc_canon.clone()); continue; } + if file.ends_with(".wasm") { + wasm_proc_macros.insert(loc_canon.clone()); + continue; + } crate_rejections .via_filename .push(CrateMismatch { path: loc_orig.clone(), got: String::new() }); } // Extract the dylib/rlib/rmeta triple. - self.extract_lib(crate_rejections, rlibs, rmetas, dylibs, sdylib_interfaces) - .map(|opt| opt.map(|(_, lib)| lib)) + self.extract_lib( + crate_rejections, + rlibs, + rmetas, + dylibs, + sdylib_interfaces, + wasm_proc_macros, + ) + .map(|opt| opt.map(|(_, lib)| lib)) } pub(crate) fn into_error( @@ -906,6 +947,49 @@ fn get_metadata_section<'p>( buf.slice(|buf| &buf[data_start..(data_start + metadata_len)]) } CrateFlavor::Rmeta => get_rmeta_metadata_section(filename)?, + CrateFlavor::Wasm => { + let buf = std::fs::read(filename).expect("could read"); + let buf = slice_owned(buf, Deref::deref); + let mut section = None; + for payload in wasmparser::Parser::new(0).parse_all(&buf.clone()) { + let payload = payload.expect("could parse"); + let wasmparser::Payload::CustomSection(reader) = payload else { + continue; + }; + if reader.name() != ".rustc" { + // compiler/rustc_codegen_ssa/src/back/metadata.rs + continue; + } + let contents = buf.clone().slice(|s| &s[reader.data_offset()..reader.range().end]); + + let header_len = METADATA_HEADER.len(); + // header + u64 length of data + let data_start = header_len + 8; + + debug!("checking {} bytes of metadata-version stamp", header_len); + let header = &contents[..cmp::min(header_len, contents.len())]; + if header != METADATA_HEADER { + return Err(MetadataError::LoadFailure(format!( + "invalid metadata version found: {}", + filename.display() + ))); + } + + // Length of the metadata - this allows linkers to pad the section if they want + let Ok(len_bytes) = <[u8; 8]>::try_from( + &contents[header_len..cmp::min(data_start, contents.len())], + ) else { + return Err(MetadataError::LoadFailure( + "invalid metadata length found".to_string(), + )); + }; + let metadata_len = u64::from_le_bytes(len_bytes) as usize; + + section = Some(contents.slice(|b| &b[data_start..(data_start + metadata_len)])); + break; + } + section.expect("found rustc metadata in wasm component") + } }; let Ok(blob) = MetadataBlob::new(raw_bytes) else { return Err(MetadataError::LoadFailure(format!( @@ -986,7 +1070,7 @@ pub fn get_proc_macros( path: &Path, metadata_loader: &dyn MetadataLoader, cfg_version: &'static str, -) -> IoResult> { +) -> IoResult> { let metadata = get_metadata_section(target, CrateFlavor::Dylib, path, metadata_loader, cfg_version, None) .map_err(|err| io::Error::other(err.to_string()))?; @@ -1000,7 +1084,7 @@ pub fn get_proc_macros( let proc_macro_info = metadata.get_proc_macro_info(); assert_eq!(proc_macro_info.len(), clients.len()); - Ok(clients.into_iter().copied().zip(proc_macro_info).collect()) + Ok(clients.into_iter().zip(proc_macro_info).collect()) } // ------------------------------------------ Error reporting ------------------------------------- diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bed2be51a3468..50b4eee361bdc 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -14,7 +14,11 @@ use rustc_data_structures::owned_slice::OwnedSlice; use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; -use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; +use rustc_expand::proc_macro::{ + AttrProcMacro, BangProcMacro, DeriveProcMacro, WasmAttrProcMacro, WasmBangProcMacro, + WasmDeriveProcMacro, +}; +use rustc_expand::wasm_proc_macro::RustcProcMacro; use rustc_hir::Safety; use rustc_hir::def::Res; use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -26,7 +30,6 @@ use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::ty::Visibility; use rustc_middle::ty::codec::TyDecoder; use rustc_middle::{bug, implement_ty_decoder}; -use rustc_proc_macro::bridge::client::Client as ProcMacroClient; use rustc_serialize::opaque::MemDecoder; use rustc_serialize::{Decodable, Decoder}; use rustc_session::config::TargetModifier; @@ -105,7 +108,7 @@ pub(crate) struct CrateMetadata { /// or `#[rustc_allow_incoherent_impl]`. incoherent_impls: FxIndexMap>, /// Proc macro function pointers for this crate, if it's a proc macro crate. - raw_proc_macros: Option<&'static [ProcMacroClient]>, + raw_proc_macros: Option>, /// Source maps for code from the crate. source_map_import_info: Lock>>, /// For every definition in this crate, maps its `DefPathHash` to its `DefIndex`. @@ -986,7 +989,7 @@ impl CrateMetadata { bug!("missing `{descr}` for {:?}", self.local_def_id(id)) } - fn raw_proc_macro(&self, tcx: TyCtxt<'_>, id: DefIndex) -> (ProcMacroClient, ProcMacroKind) { + fn raw_proc_macro(&self, tcx: TyCtxt<'_>, id: DefIndex) -> (RustcProcMacro, ProcMacroKind) { // DefIndex's in root.proc_macro_data have a one-to-one correspondence // with items in 'raw_proc_macros'. let (pos, (_id, kind)) = self @@ -999,7 +1002,7 @@ impl CrateMetadata { .enumerate() .find(|(_pos, (i, _))| *i == id) .unwrap(); - (self.raw_proc_macros.unwrap()[pos], kind.decode((self, tcx))) + (self.raw_proc_macros.as_ref().unwrap()[pos].clone(), kind.decode((self, tcx))) } fn opt_item_name(&self, item_index: DefIndex) -> Option { @@ -1058,21 +1061,56 @@ impl CrateMetadata { fn load_proc_macro<'tcx>(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> SyntaxExtension { let (name, kind, helper_attrs) = match self.raw_proc_macro(tcx, id) { - (client, ProcMacroKind::CustomDerive { trait_name, attributes }) => { + ( + RustcProcMacro::WasmExpand1 { client }, + ProcMacroKind::CustomDerive { trait_name, attributes }, + ) => { let helper_attrs = - attributes.into_iter().map(|attr| Symbol::intern(&attr)).collect(); + attributes.iter().cloned().map(|s| Symbol::intern(&s)).collect::>(); ( - trait_name, - SyntaxExtensionKind::Derive(Arc::new(DeriveProcMacro { client })), + Symbol::intern(&trait_name), + SyntaxExtensionKind::Derive(Arc::new(WasmDeriveProcMacro { + client: client.clone(), + })), helper_attrs, ) } - (client, ProcMacroKind::Attr { name }) => { - (name, SyntaxExtensionKind::Attr(Arc::new(AttrProcMacro { client })), Vec::new()) - } - (client, ProcMacroKind::Bang { name }) => { - (name, SyntaxExtensionKind::Bang(Arc::new(BangProcMacro { client })), Vec::new()) + (RustcProcMacro::WasmExpand2 { client }, ProcMacroKind::Attr { name }) => ( + Symbol::intern(&name), + SyntaxExtensionKind::Attr(Arc::new(WasmAttrProcMacro { client: client.clone() })), + Vec::new(), + ), + (RustcProcMacro::WasmExpand1 { client }, ProcMacroKind::Bang { name }) => ( + Symbol::intern(&name), + SyntaxExtensionKind::Bang(Arc::new(WasmBangProcMacro { client: client.clone() })), + Vec::new(), + ), + + ( + RustcProcMacro::Dylib { client }, + ProcMacroKind::CustomDerive { trait_name, attributes }, + ) => { + let helper_attrs = + attributes.iter().cloned().map(|s| Symbol::intern(&s)).collect::>(); + ( + Symbol::intern(&trait_name), + SyntaxExtensionKind::Derive(Arc::new(DeriveProcMacro { + client: client.clone(), + })), + helper_attrs, + ) } + (RustcProcMacro::Dylib { client }, ProcMacroKind::Attr { name }) => ( + Symbol::intern(&name), + SyntaxExtensionKind::Attr(Arc::new(AttrProcMacro { client: client.clone() })), + Vec::new(), + ), + (RustcProcMacro::Dylib { client }, ProcMacroKind::Bang { name }) => ( + Symbol::intern(&name), + SyntaxExtensionKind::Bang(Arc::new(BangProcMacro { client: client.clone() })), + Vec::new(), + ), + _ => unreachable!(), }; let sess = tcx.sess; @@ -1083,7 +1121,7 @@ impl CrateMetadata { self.get_span(tcx, id), helper_attrs, self.root.edition, - Symbol::intern(&name), + name, &attrs, false, ) @@ -1889,7 +1927,7 @@ impl CrateMetadata { tcx: TyCtxt<'_>, blob: MetadataBlob, root: CrateRoot, - raw_proc_macros: Option<&'static [ProcMacroClient]>, + raw_proc_macros: Option>, cnum: CrateNum, cnum_map: CrateNumMap, dep_kind: CrateDepKind, diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index c2b517824a058..641d42b2e2649 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -22,6 +22,7 @@ use rustc_span::{Span, Symbol}; #[derive(PartialEq, Clone, Debug, StableHash, Encodable, Decodable)] pub struct CrateSource { pub dylib: Option, + pub wasm: Option, pub rlib: Option, pub rmeta: Option, pub sdylib_interface: Option, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index d508c963791d4..9cfd5c225c803 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2870,6 +2870,8 @@ written to standard error output)"), // FIXME remove this after a couple releases wasm_c_abi: () = ((), parse_wasm_c_abi, [TRACKED], "use spec-compliant C ABI for `wasm32-unknown-unknown` (deprecated, always enabled)"), + wasm_proc_macros: bool = (false, parse_bool, [TRACKED], + "enable support for compiling and loading wasm proc macros"), write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], "whether long type names should be written to files instead of being printed in errors"), // tidy-alphabetical-end diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 120eb04c6e8c4..75b34c0ccc049 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -96,10 +96,24 @@ pub fn filename_for_input( CrateType::Rlib => { OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib"))) } - CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib | CrateType::Sdylib => { + CrateType::Cdylib | CrateType::Dylib | CrateType::Sdylib => { let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix); OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } + CrateType::ProcMacro => { + if sess.target.is_like_wasm { + // Override the output file to start with `lib`. + // FIXME: This is a bit of a hack to make our crate loading code avoid needing a + // 'wasm_proc_macro' search path. We probably *do* actually want wasm proc macros to + // have their own search path inside the session, in which case this wouldn't be + // needed. + let (prefix, suffix) = ("lib", &sess.target.dll_suffix); + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) + } else { + let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix); + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) + } + } CrateType::StaticLib => { let (prefix, suffix) = sess.staticlib_components(false); OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) @@ -130,7 +144,7 @@ pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool return true; } } - if let CrateType::ProcMacro | CrateType::Dylib = crate_type + if let CrateType::Dylib = crate_type && sess.target.only_cdylib { return true; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b7c0923163b4d..191e31e13f53b 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -160,6 +160,7 @@ pub struct Session { target_filesearch: FileSearch, host_filesearch: FileSearch, + wasm_filesearch: FileSearch, /// The names of intrinsics that the current codegen backend replaces /// with its own implementations. @@ -426,6 +427,9 @@ impl Session { pub fn host_filesearch(&self) -> &filesearch::FileSearch { &self.host_filesearch } + pub fn wasm_filesearch(&self) -> &filesearch::FileSearch { + &self.wasm_filesearch + } /// Returns a list of directories where target-specific tool binaries are located. Some fallback /// directories are also returned, for example if `--sysroot` is used but tools are missing @@ -1072,6 +1076,18 @@ pub fn build_session( None }; + let wasm_triple = TargetTuple::from_tuple("wasm32-wasip2"); + let wasm_tlib_path = + Arc::new(SearchPath::from_sysroot_and_triple(sopts.sysroot.path(), wasm_triple.tuple())); + let (wasm_target, target_warnings) = + Target::search(&wasm_triple, sopts.sysroot.path(), sopts.unstable_opts.unstable_options) + .unwrap_or_else(|e| { + dcx.handle().fatal(format!("Error loading host specification: {e}")) + }); + for warning in target_warnings.warning_messages() { + dcx.handle().warn(warning) + } + let psess = ParseSess::with_dcx(dcx, source_map); let host_triple = config::host_tuple(); @@ -1102,6 +1118,8 @@ pub fn build_session( let target_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target); let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host); + let wasm_filesearch = + filesearch::FileSearch::new(&sopts.search_paths, &wasm_tlib_path, &wasm_target); let timings = TimingSectionHandler::new(sopts.json_timings); @@ -1133,6 +1151,7 @@ pub fn build_session( file_depinfo: Default::default(), target_filesearch, host_filesearch, + wasm_filesearch, replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` fallback_intrinsics: FxHashSet::default(), // filled by `run_compiler` thin_lto_supported: true, // filled by `run_compiler` diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index 64280d5ce4b5e..1b05412e92645 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -79,6 +79,7 @@ use crate::{BytePos, SPAN_TRACK, SpanData}; /// This is performed using the callback `SPAN_TRACK` to access the query engine. #[derive(Clone, Copy, Eq, PartialEq, Hash)] #[rustc_pass_by_value] +#[repr(C)] // we transmute to u64 and under -Zrandomize-layout this is otherwise 96 bits. pub struct Span { lo_or_index: u32, len_with_tag_or_marker: u16, @@ -454,6 +455,16 @@ impl Span { // Field order must match `to_raw_span`. Span { lo_or_index: a, len_with_tag_or_marker: b, ctxt_or_parent_or_marker: c } } + + #[inline] + pub fn encode_raw(self) -> u64 { + unsafe { std::mem::transmute(self) } + } + + #[inline] + pub fn decode_raw(sp: u64) -> Span { + unsafe { std::mem::transmute(sp) } + } } #[derive(Default)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1d3f0adf86829..abf4420e9f52a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1040,6 +1040,7 @@ symbols! { ge, gen_blocks, gen_future, + generate_export, generator_clone, generators, generic_arg_infer, @@ -1134,6 +1135,7 @@ symbols! { internal, internal_eq_trait_method_impls, internal_features, + internal_wit_bindgen, interrupt, into_async_iter_into_iter, into_future, diff --git a/library/Cargo.lock b/library/Cargo.lock index 325cba5d60fdb..d39a1f49f2874 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -217,9 +217,11 @@ dependencies = [ name = "proc_macro" version = "0.0.0" dependencies = [ + "alloc", "core", "rustc-literal-escaper", "std", + "wit-bindgen", ] [[package]] diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index 286080e1bea2e..3fce666c9cae8 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -5,12 +5,18 @@ edition = "2024" [dependencies] std = { path = "../std" } +alloc = { path = "../alloc" } # Workaround: when documenting this crate rustdoc will try to load crate named # `core` when resolving doc links. Without this line a different `core` will be # loaded from sysroot causing duplicate lang items and other similar errors. core = { path = "../core" } rustc-literal-escaper = { version = "0.0.8", features = ["rustc-dep-of-std"] } +[target.'cfg(target_family = "wasm")'.dependencies] +wit-bindgen = { version = '0.57.1', features = [ + 'rustc-dep-of-std', +], default-features = false } + [features] default = ["rustc-dep-of-std"] rustc-dep-of-std = [] diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 48e6245ecf98b..12a8616e27fe7 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -135,6 +135,8 @@ macro_rules! define_client_side { } } } + +#[cfg(not(target_family = "wasm"))] with_api!(define_client_side, TokenStream, Span, Symbol); struct Bridge<'a> { diff --git a/library/proc_macro/src/bridge/client_wasi.rs b/library/proc_macro/src/bridge/client_wasi.rs new file mode 100644 index 0000000000000..12f7f2e3ac82e --- /dev/null +++ b/library/proc_macro/src/bridge/client_wasi.rs @@ -0,0 +1,481 @@ +//! This module implements the client (in-wasm) for WebAssembly-based proc macros. +//! +//! At a high level, unlike for native proc macros, we go through a component model interface that +//! mirrors the API defined in bridge/mod.rs (see library/proc_macro/wasm-interface.wit). Where +//! possible, we directly use types defined by WIT in the bridge interface, but where needed we have +//! wrappers around it. The raw interface is mandated to be closely mirrored because we use +//! `with_api!` to generate it (see bottom of this file). From there input/output is converted with +//! the `Encode` (wasm -> host) and `Decode` (host -> wasm) traits. These are similar to those used +//! for the buffer encoding in native code, but typically just map types with very little real +//! logic (unlike the buffer encoding), low-level serde is handled by wit-bindgen. + +use std::fmt; +use std::ops::Bound; + +pub(crate) use host::TokenStream; + +pub(crate) use crate::bridge::Methods; +pub(crate) use crate::bridge::symbol::Symbol; +use crate::wasi_bindgen::exports::rust_lang::rust::custom_derive as cd; +use crate::wasi_bindgen::rust_lang::rust::host; + +pub(crate) fn is_available() -> bool { + // On wasm we currently don't support executing libproc_macro without the bridge (since it's + // statically imported) -- unclear whether that can change in the future, but for now just + // always return true. + true +} + +#[derive(Copy, Clone)] +pub(crate) struct Span(pub(crate) host::Span); + +impl Span { + pub(crate) fn def_site() -> Span { + Span(host::span_def_site()) + } + + pub(crate) fn call_site() -> Span { + Span(host::span_call_site()) + } + + pub(crate) fn mixed_site() -> Span { + Span(host::span_mixed_site()) + } + + pub(crate) fn byte_range(self) -> std::ops::Range { + Methods::span_byte_range(self) + } + + pub(crate) fn parent(self) -> Option { + Methods::span_parent(self) + } + + pub(crate) fn source(self) -> Span { + Methods::span_source(self) + } +} + +impl PartialEq for Span { + fn eq(&self, other: &Span) -> bool { + host::span_is_same(self.0, other.0) + } +} + +impl fmt::Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&host::span_debug(self.0)) + } +} + +impl Clone for TokenStream { + fn clone(&self) -> Self { + host::ts_clone(self) + } +} + +impl !Send for TokenStream {} +impl !Sync for TokenStream {} + +#[derive(Copy, Clone)] +pub enum Client { + Expand1 { expand: fn(crate::TokenStream) -> crate::TokenStream }, + Expand2 { expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream }, +} + +impl Client { + pub const fn expand1(expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self { + Self::Expand1 { expand } + } + + pub const fn expand2( + expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + ) -> Self { + Self::Expand2 { expand } + } +} + +impl cd::GuestCustomDerive for Client { + fn get_kind(&self) -> cd::DeriveKind { + match self { + Self::Expand1 { .. } => cd::DeriveKind::Expand1, + Self::Expand2 { .. } => cd::DeriveKind::Expand2, + } + } + + fn expand1(&self, tokens: TokenStream) -> TokenStream { + setup_hook(); + match self { + Self::Expand1 { expand } => { + let output = (*expand)(crate::TokenStream(Some(tokens))); + let output = output.0.unwrap_or_else(|| TokenStream::new()); + output + } + Self::Expand2 { .. } => unreachable!("should not be called by host"), + } + } + + fn expand2(&self, a: TokenStream, b: TokenStream) -> TokenStream { + setup_hook(); + + match self { + Self::Expand2 { expand } => { + let output = (*expand)(crate::TokenStream(Some(a)), crate::TokenStream(Some(b))); + let output = output.0.unwrap_or_else(|| TokenStream::new()); + output + } + Self::Expand1 { .. } => { + unreachable!("should not be called by host") + } + } + } +} + +fn setup_hook() { + static ONCE: std::sync::Once = std::sync::Once::new(); + ONCE.call_once(|| { + let prev = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + // If the host wants us to also emit it, run the default panic hook. + if host::report_panic(info.payload_as_str()) { + prev(info) + } + })); + }); +} + +#[allow_internal_unstable(proc_macro_internals, staged_api)] +pub macro generate_export($list:ident) { + use $crate::wasi_bindgen::exports::rust_lang::rust::custom_derive as cd; + + struct ExportAbi; + + impl cd::Guest for ExportAbi { + type CustomDerive = $crate::bridge::client::Client; + fn get_custom_derives() -> Vec { + $list.iter().map(|d| cd::CustomDerive::new(*d)).collect() + } + } + + $crate::wasi_bindgen::export!(ExportAbi with_types_in $crate::wasi_bindgen); +} + +trait Encode { + fn convert(input: Self) -> Output; +} + +impl Decode for super::LitKind { + fn convert(v: super::LitKind) -> host::LiteralKind { + match v { + super::LitKind::Byte => host::LiteralKind::Byte, + super::LitKind::Char => host::LiteralKind::Char, + super::LitKind::Integer => host::LiteralKind::Integer, + super::LitKind::Float => host::LiteralKind::Float, + super::LitKind::ByteStr => host::LiteralKind::ByteStr, + super::LitKind::ByteStrRaw(n) => host::LiteralKind::ByteStrRaw(n), + super::LitKind::Str => host::LiteralKind::Str, + super::LitKind::StrRaw(n) => host::LiteralKind::StrRaw(n), + super::LitKind::CStr => host::LiteralKind::CStr, + super::LitKind::CStrRaw(n) => host::LiteralKind::CStrRaw(n), + super::LitKind::ErrWithGuar => host::LiteralKind::ErrWithGuar, + } + } +} + +impl Encode for host::LiteralKind { + fn convert(v: Self) -> super::LitKind { + match v { + host::LiteralKind::Byte => super::LitKind::Byte, + host::LiteralKind::Char => super::LitKind::Char, + host::LiteralKind::Integer => super::LitKind::Integer, + host::LiteralKind::Float => super::LitKind::Float, + host::LiteralKind::ByteStr => super::LitKind::ByteStr, + host::LiteralKind::ByteStrRaw(n) => super::LitKind::ByteStrRaw(n), + host::LiteralKind::Str => super::LitKind::Str, + host::LiteralKind::StrRaw(n) => super::LitKind::StrRaw(n), + host::LiteralKind::CStr => super::LitKind::CStr, + host::LiteralKind::CStrRaw(n) => super::LitKind::CStrRaw(n), + host::LiteralKind::ErrWithGuar => super::LitKind::ErrWithGuar, + } + } +} + +impl Encode> for Result { + fn convert(input: Self) -> Result { + match input { + Ok(v) => Ok(Symbol::new(&v)), + Err(()) => Err(()), + } + } +} + +impl Encode for String { + fn convert(input: Self) -> String { + input + } +} + +impl Encode for host::Span { + fn convert(input: Self) -> Span { + Span(input) + } +} + +impl Encode> for Option { + fn convert(input: Self) -> Option { + input.map(Span) + } +} + +impl Encode for u64 { + fn convert(input: Self) -> usize { + input.try_into().unwrap() + } +} + +trait Decode { + fn convert(input: Self) -> Out; +} + +impl<'a> Decode<&'a str> for &'a str { + fn convert(input: &str) -> &str { + input + } +} + +impl Decode for usize { + fn convert(input: Self) -> u64 { + input.try_into().unwrap() + } +} + +impl Decode for Span { + fn convert(input: Self) -> host::Span { + input.0 + } +} + +impl Decode for crate::bridge::Diagnostic { + fn convert(input: Self) -> host::Diagnostic { + fn to_internal(diag: crate::bridge::Diagnostic) -> host::Diagnostic { + host::Diagnostic::new(host::DiagnosticInner { + level: match diag.level { + crate::Level::Error => host::Level::Error, + crate::Level::Warning => host::Level::Warning, + crate::Level::Note => host::Level::Note, + crate::Level::Help => host::Level::Help, + }, + message: diag.message, + spans: diag.spans.into_iter().map(|s| s.0).collect(), + children: diag.children.into_iter().map(to_internal).collect(), + }) + } + + to_internal(input) + } +} + +impl Encode>> for Vec { + fn convert(input: Self) -> Vec> { + input.into_iter().map(Encode::convert).collect() + } +} + +impl Encode> for host::TokenTree { + fn convert(input: Self) -> super::TokenTree { + match input { + host::TokenTree::Group(g) => super::TokenTree::Group(super::Group { + delimiter: match g.delimiter { + host::Delimiter::Parenthesis => super::Delimiter::Parenthesis, + host::Delimiter::Brace => super::Delimiter::Brace, + host::Delimiter::Bracket => super::Delimiter::Bracket, + host::Delimiter::None => super::Delimiter::None, + }, + stream: g.stream, + span: super::DelimSpan { + open: Span(g.span.open), + close: Span(g.span.close), + entire: Span(g.span.entire), + }, + }), + host::TokenTree::Punct(p) => super::TokenTree::Punct(super::Punct { + ch: p.ch, + joint: p.joint, + span: Span(p.span), + }), + host::TokenTree::Ident(i) => super::TokenTree::Ident(super::Ident { + sym: Symbol::new(&i.sym), + is_raw: i.is_raw, + span: Span(i.span), + }), + host::TokenTree::Literal(l) => super::TokenTree::Literal(super::Literal { + kind: Encode::convert(l.kind), + span: Span(l.span), + suffix: l.suffix.map(|s| Symbol::new(&s)), + symbol: Symbol::new(&l.symbol), + }), + } + } +} + +impl Decode> for Vec { + fn convert(input: Self) -> Vec { + input.into_iter().map(Decode::convert).collect() + } +} + +impl Decode for Bound { + fn convert(input: Self) -> host::RangeBound { + match input { + Bound::Included(v) => { + host::RangeBound { value: v as u64, bound: host::Bound::Included } + } + Bound::Excluded(v) => { + host::RangeBound { value: v as u64, bound: host::Bound::Excluded } + } + Bound::Unbounded => host::RangeBound { value: 0, bound: host::Bound::Unbounded }, + } + } +} + +impl Encode> for (u64, u64) { + fn convert(input: Self) -> std::ops::Range { + input.0.try_into().unwrap()..input.1.try_into().unwrap() + } +} + +impl Encode> for Option { + fn convert(input: Self) -> Option { + input + } +} + +impl<'a> Decode> for Option<&'a str> { + fn convert(input: Option<&str>) -> Option<&str> { + input + } +} + +impl Encode<()> for () { + fn convert(_: ()) -> () {} +} + +impl Encode for bool { + fn convert(v: bool) -> bool { + v + } +} + +impl Encode, String>> for Result { + fn convert(input: Self) -> Result, String> { + match input { + Ok(l) => Ok(super::Literal { + kind: Encode::convert(l.kind), + symbol: Symbol::new(&l.symbol), + suffix: l.suffix.map(|s| Symbol::new(&s)), + span: Span(l.span), + }), + Err(e) => Err(e), + } + } +} + +impl Decode for TokenStream { + fn convert(input: TokenStream) -> TokenStream { + input + } +} + +impl<'a> Decode<&'a TokenStream> for &'a TokenStream { + fn convert(input: &TokenStream) -> &TokenStream { + &input + } +} + +impl Encode for TokenStream { + fn convert(input: TokenStream) -> TokenStream { + input + } +} + +impl Encode> for Result { + fn convert(input: Result) -> Result { + input + } +} + +impl Encode> for Result { + fn convert(input: Result) -> Result { + input + } +} + +impl Decode for super::TokenTree { + fn convert(input: Self) -> host::TokenTree { + match input { + super::TokenTree::Group(g) => host::TokenTree::Group(host::Group { + delimiter: match g.delimiter { + super::Delimiter::Parenthesis => host::Delimiter::Parenthesis, + super::Delimiter::Brace => host::Delimiter::Brace, + super::Delimiter::Bracket => host::Delimiter::Bracket, + super::Delimiter::None => host::Delimiter::None, + }, + stream: g.stream, + span: host::DelimSpan { + open: g.span.open.0, + close: g.span.close.0, + entire: g.span.entire.0, + }, + }), + super::TokenTree::Punct(p) => { + host::TokenTree::Punct(host::Punct { ch: p.ch, joint: p.joint, span: p.span.0 }) + } + super::TokenTree::Ident(i) => host::TokenTree::Ident(host::Ident { + sym: i.sym.to_string(), + is_raw: i.is_raw, + span: i.span.0, + }), + super::TokenTree::Literal(l) => host::TokenTree::Literal(host::Literal { + kind: Decode::convert(l.kind), + span: l.span.0, + suffix: l.suffix.map(|s| s.to_string()), + symbol: l.symbol.to_string(), + }), + } + } +} + +impl Decode> for Option { + fn convert(input: Self) -> Option { + input + } +} + +impl Decode> for Vec> { + fn convert(input: Self) -> Vec { + input.into_iter().map(Decode::convert).collect() + } +} + +macro_rules! wasm { + ( + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* + ) => { + impl Methods { + $( + pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { + Encode::convert(host::$method($(Decode::convert($arg),)+)) + } + )* + } + } +} + +mod api_instantiated { + use std::ops::{Bound, Range}; + + use super::{Decode, Encode, Span, TokenStream, host}; + use crate::bridge::symbol::Symbol; + use crate::bridge::{Diagnostic, Literal, Methods, TokenTree}; + + with_api!(wasm, TokenStream, Span, Symbol); +} diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 8b60043f5dc2a..7cfabba98b4bc 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -7,6 +7,7 @@ //! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap). #![deny(unsafe_code)] +#![cfg_attr(target_family = "wasm", allow(unused))] use std::hash::Hash; use std::marker; @@ -87,37 +88,54 @@ pub(crate) struct Methods; #[allow(unsafe_code)] mod arena; #[allow(unsafe_code)] +#[cfg(not(target_family = "wasm"))] mod buffer; #[deny(unsafe_code)] +#[cfg_attr(target_family = "wasm", path = "client_wasi.rs")] +#[cfg_attr(not(target_family = "wasm"), path = "client.rs")] pub mod client; #[allow(unsafe_code)] +#[cfg(not(target_family = "wasm"))] mod closure; #[forbid(unsafe_code)] mod fxhash; #[forbid(unsafe_code)] +#[cfg(not(target_family = "wasm"))] mod handle; #[macro_use] #[forbid(unsafe_code)] +#[cfg(not(target_family = "wasm"))] mod rpc; #[forbid(unsafe_code)] mod panic_message; #[allow(unsafe_code)] +#[cfg(not(target_family = "wasm"))] mod selfless_reify; #[forbid(unsafe_code)] +#[cfg(not(target_family = "wasm"))] pub mod server; #[allow(unsafe_code)] mod symbol; +#[cfg(not(target_family = "wasm"))] use buffer::Buffer; +#[cfg(not(target_family = "wasm"))] pub use panic_message::PanicMessage; +#[cfg(not(target_family = "wasm"))] use rpc::{Decode, Encode}; +#[cfg(target_family = "wasm")] +macro_rules! rpc_encode_decode { + ($($t:tt)*) => {}; +} + /// Configuration for establishing an active connection between a server and a /// client. The server creates the bridge config (`run_server` in `server.rs`), /// then passes it to the client through the function pointer in the `run` field /// of `client::Client`. The client constructs a local `Bridge` from the config /// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`). #[repr(C)] +#[cfg(not(target_family = "wasm"))] pub struct BridgeConfig<'a> { /// Buffer used to pass initial input to the client. input: Buffer, @@ -129,7 +147,9 @@ pub struct BridgeConfig<'a> { force_show_panics: bool, } +#[cfg(not(target_family = "wasm"))] impl !Send for BridgeConfig<'_> {} +#[cfg(not(target_family = "wasm"))] impl !Sync for BridgeConfig<'_> {} macro_rules! declare_tags { @@ -140,6 +160,7 @@ macro_rules! declare_tags { pub(super) enum ApiTags { $($method),* } + #[cfg(not(target_family = "wasm"))] rpc_encode_decode!(enum ApiTags { $($method),* }); } } @@ -323,7 +344,9 @@ macro_rules! mark_compound { macro_rules! compound_traits { ($($t:tt)*) => { + #[cfg(not(target_family = "wasm"))] rpc_encode_decode!($($t)*); + #[cfg(not(target_family = "wasm"))] mark_compound!($($t)*); }; } diff --git a/library/proc_macro/src/bridge/panic_message.rs b/library/proc_macro/src/bridge/panic_message.rs index a91bdddea576c..f1ecbf4fa4c56 100644 --- a/library/proc_macro/src/bridge/panic_message.rs +++ b/library/proc_macro/src/bridge/panic_message.rs @@ -1,5 +1,6 @@ use std::any::Any; +#[cfg(not(target_family = "wasm"))] use crate::bridge::{Buffer, Decode, Encode}; /// Simplified version of panic payloads, ignoring @@ -53,6 +54,7 @@ impl PanicMessage { } } +#[cfg(not(target_family = "wasm"))] impl Encode for PanicMessage { #[inline] fn encode(self, w: &mut Buffer, s: &mut S) { @@ -60,6 +62,7 @@ impl Encode for PanicMessage { } } +#[cfg(not(target_family = "wasm"))] impl Decode<'_, '_, S> for PanicMessage { #[inline] fn decode(r: &mut &[u8], s: &mut S) -> Self { diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index ff525c6cd7735..537b8f6d6dba0 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -3,9 +3,9 @@ use std::cell::Cell; use std::hash::Hash; use std::ops::{Bound, Range}; +use std::panic; use std::sync::atomic::AtomicU32; use std::sync::mpsc; -use std::{panic, thread}; use crate::bridge::{ ApiTags, BridgeConfig, Buffer, Decode, Diagnostic, Encode, ExpnGlobals, Literal, Mark, Marked, @@ -113,7 +113,7 @@ macro_rules! define_dispatcher { // If client and server happen to use the same `std`, // `catch_unwind` asserts that the panic counter was 0, // even when the closure passed to it didn't panic. - let r = if thread::panicking() { + let r = if std::thread::panicking() { Ok(call_method()) } else { panic::catch_unwind(panic::AssertUnwindSafe(call_method)) @@ -194,7 +194,7 @@ impl ExecutionStrategy for MaybeCrossThread { if self.cross_thread || ALREADY_RUNNING_SAME_THREAD.get() { let (mut server, mut client) = MessagePipe::new(); - let join_handle = thread::spawn(move || { + let join_handle = std::thread::spawn(move || { let mut dispatch = |b: Buffer| -> Buffer { client.send(b); client.recv().expect("server died while client waiting for reply") diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index 1e632489f31e1..157beaac03ead 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -13,7 +13,9 @@ use std::cell::RefCell; use std::fmt; use std::num::NonZero; -use crate::bridge::{Buffer, Decode, Encode, Mark, arena, client, fxhash, server}; +#[cfg(not(target_family = "wasm"))] +use crate::bridge::{Buffer, Decode, Encode, Mark, server}; +use crate::bridge::{arena, client, fxhash}; /// Handle for a symbol string stored within the Interner. #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -59,6 +61,7 @@ impl Symbol { /// Clear out the thread-local symbol interner, making all previously /// created symbols invalid such that `with` will panic when called on them. + #[cfg(not(target_family = "wasm"))] pub(crate) fn invalidate_all() { INTERNER.with_borrow_mut(|i| i.clear()); } @@ -94,24 +97,28 @@ impl fmt::Display for Symbol { } } +#[cfg(not(target_family = "wasm"))] impl Encode for Symbol { fn encode(self, w: &mut Buffer, s: &mut S) { self.with(|sym| sym.encode(w, s)) } } +#[cfg(not(target_family = "wasm"))] impl Decode<'_, '_, server::HandleStore> for server::MarkedSymbol { fn decode(r: &mut &[u8], s: &mut server::HandleStore) -> Self { Mark::mark(S::intern_symbol(<&str>::decode(r, s))) } } +#[cfg(not(target_family = "wasm"))] impl Encode> for server::MarkedSymbol { fn encode(self, w: &mut Buffer, s: &mut server::HandleStore) { S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s)) } } +#[cfg(not(target_family = "wasm"))] impl Decode<'_, '_, S> for Symbol { fn decode(r: &mut &[u8], s: &mut S) -> Self { Symbol::new(<&str>::decode(r, s)) @@ -178,6 +185,7 @@ impl Interner { /// Clear all symbols from the store, invalidating them such that `get` will /// panic if they are accessed in the future. + #[cfg(not(target_family = "wasm"))] fn clear(&mut self) { // NOTE: Be careful not to panic here, as we may be called on the client // when a `catch_unwind` isn't installed. diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 2cba4b52fc276..30c988846ddfa 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -23,12 +23,12 @@ #![feature(allow_internal_unstable)] #![feature(decl_macro)] #![feature(negative_impls)] -#![feature(panic_can_unwind)] +#![cfg_attr(not(target_family = "wasm"), feature(panic_can_unwind))] #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(extend_one)] -#![feature(mem_conjure_zst)] #![feature(f16)] +#![cfg_attr(not(target_family = "wasm"), feature(mem_conjure_zst))] #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] @@ -45,7 +45,6 @@ mod diagnostic; mod escape; mod to_tokens; -use core::convert::From; use core::ops::BitOr; use std::borrow::Cow; use std::ffi::CStr; @@ -54,6 +53,31 @@ use std::path::PathBuf; use std::str::FromStr; use std::{error, fmt}; +#[cfg(target_family = "wasm")] +#[unstable(feature = "proc_macro_internals", issue = "none")] +#[doc(hidden)] +#[allow(unreachable_pub)] +pub mod wasi_bindgen { + // There's a cli (wit-bindgen-cli) that can be used to do this instead of this rustc internal + // macro, but then we'd need to check in the output and setup tidy to enforce it's always been + // run. Since the majority of these dependencies are part of the rustc closure anyway (from + // wasmtime::bindgen's own parsing of the same wit file), we went with this builtin macro hack. + // + // Note that even if we could use the macro directly we'd still need some post-processing (or + // upstream a change to the macro to add an option / gate on cfg), see the string replacement + // done on the internal macro's implementation for details. + // + // Note also that if this used a normal proc macro we'd also need to worry about downstream user + // code supporting cross-compilation (notably to wasm, since that's not a host target). + #[allow_internal_unstable(proc_macro_internals)] + #[rustc_builtin_macro] + macro internal_wit_bindgen($($t:tt)*) { + /* compiler built-in */ + } + + internal_wit_bindgen!(); +} + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub use diagnostic::{Diagnostic, Level, MultiSpan}; use rustc_literal_escaper::{ @@ -427,7 +451,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { return; } - stream.0 = Some(BridgeMethods::ts_concat_trees(stream.0.take(), self.trees)) + stream.0 = Some(BridgeMethods::ts_concat_trees(stream.0.take(), self.trees)); } } diff --git a/library/proc_macro/wasm-interface.wit b/library/proc_macro/wasm-interface.wit new file mode 100644 index 0000000000000..2e826d2dbfe49 --- /dev/null +++ b/library/proc_macro/wasm-interface.wit @@ -0,0 +1,176 @@ +package rust-lang:rust; + +interface host { + resource token-stream { + constructor(); + } + + type symbol = string; + + enum bound { + included, + excluded, + unbounded, + } + + record range-bound { + value: u64, + bound: bound, + } + + record span { + inner: u64 + } + + span-is-same: func(a: span, b: span) -> bool; + span-def-site: func() -> span; + span-call-site: func() -> span; + span-mixed-site: func() -> span; + span-parent: func(sp: span) -> option; + span-source: func(sp: span) -> span; + // -> Range (start, end) + span-byte-range: func(sp: span) -> tuple; + span-debug: func(sp: span) -> string; + span-line: func(sp: span) -> u64; + span-column: func(sp: span) -> u64; + span-file: func(sp: span) -> string; + span-local-file: func(sp: span) -> option; + span-start: func(sp: span) -> span; + span-end: func(sp: span) -> span; + span-join: func(sp: span, other: span) -> option; + span-source-text: func(sp: span) -> option; + span-resolved-at: func(sp: span, other: span) -> span; + span-subspan: func(sp: span, start: range-bound, end: range-bound) -> option; + span-save-span: func(sp: span) -> u64; + span-recover-proc-macro-span: func(id: u64) -> span; + + record delim-span { + open: span, + close: span, + entire: span, + } + + enum delimiter { + parenthesis, + brace, + bracket, + none, + } + + record group { + delimiter: delimiter, + %stream: option, + span: delim-span, + } + + record punct { + ch: u8, + joint: bool, + span: span, + } + + record ident { + sym: symbol, + is-raw: bool, + span: span, + } + + record literal { + kind: literal-kind, + symbol: symbol, + suffix: option, + span: span, + } + + variant literal-kind { + byte, + %char, + integer, + float, + str, + str-raw(u8), + byte-str, + byte-str-raw(u8), + c-str, + c-str-raw(u8), + // This should have an `ErrorGuaranteed`, except that type isn't available + // in this crate. (Imagine it is there.) Hence the `WithGuar` suffix. Must + // only be constructed in `LitKind::from_internal`, where an + // `ErrorGuaranteed` is available. + err-with-guar, + } + + variant token-tree { + group(group), + punct(punct), + ident(ident), + literal(literal), + } + + enum level { + error, + warning, + note, + help, + } + + record diagnostic-inner { + level: level, + message: string, + spans: list, + children: list, + } + + resource diagnostic { + constructor(d: diagnostic-inner); + } + + emit-diagnostic: func(diag: diagnostic); + literal-from-str: func(s: string) -> result; + symbol-normalize-and-validate-ident: func(s: string) -> result; + + injected-env-var: func(k: string) -> option; + track-env-var: func(k: string, v: option); + track-path: func(path: string); + + // Today we don't report additional metadata but we could. + // Returns whether or not to also emit the panic to stderr (via the default panic hook). + report-panic: func(msg: option) -> bool; + + ts-drop: func(ts: token-stream); + ts-clone: func(ts: borrow) -> token-stream; + ts-is-empty: func(ts: borrow) -> bool; + ts-expand-expr: func(ts: borrow) -> result; + ts-from-str: func(src: string) -> result; + ts-to-string: func(ts: borrow) -> string; + ts-from-token-tree: func(tt: token-tree) -> token-stream; + ts-concat-trees: func(base: option, trees: list) -> token-stream; + ts-concat-streams: func(base: option, trees: list) -> token-stream; + ts-into-trees: func(ts: token-stream) -> list; +} + +interface custom-derive { + use host.{token-stream}; + + enum derive-kind { + expand1, + expand2, + } + + resource custom-derive { + get-kind: func() -> derive-kind; + + // for derive and bang macros + expand1: func(tokens: token-stream) -> token-stream; + + // defined for attr macros only + expand2: func(tokens: token-stream, tokens2: token-stream) -> token-stream; + } + + get-custom-derives: func() -> list; +} + +world proc-macro { + import host; + export custom-derive; +} diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 6b232cc039492..7fa5891d9bb89 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2190,6 +2190,14 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the builder.ensure(compile::Rustc::new(test_compiler, target)); } + // Build the standard library for wasm32-wasip2 (current target for wasm proc macros). + if builder.config.wasm_proc_macro { + builder.ensure(compile::Std::new( + test_compiler, + TargetSelection::from_user("wasm32-wasip2"), + )); + } + if suite == "debuginfo" { builder.ensure(dist::DebuggerScripts { sysroot: builder.sysroot(test_compiler).to_path_buf(), @@ -2239,6 +2247,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let is_rustdoc = suite == "rustdoc-ui" || suite == "rustdoc-js"; + if builder.config.wasm_proc_macro { + cmd.arg("--wasm-proc-macro"); + } + // There are (potentially) 2 `cargo`s to consider: // // - A "bootstrap" cargo, which is the same cargo used to build bootstrap itself, and is diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2a623acc1f04b..9d5e7843599b1 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -335,6 +335,8 @@ pub struct Config { pub skip_std_check_if_no_download_rustc: bool, pub exec_ctx: ExecutionContext, + + pub wasm_proc_macro: bool, } impl Config { @@ -607,6 +609,7 @@ impl Config { std_features: rust_std_features, break_on_ice: rust_break_on_ice, rustflags: rust_rustflags, + wasm_proc_macro, } = toml_rust.unwrap_or_default(); let Llvm { @@ -1593,6 +1596,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to .unwrap_or(rust_debug == Some(true)), vendor, verbose_tests, + wasm_proc_macro: wasm_proc_macro.unwrap_or(false), windows_rc: build_windows_rc.map(PathBuf::from), yarn: build_yarn.map(PathBuf::from), // tidy-alphabetical-end diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 5c0fd37e46c2e..7c3364ba52f1e 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -75,6 +75,7 @@ define_config! { std_features: Option> = "std-features", break_on_ice: Option = "break-on-ice", parallel_frontend_threads: Option = "parallel-frontend-threads", + wasm_proc_macro: Option = "wasm-proc-macro", } } @@ -382,6 +383,7 @@ pub fn check_incompatible_options_for_ci_rustc( bootstrap_override_lld: _, bootstrap_override_lld_legacy: _, rustflags: _, + wasm_proc_macro: _, } = ci_rust_config; // There are two kinds of checks for CI rustc incompatible options: diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 1ab2b92070c93..12ee71ea0b349 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -71,7 +71,7 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { /// by combining the primary build target, host targets, and any additional targets. For /// each target, it calls [`fill_target_compiler`] to configure the necessary compiler tools. pub fn fill_compilers(build: &mut Build) { - let targets: HashSet<_> = match build.config.cmd { + let mut targets: HashSet<_> = match build.config.cmd { // We don't need to check cross targets for these commands. crate::Subcommand::Clean { .. } | crate::Subcommand::Check { .. } @@ -93,7 +93,14 @@ pub fn fill_compilers(build: &mut Build) { } }; - for target in targets.into_iter() { + // When we intend to build wasm proc macros, we'll need to detect a toolchain for linking those + // as well. In the future it would be good to make this a no-op given that we shouldn't need to + // build any C/C++ code for wasm... + if build.config.wasm_proc_macro { + targets.insert(TargetSelection::from_user("wasm32-wasip2")); + } + + for target in targets { fill_target_compiler(build, target); } } diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 6dde0ae28064b..626255f67ebd9 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -76,6 +76,8 @@ pub static CRATES: &[&str] = &[ "wasm-encoder", "wasm-metadata", "wasmparser", + "wasmtime-internal-component-util", + "wasmtime-internal-wit-bindgen", "winnow", "wit-bindgen-core", "wit-bindgen-rust", diff --git a/src/doc/unstable-book/src/compiler-flags/wasm-proc-macros.md b/src/doc/unstable-book/src/compiler-flags/wasm-proc-macros.md new file mode 100644 index 0000000000000..1f47f4373968e --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/wasm-proc-macros.md @@ -0,0 +1,8 @@ +# `wasm-proc-macros` + +This option controls whether to enable support for compiling and loading +`--crate-type=proc-macro` to/from WASM rather than the normal host dylib target. + +Currently we expect that proc macros are compiled to the `wasm32-wasip2` +target. The exact target will likely change in the future. When this flag is +passed, both regular dylib proc macros and wasm proc macros are supported. diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 1cf9c94a11721..dc3287b02c925 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -772,6 +772,8 @@ pub(crate) struct Config { pub(crate) parallel_frontend_threads: u32, /// Number of times to execute each test. pub(crate) iteration_count: u32, + + pub(crate) wasm_proc_macro: bool, } impl Config { diff --git a/src/tools/compiletest/src/directives/cfg.rs b/src/tools/compiletest/src/directives/cfg.rs index 25ebb86ad28d3..3acf100aa49ef 100644 --- a/src/tools/compiletest/src/directives/cfg.rs +++ b/src/tools/compiletest/src/directives/cfg.rs @@ -209,6 +209,12 @@ pub(crate) fn prepare_conditions(config: &Config) -> PreparedConditions { "when std is built with remapping of debuginfo", ); + builder.cond( + "wasm-proc-macro", + config.wasm_proc_macro, + "when wasm-proc-macro is enabled in bootstrap.toml", + ); + for &debugger in Debugger::STR_VARIANTS { builder.cond( debugger, diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 058047cd07548..d10b3d7db8d04 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -136,6 +136,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-vxworks", "ignore-wasi", "ignore-wasm", + "ignore-wasm-proc-macro", "ignore-wasm32", "ignore-wasm32-unknown-unknown", "ignore-wasm64", @@ -264,6 +265,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-uefi", "only-unix", "only-visionos", + "only-wasm-proc-macro", "only-wasm32", "only-wasm32-unknown-emscripten", "only-wasm32-unknown-unknown", diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 65d7b5360ae0d..82f4db999ef05 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -1009,7 +1009,7 @@ fn test_supported_crate_types() { let config = cfg().target("wasm32-unknown-unknown").build(); assert_eq!( config.supported_crate_types().iter().map(String::as_str).collect::>(), - HashSet::from(["bin", "cdylib", "lib", "rlib", "staticlib"]), + HashSet::from(["bin", "cdylib", "lib", "rlib", "staticlib", "proc-macro"]), ); // rlib is supported diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 0836caa590f81..3f3242d75367e 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -234,7 +234,8 @@ fn parse_config(args: Vec) -> Config { "number of parallel threads to use for the frontend when building test artifacts", "THREADS_COUNT", ) - .optopt("", "iteration-count", "number of times to execute each test", "COUNT"); + .optopt("", "iteration-count", "number of times to execute each test", "COUNT") + .optflag("", "wasm-proc-macro", "compile proc macros for wasm"); let (argv0, args_) = args.split_first().unwrap(); if args.len() == 1 || args[1] == "-h" || args[1] == "--help" { @@ -523,6 +524,7 @@ fn parse_config(args: Vec) -> Config { parallel_frontend_threads, iteration_count, + wasm_proc_macro: matches.opt_present("wasm-proc-macro"), } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 4243c60001244..7db44af0c1f0b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -90,7 +90,7 @@ fn disable_error_reporting R, R>(f: F) -> R { } /// The platform-specific library name -fn get_lib_name(name: &str, aux_type: AuxType) -> Option { +fn get_lib_name(name: &str, aux_type: AuxType, wasm_proc_macro: bool) -> Option { match aux_type { AuxType::Bin => None, // In some cases (e.g. MUSL), we build a static @@ -98,6 +98,7 @@ fn get_lib_name(name: &str, aux_type: AuxType) -> Option { // In this case, the only path we can pass // with '--extern-meta' is the '.rlib' file AuxType::Lib => Some(format!("lib{name}.rlib")), + AuxType::ProcMacro if wasm_proc_macro => Some(format!("lib{name}.wasm")), AuxType::Dylib | AuxType::ProcMacro => Some(dylib_name(name)), } } @@ -1251,7 +1252,8 @@ impl<'test> TestCx<'test> { aux_name: &str, aux_path: &str, aux_type: AuxType| { - let lib_name = get_lib_name(&path_to_crate_name(aux_path), aux_type); + let lib_name = + get_lib_name(&path_to_crate_name(aux_path), aux_type, self.config.wasm_proc_macro); if let Some(lib_name) = lib_name { let modifiers_and_name = match extern_modifiers { Some(modifiers) => format!("{modifiers}:{aux_name}"), @@ -1282,7 +1284,11 @@ impl<'test> TestCx<'test> { // to `-Zcodegen-backend` when compiling the test file. if let Some(aux_file) = &self.props.aux.codegen_backend { let aux_type = self.build_auxiliary(aux_file, aux_dir, None); - if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) { + if let Some(lib_name) = get_lib_name( + aux_file.trim_end_matches(".rs"), + aux_type, + self.config.wasm_proc_macro, + ) { let lib_path = aux_dir.join(&lib_name); rustc.arg(format!("-Zcodegen-backend={}", lib_path)); } @@ -1353,7 +1359,27 @@ impl<'test> TestCx<'test> { let aux_path = self.resolve_aux_path(source_path); let mut aux_props = self.props.from_aux_file(&aux_path, self.revision, self.config); if aux_type == Some(AuxType::ProcMacro) { - aux_props.force_host = true; + if self.config.wasm_proc_macro { + aux_props.compile_flags.push("--target=wasm32-wasip2".to_owned()); + // Override any earlier linkers for now, otherwise we fail to build since compiletest + // thinks we're building for a different target and passes its linker (if one is + // configured). + // + // wasm32-wasip2 should in principle always be able to link with wasm-component-ld + + // wasm-ld. This does mean that rust.lld needs to be enabled to build wasm-ld wrapper + // around rust-lld. + aux_props.compile_flags.push("-Clinker=wasm-component-ld".to_owned()); + aux_props.compile_flags.push(format!( + "-Clink-arg=--wasm-ld-path={}", + self.config + .sysroot_base + .join("lib/rustlib") + .join(&self.config.host) + .join("bin/gcc-ld/wasm-ld") + )); + } else { + aux_props.force_host = true; + } } let mut aux_dir = aux_dir.to_path_buf(); if aux_type == Some(AuxType::Bin) { @@ -1581,6 +1607,11 @@ impl<'test> TestCx<'test> { // Use a single thread for efficiency and a deterministic error message order compiler.arg("-Zthreads=1"); + // Enable wasm proc macros. + if self.config.wasm_proc_macro { + compiler.arg("-Zwasm-proc-macros"); + } + // Hide libstd sources from ui tests to make sure we generate the stderr // output that users will see. // Without this, we may be producing good diagnostics in-tree but users diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index 57ce0c5a6d5f7..3b9cb9a9aa6fe 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -143,5 +143,6 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { jobs: Default::default(), parallel_frontend_threads: Config::DEFAULT_PARALLEL_FRONTEND_THREADS, iteration_count: Config::DEFAULT_ITERATION_COUNT, + wasm_proc_macro: false, } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs index 4976298d5fcd1..f5b7a87d7a11c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs @@ -2,6 +2,7 @@ use crate::{ ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, token_stream::TokenStream, }; +use rustc_expand::wasm_proc_macro::RustcProcMacro; use rustc_proc_macro::bridge; impl From for crate::PanicMessage { @@ -10,12 +11,10 @@ impl From for crate::PanicMessage { } } -pub(crate) struct ProcMacros(Vec<(bridge::client::Client, rustc_metadata::ProcMacroKind)>); +pub(crate) struct ProcMacros(Vec<(RustcProcMacro, rustc_metadata::ProcMacroKind)>); impl ProcMacros { - pub(super) fn new( - macros: Vec<(bridge::client::Client, rustc_metadata::ProcMacroKind)>, - ) -> Self { + pub(super) fn new(macros: Vec<(RustcProcMacro, rustc_metadata::ProcMacroKind)>) -> Self { ProcMacros(macros) } @@ -33,6 +32,9 @@ impl ProcMacros { let parsed_attributes = attribute.unwrap_or_default(); for (client, kind) in &self.0 { + let RustcProcMacro::Dylib { client } = client else { + panic!("not yet implemented support for wasm proc macro, should reuse rustc impl"); + }; match kind { rustc_metadata::ProcMacroKind::CustomDerive { trait_name, .. } if trait_name.as_str() == macro_name => diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 2a3a1bc002601..05874f7b87217 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -17,6 +17,7 @@ extern crate rustc_codegen_ssa; extern crate rustc_driver as _; +extern crate rustc_expand; extern crate rustc_interface; extern crate rustc_lexer; extern crate rustc_metadata; diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 60b321641ad76..32bf2e18800eb 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -287,29 +287,59 @@ const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6); /// rustc. Please check with the compiler team before adding an entry. const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start + "addr2line", "adler2", "aho-corasick", "allocator-api2", // FIXME: only appears in Cargo.lock due to https://github.com/rust-lang/cargo/issues/10801 + "ambient-authority", + "android_system_properties", "annotate-snippets", "anstream", "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", + "anyhow", "ar_archive_writer", + "arbitrary", "arrayref", "arrayvec", + "async-trait", "bitflags", "blake3", "block-buffer", "block2", "bstr", + "bumpalo", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-primitives", + "cap-std", + "cap-time-ext", "cc", "cfg-if", "cfg_aliases", + "chacha20", + "cobs", "colorchoice", "constant_time_eq", + "core-foundation-sys", + "cpp_demangle", "cpufeatures", + "cranelift-assembler-x64", + "cranelift-assembler-x64-meta", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-isle", + "cranelift-native", + "cranelift-srcgen", "crc32fast", "crossbeam-deque", "crossbeam-epoch", @@ -329,7 +359,9 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "dyn-clone", "either", "elsa", + "embedded-io", "ena", + "encoding_rs", "equivalent", "errno", "expect-test", @@ -342,22 +374,44 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "fluent-syntax", "fnv", "foldhash", + "form_urlencoded", + "fs-set-times", + "futures", + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", "generic-array", "getopts", "getrandom", "gimli", "gsgdt", "hashbrown", + "heck", + "iana-time-zone", + "iana-time-zone-haiku", "icu_collections", "icu_list", "icu_locale", "icu_locale_core", "icu_locale_data", + "icu_normalizer", + "icu_normalizer_data", + "icu_properties", + "icu_properties_data", "icu_provider", + "id-arena", "ident_case", + "idna", + "idna_adapter", "indexmap", "intl-memoizer", "intl_pluralrules", + "io-extras", + "io-lifetimes", + "ipnet", "is_terminal_polyfill", "itertools", "itoa", @@ -366,20 +420,27 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "jiff-tzdb", "jiff-tzdb-platform", "jobserver", + "js-sys", "lazy_static", "leb128", + "leb128fmt", "libc", "libloading", + "libm", "linux-raw-sys", "litemap", "lock_api", "log", + "mach2", "matchers", + "maybe-owned", "md-5", "measureme", "memchr", + "memfd", "memmap2", "miniz_oxide", + "mio", "nix", "nu-ansi-term", "objc2", @@ -391,18 +452,23 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "parking_lot", "parking_lot_core", "pathdiff", + "percent-encoding", "perf-event-open-sys", "pin-project-lite", "polonius-engine", "portable-atomic", // dependency for platforms doesn't support `AtomicU64` in std "portable-atomic-util", + "postcard", "potential_utf", "ppv-lite86", + "prettyplease", "proc-macro-hack", "proc-macro2", "psm", "pulldown-cmark", "pulldown-cmark-escape", + "pulley-interpreter", + "pulley-macros", "punycode", "quote", "r-efi", @@ -414,6 +480,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "redox_syscall", "ref-cast", "ref-cast-impl", + "regalloc2", "regex", "regex-automata", "regex-syntax", @@ -423,6 +490,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rustc-stable-hash", "rustc_apfloat", "rustix", + "rustix-linux-procfs", + "rustversion", "ruzstd", // via object in thorin-dwp "ryu", "schemars", @@ -430,6 +499,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "scoped-tls", "scopeguard", "self_cell", + "semver", "serde", "serde_core", "serde_derive", @@ -441,14 +511,18 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "sharded-slab", "shlex", "simd-adler32", + "slab", "smallvec", + "socket2", "stable_deref_trait", "stacker", "static_assertions", "strsim", "syn", "synstructure", + "target-lexicon", "tempfile", + "termcolor", "termize", "thin-vec", "thiserror", @@ -459,6 +533,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "tinystr", "tinyvec", "tinyvec_macros", + "tokio", "tracing", "tracing-attributes", "tracing-core", @@ -480,13 +555,38 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "unicode-script", "unicode-security", "unicode-width", + "unicode-xid", + "url", "utf8_iter", "utf8parse", "valuable", "version_check", "wasi", + "wasip2", + "wasip3", + "wasm-bindgen", + "wasm-bindgen-macro", + "wasm-bindgen-macro-support", + "wasm-bindgen-shared", "wasm-encoder", + "wasm-metadata", "wasmparser", + "wasmprinter", + "wasmtime", + "wasmtime-environ", + "wasmtime-internal-component-macro", + "wasmtime-internal-component-util", + "wasmtime-internal-core", + "wasmtime-internal-cranelift", + "wasmtime-internal-fiber", + "wasmtime-internal-jit-debug", + "wasmtime-internal-jit-icache-coherence", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", + "wasmtime-internal-wit-bindgen", + "wasmtime-wasi", + "wasmtime-wasi-io", + "winapi-util", "windows", "windows-collections", "windows-core", @@ -508,7 +608,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", + "winx", + "wit-bindgen", + "wit-bindgen-core", "wit-bindgen-rt@0.39.0", // pinned to a specific version due to using a binary blob: + "wit-bindgen-rust", + "wit-bindgen-rust-macro", + "wit-component", + "wit-parser", "writeable", "yoke", "yoke-derive", diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index f1ac90ba0bc5e..c1a1c1346ed9b 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -62,6 +62,7 @@ const EXCEPTION_PATHS: &[&str] = &[ "library/std/src/lib.rs", // for miniz_oxide leaking docs, which itself workaround "library/std/src/path.rs", "library/std/src/io/error.rs", // Repr unpacked needed for UEFI + "library/proc_macro", // proc-macro wasm scatters cfgs throughout ]; pub fn check(library_path: &Path, tidy_ctx: TidyCtx) { diff --git a/tests/ui/print-request/supported-crate-types.wasm.stdout b/tests/ui/print-request/supported-crate-types.wasm.stdout index ca1de519598af..1ddfc8fd496d8 100644 --- a/tests/ui/print-request/supported-crate-types.wasm.stdout +++ b/tests/ui/print-request/supported-crate-types.wasm.stdout @@ -1,5 +1,6 @@ bin cdylib lib +proc-macro rlib staticlib diff --git a/tests/ui/proc-macro/auxiliary/make-macro.rs b/tests/ui/proc-macro/auxiliary/make-macro.rs index a0db47885a10d..aa60c9e9530d4 100644 --- a/tests/ui/proc-macro/auxiliary/make-macro.rs +++ b/tests/ui/proc-macro/auxiliary/make-macro.rs @@ -1,4 +1,7 @@ -//@ force-host +//@ revisions: wasm native +//@[wasm] compile-flags: --target=wasm32-wasip2 --crate-type=rlib +//@[wasm] needs-llvm-components: webassembly +//@[native] force-host #[macro_export] macro_rules! make_it { diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.native.stdout similarity index 71% rename from tests/ui/proc-macro/meta-macro-hygiene.stdout rename to tests/ui/proc-macro/meta-macro-hygiene.native.stdout index 6dea8709019bc..b76454da1bc44 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.native.stdout @@ -1,6 +1,6 @@ -Def site: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) -Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:27:37: 27:43 (#3) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:27:43: 27:44 (#3) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:27:44: 27:45 (#3) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:27:45: 27:50 (#3) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:27:50: 27:51 (#3) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:27:51: 27:53 (#3) }] -Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }] +Def site: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) +Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:30:37: 30:43 (#3) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:30:43: 30:44 (#3) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:30:44: 30:45 (#3) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:30:45: 30:50 (#3) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:30:50: 30:51 (#3) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:30:51: 30:53 (#3) }] +Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }] #![feature /* 0#0 */(prelude_import /* 0#0 */)] // ignore-tidy-file-linelength // @@ -9,6 +9,9 @@ Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro //@ edition:2018 //@ compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene -Z trim-diagnostic-paths=no //@ check-pass +//@ revisions: wasm native +//@[wasm] only-wasm-proc-macro +//@[native] ignore-wasm-proc-macro //@ normalize-stdout: "\d+#" -> "0#" //@ normalize-stdout: "expn\d{3,}" -> "expnNNN" //@ normalize-stdout: "extern crate compiler_builtins /\* \d+ \*/" -> "extern crate compiler_builtins /* NNN */" diff --git a/tests/ui/proc-macro/meta-macro-hygiene.rs b/tests/ui/proc-macro/meta-macro-hygiene.rs index d6925574892f4..ac3914db9513c 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.rs +++ b/tests/ui/proc-macro/meta-macro-hygiene.rs @@ -5,6 +5,9 @@ //@ edition:2018 //@ compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene -Z trim-diagnostic-paths=no //@ check-pass +//@ revisions: wasm native +//@[wasm] only-wasm-proc-macro +//@[native] ignore-wasm-proc-macro //@ normalize-stdout: "\d+#" -> "0#" //@ normalize-stdout: "expn\d{3,}" -> "expnNNN" //@ normalize-stdout: "extern crate compiler_builtins /\* \d+ \*/" -> "extern crate compiler_builtins /* NNN */" diff --git a/tests/ui/proc-macro/meta-macro-hygiene.wasm.stdout b/tests/ui/proc-macro/meta-macro-hygiene.wasm.stdout new file mode 100644 index 0000000000000..b76454da1bc44 --- /dev/null +++ b/tests/ui/proc-macro/meta-macro-hygiene.wasm.stdout @@ -0,0 +1,68 @@ +Def site: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) +Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:30:37: 30:43 (#3) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:30:43: 30:44 (#3) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:30:44: 30:45 (#3) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:30:45: 30:50 (#3) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:30:50: 30:51 (#3) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:30:51: 30:53 (#3) }] +Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#4) }] +#![feature /* 0#0 */(prelude_import /* 0#0 */)] +// ignore-tidy-file-linelength +// +//@ aux-build:make-macro.rs +//@ proc-macro: meta-macro.rs +//@ edition:2018 +//@ compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene -Z trim-diagnostic-paths=no +//@ check-pass +//@ revisions: wasm native +//@[wasm] only-wasm-proc-macro +//@[native] ignore-wasm-proc-macro +//@ normalize-stdout: "\d+#" -> "0#" +//@ normalize-stdout: "expn\d{3,}" -> "expnNNN" +//@ normalize-stdout: "extern crate compiler_builtins /\* \d+ \*/" -> "extern crate compiler_builtins /* NNN */" +// +// We don't care about symbol ids, so we set them all to 0 +// in the stdout + +#![no_std /* 0#0 */] +extern crate core /* 0#1 */; +#[prelude_import /* 0#1 */] +use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*; +// Don't load unnecessary hygiene information from std +extern crate std /* 0#0 */; + +extern crate meta_macro /* 0#0 */; + +macro_rules! produce_it + /* + 0#0 + */ { + () => + { + meta_macro /* 0#0 */::print_def_site /* 0#0 + */!($crate /* 0#0 */::dummy /* 0#0 */!()); + // `print_def_site!` will respan the `$crate` identifier + // with `Span::def_site()`. This should cause it to resolve + // relative to `meta_macro`, *not* `make_macro` (despite + // the fact that `print_def_site` is produced by a + // `macro_rules!` macro in `make_macro`). + }; +} + +fn main /* 0#0 */() { ; } + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) +crate0::{{expn2}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "produce_it") +crate0::{{expn3}}: parent: crate0::{{expn2}}, call_site_ctxt: #3, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site") +crate0::{{expn4}}: parent: crate0::{{expn3}}, call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "$crate::dummy") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, Opaque) +#2: parent: #0, outer_mark: (crate0::{{expn1}}, Transparent) +#3: parent: #0, outer_mark: (crate0::{{expn2}}, SemiOpaque) +#4: parent: #0, outer_mark: (crate0::{{expn3}}, Opaque) +#5: parent: #3, outer_mark: (crate0::{{expn3}}, Transparent) +#6: parent: #0, outer_mark: (crate0::{{expn3}}, SemiOpaque) +#7: parent: #0, outer_mark: (crate0::{{expn4}}, Opaque) +#8: parent: #4, outer_mark: (crate0::{{expn4}}, Transparent) +#9: parent: #4, outer_mark: (crate0::{{expn4}}, SemiOpaque) +*/ diff --git a/tests/ui/proc-macro/meta-macro.native.stdout b/tests/ui/proc-macro/meta-macro.native.stdout new file mode 100644 index 0000000000000..191876197156f --- /dev/null +++ b/tests/ui/proc-macro/meta-macro.native.stdout @@ -0,0 +1,3 @@ +Def site: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#3) +Input: TokenStream [] +Respanned: TokenStream [] diff --git a/tests/ui/proc-macro/meta-macro.rs b/tests/ui/proc-macro/meta-macro.rs index 02ef45c0c21f5..a04dafc5908be 100644 --- a/tests/ui/proc-macro/meta-macro.rs +++ b/tests/ui/proc-macro/meta-macro.rs @@ -3,6 +3,9 @@ //@ edition:2018 //@ compile-flags: -Z span-debug //@ run-pass +//@ revisions: wasm native +//@[wasm] only-wasm-proc-macro +//@[native] ignore-wasm-proc-macro #![no_std] // Don't load unnecessary hygiene information from std extern crate std; diff --git a/tests/ui/proc-macro/meta-macro.wasm.stdout b/tests/ui/proc-macro/meta-macro.wasm.stdout new file mode 100644 index 0000000000000..191876197156f --- /dev/null +++ b/tests/ui/proc-macro/meta-macro.wasm.stdout @@ -0,0 +1,3 @@ +Def site: $DIR/auxiliary/make-macro.rs:10:9: 10:56 (#3) +Input: TokenStream [] +Respanned: TokenStream []