Skip to content

Commit d1ef4b5

Browse files
JakobDegenfacebook-github-bot
authored andcommitted
module: Make generics work for attrs
Summary: Like it says in the title Mostly pretty boring, but requires knowing not to pass late bound lifetimes Reviewed By: Nero5023 Differential Revision: D73726010 fbshipit-source-id: 5323dd03a6388634e7b20df3c94ab9975e781f23
1 parent b80ede1 commit d1ef4b5

File tree

4 files changed

+108
-16
lines changed

4 files changed

+108
-16
lines changed

starlark/src/tests/derive/module/generic.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,63 @@
1515
* limitations under the License.
1616
*/
1717

18+
use std::marker::PhantomData;
19+
1820
use starlark_derive::starlark_module;
1921

2022
use crate as starlark;
2123
use crate::assert::Assert;
2224
use crate::environment::GlobalsBuilder;
25+
use crate::environment::MethodsBuilder;
26+
use crate::typing::Ty;
27+
use crate::values::AllocValue;
28+
use crate::values::Heap;
29+
use crate::values::Value;
30+
use crate::values::none::NoneType;
31+
use crate::values::type_repr::StarlarkTypeRepr;
2332

2433
#[starlark_module]
25-
fn generic_builder<T: Default, U>(globals: &mut GlobalsBuilder)
34+
fn global_builder<T: Default, U>(globals: &mut GlobalsBuilder)
2635
where
2736
U: std::fmt::Display + Default,
2837
{
2938
const MY_STR: &str = &U::default().to_string();
3039
}
3140

