@@ -872,12 +872,6 @@ fn (t &Transformer) is_interface_var(name string) bool {
872872// get_var_type_name returns the type name of a variable from scope lookup
873873fn (t &Transformer) get_var_type_name (name string ) string {
874874 typ := t.lookup_var_type (name) or { return '' }
875- if typ is types.Alias {
876- if typ.name != '' {
877- return typ.name
878- }
879- return t.type_to_name (typ.base_type)
880- }
881875 if typ is types.String {
882876 return 'string'
883877 }
@@ -5505,9 +5499,12 @@ fn (mut t Transformer) transform_return_stmt(stmt ast.ReturnStmt) ast.ReturnStmt
55055499 }
55065500 }
55075501 transformed := t.transform_expr (expr)
5508- // Wrap variant values in sum type initialization if needed
5502+ // Wrap variant values in sum type initialization if needed.
5503+ // Use wrap_sumtype_value_transformed because the value is already transformed above.
5504+ // Using wrap_sumtype_value would transform the value a second time, causing
5505+ // double smartcast dereferences (e.g., ((T*)(((T*)(x._data._T))->_data._T))->field).
55095506 if t.cur_fn_ret_type_name != '' && t.is_sum_type (t.cur_fn_ret_type_name) {
5510- if wrapped := t.wrap_sumtype_value (transformed, t.cur_fn_ret_type_name) {
5507+ if wrapped := t.wrap_sumtype_value_transformed (transformed, t.cur_fn_ret_type_name) {
55115508 exprs << wrapped
55125509 continue
55135510 }
@@ -7791,6 +7788,38 @@ fn (mut t Transformer) transform_init_expr(expr ast.InitExpr) ast.Expr {
77917788 field_type_name = t.get_init_expr_field_type_name (expr.typ, field.name)
77927789 }
77937790 if t.is_sum_type (field_type_name) {
7791+ // If the value is a variable whose declared type is already this sum type
7792+ // (e.g., `expr: ast.Expr` used in `GenericArgOrIndexExpr{expr: expr}`),
7793+ // skip wrapping. At the C level, the variable is already a tagged union
7794+ // of the correct type, so wrapping would produce invalid C.
7795+ if field.value is ast.Ident {
7796+ if var_type := t.lookup_var_type (field.value.name) {
7797+ if var_type is types.SumType {
7798+ var_st_name := types.sum_type_name (var_type)
7799+ if var_st_name == field_type_name
7800+ || match_sumtype_variant_name (var_st_name, [field_type_name]) != '' {
7801+ // Variable is already the target sum type. Remove any
7802+ // smartcast context temporarily so transform_expr returns
7803+ // the raw variable (tagged union value) without deref.
7804+ if sc_ctx := t.find_smartcast_for_expr (field.value.name) {
7805+ removed := t.remove_matching_smartcasts (sc_ctx)
7806+ transformed_direct := t.transform_expr (field.value)
7807+ t.restore_smartcasts (removed)
7808+ fields << ast.FieldInit{
7809+ name: field.name
7810+ value: transformed_direct
7811+ }
7812+ } else {
7813+ fields << ast.FieldInit{
7814+ name: field.name
7815+ value: t.transform_expr (field.value)
7816+ }
7817+ }
7818+ continue
7819+ }
7820+ }
7821+ }
7822+ }
77947823 // This is a sum type field - wrap the value in sum type initialization
77957824 if wrapped := t.wrap_sumtype_value (field.value, field_type_name) {
77967825 fields << ast.FieldInit{
@@ -9323,8 +9352,24 @@ fn (t &Transformer) type_expr_name_full(expr ast.Expr) string {
93239352
93249353// get_struct_field_type_name returns the type name of a field in a struct
93259354fn (t &Transformer) get_struct_field_type_name (struct_name string , field_name string ) string {
9326- // Look up the struct type in scopes
9355+ // Look up the struct type in scopes.
9356+ // When the struct name is not module-qualified, prefer the current module scope
9357+ // to avoid collisions (e.g., ast.FnType vs types.FnType both named "FnType").
93279358 lock t.env.scopes {
9359+ // First: try the current module scope for non-qualified names.
9360+ if ! struct_name.contains ('__' ) && t.cur_module != '' && t.cur_module != 'main'
9361+ && t.cur_module != 'builtin' {
9362+ if cur_scope := t.env.scopes[t.cur_module] {
9363+ if obj := cur_scope.objects[struct_name] {
9364+ if obj is types.Type {
9365+ result := t.get_field_type_name (obj, field_name)
9366+ if result != '' {
9367+ return result
9368+ }
9369+ }
9370+ }
9371+ }
9372+ }
93289373 scope_names := t.env.scopes.keys ()
93299374 for scope_name in scope_names {
93309375 scope := t.env.scopes[scope_name] or { continue }
@@ -9382,16 +9427,83 @@ fn (mut t Transformer) wrap_sumtype_value_transformed(value ast.Expr, sumtype_na
93829427 if variants.len == 0 {
93839428 return none
93849429 }
9385- typ := t.get_expr_type (value) or { return none }
9386- c_name := t.type_to_c_name (typ)
9387- if c_name == '' || c_name == 'void' {
9430+ mut variant_name := ''
9431+ // Try checker-provided type first (works for original expressions with valid pos.id)
9432+ if typ := t.get_expr_type (value) {
9433+ c_name := t.type_to_c_name (typ)
9434+ if c_name != '' && c_name != 'void' {
9435+ variant_name = t.match_variant (c_name, variants) or { '' }
9436+ }
9437+ }
9438+ // Fallback: infer variant from expression structure (needed for already-transformed
9439+ // expressions that have lost their position IDs after transformation)
9440+ if variant_name == '' {
9441+ variant_name = t.infer_variant_from_expr (value, variants)
9442+ }
9443+ if variant_name == '' {
93889444 return none
93899445 }
9390- variant_name := t.match_variant (c_name, variants) or { return none }
93919446 // Value is already transformed, just wrap it
93929447 return t.build_sumtype_init (value, variant_name, sumtype_name)
93939448}
93949449
9450+ // infer_variant_from_expr determines which variant of a sum type a value belongs to
9451+ // by examining the expression structure. Used as a fallback when position-based type
9452+ // lookup (get_expr_type) fails for already-transformed expressions.
9453+ fn (t &Transformer) infer_variant_from_expr (value ast.Expr, variants []string ) string {
9454+ if value is ast.InitExpr {
9455+ type_name := t.get_init_expr_type_name (value.typ)
9456+ matched := t.match_variant (type_name, variants) or { '' }
9457+ if matched != '' {
9458+ return matched
9459+ }
9460+ // Try with module prefix
9461+ if t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
9462+ mangled := '${t .cur_module }__${type_name }'
9463+ if m := t.match_variant (mangled, variants) {
9464+ return m
9465+ }
9466+ }
9467+ }
9468+ if value is ast.BasicLiteral {
9469+ if value.kind == .number {
9470+ if value.value.contains ('.' ) {
9471+ return match_sumtype_variant_name ('f64' , variants)
9472+ }
9473+ return match_sumtype_variant_name ('int' , variants)
9474+ }
9475+ if value.kind == .string {
9476+ return match_sumtype_variant_name ('string' , variants)
9477+ }
9478+ }
9479+ if value is ast.StringLiteral || value is ast.StringInterLiteral {
9480+ return match_sumtype_variant_name ('string' , variants)
9481+ }
9482+ if value is ast.Ident {
9483+ var_type := t.get_var_type_name (value.name)
9484+ if var_type != '' {
9485+ matched := t.match_variant (var_type, variants) or { '' }
9486+ if matched != '' {
9487+ return matched
9488+ }
9489+ }
9490+ }
9491+ if value is ast.CastExpr {
9492+ matched := t.match_variant (t.type_expr_name_full (value.typ), variants) or { '' }
9493+ if matched != '' {
9494+ return matched
9495+ }
9496+ }
9497+ if value is ast.CallExpr {
9498+ matched := match_sumtype_variant_name (t.get_call_return_type (ast.Expr (value)),
9499+ variants)
9500+ if matched != '' {
9501+ return matched
9502+ }
9503+ }
9504+ return ''
9505+ }
9506+
93959507// match_variant finds the variant in variants that matches c_name (exact or short name match)
93969508fn (t &Transformer) match_variant (c_name string , variants []string ) ? string {
93979509 if c_name in variants {
@@ -9497,6 +9609,22 @@ fn (t &Transformer) build_sumtype_init(transformed_value ast.Expr, variant_name
94979609 }
94989610}
94999611
9612+ fn match_sumtype_variant_name (candidate string , variants []string ) string {
9613+ if candidate.len == 0 {
9614+ return ''
9615+ }
9616+ if candidate in variants {
9617+ return candidate
9618+ }
9619+ cand_short := if candidate.contains ('__' ) { candidate.all_after_last ('__' ) } else { candidate }
9620+ for v in variants {
9621+ v_short := if v.contains ('__' ) { v.all_after_last ('__' ) } else { v }
9622+ if cand_short == v_short || cand_short == v {
9623+ return v
9624+ }
9625+ }
9626+ return ''
9627+ }
95009628fn (mut t Transformer) transform_infix_expr (expr ast.InfixExpr) ast.Expr {
95019629 // Preserve short-circuit semantics while making smartcasts available to all
95029630 // following terms in `&&` chains (including multiple `is` checks).
@@ -10476,31 +10604,48 @@ fn (mut t Transformer) transform_call_expr(expr ast.CallExpr) ast.Expr {
1047610604 }
1047710605}
1047810606
10479- fn (t &Transformer) lookup_call_fn_type (lhs ast.Expr) ? types.FnType {
10607+ struct CallFnInfo {
10608+ param_types []types.Type
10609+ param_names []string
10610+ }
10611+
10612+ fn (t &Transformer) lookup_call_fn_info (lhs ast.Expr) ? CallFnInfo {
1048010613 // Prefer checker-resolved callable type for this exact call target.
1048110614 // This is the most reliable source for method parameter info (names + types).
1048210615 if lhs_type := t.get_expr_type (lhs) {
1048310616 callable := t.unwrap_alias_and_pointer_type (lhs_type)
1048410617 if callable is types.FnType {
10485- return callable
10618+ return CallFnInfo{
10619+ param_types: callable.get_param_types ()
10620+ param_names: callable.get_param_names ()
10621+ }
1048610622 }
1048710623 }
1048810624 if lhs is ast.Ident {
1048910625 if t.cur_module != '' {
1049010626 if fn_type := t.env.lookup_fn (t.cur_module, lhs.name) {
10491- return fn_type
10627+ return CallFnInfo{
10628+ param_types: fn_type.get_param_types ()
10629+ param_names: fn_type.get_param_names ()
10630+ }
1049210631 }
1049310632 }
1049410633 if fn_type := t.env.lookup_fn ('builtin' , lhs.name) {
10495- return fn_type
10634+ return CallFnInfo{
10635+ param_types: fn_type.get_param_types ()
10636+ param_names: fn_type.get_param_names ()
10637+ }
1049610638 }
1049710639 return none
1049810640 }
1049910641 if lhs is ast.SelectorExpr {
1050010642 if lhs.lhs is ast.Ident {
1050110643 mod_name := (lhs.lhs as ast.Ident ).name
1050210644 if fn_type := t.env.lookup_fn (mod_name, lhs.rhs.name) {
10503- return fn_type
10645+ return CallFnInfo{
10646+ param_types: fn_type.get_param_types ()
10647+ param_names: fn_type.get_param_names ()
10648+ }
1050410649 }
1050510650 }
1050610651 if recv_type := t.get_expr_type (lhs.lhs) {
@@ -10515,7 +10660,10 @@ fn (t &Transformer) lookup_call_fn_type(lhs ast.Expr) ?types.FnType {
1051510660 }
1051610661 for name in lookup_names {
1051710662 if fn_type := t.env.lookup_method (name, lhs.rhs.name) {
10518- return fn_type
10663+ return CallFnInfo{
10664+ param_types: fn_type.get_param_types ()
10665+ param_names: fn_type.get_param_names ()
10666+ }
1051910667 }
1052010668 }
1052110669 }
@@ -10524,8 +10672,8 @@ fn (t &Transformer) lookup_call_fn_type(lhs ast.Expr) ?types.FnType {
1052410672}
1052510673
1052610674fn (t &Transformer) lookup_call_param_types (lhs ast.Expr) []types.Type {
10527- if fn_type := t.lookup_call_fn_type (lhs) {
10528- return fn_type. get_param_types ()
10675+ if info := t.lookup_call_fn_info (lhs) {
10676+ return info.param_types
1052910677 }
1053010678 return []types.Type{}
1053110679}
@@ -10689,12 +10837,12 @@ fn (t &Transformer) resolve_method_call_name(receiver ast.Expr, method_name stri
1068910837}
1069010838
1069110839fn (mut t Transformer) lower_missing_call_args (lhs ast.Expr, args []ast.Expr) []ast.Expr {
10692- fn_type := t.lookup_call_fn_type (lhs) or { return args }
10693- param_types := fn_type. get_param_types ()
10840+ info := t.lookup_call_fn_info (lhs) or { return args }
10841+ param_types := info.param_types
1069410842 if param_types.len == 0 {
1069510843 return args
1069610844 }
10697- param_names := fn_type. get_param_names ()
10845+ param_names := info.param_names
1069810846
1069910847 mut out := []ast.Expr{cap: args.len}
1070010848 for arg in args {
@@ -10743,14 +10891,14 @@ fn (mut t Transformer) empty_struct_arg_expr(param_type types.Type) ast.Expr {
1074310891 for cur is types.Alias {
1074410892 cur = cur.base_type ()
1074510893 }
10894+ inner := cur.base_type ()
1074610895 if cur is types.Pointer {
10747- ptr := cur as types.Pointer
1074810896 init_pos := t.next_synth_pos ()
1074910897 ptr_pos := t.next_synth_pos ()
10750- t.register_synth_type (init_pos, ptr.base_type )
10898+ t.register_synth_type (init_pos, inner )
1075110899 t.register_synth_type (ptr_pos, param_type)
1075210900 init_expr := ast.Expr (ast.InitExpr{
10753- typ: t.type_to_ast_type_expr (ptr.base_type )
10901+ typ: t.type_to_ast_type_expr (inner )
1075410902 pos: init_pos
1075510903 })
1075610904 return ast.Expr (ast.PrefixExpr{
@@ -10907,9 +11055,9 @@ fn (mut t Transformer) lower_struct_shorthand_call(args []ast.Expr, param_types
1090711055 for cur is types.Alias {
1090811056 cur = cur.base_type ()
1090911057 }
11058+ inner := cur.base_type ()
1091011059 if cur is types.Pointer {
10911- ptr := cur as types.Pointer
10912- init_typ = ptr.base_type
11060+ init_typ = inner
1091311061 }
1091411062 }
1091511063 init_pos := t.next_synth_pos ()
@@ -11088,17 +11236,35 @@ fn (mut t Transformer) transform_call_or_cast_expr(expr ast.CallOrCastExpr) ast.
1108811236 if expr.expr ! is ast.EmptyExpr {
1108911237 args << t.transform_expr (expr.expr)
1109011238 }
11091- // Route through transform_call_expr so method resolution stays in transformer,
11092- // not in the backend.
11093- return t.transform_call_expr (ast.CallExpr{
11239+ // Resolve method name using the ORIGINAL (pre-smartcast) receiver,
11240+ // then build the result directly. Do NOT route through transform_call_expr
11241+ // because it would re-transform the already-casted receiver and args,
11242+ // causing double smartcast dereferences.
11243+ if resolved := t.resolve_method_call_name (sel.lhs, sel.rhs.name) {
11244+ is_static := t.is_static_method_call (sel.lhs)
11245+ mut final_args := []ast.Expr{cap: args.len + 1 }
11246+ if ! is_static {
11247+ final_args << casted_receiver
11248+ }
11249+ final_args << args
11250+ return ast.CallExpr{
11251+ lhs: ast.Ident{
11252+ name: resolved
11253+ }
11254+ args: final_args
11255+ pos: expr.pos
11256+ }
11257+ }
11258+ // Fallback: keep as method call with casted receiver (let cleanc resolve)
11259+ return ast.CallExpr{
1109411260 lhs: ast.Expr (ast.SelectorExpr{
1109511261 lhs: casted_receiver
1109611262 rhs: sel.rhs
1109711263 pos: sel.pos
1109811264 })
1109911265 args: args
1110011266 pos: expr.pos
11101- })
11267+ }
1110211268 }
1110311269 }
1110411270 // Check for interface method call: iface.method(arg)
@@ -11167,7 +11333,7 @@ fn (mut t Transformer) transform_call_or_cast_expr(expr ast.CallOrCastExpr) ast.
1116711333 }
1116811334 // Check for explicit sum type cast: SumType(value) -> proper wrapping.
1116911335 // Important: do not transform `value` before variant inference, otherwise casts like
11170- // `Expr(EmptyExpr(0))` turn into `CastExpr` and `infer_variant_type` can no longer
11336+ // `Expr(EmptyExpr(0))` turn into `CastExpr` and variant inference can no longer
1117111337 // recover the variant.
1117211338 sumtype_name := t.type_expr_name_full (expr.lhs)
1117311339 if sumtype_name != '' && t.call_or_cast_lhs_is_type (expr.lhs) && t.is_sum_type (sumtype_name) {
@@ -11502,6 +11668,19 @@ fn (t &Transformer) type_to_name(typ types.Type) string {
1150211668 if typ is types.SumType {
1150311669 return types.sum_type_name (typ)
1150411670 }
11671+ inner := typ.base_type ()
11672+ if typ is types.OptionType {
11673+ return '_option_' + t.type_to_name (inner)
11674+ }
11675+ if typ is types.ResultType {
11676+ return '_result_' + t.type_to_name (inner)
11677+ }
11678+ if typ is types.Pointer {
11679+ return t.type_to_name (inner) + '*'
11680+ }
11681+ if typ is types.FnType {
11682+ return 'FnType'
11683+ }
1150511684 return ''
1150611685}
1150711686
0 commit comments