Skip to content

Commit ef8b2b4

Browse files
authored
Merge pull request #524 from Lemiczek/bitflags_u64_type
Implementation of `EngineBitfield` trait
2 parents 7dee976 + b4cdc29 commit ef8b2b4

File tree

8 files changed

+137
-61
lines changed

8 files changed

+137
-61
lines changed

godot-codegen/src/codegen_special_cases.rs

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ fn is_type_excluded(ty: &str, ctx: &mut Context) -> bool {
4343
None => false,
4444
Some(class) => is_class_excluded(class.as_str()),
4545
},
46+
RustTy::EngineBitfield {
47+
surrounding_class, ..
48+
} => match surrounding_class.as_ref() {
49+
None => false,
50+
Some(class) => is_class_excluded(class.as_str()),
51+
},
4652
RustTy::EngineClass { inner_class, .. } => is_class_excluded(&inner_class.to_string()),
4753
}
4854
}

godot-codegen/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ enum RustTy {
185185
surrounding_class: Option<String>,
186186
},
187187

188+
/// `module::Bitfield`
189+
EngineBitfield {
190+
tokens: TokenStream,
191+
/// `None` for globals
192+
#[allow(dead_code)] // only read in minimal config
193+
surrounding_class: Option<String>,
194+
},
195+
188196
/// `Gd<Node>`
189197
EngineClass {
190198
/// Tokens with full `Gd<T>`
@@ -219,6 +227,7 @@ impl ToTokens for RustTy {
219227
} => quote! { *mut #inner }.to_tokens(tokens),
220228
RustTy::EngineArray { tokens: path, .. } => path.to_tokens(tokens),
221229
RustTy::EngineEnum { tokens: path, .. } => path.to_tokens(tokens),
230+
RustTy::EngineBitfield { tokens: path, .. } => path.to_tokens(tokens),
222231
RustTy::EngineClass { tokens: path, .. } => path.to_tokens(tokens),
223232
}
224233
}

godot-codegen/src/util.rs

+104-60
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,37 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
241241
unique_ords.sort();
242242
unique_ords.dedup();
243243

244-
let bitfield_ops = if enum_.is_bitfield {
245-
let tokens = quote! {
244+
let mut derives = vec!["Copy", "Clone", "Eq", "PartialEq", "Hash", "Debug"];
245+
246+
if enum_.is_bitfield {
247+
derives.push("Default");
248+
}
249+
250+
let derives = derives.into_iter().map(ident);
251+
252+
let index_enum_impl = if enum_.is_bitfield {
253+
// Bitfields don't implement IndexEnum.
254+
TokenStream::new()
255+
} else {
256+
// Enums implement IndexEnum only if they are "index-like" (see docs).
257+
if let Some(enum_max) = try_count_index_enum(enum_) {
258+
quote! {
259+
impl crate::obj::IndexEnum for #enum_name {
260+
const ENUMERATOR_COUNT: usize = #enum_max;
261+
}
262+
}
263+
} else {
264+
TokenStream::new()
265+
}
266+
};
267+
268+
let bitfield_ops;
269+
let self_as_trait;
270+
let engine_impl;
271+
let enum_ord_type;
272+
273+
if enum_.is_bitfield {
274+
bitfield_ops = quote! {
246275
// impl #enum_name {
247276
// pub const UNSET: Self = Self { ord: 0 };
248277
// }
@@ -254,97 +283,85 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
254283
}
255284
}
256285
};
286+
enum_ord_type = quote! { u64 };
287+
self_as_trait = quote! { <Self as crate::obj::EngineBitfield> };
288+
engine_impl = quote! {
289+
impl crate::obj::EngineBitfield for #enum_name {
290+
fn try_from_ord(ord: u64) -> Option<Self> {
291+
Some(Self { ord })
292+
}
257293

258-
Some(tokens)
259-
} else {
260-
None
261-
};
262-
263-
let try_from_ord = if enum_.is_bitfield {
264-
quote! {
265-
fn try_from_ord(ord: i32) -> Option<Self> {
266-
Some(Self { ord })
294+
fn ord(self) -> u64 {
295+
self.ord
296+
}
267297
}
268-
}
298+
};
269299
} else {
270-
quote! {
271-
fn try_from_ord(ord: i32) -> Option<Self> {
272-
match ord {
273-
#( ord @ #unique_ords )|* => Some(Self { ord }),
274-
_ => None,
300+
bitfield_ops = TokenStream::new();
301+
enum_ord_type = quote! { i32 };
302+
self_as_trait = quote! { <Self as crate::obj::EngineEnum> };
303+
engine_impl = quote! {
304+
impl crate::obj::EngineEnum for #enum_name {
305+
// fn try_from_ord(ord: i32) -> Option<Self> {
306+
// match ord {
307+
// #(
308+
// #matches
309+
// )*
310+
// _ => None,
311+
// }
312+
// }
313+
314+
fn try_from_ord(ord: i32) -> Option<Self> {
315+
match ord {
316+
#( ord @ #unique_ords )|* => Some(Self { ord }),
317+
_ => None,
318+
}
275319
}
276-
}
277-
}
278-
};
279-
280-
let mut derives = vec!["Copy", "Clone", "Eq", "PartialEq", "Debug", "Hash"];
281-
282-
if enum_.is_bitfield {
283-
derives.push("Default");
284-
}
285320

286-
let index_enum_impl = if let Some(enum_max) = try_count_index_enum(enum_) {
287-
quote! {
288-
impl crate::obj::IndexEnum for #enum_name {
289-
const ENUMERATOR_COUNT: usize = #enum_max;
321+
fn ord(self) -> i32 {
322+
self.ord
323+
}
290324
}
291-
}
292-
} else {
293-
TokenStream::new()
325+
};
294326
};
295327

