Skip to content

Commit b26800c

Browse files
JakobDegenfacebook-github-bot
authored andcommitted
module: Make functions and methods work with generics
Summary: Like it says in the title, at this point just thread it through Unfortunately, this whole thing with generics is not actually going to work out. I wanted to use this for something like an alternative to the `internal_provider` macro (though not on providers, but enums instead) - currently, we use a macro to generate a `#[starlark_module]`, but I instead wanted to write something like ``` impl<P: InternalProvider> StarlarkValue<'v> for P ``` and then generate impls of `InternalProvider`. The problem is that even with the things so far done, it turns out that getting the lifetimes right across the type and trait definitions, `StarlarkValue` impl, and `MethodsBuilder`, is really, really hard. I got to the point where I was pretty close, but am now calling it because: 1. The amount of type system knowledge needed to be able to maintain the result is extreme 2. Some of the patterns I was using were getting pretty close to known unsoundnesses in the language, specifically #84366 This is not without hope though - actually doing the work of getting rid of the `Value`/`FrozenValue` impls would make this a lot more feasible because we wouldn't have the `V` flying around too, saving a bunch of HRTBs and such things I'm submitting the stack anyway since, on balance, I think it's probably better to have than not Reviewed By: Nero5023 Differential Revision: D73726001 fbshipit-source-id: 014678dbdfc18a2ef5404cd7bc5548521a2a104a
1 parent 7d35881 commit b26800c

File tree

3 files changed

+60
-27
lines changed

3 files changed

+60
-27
lines changed

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,24 @@ where
6969
}
7070
}
7171

72+
#[starlark_module]
73+
fn global_builder_for_func<T: Default, U>(globals: &mut GlobalsBuilder)
74+
where
75+
U: std::fmt::Display + Default,
76+
{
77+
fn make_my_str() -> starlark::Result<String> {
78+
let _t = T::default();
79+
Ok(U::default().to_string())
80+
}
81+
}
82+
7283
#[test]
7384
fn test_generic_builder() {
7485
let mut a = Assert::new();
75-
a.globals_add(global_builder::<u8, u8>);
86+
a.globals_add(|g| {
87+
global_builder::<u8, u8>(g);
88+
global_builder_for_func::<u8, u8>(g);
89+
});
7690
a.eq("\"0\"", "MY_STR");
91+
a.eq("\"0\"", "make_my_str()");
7792
}

starlark_derive/src/module/render.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ fn render_stmt(x: StarStmt, generics: &StarGenerics) -> syn::Result<syn::Stmt> {
8282
match x {
8383
StarStmt::Const(x) => Ok(render_const(x)),
8484
StarStmt::Attr(x) => Ok(render_attr(x, generics)),
85-
StarStmt::Fun(x) => render_fun(x),
85+
StarStmt::Fun(x) => render_fun(x, generics),
8686
}
8787
}
8888

starlark_derive/src/module/render/fun.rs

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::module::typ::StarArg;
3333
use crate::module::typ::StarArgSource;
3434
use crate::module::typ::StarFun;
3535
use crate::module::typ::StarFunSource;
36+
use crate::module::typ::StarGenerics;
3637
use crate::module::util::ident_string;
3738

3839
impl StarFun {
@@ -143,11 +144,13 @@ impl StarFun {
143144
}
144145

145146
/// Globals builder call to register the function.
146-
fn builder_set(&self) -> syn::Result<syn::Stmt> {
147+
fn builder_set(&self, generics: &StarGenerics) -> syn::Result<syn::Stmt> {
147148
let name_str = self.name_str();
148-
let components = render_native_callable_components(self)?;
149+
let components = render_native_callable_components(self, generics)?;
149150
let param_spec = render_signature(self)?;
150151

152+
let turbofish = generics.turbofish();
153+
151154
let special_builtin_function = self.special_builtin_function_expr();
152155

153156
if self.is_method() {
@@ -169,7 +172,7 @@ impl StarFun {
169172
#name_str,
170173
#components,
171174
#param_spec,
172-
__starlark_invoke_outer,
175+
__starlark_invoke_outer #turbofish,
173176
);
174177
})
175178
} else {
@@ -184,20 +187,24 @@ impl StarFun {
184187
#as_type,
185188
#ty_custom,
186189
#special_builtin_function,
187-
__starlark_invoke_outer,
190+
__starlark_invoke_outer #turbofish,
188191
);
189192
})
190193
}
191194
}
192195
}
193196

