Skip to content

Commit 02c3cc2

Browse files
authored
Merge pull request #29 from alecmocatta/stable
Building on stable
2 parents b9be939 + d38632c commit 02c3cc2

File tree

8 files changed

+488
-79
lines changed

8 files changed

+488
-79
lines changed

Cargo.toml

+11-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[package]
44
name = "serde_closure"
5-
version = "0.2.13"
5+
version = "0.2.14"
66
license = "MIT OR Apache-2.0"
77
authors = ["Alec Mocatta <[email protected]>"]
88
categories = ["development-tools","encoding","rust-patterns","network-programming"]
@@ -14,18 +14,26 @@ This library provides macros that wrap closures to make them serializable and de
1414
"""
1515
repository = "https://github.com/alecmocatta/serde_closure"
1616
homepage = "https://github.com/alecmocatta/serde_closure"
17-
documentation = "https://docs.rs/serde_closure/0.2.13"
17+
documentation = "https://docs.rs/serde_closure"
1818
readme = "README.md"
1919
edition = "2018"
2020

2121
[badges]
2222
azure-devops = { project = "alecmocatta/serde_closure", pipeline = "tests", build = "10" }
2323
maintenance = { status = "actively-developed" }
2424

25+
[features]
26+
default = ["nightly"]
27+
nightly = []
28+
2529
[dependencies]
26-
serde_closure_derive = { version = "=0.2.13", path = "serde_closure_derive" }
30+
serde_closure_derive = { version = "=0.2.14", path = "serde_closure_derive" }
2731
serde = { version = "1.0", features = ["derive"] }
2832

2933
[dev-dependencies]
3034
serde_json = "1.0"
3135
bincode = "1.0"
36+
37+
[[test]]
38+
name = "test"
39+
required-features = ["nightly"]

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![MIT / Apache 2.0 licensed](https://img.shields.io/crates/l/serde_closure.svg?maxAge=2592000)](#License)
55
[![Build Status](https://dev.azure.com/alecmocatta/serde_closure/_apis/build/status/tests?branchName=master)](https://dev.azure.com/alecmocatta/serde_closure/_build?definitionId=10)
66

7-
[📖 Docs](https://docs.rs/serde_closure/0.2.13/serde_closure/) | [💬 Chat](https://constellation.zulipchat.com/#narrow/stream/213236-subprojects)
7+
[📖 Docs](https://docs.rs/serde_closure) | [💬 Chat](https://constellation.zulipchat.com/#narrow/stream/213236-subprojects)
88

99
Serializable and debuggable closures.
1010

@@ -30,9 +30,9 @@ requires nightly Rust for the `unboxed_closures` and `fn_traits` features (rust
3030
issue [#29625](https://github.com/rust-lang/rust/issues/29625)).
3131

3232
* There are three macros,
33-
[`FnOnce`](https://docs.rs/serde_closure/0.2.13/serde_closure/macro.FnOnce.html),
34-
[`FnMut`](https://docs.rs/serde_closure/0.2.13/serde_closure/macro.FnMut.html)
35-
and [`Fn`](https://docs.rs/serde_closure/0.2.13/serde_closure/macro.Fn.html),
33+
[`FnOnce`](https://docs.rs/serde_closure/0.2/serde_closure/macro.FnOnce.html),
34+
[`FnMut`](https://docs.rs/serde_closure/0.2/serde_closure/macro.FnMut.html)
35+
and [`Fn`](https://docs.rs/serde_closure/0.2/serde_closure/macro.Fn.html),
3636
corresponding to the three types of Rust closure.
3737
* Wrap your closure with one of the macros and it will now implement `Copy`,
3838
`Clone`, `PartialEq`, `Eq`, `Hash`, `PartialOrd`, `Ord`, `Serialize`,

azure-pipelines.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ jobs:
1414
endpoint: alecmocatta
1515
default:
1616
rust_toolchain: nightly
17-
rust_lint_toolchain: nightly-2020-06-10
17+
rust_lint_toolchain: nightly-2020-07-09
1818
rust_flags: ''
19-
rust_features: ''
19+
rust_features_clippy: ';nightly'
20+
rust_features: 'nightly'
2021
rust_target_check: ''
2122
rust_target_build: ''
2223
rust_target_run: ''

serde_closure_derive/Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "serde_closure_derive"
3-
version = "0.2.13"
3+
version = "0.2.14"
44
license = "MIT OR Apache-2.0"
55
authors = ["Alec Mocatta <[email protected]>"]
66
categories = ["development-tools","encoding","rust-patterns","network-programming"]
@@ -14,7 +14,7 @@ See https://crates.io/crates/serde_closure for documentation.
1414
"""
1515
repository = "https://github.com/alecmocatta/serde_closure"
1616
homepage = "https://github.com/alecmocatta/serde_closure"
17-
documentation = "https://docs.rs/serde_closure/0.2.13"
17+
documentation = "https://docs.rs/serde_closure"
1818
edition = "2018"
1919

