Skip to content

Commit 7096511

Browse files
medvednikovclaude
andcommitted
v2: re-apply transformer lowering with fixes for self-compilation
Re-apply the transformer lowering of CallOrCastExpr, RangeExpr, and FieldInit (originally de90b5e, reverted in dcc1da72e) with the following fixes: - Fix smartcast double-deref in method calls: resolve method name directly instead of routing through transform_call_expr which re-applies smartcast on already-casted receivers - Fix sum type wrapping for same-type variables in transform_init_expr: skip wrapping when variable's declared type already matches the target sum type field - Add infer_variant_from_expr fallback for wrap_sumtype_value_transformed: when get_expr_type fails on already-transformed expressions (lost position IDs), infer variant from expression structure (InitExpr type name, literals, variable types, cast types) - Remove Alias check in get_var_type_name that caused NULL pointer dereference at runtime Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ed91d26 commit 7096511

1 file changed

Lines changed: 213 additions & 34 deletions

File tree

‎vlib/v2/transformer/transformer.v‎

Lines changed: 213 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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
873873
fn (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
93259354
fn (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)
93969508
fn (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+
}
95009628
fn (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

1052610674
fn (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

1069110839
fn (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

Comments
 (0)