194-
pub(crate) fn render_fun(x: StarFun) -> syn::Result<syn::Stmt> {
197+
pub(crate) fn render_fun(x: StarFun, generics: &StarGenerics) -> syn::Result<syn::Stmt> {
198+
let generic_decls = generics.decls();
199+
let where_clause = generics.where_clause();
200+
let turbofish = generics.turbofish();
201+
195202
let (this_outer_param, this_inner_param, this_prepare, this_arg) = x.this_param_arg();
196203
let (eval_param, eval_arg) = x.eval_param_arg();
197204
let (heap_param, heap_arg) = x.heap_param_arg();
198205
let (binding_params, prepare, binding_args) = x.binding_params_arg()?;
199206

200-
let builder_set = x.builder_set()?;
207+
let builder_set = x.builder_set(generics)?;
201208

202209
let StarFun {
203210
attrs,
@@ -228,9 +235,9 @@ pub(crate) fn render_fun(x: StarFun) -> syn::Result<syn::Stmt> {
228235
// so the warning would be precise.
229236
#[allow(clippy::extra_unused_lifetimes)]
230237
#( #attrs )*
231-
fn __starlark_invoke_impl<'v>(
238+
fn __starlark_invoke_impl #generic_decls(
232239
#( #invoke_params, )*
233-
) -> #return_type {
240+
) -> #return_type #where_clause {
234241
#body
235242
}
236243
};
@@ -242,29 +249,29 @@ pub(crate) fn render_fun(x: StarFun) -> syn::Result<syn::Stmt> {
242249
// https://github.com/rust-lang/rfcs/pull/2515
243250
// Until then we use this hack as a workaround.
244251
#[allow(dead_code)] // Function is not used when return type is specified explicitly.
245-
fn __starlark_return_type_starlark_type_repr() -> starlark::typing::Ty {
252+
fn __starlark_return_type_starlark_type_repr #generic_decls () -> starlark::typing::Ty #where_clause {
246253
fn get_impl<'v, T: starlark::values::AllocValue<'v>, E>(
247254
_f: fn(
248255
#( #param_types, )*
249256
) -> std::result::Result<T, E>,
250257
) -> starlark::typing::Ty {
251258
<T as starlark::values::type_repr::StarlarkTypeRepr>::starlark_type_repr()
252259
}
253-
get_impl(__starlark_invoke_impl)
260+
get_impl(__starlark_invoke_impl #turbofish)
254261
}
255262
};
256263

257264
let impl_invoke_outer: syn::ItemFn = syn::parse_quote! {
258265
#[allow(non_snake_case)] // Starlark doesn't have this convention
259-
fn __starlark_invoke_outer<'v>(
266+
fn __starlark_invoke_outer #generic_decls (
260267
eval: &mut starlark::eval::Evaluator<'v, '_, '_>,
261268
#(#this_outer_param,)*
262269
signature: &starlark::eval::ParametersSpec<starlark::values::FrozenValue>,
263270
parameters: &starlark::eval::Arguments<'v, '_>,
264-
) -> starlark::Result<starlark::values::Value<'v>> {
271+
) -> starlark::Result<starlark::values::Value<'v>> #where_clause {
265272
#this_prepare
266273
#prepare
267-
match __starlark_invoke_impl(#( #invoke_args, )*) {
274+
match __starlark_invoke_impl #turbofish (#( #invoke_args, )*) {
268275
Ok(v) => Ok(eval.heap().alloc(v)),
269276
Err(e) => Err(starlark::__derive_refs::invoke_macro_error::InvokeMacroError::into_starlark_error(e)),
270277
}
@@ -486,20 +493,26 @@ fn render_option(expr: Option<syn::Expr>) -> syn::Expr {
486493
}
487494
}
488495