296-
let derives = derives.into_iter().map(ident);
297-
298328
// Enumerator ordinal stored as i32, since that's enough to hold all current values and the default repr in C++.
299329
// Public interface is i64 though, for consistency (and possibly forward compatibility?).
300-
// TODO maybe generalize GodotFfi over EngineEnum trait
330+
// Bitfield ordinals are stored as u64. See also: https://github.com/godotengine/godot-cpp/pull/1320
301331
quote! {
302332
#[repr(transparent)]
303333
#[derive(#( #derives ),*)]
304334
pub struct #enum_name {
305-
ord: i32
335+
ord: #enum_ord_type
306336
}
307337
impl #enum_name {
308338
#(
309339
#enumerators
310340
)*
311341
}
312-
impl crate::obj::EngineEnum for #enum_name {
313-
// fn try_from_ord(ord: i32) -> Option<Self> {
314-
// match ord {
315-
// #(
316-
// #matches
317-
// )*
318-
// _ => None,
319-
// }
320-
// }
321342

322-
#try_from_ord
343+
#engine_impl
344+
345+
#bitfield_ops
323346

324-
fn ord(self) -> i32 {
325-
self.ord
326-
}
327-
}
328347
#index_enum_impl
329348

330349
impl crate::builtin::meta::GodotConvert for #enum_name {
331-
type Via = i32;
350+
type Via = #enum_ord_type;
332351
}
333352

334353
impl crate::builtin::meta::ToGodot for #enum_name {
335354
fn to_godot(&self) -> Self::Via {
336-
<Self as crate::obj::EngineEnum>::ord(*self)
355+
#self_as_trait::ord(*self)
337356
}
338357
}
339358

340359
impl crate::builtin::meta::FromGodot for #enum_name {
341360
fn try_from_godot(via: Self::Via) -> std::result::Result<Self, crate::builtin::meta::ConvertError> {
342-
<Self as crate::obj::EngineEnum>::try_from_ord(via)
361+
#self_as_trait::try_from_ord(via)
343362
.ok_or_else(|| crate::builtin::meta::FromGodotError::InvalidEnum.into_error(via))
344363
}
345364
}
346-
347-
#bitfield_ops
348365
}
349366
}
350367

@@ -670,11 +687,28 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
670687
};
671688
}
672689

