Skip to content

Commit 7eafdd7

Browse files
bors[bot]chitoyuu
andauthored
Merge #983
983: Handle generics in NativeScript derive and `#[methods]` r=chitoyuu a=chitoyuu The derive macros are now aware of both lifetime and type parameters. `'static` bounds are added to both kinds, which improves the error message shown when a user tries to put a lifetime into a NativeClass. This behavior is chosen because unlike type parameters, lifetime parameters aren't very useful in this context: only `'static` would be supported anyway, and any attempted usage is hightly likely to have originated from user error. Also added the `#[skip]` attribute for skipping fields in `FromVarargs`. This is useful mainly for skipping marker fields in generic code. Close #601 ## Error message The error message that appears when lifetime parameters are declared for `NativeClass` types now clearly indicate where the problem lies and what the expectations are: ``` error[E0478]: lifetime bound not satisfied --> tests/ui/derive_fail_lifetime.rs:3:10 | 3 | #[derive(NativeClass)] | ^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> tests/ui/derive_fail_lifetime.rs:4:12 | 4 | struct Foo<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime = note: this error originates in the derive macro `NativeClass` (in Nightly builds, run with -Z macro-backtrace for more info) ``` Compares this with the old error message: ``` error[E0726]: implicit elided lifetime not allowed here --> tests/ui/derive_fail_lifetime.rs:4:8 | 4 | struct Foo<'a> { | ^^^ expected lifetime parameter | = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | 4 | struct Foo<'_><'a> { | ++++ error[E0106]: missing lifetime specifier --> tests/ui/derive_fail_lifetime.rs:4:8 | 4 | struct Foo<'a> { | ^^^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 3 ~ #[derive(NativeClass<'a>)] 4 ~ struct Foo<'a><'a> { | error[E0277]: the trait bound `&gdnative::prelude::Node: OwnerArg<'_, Reference, Shared >` is not satisfied --> tests/ui/derive_fail_lifetime.rs:3:10 | 3 | #[derive(NativeClass)] | ^^^^^^^^^^^ the trait `OwnerArg<'_, Reference, Shared>` is not implemented for `&gdnative::prelude::Node` | = help: the following other types implement trait `OwnerArg<'a, T, Own>`: &'a T TRef<'a, T, Own> = note: this error originates in the derive macro `NativeClass` (in Nightly builds, r un with -Z macro-backtrace for more info) ``` Close #174 Co-authored-by: Chitose Yuuzaki <chitoyuu@potatoes.gay>
2 parents d0538f0 + 81e1bff commit 7eafdd7

File tree

12 files changed

+365
-52
lines changed

12 files changed

+365
-52
lines changed

gdnative-derive/src/extend_bounds.rs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::HashSet;
22

3+
use syn::spanned::Spanned;
34
use syn::visit::{self, Visit};
45
use syn::{Generics, Ident, Type, TypePath};
56

@@ -46,7 +47,12 @@ impl<'ast> BoundsVisitor<'ast> {
4647
}
4748
}
4849