41+
struct CustomNone<T>(PhantomData<T>);
42+
43+
impl<T> StarlarkTypeRepr for CustomNone<T> {
44+
type Canonical = NoneType;
45+
46+
/// The representation of a type that a user would use verbatim in starlark type annotations
47+
fn starlark_type_repr() -> Ty {
48+
NoneType::starlark_type_repr()
49+
}
50+
}
51+
52+
impl<'v, T> AllocValue<'v> for CustomNone<T> {
53+
fn alloc_value(self, _heap: &'v Heap) -> Value<'v> {
54+
Value::new_none()
55+
}
56+
}
57+
58+
#[starlark_module]
59+
fn method_builder<T: Default, U>(globals: &mut MethodsBuilder)
60+
where
61+
U: std::fmt::Display + Default,
62+
{
63+
// Just check that this compiles
64+
#[starlark(attribute)]
65+
fn test_attribute(this: u32) -> starlark::Result<CustomNone<T>> {
66+
let _u = U::default().to_string();
67+
let _t = T::default();
68+
Ok(CustomNone(PhantomData))
69+
}
70+
}
71+
3272
#[test]
3373
fn test_generic_builder() {
3474
let mut a = Assert::new();
35-
a.globals_add(generic_builder::<u8, u8>);
75+
a.globals_add(global_builder::<u8, u8>);
3676
a.eq("\"0\"", "MY_STR");
3777
}

starlark_derive/src/module/parse.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use syn::spanned::Spanned;
3636

3737
use crate::module::parse::fun::parse_fun;
3838
use crate::module::typ::StarConst;
39+
use crate::module::typ::StarGenerics;
3940
use crate::module::typ::StarModule;
4041
use crate::module::typ::StarStmt;
4142
use crate::module::util::is_type_name;
@@ -107,6 +108,7 @@ pub(crate) fn parse(mut input: ItemFn) -> syn::Result<StarModule> {
107108
.collect::<syn::Result<_>>()?;
108109

109110
Ok(StarModule {
111+
generics: StarGenerics::new(input.sig.generics.clone()),
110112
module_kind,
111113
input,
112114
docstring: module_docstring,

starlark_derive/src/module/render.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use crate::module::typ::SpecialParam;
3030
use crate::module::typ::StarAttr;
3131
use crate::module::typ::StarConst;
3232
use crate::module::typ::StarFun;
33+
use crate::module::typ::StarGenerics;
3334
use crate::module::typ::StarModule;
3435
use crate::module::typ::StarStmt;
3536
use crate::module::util::ident_string;
@@ -44,11 +45,12 @@ fn render_impl(x: StarModule) -> syn::Result<syn::ItemFn> {
4445
docstring,
4546
stmts,
4647
module_kind,
48+
generics,
4749
} = x;
4850
let statics = format_ident!("{}", module_kind.statics_type_name());
4951
let stmts: Vec<_> = stmts
5052
.into_iter()
51-
.map(render_stmt)
53+
.map(|s| render_stmt(s, &generics))
5254
.collect::<syn::Result<_>>()?;
5355
let set_docstring = docstring.map(|ds| quote!(globals_builder.set_docstring(#ds);));
5456

@@ -68,22 +70,21 @@ fn render_impl(x: StarModule) -> syn::Result<syn::ItemFn> {
6870
}
6971
},
7072
};
71-
let turbo = input.sig.generics.split_for_impl().1;
72-
let turbo = turbo.as_turbofish();
73+
let turbofish = generics.turbofish();
7374
input.block = syn::parse_quote! {
7475
{
7576
#inner_fn
7677
static RES: starlark::environment::#statics = starlark::environment::#statics::new();
77-
RES.populate(build #turbo, globals_builder);
78+
RES.populate(build #turbofish, globals_builder);
7879
}
7980
};
8081
Ok(input)
8182
}
8283

83-
fn render_stmt(x: StarStmt) -> syn::Result<syn::Stmt> {
84+
fn render_stmt(x: StarStmt, generics: &StarGenerics) -> syn::Result<syn::Stmt> {
8485
match x {
8586
StarStmt::Const(x) => Ok(render_const(x)),
86-
StarStmt::Attr(x) => Ok(render_attr(x)),
87+
StarStmt::Attr(x) => Ok(render_attr(x, generics)),
8788
StarStmt::Fun(x) => render_fun(x),
8889
}
8990
}
@@ -96,7 +97,7 @@ fn render_const(x: StarConst) -> syn::Stmt {
9697
}
9798
}
9899

99-
fn render_attr(x: StarAttr) -> syn::Stmt {
100+
fn render_attr(x: StarAttr, generics: &StarGenerics) -> syn::Stmt {
100101
let StarAttr {
101102
name,
102103
this,
@@ -114,6 +115,10 @@ fn render_attr(x: StarAttr) -> syn::Stmt {
114115
None => render_none(),
115116
};
116117

118+
let generic_decls = generics.decls();
119+
let where_clause = generics.where_clause();
120+
let turbofish = generics.turbofish();
121+
117122
let let_heap = if let Some(SpecialParam {
118123
param: SimpleParam { ident, ty, .. },
119124
}) = heap
@@ -132,24 +137,28 @@ fn render_attr(x: StarAttr) -> syn::Stmt {
132137
#( #attrs )*
133138
#[allow(non_snake_case)] // Starlark doesn't have this convention
134139
#[allow(unused_variables)]
135-
fn #name_inner<'v>(
140+
fn #name_inner #generic_decls(
136141
this: #this_return_type,
137142
__heap: &'v starlark::values::Heap,
138-
) -> #return_type {
143+
) -> #return_type
144+
#where_clause
145+
{
139146
#let_heap
140147
#body
141148
}
142149
};
143150

144151
let outer: syn::ItemFn = syn::parse_quote! {
145152
#[allow(non_snake_case)]
146-
fn #name<'v>(
153+
fn #name #generic_decls(
147154
_ignored: std::option::Option<starlark::values::FrozenValue>,
148155
#this_value: starlark::values::Value<'v>,
149156
heap: &'v starlark::values::Heap,
150-
) -> starlark::Result<starlark::values::Value<'v>> {
157+
) -> starlark::Result<starlark::values::Value<'v>>
158+
#where_clause
159+
{
151160
#unpack
152-
Ok(heap.alloc(#name_inner(this, heap)?))
161+
Ok(heap.alloc(#name_inner #turbofish(this, heap)?))
153162
}
154163
};
155164

@@ -162,8 +171,8 @@ fn render_attr(x: StarAttr) -> syn::Stmt {
162171
#name_str,
163172
#speculative_exec_safe,
164173
#docstring,
165-
starlark::values::type_repr::type_repr_from_attr_impl(#name_inner),
166-
#name
174+
starlark::values::type_repr::type_repr_from_attr_impl(#name_inner #turbofish),
175+
#name #turbofish
167176
);
168177
}
169178
}

starlark_derive/src/module/typ.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,47 @@ pub(crate) struct StarModule {
3737
pub(crate) input: syn::ItemFn,
3838
pub(crate) docstring: Option<String>,
3939
pub(crate) stmts: Vec<StarStmt>,
40+
pub(crate) generics: StarGenerics,
41+
}
42+
43+
/// The generics the user provided on the starlark module
44+
#[derive(Debug)]
45+
pub(crate) struct StarGenerics {
46+
/// The user provided generics
47+
///
48+
/// This is what we use to instantiate functions we define with their generics, because the `'v`
49+
/// lifetime is late bound and therefore never passed as an explicit type parameter
50+
generics: syn::Generics,
51+
/// The user provided generics with a `'v` param added in front.
52+
///
53+
/// This is what we use to *declare* generics on functions that we define, because all of our
54+
/// functions want a `'v` param
55+
generics_with_v: syn::Generics,
56+
}
57+
58+
impl StarGenerics {
59+
pub(crate) fn new(g: syn::Generics) -> Self {
60+
let mut with_v = g.clone();
61+
with_v
62+
.params
63+
.insert(0, syn::GenericParam::Lifetime(syn::parse_quote! { 'v }));
64+
Self {
65+
generics: g,
66+
generics_with_v: with_v,
67+
}
68+
}
69+
70+
pub(crate) fn decls(&self) -> syn::ImplGenerics<'_> {
71+
self.generics_with_v.split_for_impl().0
72+
}
73+
74+
pub(crate) fn turbofish(&self) -> syn::Turbofish<'_> {
75+
self.generics.split_for_impl().1.as_turbofish()
76+
}
77+
78+
pub(crate) fn where_clause(&self) -> Option<&syn::WhereClause> {
79+
self.generics_with_v.split_for_impl().2
80+
}
4081
}
4182

4283
#[allow(clippy::large_enum_variant)]

0 commit comments

Comments
 (0)