Skip to content

Commit 318049a

Browse files
Fix for Unreal codegen for Optional fields (#3360)
# Description of Changes - Fixed logic issue around Option<Vec<Option<>>> applying the wrong types for primitives and dropping Optional - Fixed an issue with enum vs enum variants wrapped in Option<> producing the incorrect Unreal type - Removed unnecessary and incorrect header bindings - Type fix in the tests around the Optional Int32 # API and ABI breaking changes No breaking changes # Expected complexity level and risk 2 - Reworked incorrect optional lookups which can happen recursively # Testing I built out a few simple and complex objects in a Rust module to triple-check possible cases beyond what the test framework calls out. - [x] Tested many combinations from a Rust module to an Unreal project - [x] Ran and updated Unreal test cases as necessary
1 parent 4d48ca2 commit 318049a

File tree

17 files changed

+506
-542
lines changed

17 files changed

+506
-542
lines changed

crates/codegen/src/unrealcpp.rs

Lines changed: 38 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,20 +3123,20 @@ fn collect_optional_types(module: &ModuleDef) -> HashSet<String> {
31233123
fn get_cpp_type_for_array_element(elem_type_str: &str, _: &ModuleDef, module_name: &str) -> String {
31243124
match elem_type_str {
31253125
"Bool" => "bool".to_string(),
3126-
"I8" => "int8".to_string(),
3127-
"U8" => "uint8".to_string(),
3128-
"I16" => "int16".to_string(),
3129-
"U16" => "uint16".to_string(),
3130-
"I32" => "int32".to_string(),
3131-
"U32" => "uint32".to_string(),
3132-
"I64" => "int64".to_string(),
3133-
"U64" => "uint64".to_string(),
3134-
"F32" => "float".to_string(),
3135-
"F64" => "double".to_string(),
3136-
"I128" => "FSpacetimeDBInt128".to_string(),
3137-
"U128" => "FSpacetimeDBUInt128".to_string(),
3138-
"I256" => "FSpacetimeDBInt256".to_string(),
3139-
"U256" => "FSpacetimeDBUInt256".to_string(),
3126+
"I8" | "Int8" => "int8".to_string(),
3127+
"U8" | "UInt8" => "uint8".to_string(),
3128+
"I16" | "Int16" => "int16".to_string(),
3129+
"U16" | "UInt16" => "uint16".to_string(),
3130+
"I32" | "Int32" => "int32".to_string(),
3131+
"U32" | "UInt32" => "uint32".to_string(),
3132+
"I64" | "Int64" => "int64".to_string(),
3133+
"U64" | "UInt64" => "uint64".to_string(),
3134+
"F32" | "Float" => "float".to_string(),
3135+
"F64" | "Double" => "double".to_string(),
3136+
"I128" | "Int128" => "FSpacetimeDBInt128".to_string(),
3137+
"U128" | "UInt128" => "FSpacetimeDBUInt128".to_string(),
3138+
"I256" | "Int256" => "FSpacetimeDBInt256".to_string(),
3139+
"U256" | "UInt256" => "FSpacetimeDBUInt256".to_string(),
31403140
"String" => "FString".to_string(),
31413141
"Identity" => "FSpacetimeDBIdentity".to_string(),
31423142
"ConnectionId" => "FSpacetimeDBConnectionId".to_string(),
@@ -3159,20 +3159,20 @@ fn get_array_element_type_name(module: &ModuleDef, elem: &AlgebraicTypeUse) -> S
31593159
match elem {
31603160
AlgebraicTypeUse::Primitive(p) => match p {
31613161
PrimitiveType::Bool => "Bool".to_string(),
3162-
PrimitiveType::I8 => "I8".to_string(),
3163-
PrimitiveType::U8 => "U8".to_string(),
3164-
PrimitiveType::I16 => "I16".to_string(),
3165-
PrimitiveType::U16 => "U16".to_string(),
3166-
PrimitiveType::I32 => "I32".to_string(),
3167-
PrimitiveType::U32 => "U32".to_string(),
3168-
PrimitiveType::I64 => "I64".to_string(),
3169-
PrimitiveType::U64 => "U64".to_string(),
3170-
PrimitiveType::F32 => "F32".to_string(),
3171-
PrimitiveType::F64 => "F64".to_string(),
3172-
PrimitiveType::I128 => "I128".to_string(),
3173-
PrimitiveType::U128 => "U128".to_string(),
3174-
PrimitiveType::I256 => "I256".to_string(),
3175-
PrimitiveType::U256 => "U256".to_string(),
3162+
PrimitiveType::I8 => "Int8".to_string(),
3163+
PrimitiveType::U8 => "UInt8".to_string(),
3164+
PrimitiveType::I16 => "Int16".to_string(),
3165+
PrimitiveType::U16 => "UInt16".to_string(),
3166+
PrimitiveType::I32 => "Int32".to_string(),
3167+
PrimitiveType::U32 => "UInt32".to_string(),
3168+
PrimitiveType::I64 => "Int64".to_string(),
3169+
PrimitiveType::U64 => "UInt64".to_string(),
3170+
PrimitiveType::F32 => "Float".to_string(),
3171+
PrimitiveType::F64 => "Double".to_string(),
3172+
PrimitiveType::I128 => "Int128".to_string(),
3173+
PrimitiveType::U128 => "UInt128".to_string(),
3174+
PrimitiveType::I256 => "Int256".to_string(),
3175+
PrimitiveType::U256 => "UInt256".to_string(),
31763176
},
31773177
AlgebraicTypeUse::String => "String".to_string(),
31783178
AlgebraicTypeUse::Identity => "Identity".to_string(),
@@ -3183,11 +3183,7 @@ fn get_array_element_type_name(module: &ModuleDef, elem: &AlgebraicTypeUse) -> S
31833183
AlgebraicTypeUse::Ref(r) => type_ref_name(module, *r),
31843184
AlgebraicTypeUse::Option(nested_inner) => {
31853185
// Handle optional elements in arrays like Vec<Option<i32>>
3186-
let inner_optional_name = get_optional_type_name(module, nested_inner);
3187-
inner_optional_name
3188-
.strip_prefix("Optional")
3189-
.unwrap_or(&inner_optional_name)
3190-
.to_string()
3186+
get_optional_type_name(module, nested_inner)
31913187
}
31923188
_ => "Unknown".to_string(),
31933189
}
@@ -3271,15 +3267,15 @@ fn generate_optional_type(optional_name: &str, module: &ModuleDef, api_macro: &s
32713267
let module_name_pascal = module_name.to_case(Case::Pascal);
32723268
format!("F{module_name_pascal}{inner_type_str}")
32733269
}
3274-
_ if inner_type_str.starts_with("Vec") => {
3275-
// Handle OptionalVecXxx -> should use TArray<FModuleOptionalXxx>
3276-
let elem_type_str = &inner_type_str[3..]; // Remove "Vec" prefix
3270+
_ if inner_type_str.starts_with("VecOptional") => {
3271+
// Handle specific optional array types like OptionalVecOptionalI32, OptionalVecOptionalString, etc.
3272+
let elem_type_str = &inner_type_str[11..]; // Remove "VecOptional" prefix
32773273
let module_name_pascal = module_name.to_case(Case::Pascal);
32783274
format!("TArray<F{module_name_pascal}Optional{elem_type_str}>")
32793275
}
3280-
_ if inner_type_str.starts_with("OptionalVec") => {
3281-
// Handle specific optional array types like OptionalVecI32, OptionalVecString, etc.
3282-
let elem_type_str = &inner_type_str[11..]; // Remove "OptionalVec" prefix
3276+
_ if inner_type_str.starts_with("Vec") => {
3277+
// Handle OptionalVecXxx -> should use TArray<FModuleOptionalXxx>
3278+
let elem_type_str = &inner_type_str[3..]; // Remove "Vec" prefix
32833279
let cpp_elem_type = get_cpp_type_for_array_element(elem_type_str, module, module_name);
32843280
format!("TArray<{cpp_elem_type}>")
32853281
}
@@ -3295,7 +3291,7 @@ fn generate_optional_type(optional_name: &str, module: &ModuleDef, api_macro: &s
32953291
type_name == inner_type_str
32963292
&& matches!(
32973293
module.typespace_for_generate()[type_def.ty],
3298-
AlgebraicTypeDef::Sum(_) | AlgebraicTypeDef::PlainEnum(_)
3294+
AlgebraicTypeDef::PlainEnum(_)
32993295
)
33003296
});
33013297

@@ -3321,41 +3317,10 @@ fn generate_optional_type(optional_name: &str, module: &ModuleDef, api_macro: &s
33213317
// Basic types, no extra includes needed
33223318
}
33233319
_ if inner_type_str.starts_with("Vec") => {
3324-
// Handle OptionalVecXxx -> needs OptionalXxx include
3325-
let elem_type_str = &inner_type_str[3..]; // Remove "Vec" prefix
3326-
let module_name_pascal = module_name.to_case(Case::Pascal);
3327-
extra_includes.push(format!(
3328-
"ModuleBindings/Optionals/{module_name_pascal}Optional{elem_type_str}.g.h"
3329-
));
3320+
// Not required
33303321
}
33313322
_ if inner_type_str.starts_with("OptionalVec") => {
3332-
// Handle includes for specific optional array types
3333-
let elem_type_str = &inner_type_str[11..]; // Remove "OptionalVec" prefix
3334-
match elem_type_str {
3335-
"Identity" | "ConnectionId" | "Timestamp" | "TimeDuration" | "ScheduleAt" => {
3336-
extra_includes.push("Types/Builtins.h".to_string());
3337-
}
3338-
"I128" | "U128" | "I256" | "U256" => {
3339-
extra_includes.push("Types/LargeIntegers.h".to_string());
3340-
}
3341-
_ if elem_type_str.starts_with("Int32") => {
3342-
// Handle nested optional includes like Int32 from OptionalInt32
3343-
let module_name_pascal = module_name.to_case(Case::Pascal);
3344-
extra_includes.push(format!(
3345-
"ModuleBindings/Optionals/{module_name_pascal}OptionalInt32.g.h"
3346-
));
3347-
}
3348-
_ if !elem_type_str.starts_with("Bool")
3349-
&& !elem_type_str.starts_with("I")
3350-
&& !elem_type_str.starts_with("U")
3351-
&& !elem_type_str.starts_with("F")
3352-
&& elem_type_str != "String" =>
3353-
{
3354-
// Custom type, need its header
3355-
extra_includes.push(format!("ModuleBindings/Types/{elem_type_str}Type.g.h"));
3356-
}
3357-
_ => {} // Primitive types don't need extra includes
3358-
}
3323+
// Not required
33593324
}
33603325
_ if inner_type_str.starts_with("Optional") => {
33613326
// Nested optional, need its header

0 commit comments

Comments
 (0)