2020
[badges]
@@ -27,4 +27,4 @@ proc-macro = true
2727
[dependencies]
2828
proc-macro2 = { version = "1.0.1", default-features = false, features = ["span-locations"] }
2929
quote = { version = "1.0.2", default-features = false }
30-
syn = { version = "1.0.5", default-features = false, features = ["clone-impls", "full", "parsing", "printing", "proc-macro"] }
30+
syn = { version = "1.0.5", default-features = false, features = ["clone-impls", "full", "parsing", "printing", "proc-macro", "visit-mut"] }

serde_closure_derive/src/lib.rs

+118-38
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@
77
//! This library provides macros that wrap closures to make them serializable
88
//! and debuggable.
99
//!
10-
//! See [`serde_closure`](https://docs.rs/serde_closure/) for
10+
//! See [`serde_closure`](https://docs.rs/serde_closure) for
1111
//! documentation.
1212
13-
#![doc(html_root_url = "https://docs.rs/serde_closure_derive/0.2.13")]
14-
#![feature(proc_macro_diagnostic)]
13+
#![doc(html_root_url = "https://docs.rs/serde_closure_derive/0.2.14")]
1514

1615
use proc_macro2::{Span, TokenStream};
1716
use quote::{quote, ToTokens};
1817
use std::{collections::HashSet, iter, iter::successors, mem::take, str};
1918
use syn::{
20-
parse::{Parse, ParseStream}, parse2, spanned::Spanned, token::Bracket, Arm, Block, Error, Expr, ExprArray, ExprAssign, ExprAssignOp, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBox, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, Ident, Local, Member, Pat, PatBox, PatIdent, PatReference, PatSlice, PatTuple, PatTupleStruct, PatType, Path, PathArguments, PathSegment, Stmt, Type, TypeInfer, TypeReference, UnOp
19+
parse::{Parse, ParseStream}, parse2, parse_macro_input, token::Bracket, visit_mut::{self, VisitMut}, Arm, AttributeArgs, Block, Error, Expr, ExprArray, ExprAssign, ExprAssignOp, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBox, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, Ident, Item, Lifetime, LifetimeDef, Local, Member, Pat, PatBox, PatIdent, PatReference, PatSlice, PatTuple, PatTupleStruct, PatType, Path, PathArguments, PathSegment, ReturnType, Stmt, TraitBound, Type, TypeInfer, TypeReference, TypeTuple, UnOp
2120
};
2221

2322
#[proc_macro]
@@ -45,6 +44,79 @@ pub fn FnOnce(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
4544
.into()
4645
}
4746