673-
let qualified_enum = ty
674-
.strip_prefix("enum::")
675-
.or_else(|| ty.strip_prefix("bitfield::"));
690+
if let Some(bitfield) = ty.strip_prefix("bitfield::") {
691+
return if let Some((class, enum_)) = bitfield.split_once('.') {
692+
// Class-local bitfield.
693+
let module = ModName::from_godot(class);
694+
let bitfield_ty = make_enum_name(enum_);
695+
696+
RustTy::EngineBitfield {
697+
tokens: quote! { crate::engine::#module::#bitfield_ty},
698+
surrounding_class: Some(class.to_string()),
699+
}
700+
} else {
701+
// Global bitfield.
702+
let bitfield_ty = make_enum_name(bitfield);
703+
704+
RustTy::EngineBitfield {
705+
tokens: quote! { crate::engine::global::#bitfield_ty },
706+
surrounding_class: None,
707+
}
708+
};
709+
}
676710

677-
if let Some(qualified_enum) = qualified_enum {
711+
if let Some(qualified_enum) = ty.strip_prefix("enum::") {
678712
return if let Some((class, enum_)) = qualified_enum.split_once('.') {
679713
// Class-local enum
680714
let module = ModName::from_godot(class);
@@ -799,6 +833,7 @@ fn to_rust_expr_inner(expr: &str, ty: &RustTy, is_inner: bool) -> TokenStream {
799833
if let Ok(num) = expr.parse::<i64>() {
800834
let lit = Literal::i64_unsuffixed(num);
801835
return match ty {
836+
RustTy::EngineBitfield { .. } => quote! { crate::obj::EngineBitfield::from_ord(#lit) },
802837
RustTy::EngineEnum { .. } => quote! { crate::obj::EngineEnum::from_ord(#lit) },
803838
RustTy::BuiltinIdent(ident) if ident == "Variant" => quote! { Variant::from(#lit) },
804839
RustTy::BuiltinIdent(ident)
@@ -948,6 +983,12 @@ fn gdscript_to_rust_expr() {
948983
};
949984
let ty_enum = Some(&ty_enum);
950985

986+
let ty_bitfield = RustTy::EngineBitfield {
987+
tokens: quote! { SomeEnum },
988+
surrounding_class: None,
989+
};
990+
let ty_bitfield = Some(&ty_bitfield);
991+
951992
let ty_variant = RustTy::BuiltinIdent(ident("Variant"));
952993
let ty_variant = Some(&ty_variant);
953994

@@ -997,6 +1038,9 @@ fn gdscript_to_rust_expr() {
9971038
// enum (from int)
9981039
("7", ty_enum, quote! { crate::obj::EngineEnum::from_ord(7) }),
9991040

1041+
// bitfield (from int)
1042+
("7", ty_bitfield, quote! { crate::obj::EngineBitfield::from_ord(7) }),
1043+
10001044
// Variant (from int)
10011045
("8", ty_variant, quote! { Variant::from(8) }),
10021046

godot-core/src/builtin/meta/godot_convert/convert_error.rs

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ pub(crate) enum FromGodotError {
145145
expected: array_inner::TypeInfo,
146146
got: array_inner::TypeInfo,
147147
},
148+
/// InvalidEnum is also used by bitfields.
148149
InvalidEnum,
149150
ZeroInstanceId,
150151
}

godot-core/src/builtin/meta/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ pub struct PropertyInfo {
248248
impl PropertyInfo {
249249
/// Converts to the FFI type. Keep this object allocated while using that!
250250
pub fn property_sys(&self) -> sys::GDExtensionPropertyInfo {
251+
use crate::obj::EngineBitfield as _;
251252
use crate::obj::EngineEnum as _;
252253

253254
sys::GDExtensionPropertyInfo {
@@ -261,6 +262,7 @@ impl PropertyInfo {
261262
}
262263

263264
pub fn empty_sys() -> sys::GDExtensionPropertyInfo {
265+
use crate::obj::EngineBitfield as _;
264266
use crate::obj::EngineEnum as _;
265267

266268
sys::GDExtensionPropertyInfo {

godot-core/src/builtin/meta/registration/method.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl ClassMethodInfo {
9797
}
9898

9999
pub fn register_extension_class_method(&self) {
100-
use crate::obj::EngineEnum as _;
100+
use crate::obj::EngineBitfield as _;
101101

102102
let (return_value_info, return_value_metadata) = match &self.return_value {
103103
Some(info) => (Some(&info.info), info.metadata),

godot-core/src/obj/traits.rs

+13
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,19 @@ pub trait EngineEnum: Copy {
182182
}
183183
}
184184

185+
/// Auto-implemented for all engine-provided bitfields.
186+
pub trait EngineBitfield: Copy {
187+
fn try_from_ord(ord: u64) -> Option<Self>;
188+
189+
/// Ordinal value of the bit flag, as specified in Godot.
190+
fn ord(self) -> u64;
191+
192+
fn from_ord(ord: u64) -> Self {
193+
Self::try_from_ord(ord)
194+
.unwrap_or_else(|| panic!("ordinal {ord} does not map to any valid bit flag"))
195+
}
196+
}
197+
185198
/// Trait for enums that can be used as indices in arrays.
186199
///
187200
/// The conditions for a Godot enum to be "index-like" are:

godot/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ pub mod prelude {
235235

236236
// Make trait methods available
237237
pub use super::engine::NodeExt as _;
238+
pub use super::obj::EngineBitfield as _;
238239
pub use super::obj::EngineEnum as _;
239240
pub use super::obj::UserClass as _; // new_gd(), alloc_gd()
240241
pub use super::obj::WithBaseField as _; // to_gd()

0 commit comments

Comments
 (0)