@@ -14,6 +14,7 @@ use quote::{quote, ToTokens};
1414
1515use crate :: context:: Context ;
1616use crate :: conv;
17+ use crate :: generator:: functions_common:: FnParamDecl ;
1718use crate :: models:: domain:: { ArgPassing , GodotTy , ModName , RustTy , TyName } ;
1819use crate :: special_cases:: is_builtin_type_scalar;
1920use crate :: util:: ident;
@@ -53,7 +54,6 @@ fn to_hardcoded_rust_ident(full_ty: &GodotTy) -> Option<&str> {
5354 // Others
5455 ( "bool" , None ) => "bool" ,
5556 ( "String" , None ) => "GString" ,
56- ( "Array" , None ) => "VariantArray" ,
5757
5858 // Types needed for native structures mapping
5959 ( "uint8_t" , None ) => "u8" ,
@@ -124,7 +124,7 @@ pub(crate) fn to_rust_type_abi(ty: &str, ctx: &mut Context) -> (RustTy, bool) {
124124 ty : ident ( "f64" ) ,
125125 arg_passing : ArgPassing :: ByValue ,
126126 } ,
127- _ => to_rust_type ( ty, None , ctx) ,
127+ _ => to_rust_type ( ty, None , FnParamDecl :: FnPublic , ctx) ,
128128 } ;
129129
130130 ( ty, is_obj)
@@ -134,24 +134,37 @@ pub(crate) fn to_rust_type_abi(ty: &str, ctx: &mut Context) -> (RustTy, bool) {
134134///
135135/// Uses an internal cache (via `ctx`), as several types are ubiquitous.
136136// TODO take TyName as input
137- pub ( crate ) fn to_rust_type < ' a > ( ty : & ' a str , meta : Option < & ' a String > , ctx : & mut Context ) -> RustTy {
137+ pub ( crate ) fn to_rust_type < ' a > (
138+ ty : & ' a str ,
139+ meta : Option < & ' a String > ,
140+ decl : FnParamDecl ,
141+ ctx : & mut Context ,
142+ ) -> RustTy {
138143 let full_ty = GodotTy {
139144 ty : ty. to_string ( ) ,
140145 meta : meta. cloned ( ) ,
141146 } ;
142147
143- // Separate find + insert slightly slower, but much easier with lifetimes
144- // The insert path will be hit less often and thus doesn't matter
145- if let Some ( rust_ty) = ctx. find_rust_type ( & full_ty) {
146- rust_ty. clone ( )
148+ // Don't cache untyped Array, as it depends on context (decl parameter)
149+ let is_context_dependent = ty == "Array" && full_ty. meta . is_none ( ) ;
150+
151+ if is_context_dependent {
152+ // Skip cache for context-dependent types
153+ to_rust_type_uncached ( & full_ty, decl, ctx)
147154 } else {
148- let rust_ty = to_rust_type_uncached ( & full_ty, ctx) ;
149- ctx. insert_rust_type ( full_ty, rust_ty. clone ( ) ) ;
150- rust_ty
155+ // Separate find + insert slightly slower, but much easier with lifetimes
156+ // The insert path will be hit less often and thus doesn't matter
157+ if let Some ( rust_ty) = ctx. find_rust_type ( & full_ty) {
158+ rust_ty. clone ( )
159+ } else {
160+ let rust_ty = to_rust_type_uncached ( & full_ty, decl, ctx) ;
161+ ctx. insert_rust_type ( full_ty, rust_ty. clone ( ) ) ;
162+ rust_ty
163+ }
151164 }
152165}
153166
154- fn to_rust_type_uncached ( full_ty : & GodotTy , ctx : & mut Context ) -> RustTy {
167+ fn to_rust_type_uncached ( full_ty : & GodotTy , decl : FnParamDecl , ctx : & mut Context ) -> RustTy {
155168 let ty = full_ty. ty . as_str ( ) ;
156169
157170 /// Transforms a Godot class/builtin/enum IDENT (without `::` or other syntax) to a Rust one
@@ -164,6 +177,34 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
164177 }
165178 }
166179
180+ // Special case: untyped Array depends on context
181+ if ty == "Array" && full_ty. meta . is_none ( ) {
182+ return match decl {
183+ FnParamDecl :: FnVirtual => {
184+ // Virtual method parameters (Godot → Rust): Array<Variant>.
185+ conv:: to_rust_type_abi ( "Array" , ctx) . 0
186+ }
187+ FnParamDecl :: FnReturn => {
188+ // Outbound returns (Godot → Rust): Array<Variant>.
189+ conv:: to_rust_type_abi ( "Array" , ctx) . 0
190+ }
191+ FnParamDecl :: FnReturnVirtual => {
192+ // Virtual returns (Rust → Godot): OutArray for covariance.
193+ RustTy :: BuiltinIdent {
194+ ty : ident ( "OutArray" ) ,
195+ arg_passing : ctx. get_builtin_arg_passing ( full_ty) ,
196+ }
197+ }
198+ _ => {
199+ // All other contexts (outbound parameters, internal, fields): OutArray.
200+ RustTy :: BuiltinIdent {
201+ ty : ident ( "OutArray" ) ,
202+ arg_passing : ctx. get_builtin_arg_passing ( full_ty) ,
203+ }
204+ }
205+ } ;
206+ }
207+
167208 if ty. ends_with ( '*' ) {
168209 // Pointer type; strip '*', see if const, and then resolve the inner type.
169210 let mut ty = ty[ 0 ..ty. len ( ) - 1 ] . to_string ( ) ;
@@ -188,7 +229,7 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
188229
189230 // .trim() is necessary here, as Godot places a space between a type and the stars when representing a double pointer.
190231 // Example: "int*" but "int **".
191- let inner_type = to_rust_type ( ty. trim ( ) , None , ctx) ;
232+ let inner_type = to_rust_type ( ty. trim ( ) , None , FnParamDecl :: FnPublic , ctx) ;
192233 return RustTy :: RawPointer {
193234 inner : Box :: new ( inner_type) ,
194235 is_const,
@@ -226,7 +267,7 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
226267 } ;
227268 }
228269 } else if let Some ( elem_ty) = ty. strip_prefix ( "typedarray::" ) {
229- let rust_elem_ty = to_rust_type ( elem_ty, full_ty. meta . as_ref ( ) , ctx) ;
270+ let rust_elem_ty = to_rust_type ( elem_ty, full_ty. meta . as_ref ( ) , decl , ctx) ;
230271 return if ctx. is_builtin ( elem_ty) {
231272 RustTy :: BuiltinArray {
232273 elem_type : quote ! { Array <#rust_elem_ty> } ,
@@ -331,6 +372,9 @@ fn to_rust_expr_inner(expr: &str, ty: &RustTy, is_inner: bool) -> TokenStream {
331372 "true" => return quote ! { true } ,
332373 "false" => return quote ! { false } ,
333374 "[]" | "{}" if is_inner => return quote ! { } ,
375+ "[]" if matches ! ( ty, RustTy :: BuiltinIdent { ty, .. } if ty == "OutArray" ) => {
376+ return quote ! { OutArray :: new_untyped( ) }
377+ }
334378 "[]" => return quote ! { Array :: new( ) } , // VariantArray or Array<T>
335379 "{}" => return quote ! { Dictionary :: new( ) } ,
336380 "null" => {
0 commit comments