47+
#[proc_macro_attribute]
48+
pub fn generalize(
49+
attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
50+
) -> proc_macro::TokenStream {
51+
let args: AttributeArgs = parse_macro_input!(attr);
52+
assert_eq!(args.len(), 0);
53+
let mut item = match syn::parse::<Item>(item) {
54+
Err(err) => return err.to_compile_error().into(),
55+
Ok(item) => item,
56+
};
57+
Generalizer.visit_item_mut(&mut item);
58+
item.into_token_stream().into()
59+
}
60+
61+
struct Generalizer;
62+
63+
impl VisitMut for Generalizer {
64+
fn visit_trait_bound_mut(&mut self, i: &mut TraitBound) {
65+
if let PathSegment {
66+
ident,
67+
arguments: PathArguments::Parenthesized(args),
68+
} = i.path.segments.last().unwrap()
69+
{
70+
if ident == "FnOnce" || ident == "FnMut" || ident == "Fn" {
71+
let mut lifetimes = 0;
72+
let mut inputs = args.inputs.clone();
73+
for input in &mut inputs {
74+
match input {
75+
Type::Reference(TypeReference { lifetime, .. }) if lifetime.is_none() => {
76+
*lifetime = Some(Lifetime::new(
77+
&format!(
78+
"'__serde_closure_{}",
79+
bijective_base(lifetimes, 26, alpha_lower)
80+
),
81+
Span::call_site(),
82+
));
83+
lifetimes += 1;
84+
}
85+
_ => (),
86+
}
87+
}
88+
if !inputs.empty_or_trailing() {
89+
inputs.push_punct(Default::default());
90+
}
91+
let output = match &args.output {
92+
ReturnType::Type(_, type_) => (&**type_).clone(),
93+
ReturnType::Default => Type::Tuple(TypeTuple {
94+
paren_token: Default::default(),
95+
elems: Default::default(),
96+
}),
97+
};
98+
let empty = syn::parse2(quote! {for <>}).unwrap();
99+
i.lifetimes = Some(i.lifetimes.clone().unwrap_or(empty));
100+
i.lifetimes
101+
.as_mut()
102+
.unwrap()
103+
.lifetimes
104+
.extend((0..lifetimes).map(|i| {
105+
LifetimeDef::new(Lifetime::new(
106+
&format!("'__serde_closure_{}", bijective_base(i, 26, alpha_lower)),
107+
Span::call_site(),
108+
))
109+
}));
110+
i.path = syn::parse2(
111+
quote! { ::serde_closure::traits::#ident<(#inputs), Output = #output> },
112+
)
113+
.unwrap();
114+
}
115+
}
116+
visit_mut::visit_trait_bound_mut(self, i)
117+
}
118+
}
119+
48120
struct Closure {
49121
env: Option<ExprArray>,
50122
closure: ExprClosure,
@@ -145,12 +217,14 @@ fn impl_fn_once(closure: Closure, kind: Kind) -> Result<TokenStream, Error> {
145217
pat => (*pat).clone(),
146218
});
147219
let input_types = closure.inputs.iter().map(pat_to_type);
148-
// let line_number = format!(" {}:{}:{}", closure.span().source_file(), closure.span().start().line, closure.span().start().column);
149220

150221
let type_params = &(0..env_variables.len())
151222
.map(|i| {
152223
Ident::new(
153-
&format!("__SERDE_CLOSURE_{}", bijective_base(i as u64, 26, alpha)),
224+
&format!(
225+
"__SERDE_CLOSURE_{}",
226+
bijective_base(i as u64, 26, alpha_upper)
227+
),
154228
span,
155229
)
156230
})
@@ -268,7 +342,7 @@ fn impl_fn_once(closure: Closure, kind: Kind) -> Result<TokenStream, Error> {
268342
Ok(quote! {
269343
{
270344
mod #impls_name {
271-
#![allow(warnings, unsafe_code)]
345+
#![allow(warnings)]
272346
use ::serde_closure::{
273347
internal::{self, is_phantom, to_phantom},
274348
structs,
@@ -317,11 +391,16 @@ fn impl_fn_once(closure: Closure, kind: Kind) -> Result<TokenStream, Error> {
317391
}
318392
impl<#(#type_params,)* F> #name<#(#type_params,)* F> {
319393
fn f(&self) -> F {
320-
// This is safe as an F has already been materialized (so we
321-
// know it isn't uninhabited), it's Copy, it's not Drop, and
322-
// its size is zero.
394+
// This is safe as an F has already been materialized (so we know it isn't
395+
// uninhabited), it's Copy, it's not Drop, its size is zero. Most
396+
// importantly, thanks to the `use Type` static assertion, we're guaranteed
397+
// not to be capturing anything other than `env_types_name`. Related:
398+
// https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506
323399
unsafe { MaybeUninit::uninit().assume_init() }
324400
}
401+
// Strip F due to https://play.rust-lang.org/?edition=2018&gist=a2936c8b5abb13357d97bf835203b153
402+
// Another struct could hold env vars and avoid these unsafes but that slightly
403+
// increases complexity?
325404
fn strip_f(self) -> #name<#(#type_params,)* ()> {
326405
#name {
327406
#( #env_variables: self.#env_variables, )*
@@ -461,7 +540,7 @@ fn impl_fn_once(closure: Closure, kind: Kind) -> Result<TokenStream, Error> {
461540
#body
462541
};
463542

464-
#[allow(warnings, unsafe_code)]
543+
#[allow(warnings)]
465544
{
466545
if false {
467546
let _ = closure(#ret_ref, loop {});
@@ -742,14 +821,8 @@ impl<'a> State<'a> {
742821
|| is_func || has_path_arguments)
743822
{
744823
let _ = self.env_variables.insert(ident.clone());
745-
let mut a = if !self.env_struct {
746-
Expr::Path(ExprPath {
747-
attrs: attrs.clone(),
748-
qself: None,
749-
path: path.clone(),
750-
})
751-
} else {
752-
Expr::Field(ExprField {
824+
if self.env_struct {
825+
let mut a = Expr::Field(ExprField {
753826
attrs: vec![],
754827
base: Box::new(Expr::Path(ExprPath {
755828
attrs: attrs.clone(),
@@ -765,20 +838,20 @@ impl<'a> State<'a> {
765838
})),
766839
dot_token: Default::default(),
767840
member: Member::Named(ident.clone()),
768-
})
769-
};
770-
if self.deref {
771-
a = Expr::Unary(ExprUnary {
841+
});
842+
if self.deref {
843+
a = Expr::Unary(ExprUnary {
844+
attrs: vec![],
845+
op: UnOp::Deref(Default::default()),
846+
expr: Box::new(a),
847+
});
848+
}
849+
*expr = Expr::Paren(ExprParen {
772850
attrs: vec![],
773-
op: UnOp::Deref(Default::default()),
851+
paren_token: Default::default(),
774852
expr: Box::new(a),
775853
});
776854
}
777-
*expr = Expr::Paren(ExprParen {
778-
attrs: vec![],
779-
paren_token: Default::default(),
780-
expr: Box::new(a),
781-
});
782855
} else {
783856
let _ = self.not_env_variables.insert(ident.clone());
784857
}
@@ -817,10 +890,12 @@ impl<'a> State<'a> {
817890
};
818891
mac.tokens = expr.args.to_token_stream();
819892
} else {
820-
mac.span()
821-
.unwrap()
822-
.warning("See https://github.com/alecmocatta/serde_closure/issues/16")
823-
.emit()
893+
panic!("See https://github.com/alecmocatta/serde_closure/issues/16");
894+
// proc_macro_diagnostic: https://github.com/rust-lang/rust/issues/54140
895+
// mac.span()
896+
// .unwrap()
897+
// .warning("See https://github.com/alecmocatta/serde_closure/issues/16")
898+
// .emit()
824899
}
825900
}
826901
_ => (),
@@ -829,10 +904,15 @@ impl<'a> State<'a> {
829904
}
830905

831906
#[inline(always)]
832-
fn alpha(u: u8) -> u8 {
907+
fn alpha_upper(u: u8) -> u8 {
833908
assert!(u < 26);
834909
u + b'A'
835910
}
911+
#[inline(always)]
912+
fn alpha_lower(u: u8) -> u8 {
913+
assert!(u < 26);
914+
u + b'a'
915+
}
836916
const BUF_SIZE: usize = 64; // u64::max_value() in base 2
837917
fn bijective_base(n: u64, base: u64, digits: impl Fn(u8) -> u8) -> String {
838918
let mut buffer = [0_u8; BUF_SIZE];
@@ -855,18 +935,18 @@ fn bijective_base(n: u64, base: u64, digits: impl Fn(u8) -> u8) -> String {
855935
#[test]
856936
fn bijective_base_test() {
857937
for i in 0..=26 + 26 * 26 + 26 * 26 * 26 {
858-
let _ = bijective_base(i, 26, alpha);
938+
let _ = bijective_base(i, 26, alpha_upper);
859939
}
860940
assert_eq!(
861-
bijective_base(26 + 26 * 26 + 26 * 26 * 26, 26, alpha),
941+
bijective_base(26 + 26 * 26 + 26 * 26 * 26, 26, alpha_upper),
862942
"AAAA"
863943
);
864944
assert_eq!(
865-
bijective_base(u64::max_value(), 3, alpha),
945+
bijective_base(u64::max_value(), 3, alpha_upper),
866946
"AAAABBABCBBABBAABCCAACCCACAACACCACCBAABBA"
867947
);
868948
assert_eq!(
869-
bijective_base(u64::max_value(), 2, alpha),
949+
bijective_base(u64::max_value(), 2, alpha_upper),
870950
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"
871951
);
872952
}

0 commit comments

Comments
 (0)