489-
fn render_starlark_type(typ: &syn::Type) -> syn::Expr {
496+
fn render_starlark_type(typ: &syn::Type, generics: &StarGenerics) -> syn::Expr {
497+
let decls = generics.decls();
498+
let turbofish = generics.turbofish();
499+
let where_clause = generics.where_clause();
490500
syn::parse_quote! {
491501
{
492502
#[allow(clippy::extra_unused_lifetimes)]
493-
fn get_type_string<'v>() -> starlark::typing::Ty {
503+
fn get_type_string #decls () -> starlark::typing::Ty #where_clause {
494504
<#typ as starlark::values::type_repr::StarlarkTypeRepr>::starlark_type_repr()
495505
}
496-
get_type_string()
506+
get_type_string #turbofish ()
497507
}
498508
}
499509
}
500510

501-
fn render_regular_native_callable_param(arg: &StarArg) -> syn::Result<syn::Expr> {
502-
let ty = render_starlark_type(arg.without_option());
511+
fn render_regular_native_callable_param(
512+
arg: &StarArg,
513+
generics: &StarGenerics,
514+
) -> syn::Result<syn::Expr> {
515+
let ty = render_starlark_type(arg.without_option(), generics);
503516
let name_str = ident_string(&arg.param.ident);
504517
let required: syn::Expr = match (&arg.default, arg.is_option()) {
505518
(Some(_), true) => {
@@ -541,7 +554,10 @@ fn render_regular_native_callable_param(arg: &StarArg) -> syn::Result<syn::Expr>
541554
})
542555
}
543556

544-
fn render_native_callable_components(x: &StarFun) -> syn::Result<TokenStream> {
557+
fn render_native_callable_components(
558+
x: &StarFun,
559+
generics: &StarGenerics,
560+
) -> syn::Result<TokenStream> {
545561
let docs = match x.docstring.as_ref() {
546562
Some(d) => quote!(Some(#d)),
547563
None => quote!(None),
@@ -565,28 +581,28 @@ fn render_native_callable_components(x: &StarFun) -> syn::Result<TokenStream> {
565581
let pos_only: Vec<syn::Expr> = pos_only
566582
.iter()
567583
.copied()
568-
.map(render_regular_native_callable_param)
584+
.map(|a| render_regular_native_callable_param(a, generics))
569585
.collect::<syn::Result<Vec<_>>>()?;
570586
let pos_or_named: Vec<syn::Expr> = pos_or_named
571587
.iter()
572588
.copied()
573-
.map(render_regular_native_callable_param)
589+
.map(|a| render_regular_native_callable_param(a, generics))
574590
.collect::<syn::Result<Vec<_>>>()?;
575591
let args: Option<syn::Expr> = args.map(|arg| {
576592
let name_str = ident_string(&arg.param.ident);
577-
let ty = render_starlark_type(&arg.param.ty);
593+
let ty = render_starlark_type(&arg.param.ty, generics);
578594
syn::parse_quote! {
579595
starlark::__derive_refs::param_spec::NativeCallableParam::args(#name_str, #ty)
580596
}
581597
});
582598
let named_only: Vec<syn::Expr> = named_only
583599
.iter()
584600
.copied()
585-
.map(render_regular_native_callable_param)
601+
.map(|a| render_regular_native_callable_param(a, generics))
586602
.collect::<syn::Result<Vec<_>>>()?;
587603
let kwargs: Option<syn::Expr> = kwargs.map(|arg| {
588604
let name_str = ident_string(&arg.param.ident);
589-
let ty = render_starlark_type(&arg.param.ty);
605+
let ty = render_starlark_type(&arg.param.ty, generics);
590606
syn::parse_quote! {
591607
starlark::__derive_refs::param_spec::NativeCallableParam::kwargs(#name_str, #ty)
592608
}
@@ -606,6 +622,8 @@ fn render_native_callable_components(x: &StarFun) -> syn::Result<TokenStream> {
606622
}
607623
};
608624

625+
let turbofish = generics.turbofish();
626+
609627
let speculative_exec_safe = x.speculative_exec_safe;
610628
Ok(quote!(
611629
{
@@ -614,7 +632,7 @@ fn render_native_callable_components(x: &StarFun) -> syn::Result<TokenStream> {
614632
speculative_exec_safe: #speculative_exec_safe,
615633
rust_docstring: #docs,
616634
param_spec,
617-
return_type: __starlark_return_type_starlark_type_repr(),
635+
return_type: __starlark_return_type_starlark_type_repr #turbofish(),
618636
}
619637
}
620638
))

0 commit comments

Comments
 (0)