From 041e99c78af654550215887f1a3bb4d779c4be40 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Jul 2023 13:48:37 -0700 Subject: [PATCH] Implement fallback to compiling serde_derive from source --- precompiled/serde_derive/Cargo.toml | 23 +- precompiled/serde_derive/LICENSE-APACHE | 1 + precompiled/serde_derive/LICENSE-MIT | 1 + precompiled/serde_derive/README.md | 17 +- precompiled/serde_derive/crates-io.md | 1 + precompiled/serde_derive/src/bound.rs | 1 + precompiled/serde_derive/src/de.rs | 1 + precompiled/serde_derive/src/dummy.rs | 1 + precompiled/serde_derive/src/fragment.rs | 1 + precompiled/serde_derive/src/internals | 1 + precompiled/serde_derive/src/lib.rs | 238 ++---------------- .../serde_derive/src/lib_from_source.rs | 39 +++ .../serde_derive/src/lib_precompiled.rs | 214 ++++++++++++++++ precompiled/serde_derive/src/pretend.rs | 1 + precompiled/serde_derive/src/ser.rs | 1 + precompiled/serde_derive/src/this.rs | 1 + precompiled/serde_derive/src/try.rs | 1 + 17 files changed, 303 insertions(+), 240 deletions(-) create mode 120000 precompiled/serde_derive/LICENSE-APACHE create mode 120000 precompiled/serde_derive/LICENSE-MIT mode change 100644 => 120000 precompiled/serde_derive/README.md create mode 120000 precompiled/serde_derive/crates-io.md create mode 120000 precompiled/serde_derive/src/bound.rs create mode 120000 precompiled/serde_derive/src/de.rs create mode 120000 precompiled/serde_derive/src/dummy.rs create mode 120000 precompiled/serde_derive/src/fragment.rs create mode 120000 precompiled/serde_derive/src/internals create mode 100644 precompiled/serde_derive/src/lib_from_source.rs create mode 100644 precompiled/serde_derive/src/lib_precompiled.rs create mode 120000 precompiled/serde_derive/src/pretend.rs create mode 120000 precompiled/serde_derive/src/ser.rs create mode 120000 precompiled/serde_derive/src/this.rs create mode 120000 precompiled/serde_derive/src/try.rs diff --git a/precompiled/serde_derive/Cargo.toml b/precompiled/serde_derive/Cargo.toml index 19b4b180f..5985f6daa 100644 --- a/precompiled/serde_derive/Cargo.toml +++ b/precompiled/serde_derive/Cargo.toml @@ -1,25 +1,34 @@ [package] -name = "serde_derive-x86_64-unknown-linux-gnu" -version = "1.0.171-alpha.3" +name = "serde_derive" +version = "1.0.171" authors = ["David Tolnay "] categories = ["no-std", "no-std::no-alloc"] -description = "Precompiled implementation of #[derive(Serialize, Deserialize)]" +description = "Implementation of #[derive(Serialize, Deserialize)]" documentation = "https://serde.rs/derive.html" edition = "2015" homepage = "https://serde.rs" include = ["serde_derive-x86_64-unknown-linux-gnu", "src"] keywords = ["serde", "serialization", "no_std", "derive"] license = "MIT OR Apache-2.0" +readme = "crates-io.md" repository = "https://github.com/serde-rs/serde" - -[lib] -name = "serde_derive" -proc-macro = true +rust-version = "1.56" [features] default = [] deserialize_in_place = [] +[lib] +proc-macro = true + +[target.'cfg(not(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu")))'.dependencies] +proc-macro2 = "1" +quote = "1" +syn = "2.0.25" + +[dev-dependencies] +serde = { version = "1", path = "../../serde" } + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/precompiled/serde_derive/LICENSE-APACHE b/precompiled/serde_derive/LICENSE-APACHE new file mode 120000 index 000000000..1cd601d0a --- /dev/null +++ b/precompiled/serde_derive/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/precompiled/serde_derive/LICENSE-MIT b/precompiled/serde_derive/LICENSE-MIT new file mode 120000 index 000000000..b2cfbdc7b --- /dev/null +++ b/precompiled/serde_derive/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/precompiled/serde_derive/README.md b/precompiled/serde_derive/README.md deleted file mode 100644 index 410f352ac..000000000 --- a/precompiled/serde_derive/README.md +++ /dev/null @@ -1,16 +0,0 @@ -```toml -# Cargo.toml - -[dependencies] -serde = "1" # no features=["derive"] -serde_derive-x86_64-unknown-linux-gnu = "1.0.171-alpha.3" -``` - -```rust -use serde_derive::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize)] -pub struct MyStruct { - /* ... */ -} -``` diff --git a/precompiled/serde_derive/README.md b/precompiled/serde_derive/README.md new file mode 120000 index 000000000..fe8400541 --- /dev/null +++ b/precompiled/serde_derive/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/precompiled/serde_derive/crates-io.md b/precompiled/serde_derive/crates-io.md new file mode 120000 index 000000000..478d47a67 --- /dev/null +++ b/precompiled/serde_derive/crates-io.md @@ -0,0 +1 @@ +../../crates-io.md \ No newline at end of file diff --git a/precompiled/serde_derive/src/bound.rs b/precompiled/serde_derive/src/bound.rs new file mode 120000 index 000000000..94348c7ff --- /dev/null +++ b/precompiled/serde_derive/src/bound.rs @@ -0,0 +1 @@ +../../../serde_derive/src/bound.rs \ No newline at end of file diff --git a/precompiled/serde_derive/src/de.rs b/precompiled/serde_derive/src/de.rs new file mode 120000 index 000000000..fcf7fdca5 --- /dev/null +++ b/precompiled/serde_derive/src/de.rs @@ -0,0 +1 @@ +../../../serde_derive/src/de.rs \ No newline at end of file diff --git a/precompiled/serde_derive/src/dummy.rs b/precompiled/serde_derive/src/dummy.rs new file mode 120000 index 000000000..a67e6bfc0 --- /dev/null +++ b/precompiled/serde_derive/src/dummy.rs @@ -0,0 +1 @@ +../../../serde_derive/src/dummy.rs \ No newline at end of file diff --git a/precompiled/serde_derive/src/fragment.rs b/precompiled/serde_derive/src/fragment.rs new file mode 120000 index 000000000..e6e66b566 --- /dev/null +++ b/precompiled/serde_derive/src/fragment.rs @@ -0,0 +1 @@ +../../../serde_derive/src/fragment.rs \ No newline at end of file diff --git a/precompiled/serde_derive/src/internals b/precompiled/serde_derive/src/internals new file mode 120000 index 000000000..f1644ee79 --- /dev/null +++ b/precompiled/serde_derive/src/internals @@ -0,0 +1 @@ +../../../serde_derive/src/internals/ \ No newline at end of file diff --git a/precompiled/serde_derive/src/lib.rs b/precompiled/serde_derive/src/lib.rs index 21368861a..bd532b9f1 100644 --- a/precompiled/serde_derive/src/lib.rs +++ b/precompiled/serde_derive/src/lib.rs @@ -1,221 +1,25 @@ -extern crate proc_macro; - -mod buffer; -mod bytecode; +//! This crate provides Serde's two derive macros. +//! +//! ```edition2021 +//! # use serde_derive::{Deserialize, Serialize}; +//! # +//! #[derive(Serialize, Deserialize)] +//! # struct S; +//! # +//! # fn main() {} +//! ``` +//! +//! Please refer to [https://serde.rs/derive.html] for how to set this up. +//! +//! [https://serde.rs/derive.html]: https://serde.rs/derive.html + +#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.171")] +#![allow(unknown_lints, bare_trait_objects)] -use crate::buffer::{InputBuffer, OutputBuffer}; -use crate::bytecode::Bytecode; -use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; -use std::io::{Read, Write}; -use std::iter::FromIterator; -use std::process::{Command, Stdio}; -use std::str::FromStr; +extern crate proc_macro; #[cfg(not(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu")))] -compile_error! { - "this proof of concept is only compiled for x86_64-unknown-linux-gnu" -} - -#[proc_macro_derive(Serialize, attributes(serde))] -pub fn derive_serialize(input: TokenStream) -> TokenStream { - derive(0, input) -} - -#[proc_macro_derive(Deserialize, attributes(serde))] -pub fn derive_deserialize(input: TokenStream) -> TokenStream { - derive(1 + cfg!(feature = "deserialize_in_place") as u8, input) -} - -fn derive(select: u8, input: TokenStream) -> TokenStream { - let mut memory = TokenMemory::default(); - let mut buf = OutputBuffer::new(); - buf.write_u8(select); - - memory.spans.push(Span::call_site()); - for token in input { - memory.linearize_token(token, &mut buf); - } - - let path = concat!( - env!("CARGO_MANIFEST_DIR"), - "/serde_derive-x86_64-unknown-linux-gnu", - ); - let mut child = Command::new(path) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("failed to spawn process"); - - let mut stdin = child.stdin.take().unwrap(); - let mut buf = buf.into_bytes(); - stdin.write_all(&buf).unwrap(); - drop(stdin); - - let mut stdout = child.stdout.take().unwrap(); - buf.clear(); - stdout.read_to_end(&mut buf).unwrap(); - - let mut buf = InputBuffer::new(&buf); - memory.receive(&mut buf) -} - -#[derive(Default)] -struct TokenMemory { - spans: Vec, - groups: Vec, - idents: Vec, - puncts: Vec, - literals: Vec, -} - -enum Kind { - Group(Delimiter), - Ident, - Punct(Spacing), - Literal, -} - -impl TokenMemory { - // Depth-first post-order traversal. - fn linearize_token(&mut self, token: TokenTree, buf: &mut OutputBuffer) { - match token { - TokenTree::Group(group) => { - let mut len = 0usize; - for token in group.stream() { - self.linearize_token(token, buf); - len += 1; - } - assert!(len <= u32::MAX as usize); - buf.write_u8(match group.delimiter() { - Delimiter::Parenthesis => Bytecode::GROUP_PARENTHESIS, - Delimiter::Brace => Bytecode::GROUP_BRACE, - Delimiter::Bracket => Bytecode::GROUP_BRACKET, - Delimiter::None => Bytecode::GROUP_NONE, - }); - buf.write_u32(len as u32); - self.spans - .extend([group.span(), group.span_open(), group.span_close()]); - self.groups.push(group); - } - TokenTree::Ident(ident) => { - buf.write_u8(Bytecode::IDENT); - let repr = ident.to_string(); - assert!(repr.len() <= u16::MAX as usize); - buf.write_u16(repr.len() as u16); - buf.write_str(&repr); - self.spans.push(ident.span()); - self.idents.push(ident); - } - TokenTree::Punct(punct) => { - buf.write_u8(match punct.spacing() { - Spacing::Alone => Bytecode::PUNCT_ALONE, - Spacing::Joint => Bytecode::PUNCT_JOINT, - }); - let ch = punct.as_char(); - assert!(ch.is_ascii()); - buf.write_u8(ch as u8); - self.spans.push(punct.span()); - self.puncts.push(punct); - } - TokenTree::Literal(literal) => { - buf.write_u8(Bytecode::LITERAL); - let repr = literal.to_string(); - assert!(repr.len() <= u16::MAX as usize); - buf.write_u16(repr.len() as u16); - buf.write_str(&repr); - self.spans.push(literal.span()); - self.literals.push(literal); - } - } - } - - fn receive(&self, buf: &mut InputBuffer) -> TokenStream { - let mut trees = Vec::new(); - while !buf.is_empty() { - match match buf.read_u8() { - Bytecode::GROUP_PARENTHESIS => Kind::Group(Delimiter::Parenthesis), - Bytecode::GROUP_BRACE => Kind::Group(Delimiter::Brace), - Bytecode::GROUP_BRACKET => Kind::Group(Delimiter::Bracket), - Bytecode::GROUP_NONE => Kind::Group(Delimiter::None), - Bytecode::IDENT => Kind::Ident, - Bytecode::PUNCT_ALONE => Kind::Punct(Spacing::Alone), - Bytecode::PUNCT_JOINT => Kind::Punct(Spacing::Joint), - Bytecode::LITERAL => Kind::Literal, - Bytecode::LOAD_GROUP => { - let identity = buf.read_u32(); - let group = self.groups[identity as usize].clone(); - trees.push(TokenTree::Group(group)); - continue; - } - Bytecode::LOAD_IDENT => { - let identity = buf.read_u32(); - let ident = self.idents[identity as usize].clone(); - trees.push(TokenTree::Ident(ident)); - continue; - } - Bytecode::LOAD_PUNCT => { - let identity = buf.read_u32(); - let punct = self.puncts[identity as usize].clone(); - trees.push(TokenTree::Punct(punct)); - continue; - } - Bytecode::LOAD_LITERAL => { - let identity = buf.read_u32(); - let literal = self.literals[identity as usize].clone(); - trees.push(TokenTree::Literal(literal)); - continue; - } - Bytecode::SET_SPAN => { - trees.last_mut().unwrap().set_span(self.read_span(buf)); - continue; - } - _ => unreachable!(), - } { - Kind::Group(delimiter) => { - let len = buf.read_u32(); - let stream = trees.drain(trees.len() - len as usize..).collect(); - let group = Group::new(delimiter, stream); - trees.push(TokenTree::Group(group)); - } - Kind::Ident => { - let len = buf.read_u16(); - let repr = buf.read_str(len as usize); - let span = self.read_span(buf); - let ident = if let Some(repr) = repr.strip_prefix("r#") { - Ident::new_raw(repr, span) - } else { - Ident::new(repr, span) - }; - trees.push(TokenTree::Ident(ident)); - } - Kind::Punct(spacing) => { - let ch = buf.read_u8(); - assert!(ch.is_ascii()); - let punct = Punct::new(ch as char, spacing); - trees.push(TokenTree::Punct(punct)); - } - Kind::Literal => { - let len = buf.read_u16(); - let repr = buf.read_str(len as usize); - let literal = Literal::from_str(repr).unwrap(); - trees.push(TokenTree::Literal(literal)); - } - } - } - - TokenStream::from_iter(trees) - } +include!("lib_from_source.rs"); - fn read_span(&self, buf: &mut InputBuffer) -> Span { - let lo = buf.read_u32(); - let hi = buf.read_u32(); - let span = self.spans[lo as usize]; - if lo == hi { - span - } else { - #[cfg(any())] // FIXME - return span.join(self.spans[hi as usize]).unwrap_or(span); - span - } - } -} +#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] +include!("lib_precompiled.rs"); diff --git a/precompiled/serde_derive/src/lib_from_source.rs b/precompiled/serde_derive/src/lib_from_source.rs new file mode 100644 index 000000000..3e3f7125a --- /dev/null +++ b/precompiled/serde_derive/src/lib_from_source.rs @@ -0,0 +1,39 @@ +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +extern crate proc_macro2; + +mod internals; + +use proc_macro::TokenStream; +use syn::DeriveInput; + +#[macro_use] +mod bound; +#[macro_use] +mod fragment; + +mod de; +mod dummy; +mod pretend; +mod ser; +mod this; +mod try; + +#[proc_macro_derive(Serialize, attributes(serde))] +pub fn derive_serialize(input: TokenStream) -> TokenStream { + let mut input = parse_macro_input!(input as DeriveInput); + ser::expand_derive_serialize(&mut input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +#[proc_macro_derive(Deserialize, attributes(serde))] +pub fn derive_deserialize(input: TokenStream) -> TokenStream { + let mut input = parse_macro_input!(input as DeriveInput); + de::expand_derive_deserialize(&mut input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} diff --git a/precompiled/serde_derive/src/lib_precompiled.rs b/precompiled/serde_derive/src/lib_precompiled.rs new file mode 100644 index 000000000..0821f334f --- /dev/null +++ b/precompiled/serde_derive/src/lib_precompiled.rs @@ -0,0 +1,214 @@ +mod buffer; +mod bytecode; + +use crate::buffer::{InputBuffer, OutputBuffer}; +use crate::bytecode::Bytecode; +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use std::io::{Read, Write}; +use std::iter::FromIterator; +use std::process::{Command, Stdio}; +use std::str::FromStr; + +#[proc_macro_derive(Serialize, attributes(serde))] +pub fn derive_serialize(input: TokenStream) -> TokenStream { + derive(0, input) +} + +#[proc_macro_derive(Deserialize, attributes(serde))] +pub fn derive_deserialize(input: TokenStream) -> TokenStream { + derive(1 + cfg!(feature = "deserialize_in_place") as u8, input) +} + +fn derive(select: u8, input: TokenStream) -> TokenStream { + let mut memory = TokenMemory::default(); + let mut buf = OutputBuffer::new(); + buf.write_u8(select); + + memory.spans.push(Span::call_site()); + for token in input { + memory.linearize_token(token, &mut buf); + } + + let path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/serde_derive-x86_64-unknown-linux-gnu", + ); + let mut child = Command::new(path) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to spawn process"); + + let mut stdin = child.stdin.take().unwrap(); + let mut buf = buf.into_bytes(); + stdin.write_all(&buf).unwrap(); + drop(stdin); + + let mut stdout = child.stdout.take().unwrap(); + buf.clear(); + stdout.read_to_end(&mut buf).unwrap(); + + let mut buf = InputBuffer::new(&buf); + memory.receive(&mut buf) +} + +#[derive(Default)] +struct TokenMemory { + spans: Vec, + groups: Vec, + idents: Vec, + puncts: Vec, + literals: Vec, +} + +enum Kind { + Group(Delimiter), + Ident, + Punct(Spacing), + Literal, +} + +impl TokenMemory { + // Depth-first post-order traversal. + fn linearize_token(&mut self, token: TokenTree, buf: &mut OutputBuffer) { + match token { + TokenTree::Group(group) => { + let mut len = 0usize; + for token in group.stream() { + self.linearize_token(token, buf); + len += 1; + } + assert!(len <= u32::MAX as usize); + buf.write_u8(match group.delimiter() { + Delimiter::Parenthesis => Bytecode::GROUP_PARENTHESIS, + Delimiter::Brace => Bytecode::GROUP_BRACE, + Delimiter::Bracket => Bytecode::GROUP_BRACKET, + Delimiter::None => Bytecode::GROUP_NONE, + }); + buf.write_u32(len as u32); + self.spans + .extend([group.span(), group.span_open(), group.span_close()]); + self.groups.push(group); + } + TokenTree::Ident(ident) => { + buf.write_u8(Bytecode::IDENT); + let repr = ident.to_string(); + assert!(repr.len() <= u16::MAX as usize); + buf.write_u16(repr.len() as u16); + buf.write_str(&repr); + self.spans.push(ident.span()); + self.idents.push(ident); + } + TokenTree::Punct(punct) => { + buf.write_u8(match punct.spacing() { + Spacing::Alone => Bytecode::PUNCT_ALONE, + Spacing::Joint => Bytecode::PUNCT_JOINT, + }); + let ch = punct.as_char(); + assert!(ch.is_ascii()); + buf.write_u8(ch as u8); + self.spans.push(punct.span()); + self.puncts.push(punct); + } + TokenTree::Literal(literal) => { + buf.write_u8(Bytecode::LITERAL); + let repr = literal.to_string(); + assert!(repr.len() <= u16::MAX as usize); + buf.write_u16(repr.len() as u16); + buf.write_str(&repr); + self.spans.push(literal.span()); + self.literals.push(literal); + } + } + } + + fn receive(&self, buf: &mut InputBuffer) -> TokenStream { + let mut trees = Vec::new(); + while !buf.is_empty() { + match match buf.read_u8() { + Bytecode::GROUP_PARENTHESIS => Kind::Group(Delimiter::Parenthesis), + Bytecode::GROUP_BRACE => Kind::Group(Delimiter::Brace), + Bytecode::GROUP_BRACKET => Kind::Group(Delimiter::Bracket), + Bytecode::GROUP_NONE => Kind::Group(Delimiter::None), + Bytecode::IDENT => Kind::Ident, + Bytecode::PUNCT_ALONE => Kind::Punct(Spacing::Alone), + Bytecode::PUNCT_JOINT => Kind::Punct(Spacing::Joint), + Bytecode::LITERAL => Kind::Literal, + Bytecode::LOAD_GROUP => { + let identity = buf.read_u32(); + let group = self.groups[identity as usize].clone(); + trees.push(TokenTree::Group(group)); + continue; + } + Bytecode::LOAD_IDENT => { + let identity = buf.read_u32(); + let ident = self.idents[identity as usize].clone(); + trees.push(TokenTree::Ident(ident)); + continue; + } + Bytecode::LOAD_PUNCT => { + let identity = buf.read_u32(); + let punct = self.puncts[identity as usize].clone(); + trees.push(TokenTree::Punct(punct)); + continue; + } + Bytecode::LOAD_LITERAL => { + let identity = buf.read_u32(); + let literal = self.literals[identity as usize].clone(); + trees.push(TokenTree::Literal(literal)); + continue; + } + Bytecode::SET_SPAN => { + trees.last_mut().unwrap().set_span(self.read_span(buf)); + continue; + } + _ => unreachable!(), + } { + Kind::Group(delimiter) => { + let len = buf.read_u32(); + let stream = trees.drain(trees.len() - len as usize..).collect(); + let group = Group::new(delimiter, stream); + trees.push(TokenTree::Group(group)); + } + Kind::Ident => { + let len = buf.read_u16(); + let repr = buf.read_str(len as usize); + let span = self.read_span(buf); + let ident = if let Some(repr) = repr.strip_prefix("r#") { + Ident::new_raw(repr, span) + } else { + Ident::new(repr, span) + }; + trees.push(TokenTree::Ident(ident)); + } + Kind::Punct(spacing) => { + let ch = buf.read_u8(); + assert!(ch.is_ascii()); + let punct = Punct::new(ch as char, spacing); + trees.push(TokenTree::Punct(punct)); + } + Kind::Literal => { + let len = buf.read_u16(); + let repr = buf.read_str(len as usize); + let literal = Literal::from_str(repr).unwrap(); + trees.push(TokenTree::Literal(literal)); + } + } + } + + TokenStream::from_iter(trees) + } + + fn read_span(&self, buf: &mut InputBuffer) -> Span { + let lo = buf.read_u32(); + let hi = buf.read_u32(); + let span = self.spans[lo as usize]; + if lo == hi { + span + } else { + #[cfg(any())] // FIXME + return span.join(self.spans[hi as usize]).unwrap_or(span); + span + } + } +} diff --git a/precompiled/serde_derive/src/pretend.rs b/precompiled/serde_derive/src/pretend.rs new file mode 120000 index 000000000..e9e805ca8 --- /dev/null +++ b/precompiled/serde_derive/src/pretend.rs @@ -0,0 +1 @@ +../../../serde_derive/src/pretend.rs \ No newline at end of file diff --git a/precompiled/serde_derive/src/ser.rs b/precompiled/serde_derive/src/ser.rs new file mode 120000 index 000000000..d004f10e5 --- /dev/null +++ b/precompiled/serde_derive/src/ser.rs @@ -0,0 +1 @@ +../../../serde_derive/src/ser.rs \ No newline at end of file diff --git a/precompiled/serde_derive/src/this.rs b/precompiled/serde_derive/src/this.rs new file mode 120000 index 000000000..af244455b --- /dev/null +++ b/precompiled/serde_derive/src/this.rs @@ -0,0 +1 @@ +../../../serde_derive/src/this.rs \ No newline at end of file diff --git a/precompiled/serde_derive/src/try.rs b/precompiled/serde_derive/src/try.rs new file mode 120000 index 000000000..bdcbf563e --- /dev/null +++ b/precompiled/serde_derive/src/try.rs @@ -0,0 +1 @@ +../../../serde_derive/src/try.rs \ No newline at end of file