49-
pub fn with_visitor<'ast, F>(generics: Generics, bound: &syn::Path, op: F) -> Generics
50+
pub fn with_visitor<'ast, F>(
51+
generics: Generics,
52+
bound: Option<&syn::Path>,
53+
lifetime: Option<&str>,
54+
op: F,
55+
) -> Generics
5056
where
5157
F: FnOnce(&mut BoundsVisitor<'ast>),
5258
{
@@ -55,28 +61,57 @@ where
5561
op(&mut visitor);
5662

5763
// where thing: is_trait
58-
fn where_predicate(thing: Type, is_trait: syn::Path) -> syn::WherePredicate {
64+
fn where_predicate(
65+
thing: Type,
66+
bound: Option<&syn::Path>,
67+
lifetime: Option<&str>,
68+
) -> syn::WherePredicate {
69+
let mut bounds = vec![];
70+
71+
if let Some(bound) = bound {
72+
bounds.push(syn::TypeParamBound::Trait(syn::TraitBound {
73+
paren_token: None,
74+
modifier: syn::TraitBoundModifier::None,
75+
lifetimes: None,
76+
path: bound.clone(),
77+
}));
78+
}
79+
80+
if let Some(lifetime) = lifetime {
81+
bounds.push(syn::TypeParamBound::Lifetime(syn::Lifetime::new(
82+
lifetime,
83+
thing.span(),
84+
)));
85+
}
86+
5987
syn::WherePredicate::Type(syn::PredicateType {
6088
lifetimes: None,
6189
bounded_ty: thing,
6290
colon_token: <Token![:]>::default(),
63-
bounds: vec![syn::TypeParamBound::Trait(syn::TraitBound {
64-
paren_token: None,
65-
modifier: syn::TraitBoundModifier::None,
66-
lifetimes: None,
67-
path: is_trait,
68-
})]
69-
.into_iter()
70-
.collect(),
91+
bounds: bounds.into_iter().collect(),
7192
})
7293
}
7394

7495
// place bounds on all used type parameters and associated types
75-
let new_predicates = visitor
96+
let mut new_predicates = visitor
7697
.used
7798
.into_iter()
7899
.cloned()
79-
.map(|bounded_ty| where_predicate(syn::Type::Path(bounded_ty), bound.clone()));
100+
.map(|bounded_ty| where_predicate(syn::Type::Path(bounded_ty), bound, lifetime))
101+
.collect::<Vec<_>>();
102+
103+
// Add lifetime bounds to all type parameters, regardless of usage, due to how
104+
// lifetimes for generic types are determined.
105+
new_predicates.extend(generics.type_params().map(|param| {
106+
where_predicate(
107+
syn::Type::Path(syn::TypePath {
108+
qself: None,
109+
path: param.ident.clone().into(),
110+
}),
111+
None,
112+
lifetime,
113+
)
114+
}));
80115

81116
let mut generics = generics;
82117
generics

gdnative-derive/src/lib.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extern crate quote;
88
use proc_macro::TokenStream;
99
use proc_macro2::TokenStream as TokenStream2;
1010
use quote::ToTokens;
11-
use syn::{AttributeArgs, DeriveInput, ItemFn, ItemImpl};
11+
use syn::{AttributeArgs, DeriveInput, ItemFn, ItemImpl, ItemType};
1212

1313
mod extend_bounds;
1414
mod methods;
@@ -455,6 +455,41 @@ pub fn derive_native_class(input: TokenStream) -> TokenStream {
455455
TokenStream::from(derived)
456456
}
457457

458+
/// Wires up necessary internals for a concrete monomorphization of a generic `NativeClass`,
459+
/// represented as a type alias, so it can be registered.
460+
///
461+
/// The monomorphized type will be available to Godot under the name of the type alias,
462+
/// once registered.
463+
///
464+
/// For more context, please refer to [gdnative::derive::NativeClass](NativeClass).
465+
///
466+
/// # Examples
467+
///
468+
/// ```ignore
469+
/// #[derive(NativeClass)]
470+
/// struct Pair<T> {
471+
/// a: T,
472+
/// b: T,
473+
/// }
474+
///
475+
/// #[gdnative::derive::monomorphize]
476+
/// type IntPair = Pair<i32>;
477+
///
478+
/// fn init(handle: InitHandle) {
479+
/// handle.add_class::<IntPair>();
480+
/// }
481+
/// ```
482+
#[proc_macro_attribute]
483+
pub fn monomorphize(meta: TokenStream, input: TokenStream) -> TokenStream {
484+
let args = parse_macro_input!(meta as AttributeArgs);
485+
let item_type = parse_macro_input!(input as ItemType);
486+
487+
match native_script::derive_monomorphize(args, item_type) {
488+
Ok(tokens) => tokens.into(),
489+
Err(err) => err.to_compile_error().into(),
490+
}
491+
}
492+
458493
#[proc_macro_derive(ToVariant, attributes(variant))]
459494
pub fn derive_to_variant(input: TokenStream) -> TokenStream {
460495
match variant::derive_to_variant(variant::ToVariantTrait::ToVariant, input) {
@@ -493,7 +528,21 @@ pub fn derive_from_variant(input: TokenStream) -> TokenStream {
493528
/// #[opt] baz: Option<Ref<Node>>,
494529
/// }
495530
/// ```
496-
#[proc_macro_derive(FromVarargs, attributes(opt))]
531+
///
532+
/// ## Field attributes
533+
///
534+
/// Attributes can be used to customize behavior of certain fields. All attributes are optional.
535+
///
536+
/// ### `#[opt]`
537+
///
538+
/// Marks an argument as optional. Required arguments must precede all optional arguments.
539+
/// Default values are obtained through `Default::default`.
540+
///
541+
/// ### `#[skip]`
542+
///
543+
/// Instructs the macro to skip a field. Skipped fields do not affect the signature of the
544+
/// argument list. They may be located anywhere. Values are obtained through `Default::default`.
545+
#[proc_macro_derive(FromVarargs, attributes(opt, skip))]
497546
pub fn derive_from_varargs(input: TokenStream) -> TokenStream {
498547
let derive_input = syn::parse_macro_input!(input as syn::DeriveInput);
499548
match varargs::derive_from_varargs(derive_input) {

gdnative-derive/src/methods.rs

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use syn::{spanned::Spanned, FnArg, ImplItem, ItemImpl, Pat, PatIdent, Signature, Type};
1+
use syn::{spanned::Spanned, FnArg, Generics, ImplItem, ItemImpl, Pat, PatIdent, Signature, Type};
22

33
use proc_macro2::TokenStream as TokenStream2;
44
use quote::{quote, ToTokens};
@@ -330,6 +330,7 @@ pub(crate) struct ExportArgs {
330330
pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 {
331331
let derived = crate::automatically_derived();
332332
let (impl_block, export) = impl_gdnative_expose(item_impl);
333+
let (impl_generics, _, where_clause) = impl_block.generics.split_for_impl();
333334

334335
let class_name = export.class_ty;
335336

@@ -390,7 +391,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 {
390391
quote_spanned!(ret_span=>)
391392
};
392393

393-
let method = wrap_method(&class_name, &export_method)
394+
let method = wrap_method(&class_name, &impl_block.generics, &export_method)
394395
.unwrap_or_else(|err| err.to_compile_error());
395396

396397
quote_spanned!( sig_span=>
@@ -410,7 +411,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 {
410411
#impl_block
411412

412413
#derived
413-
impl gdnative::export::NativeClassMethods for #class_name {
414+
impl #impl_generics gdnative::export::NativeClassMethods for #class_name #where_clause {
414415
fn nativeclass_register(#builder: &::gdnative::export::ClassBuilder<Self>) {
415416
use gdnative::export::*;
416417

@@ -767,11 +768,17 @@ pub(crate) fn expand_godot_wrap_method(
767768
return Err(errors);
768769
}
769770

770-
wrap_method(&class_name, &export_method.expect("ExportMethod is valid")).map_err(|e| vec![e])
771+
wrap_method(
772+
&class_name,
773+
&Generics::default(),
774+
&export_method.expect("ExportMethod is valid"),
775+
)
776+
.map_err(|e| vec![e])
771777
}
772778

773779
fn wrap_method(
774780
class_name: &Type,
781+
generics: &Generics,
775782
export_method: &ExportMethod,
776783
) -> Result<TokenStream2, syn::Error> {
777784
let ExportMethod {
@@ -783,6 +790,21 @@ fn wrap_method(
783790
let gdnative_core = crate::crate_gdnative_core();
784791
let automatically_derived = crate::automatically_derived();
785792

793+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
794+
let turbofish_ty_generics = ty_generics.as_turbofish();
795+
796+
let generic_marker_decl = if generics.params.is_empty() {
797+
quote!(())
798+
} else {
799+
quote!(core::marker::PhantomData #ty_generics)
800+
};
801+
802+
let generic_marker_ctor = if generics.params.is_empty() {
803+
quote!(())
804+
} else {
805+
quote!(core::marker::PhantomData)
806+
};
807+
786808
let sig_span = sig.ident.span();
787809
let ret_span = sig.output.span();
788810

@@ -875,8 +897,8 @@ fn wrap_method(
875897

876898
quote_spanned! { sig_span =>
877899
#automatically_derived
878-
impl #gdnative_async::StaticArgsAsyncMethod<#class_name> for ThisMethod {
879-
type Args = Args;
900+
impl #impl_generics #gdnative_async::StaticArgsAsyncMethod<#class_name> for ThisMethod #ty_generics #where_clause {
901+
type Args = Args #ty_generics;
880902

881903
fn spawn_with(
882904
&self,
@@ -885,7 +907,7 @@ fn wrap_method(
885907
__spawner.spawn(move |__ctx, __this, __args| {
886908
let __future = __this
887909
.#map_method(move |__rust_val, __base| {
888-
let Args { #(#destructure_arg_list,)* } = __args;
910+
let Args { #(#destructure_arg_list,)* __generic_marker } = __args;
889911

890912
#[allow(unused_unsafe)]
891913
unsafe {
@@ -916,17 +938,19 @@ fn wrap_method(
916938
}
917939
}
918940

919-
#gdnative_async::Async::new(#gdnative_async::StaticArgs::new(ThisMethod))
941+
#gdnative_async::Async::new(#gdnative_async::StaticArgs::new(ThisMethod #turbofish_ty_generics {
942+
_marker: #generic_marker_ctor,
943+
}))
920944
}
921945
} else {
922946
quote_spanned! { sig_span =>
923947
#automatically_derived
924-
impl #gdnative_core::export::StaticArgsMethod<#class_name> for ThisMethod {
925-
type Args = Args;
948+
impl #impl_generics #gdnative_core::export::StaticArgsMethod<#class_name> for ThisMethod #ty_generics #where_clause {
949+
type Args = Args #ty_generics;
926950
fn call(
927951
&self,
928952
__this: TInstance<'_, #class_name, #gdnative_core::object::ownership::Shared>,
929-
Args { #(#destructure_arg_list,)* }: Args,
953+
Args { #(#destructure_arg_list,)* __generic_marker }: Self::Args,
930954
) -> #gdnative_core::core_types::Variant {
931955
__this
932956
.#map_method(|__rust_val, __base| {
@@ -950,23 +974,48 @@ fn wrap_method(
950974
}
951975
}
952976

953-
#gdnative_core::export::StaticArgs::new(ThisMethod)
977+
#gdnative_core::export::StaticArgs::new(ThisMethod #turbofish_ty_generics {
978+
_marker: #generic_marker_ctor,
979+
})
954980
}
955981
};
956982

983+
// Necessary standard traits have to be implemented manually because the default derive isn't smart enough.
957984
let output = quote_spanned! { sig_span =>
958985
{
959-
#[derive(Copy, Clone, Default)]
960-
struct ThisMethod;
986+
struct ThisMethod #ty_generics #where_clause {
987+
_marker: #generic_marker_decl,
988+
}
989+
990+
impl #impl_generics Copy for ThisMethod #ty_generics #where_clause {}
991+
impl #impl_generics Clone for ThisMethod #ty_generics #where_clause {
992+
fn clone(&self) -> Self {
993+
*self
994+
}
995+
}
996+
997+
impl #impl_generics Default for ThisMethod #ty_generics #where_clause {
998+
fn default() -> Self {
999+
Self {
1000+
_marker: #generic_marker_ctor,
1001+
}
1002+
}
1003+
}
1004+
1005+
unsafe impl #impl_generics Send for ThisMethod #ty_generics #where_clause {}
1006+
unsafe impl #impl_generics Sync for ThisMethod #ty_generics #where_clause {}
9611007

9621008
use #gdnative_core::export::{NativeClass, OwnerArg};
9631009
use #gdnative_core::object::{Instance, TInstance};
9641010
use #gdnative_core::derive::FromVarargs;
9651011

9661012
#[derive(FromVarargs)]
9671013
#automatically_derived
968-
struct Args {
1014+
struct Args #ty_generics #where_clause {
9691015
#(#declare_arg_list,)*
1016+
1017+
#[skip]
1018+
__generic_marker: #generic_marker_decl,
9701019
}
9711020

9721021
#impl_body

0 commit comments

Comments
 (0)