| 1 | // Copyright (c) 2026 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | |
| 5 | module transformer |
| 6 | |
| 7 | import v2.ast |
| 8 | import v2.types |
| 9 | |
| 10 | // is_interface_var checks if a variable is an interface type by looking up its type in scope |
| 11 | fn (t &Transformer) is_interface_var(name string) bool { |
| 12 | // Special case: 'err' in or-blocks is always IError interface |
| 13 | if name == 'err' { |
| 14 | return true |
| 15 | } |
| 16 | typ := t.lookup_var_type(name) or { return false } |
| 17 | return typ is types.Interface |
| 18 | } |
| 19 | |
| 20 | // is_interface_receiver checks if an expression's type is an interface type |
| 21 | fn (t &Transformer) is_interface_receiver(expr ast.Expr) bool { |
| 22 | match expr { |
| 23 | ast.Ident { |
| 24 | return t.is_interface_var(expr.name) |
| 25 | } |
| 26 | ast.ParenExpr { |
| 27 | return t.is_interface_receiver(expr.expr) |
| 28 | } |
| 29 | ast.ModifierExpr { |
| 30 | return t.is_interface_receiver(expr.expr) |
| 31 | } |
| 32 | else {} |
| 33 | } |
| 34 | |
| 35 | if typ := t.get_interface_receiver_type(expr) { |
| 36 | base := t.unwrap_alias_and_pointer_type(typ) |
| 37 | return base is types.Interface |
| 38 | } |
| 39 | return false |
| 40 | } |
| 41 | |
| 42 | fn (t &Transformer) get_interface_receiver_type(expr ast.Expr) ?types.Type { |
| 43 | if typ := t.get_expr_type(expr) { |
| 44 | return typ |
| 45 | } |
| 46 | if typ := t.resolve_expr_type(expr) { |
| 47 | return typ |
| 48 | } |
| 49 | if expr is ast.IndexExpr { |
| 50 | if expr.expr is ast.RangeExpr { |
| 51 | return none |
| 52 | } |
| 53 | if container_type := t.get_expr_type(expr.lhs) { |
| 54 | base := t.unwrap_alias_and_pointer_type(container_type) |
| 55 | if base is types.Array { |
| 56 | return base.elem_type |
| 57 | } |
| 58 | if base is types.ArrayFixed { |
| 59 | return base.elem_type |
| 60 | } |
| 61 | if base is types.Map { |
| 62 | return base.value_type |
| 63 | } |
| 64 | } |
| 65 | } |
| 66 | return none |
| 67 | } |
| 68 | |
| 69 | // is_interface_cast checks if an expression is an interface cast like Calculator(value) |
| 70 | fn (t &Transformer) is_interface_cast(expr ast.Expr) bool { |
| 71 | if t.get_interface_cast_inner_expr(expr) != none { |
| 72 | return true |
| 73 | } |
| 74 | return false |
| 75 | } |
| 76 | |
| 77 | // get_expr_type_name returns the concrete type name for an expression using the type checker env |
| 78 | fn (t &Transformer) get_expr_type_name(expr ast.Expr) ?string { |
| 79 | if typ := t.get_expr_type(expr) { |
| 80 | name := t.type_to_c_name(typ) |
| 81 | if name != '' { |
| 82 | return name |
| 83 | } |
| 84 | } |
| 85 | // For simple identifiers, try scope lookup |
| 86 | if expr is ast.Ident { |
| 87 | if typ := t.lookup_var_type(expr.name) { |
| 88 | name := t.type_to_c_name(typ) |
| 89 | if name != '' { |
| 90 | return name |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | return none |
| 95 | } |
| 96 | |
| 97 | fn (t &Transformer) get_interface_cast_concrete_type(expr ast.Expr) ?string { |
| 98 | match expr { |
| 99 | ast.ParenExpr { |
| 100 | return t.get_interface_cast_concrete_type(expr.expr) |
| 101 | } |
| 102 | ast.CallOrCastExpr { |
| 103 | if t.interface_type_expr_is_interface(expr.lhs) { |
| 104 | if concrete := t.get_expr_type_name(expr.expr) { |
| 105 | return concrete |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | ast.CallExpr { |
| 110 | if expr.args.len == 1 && t.interface_type_expr_is_interface(expr.lhs) { |
| 111 | if concrete := t.get_expr_type_name(expr.args[0]) { |
| 112 | return concrete |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | ast.CastExpr { |
| 117 | if t.interface_type_expr_is_interface(expr.typ) { |
| 118 | if concrete := t.get_expr_type_name(expr.expr) { |
| 119 | return concrete |
| 120 | } |
| 121 | } |
| 122 | } |
| 123 | else {} |
| 124 | } |
| 125 | |
| 126 | return none |
| 127 | } |
| 128 | |
| 129 | fn (t &Transformer) get_interface_cast_inner_expr(expr ast.Expr) ?ast.Expr { |
| 130 | match expr { |
| 131 | ast.ParenExpr { |
| 132 | return t.get_interface_cast_inner_expr(expr.expr) |
| 133 | } |
| 134 | ast.CallOrCastExpr { |
| 135 | if t.interface_type_expr_is_interface(expr.lhs) { |
| 136 | return expr.expr |
| 137 | } |
| 138 | } |
| 139 | ast.CallExpr { |
| 140 | if expr.args.len == 1 && t.interface_type_expr_is_interface(expr.lhs) { |
| 141 | return expr.args[0] |
| 142 | } |
| 143 | } |
| 144 | ast.CastExpr { |
| 145 | if t.interface_type_expr_is_interface(expr.typ) { |
| 146 | return expr.expr |
| 147 | } |
| 148 | } |
| 149 | else {} |
| 150 | } |
| 151 | |
| 152 | return none |
| 153 | } |
| 154 | |
| 155 | fn (t &Transformer) interface_type_expr_is_interface(expr ast.Expr) bool { |
| 156 | type_name := t.expr_to_type_name(expr) |
| 157 | if type_name != '' { |
| 158 | if typ := t.lookup_type(type_name) { |
| 159 | return typ is types.Interface |
| 160 | } |
| 161 | } |
| 162 | if expr is ast.Ident { |
| 163 | mut scope := t.get_current_scope() or { return false } |
| 164 | obj := scope.lookup_parent(expr.name, 0) or { return false } |
| 165 | if obj is types.Type { |
| 166 | return obj is types.Interface |
| 167 | } |
| 168 | } |
| 169 | return false |
| 170 | } |
| 171 | |
| 172 | fn (t &Transformer) get_interface_array_init_concrete_type(expr &ast.ArrayInitExpr) ?string { |
| 173 | if !expr_array_has_valid_data(expr.exprs) { |
| 174 | return none |
| 175 | } |
| 176 | mut concrete_type := '' |
| 177 | for e in expr.exprs { |
| 178 | concrete := t.get_interface_cast_concrete_type(e) or { return none } |
| 179 | if concrete_type == '' { |
| 180 | concrete_type = concrete |
| 181 | } else if concrete_type != concrete { |
| 182 | return none |
| 183 | } |
| 184 | } |
| 185 | if concrete_type != '' { |
| 186 | return concrete_type |
| 187 | } |
| 188 | return none |
| 189 | } |
| 190 | |
| 191 | fn (t &Transformer) get_interface_array_expr_concrete_type(expr ast.Expr) ?string { |
| 192 | match expr { |
| 193 | ast.Ident { |
| 194 | return t.get_interface_concrete_type(expr.name) |
| 195 | } |
| 196 | ast.ParenExpr { |
| 197 | return t.get_interface_array_expr_concrete_type(expr.expr) |
| 198 | } |
| 199 | ast.ArrayInitExpr { |
| 200 | return t.get_interface_array_init_concrete_type(&expr) |
| 201 | } |
| 202 | ast.CallExpr { |
| 203 | if expr.lhs is ast.SelectorExpr { |
| 204 | sel := expr.lhs as ast.SelectorExpr |
| 205 | if sel.rhs.name in ['repeat', 'clone'] { |
| 206 | return t.get_interface_array_expr_concrete_type(sel.lhs) |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | ast.CallOrCastExpr { |
| 211 | if expr.lhs is ast.SelectorExpr { |
| 212 | sel := expr.lhs as ast.SelectorExpr |
| 213 | if sel.rhs.name in ['repeat', 'clone'] { |
| 214 | return t.get_interface_array_expr_concrete_type(sel.lhs) |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | else {} |
| 219 | } |
| 220 | |
| 221 | return none |
| 222 | } |
| 223 | |
| 224 | fn (t &Transformer) get_interface_assignment_concrete_type(expr ast.Expr) ?string { |
| 225 | if concrete := t.get_interface_cast_concrete_type(expr) { |
| 226 | return concrete |
| 227 | } |
| 228 | if concrete := t.get_interface_array_expr_concrete_type(expr) { |
| 229 | return concrete |
| 230 | } |
| 231 | if expr is ast.Ident { |
| 232 | return t.get_interface_concrete_type(expr.name) |
| 233 | } |
| 234 | if expr is ast.ParenExpr { |
| 235 | return t.get_interface_assignment_concrete_type(expr.expr) |
| 236 | } |
| 237 | return none |
| 238 | } |
| 239 | |
| 240 | fn (t &Transformer) get_interface_concrete_type_for_expr(expr ast.Expr) ?string { |
| 241 | match expr { |
| 242 | ast.Ident { |
| 243 | return t.get_interface_concrete_type(expr.name) |
| 244 | } |
| 245 | ast.ParenExpr { |
| 246 | return t.get_interface_concrete_type_for_expr(expr.expr) |
| 247 | } |
| 248 | ast.ModifierExpr { |
| 249 | return t.get_interface_concrete_type_for_expr(expr.expr) |
| 250 | } |
| 251 | ast.IndexExpr { |
| 252 | return t.get_interface_array_expr_concrete_type(expr.lhs) |
| 253 | } |
| 254 | else {} |
| 255 | } |
| 256 | |
| 257 | return none |
| 258 | } |
| 259 | |
| 260 | fn (t &Transformer) get_native_default_interface_concrete_type(expr ast.Expr, method_name string) ?string { |
| 261 | prng_methods := ['seed', 'u8', 'u16', 'u32', 'u64', 'block_size', 'free'] |
| 262 | if method_name !in prng_methods { |
| 263 | return none |
| 264 | } |
| 265 | if t.cur_module == 'rand' { |
| 266 | return 'wyrand__WyRandRNG' |
| 267 | } |
| 268 | receiver_type := t.get_interface_receiver_type(expr) or { return none } |
| 269 | base := t.unwrap_alias_and_pointer_type(receiver_type) |
| 270 | if base !is types.Interface { |
| 271 | return none |
| 272 | } |
| 273 | iface_name := t.type_to_c_name(base) |
| 274 | if iface_name !in ['PRNG', 'rand__PRNG'] { |
| 275 | return none |
| 276 | } |
| 277 | return 'wyrand__WyRandRNG' |
| 278 | } |
| 279 | |
| 280 | fn (mut t Transformer) native_interface_receiver_arg(expr ast.Expr, concrete string) ast.Expr { |
| 281 | concrete_type := t.c_name_to_type(concrete) or { return expr } |
| 282 | return t.native_interface_receiver_arg_with_type(expr, concrete_type) |
| 283 | } |
| 284 | |
| 285 | fn (mut t Transformer) native_interface_receiver_arg_with_type(expr ast.Expr, concrete_type types.Type) ast.Expr { |
| 286 | match expr { |
| 287 | ast.IndexExpr { |
| 288 | pos := t.next_synth_pos() |
| 289 | t.register_synth_type(pos, concrete_type) |
| 290 | return ast.Expr(ast.IndexExpr{ |
| 291 | lhs: expr.lhs |
| 292 | expr: expr.expr |
| 293 | is_gated: expr.is_gated |
| 294 | pos: pos |
| 295 | }) |
| 296 | } |
| 297 | ast.ParenExpr { |
| 298 | return ast.Expr(ast.ParenExpr{ |
| 299 | expr: t.native_interface_receiver_arg_with_type(expr.expr, concrete_type) |
| 300 | pos: expr.pos |
| 301 | }) |
| 302 | } |
| 303 | ast.ModifierExpr { |
| 304 | return ast.Expr(ast.ModifierExpr{ |
| 305 | kind: expr.kind |
| 306 | expr: t.native_interface_receiver_arg_with_type(expr.expr, concrete_type) |
| 307 | pos: expr.pos |
| 308 | }) |
| 309 | } |
| 310 | else {} |
| 311 | } |
| 312 | |
| 313 | return expr |
| 314 | } |
| 315 | |
| 316 | // get_interface_concrete_type returns the concrete type name for an interface variable |
| 317 | // if it was tracked during interface cast lowering (native backend only) |
| 318 | fn (t &Transformer) get_interface_concrete_type(name string) ?string { |
| 319 | if concrete := t.interface_concrete_types[name] { |
| 320 | return concrete |
| 321 | } |
| 322 | return none |
| 323 | } |
| 324 | |