| 1 | const std = @import("std"); |
| 2 | const mem = std.mem; |
| 3 | const assert = std.debug.assert; |
| 4 | const CallingConvention = std.builtin.CallingConvention; |
| 5 | |
| 6 | const aro = @import("aro"); |
| 7 | const CToken = aro.Tokenizer.Token; |
| 8 | const Tree = aro.Tree; |
| 9 | const Node = Tree.Node; |
| 10 | const TokenIndex = Tree.TokenIndex; |
| 11 | const QualType = aro.QualType; |
| 12 | |
| 13 | const ast = @import("ast.zig"); |
| 14 | const ZigNode = ast.Node; |
| 15 | const ZigTag = ZigNode.Tag; |
| 16 | const builtins = @import("builtins.zig"); |
| 17 | const helpers = @import("helpers.zig"); |
| 18 | const MacroTranslator = @import("MacroTranslator.zig"); |
| 19 | const PatternList = @import("PatternList.zig"); |
| 20 | const Scope = @import("Scope.zig"); |
| 21 | |
| 22 | const AnonymousRecordFieldNames = struct { |
| 23 | pub const Key = struct { |
| 24 | parent: QualType, |
| 25 | field: QualType, |
| 26 | }; |
| 27 | |
| 28 | pub const Context = struct { |
| 29 | pub fn hash(ctx: Context, key: Key) u64 { |
| 30 | const auto_hash = std.hash_map.getAutoHashFn(Key, Context); |
| 31 | return auto_hash(ctx, .{ |
| 32 | .parent = key.parent.unqualified(), |
| 33 | .field = key.field.unqualified(), |
| 34 | }); |
| 35 | } |
| 36 | |
| 37 | pub fn eql(ctx: Context, a: Key, b: Key) bool { |
| 38 | const auto_eql = std.hash_map.getAutoEqlFn(Key, Context); |
| 39 | return auto_eql(ctx, .{ |
| 40 | .parent = a.parent.unqualified(), |
| 41 | .field = a.field.unqualified(), |
| 42 | }, .{ |
| 43 | .parent = b.parent.unqualified(), |
| 44 | .field = b.field.unqualified(), |
| 45 | }); |
| 46 | } |
| 47 | }; |
| 48 | }; |
| 49 | |
| 50 | pub const QualTypeHashContext = struct { |
| 51 | pub fn hash(ctx: QualTypeHashContext, key: QualType) u64 { |
| 52 | const auto_hash = std.hash_map.getAutoHashFn(QualType, QualTypeHashContext); |
| 53 | return auto_hash(ctx, key.unqualified()); |
| 54 | } |
| 55 | |
| 56 | pub fn eql(ctx: QualTypeHashContext, a: QualType, b: QualType) bool { |
| 57 | const auto_eql = std.hash_map.getAutoEqlFn(QualType, QualTypeHashContext); |
| 58 | return auto_eql(ctx, a.unqualified(), b.unqualified()); |
| 59 | } |
| 60 | }; |
| 61 | |
| 62 | pub const Error = std.mem.Allocator.Error; |
| 63 | pub const MacroProcessingError = Error || error{UnexpectedMacroToken}; |
| 64 | pub const TypeError = Error || error{UnsupportedType}; |
| 65 | pub const TransError = TypeError || error{ UnsupportedTranslation, SelfReferential }; |
| 66 | |
| 67 | /// Control when to treat a trailing array as a flexible array member. |
| 68 | /// Mirrors the -fstrict-flex-arrays=<n> compiler flag. |
| 69 | pub const StrictFlexArraysLevel = enum { |
| 70 | /// Any trailing array member is a flexible array. |
| 71 | @"0", |
| 72 | /// Trailing arrays of size 0, 1, or undefined are flexible. |
| 73 | @"1", |
| 74 | /// Trailing arrays of size 0 or undefined are flexible (default). |
| 75 | @"2", |
| 76 | /// Only trailing arrays of undefined size are flexible. |
| 77 | @"3", |
| 78 | }; |
| 79 | |
| 80 | const Translator = @This(); |
| 81 | |
| 82 | /// The C AST to be translated. |
| 83 | tree: *const Tree, |
| 84 | /// The compilation corresponding to the AST. |
| 85 | comp: *aro.Compilation, |
| 86 | /// The Preprocessor that produced the source for `tree`. |
| 87 | pp: *const aro.Preprocessor, |
| 88 | |
| 89 | /// Should static functions be translated as `pub`. |
| 90 | pub_static: bool, |
| 91 | /// Should function bodies be translated. |
| 92 | func_bodies: bool, |
| 93 | /// Should macro names of literals be preserved. |
| 94 | keep_macro_literals: bool, |
| 95 | /// Should struct fields be default initialized. |
| 96 | default_init: bool, |
| 97 | /// Control when to treat a trailing array as a flexible array member. |
| 98 | strict_flex_arrays: StrictFlexArraysLevel, |
| 99 | |
| 100 | gpa: mem.Allocator, |
| 101 | arena: mem.Allocator, |
| 102 | |
| 103 | alias_list: Scope.AliasList, |
| 104 | global_scope: *Scope.Root, |
| 105 | /// Running number used for creating new unique identifiers. |
| 106 | mangle_count: u32 = 0, |
| 107 | |
| 108 | /// Table of declarations for enum, struct, union and typedef types. |
| 109 | type_decls: std.array_hash_map.Auto(Node.Index, []const u8) = .empty, |
| 110 | /// Table of record decls that have been demoted to opaques. |
| 111 | opaque_demotes: std.HashMapUnmanaged(QualType, void, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty, |
| 112 | /// Table of unnamed enums and records that are child types of typedefs. |
| 113 | unnamed_typedefs: std.HashMapUnmanaged(QualType, []const u8, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty, |
| 114 | /// Table of anonymous record to generated field names. |
| 115 | anonymous_record_field_names: std.HashMapUnmanaged( |
| 116 | AnonymousRecordFieldNames.Key, |
| 117 | []const u8, |
| 118 | AnonymousRecordFieldNames.Context, |
| 119 | std.hash_map.default_max_load_percentage, |
| 120 | ) = .empty, |
| 121 | |
| 122 | /// This one is different than the root scope's name table. This contains |
| 123 | /// a list of names that we found by visiting all the top level decls without |
| 124 | /// translating them. The other maps are updated as we translate; this one is updated |
| 125 | /// up front in a pre-processing step. |
| 126 | global_names: std.array_hash_map.String(void) = .empty, |
| 127 | |
| 128 | /// This is similar to `global_names`, but contains names which we would |
| 129 | /// *like* to use, but do not strictly *have* to if they are unavailable. |
| 130 | /// These are relevant to types, which ideally we would name like |
| 131 | /// 'struct_foo' with an alias 'foo', but if either of those names is taken, |
| 132 | /// may be mangled. |
| 133 | /// This is distinct from `global_names` so we can detect at a type |
| 134 | /// declaration whether or not the name is available. |
| 135 | weak_global_names: std.array_hash_map.String(void) = .empty, |
| 136 | |
| 137 | /// Set of identifiers known to refer to typedef declarations. |
| 138 | /// Used when parsing macros. |
| 139 | typedefs: std.array_hash_map.String(void) = .empty, |
| 140 | |
| 141 | /// The lhs lval of a compound assignment expression. |
| 142 | compound_assign_dummy: ?ZigNode = null, |
| 143 | |
| 144 | /// Set of variables whose initializers are currently being translated. |
| 145 | /// Used to detect self-referential initializers. |
| 146 | wip_var_inits: std.AutoHashMapUnmanaged(Node.Index, void) = .empty, |
| 147 | |
| 148 | pub fn getMangle(t: *Translator) u32 { |
| 149 | t.mangle_count += 1; |
| 150 | return t.mangle_count; |
| 151 | } |
| 152 | |
| 153 | /// Convert an `aro.Source.Location` to a 'file:line:column' string. |
| 154 | pub fn locStr(t: *Translator, loc: aro.Source.Location) ![]const u8 { |
| 155 | const expanded = loc.expand(t.comp); |
| 156 | const filename = expanded.path; |
| 157 | |
| 158 | const line = expanded.line_no; |
| 159 | const col = expanded.col; |
| 160 | |
| 161 | return std.fmt.allocPrint(t.arena, "{s}:{d}:{d}", .{ filename, line, col }); |
| 162 | } |
| 163 | |
| 164 | fn maybeSuppressResult(t: *Translator, used: ResultUsed, result: ZigNode) TransError!ZigNode { |
| 165 | if (used == .used) return result; |
| 166 | return ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = result }); |
| 167 | } |
| 168 | |
| 169 | pub fn addTopLevelDecl(t: *Translator, name: []const u8, decl_node: ZigNode) !void { |
| 170 | const gop = try t.global_scope.sym_table.getOrPut(t.gpa, name); |
| 171 | if (gop.found_existing) return; // Any duplicate decls are equivalent |
| 172 | gop.value_ptr.* = decl_node; |
| 173 | try t.global_scope.nodes.append(t.gpa, decl_node); |
| 174 | } |
| 175 | |
| 176 | fn fail( |
| 177 | t: *Translator, |
| 178 | err: anytype, |
| 179 | source_loc: TokenIndex, |
| 180 | comptime format: []const u8, |
| 181 | args: anytype, |
| 182 | ) (@TypeOf(err) || error{OutOfMemory}) { |
| 183 | try t.warn(&t.global_scope.base, source_loc, format, args); |
| 184 | return err; |
| 185 | } |
| 186 | |
| 187 | pub fn failDecl( |
| 188 | t: *Translator, |
| 189 | scope: *Scope, |
| 190 | tok_idx: TokenIndex, |
| 191 | name: []const u8, |
| 192 | comptime format: []const u8, |
| 193 | args: anytype, |
| 194 | ) Error!void { |
| 195 | const loc = t.tree.tokens.items(.loc)[tok_idx]; |
| 196 | return t.failDeclExtra(scope, loc, name, format, args); |
| 197 | } |
| 198 | |
| 199 | pub fn failDeclExtra( |
| 200 | t: *Translator, |
| 201 | scope: *Scope, |
| 202 | loc: aro.Source.Location, |
| 203 | name: []const u8, |
| 204 | comptime format: []const u8, |
| 205 | args: anytype, |
| 206 | ) Error!void { |
| 207 | // location |
| 208 | // pub const name = @compileError(msg); |
| 209 | const fail_msg = try std.fmt.allocPrint(t.arena, format, args); |
| 210 | const fail_decl = try ZigTag.fail_decl.create(t.arena, .{ |
| 211 | .actual = name, |
| 212 | .mangled = fail_msg, |
| 213 | .local = scope.id != .root, |
| 214 | }); |
| 215 | |
| 216 | const str = try t.locStr(loc); |
| 217 | const location_comment = try std.fmt.allocPrint(t.arena, "// {s}", .{str}); |
| 218 | const loc_node = try ZigTag.warning.create(t.arena, location_comment); |
| 219 | |
| 220 | if (scope.id == .root) { |
| 221 | try t.addTopLevelDecl(name, fail_decl); |
| 222 | try scope.appendNode(loc_node); |
| 223 | } else { |
| 224 | try scope.appendNode(fail_decl); |
| 225 | try scope.appendNode(loc_node); |
| 226 | |
| 227 | const bs = try scope.findBlockScope(t); |
| 228 | try bs.discardVariable(name); |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | fn warn(t: *Translator, scope: *Scope, tok_idx: TokenIndex, comptime format: []const u8, args: anytype) !void { |
| 233 | const loc = t.tree.tokens.items(.loc)[tok_idx]; |
| 234 | const str = try t.locStr(loc); |
| 235 | const value = try std.fmt.allocPrint(t.arena, "// {s}: warning: " ++ format, .{str} ++ args); |
| 236 | try scope.appendNode(try ZigTag.warning.create(t.arena, value)); |
| 237 | } |
| 238 | |
| 239 | pub const Options = struct { |
| 240 | gpa: mem.Allocator, |
| 241 | comp: *aro.Compilation, |
| 242 | pp: *const aro.Preprocessor, |
| 243 | tree: *const aro.Tree, |
| 244 | module_libs: bool, |
| 245 | pub_static: bool, |
| 246 | func_bodies: bool, |
| 247 | keep_macro_literals: bool, |
| 248 | default_init: bool, |
| 249 | strict_flex_arrays: StrictFlexArraysLevel, |
| 250 | }; |
| 251 | |
| 252 | pub fn translate(options: Options) mem.Allocator.Error![]u8 { |
| 253 | const gpa = options.gpa; |
| 254 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 255 | defer arena_allocator.deinit(); |
| 256 | const arena = arena_allocator.allocator(); |
| 257 | |
| 258 | var translator: Translator = .{ |
| 259 | .gpa = gpa, |
| 260 | .arena = arena, |
| 261 | .alias_list = .empty, |
| 262 | .global_scope = try arena.create(Scope.Root), |
| 263 | .comp = options.comp, |
| 264 | .pp = options.pp, |
| 265 | .tree = options.tree, |
| 266 | .pub_static = options.pub_static, |
| 267 | .func_bodies = options.func_bodies, |
| 268 | .keep_macro_literals = options.keep_macro_literals, |
| 269 | .default_init = options.default_init, |
| 270 | .strict_flex_arrays = options.strict_flex_arrays, |
| 271 | }; |
| 272 | translator.global_scope.* = Scope.Root.init(&translator); |
| 273 | defer { |
| 274 | translator.type_decls.deinit(gpa); |
| 275 | translator.alias_list.deinit(gpa); |
| 276 | translator.global_names.deinit(gpa); |
| 277 | translator.weak_global_names.deinit(gpa); |
| 278 | translator.opaque_demotes.deinit(gpa); |
| 279 | translator.unnamed_typedefs.deinit(gpa); |
| 280 | translator.anonymous_record_field_names.deinit(gpa); |
| 281 | translator.typedefs.deinit(gpa); |
| 282 | translator.global_scope.deinit(); |
| 283 | translator.wip_var_inits.deinit(gpa); |
| 284 | } |
| 285 | |
| 286 | try translator.prepopulateGlobalNameTable(); |
| 287 | try translator.transTopLevelDecls(); |
| 288 | |
| 289 | // Insert empty line before macros. |
| 290 | try translator.global_scope.nodes.append(gpa, try ZigTag.warning.create(arena, "\n")); |
| 291 | |
| 292 | try translator.transMacros(); |
| 293 | |
| 294 | for (translator.alias_list.items) |alias| { |
| 295 | if (!translator.global_scope.sym_table.contains(alias.alias)) { |
| 296 | const node = try ZigTag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name }); |
| 297 | try translator.addTopLevelDecl(alias.alias, node); |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | try translator.global_scope.processContainerMemberFns(); |
| 302 | |
| 303 | var allocating: std.Io.Writer.Allocating = .init(gpa); |
| 304 | defer allocating.deinit(); |
| 305 | |
| 306 | allocating.writer.writeAll( |
| 307 | \\const __root = @This(); |
| 308 | \\pub const __builtin = @import("std").zig.c_translation.builtins; |
| 309 | \\pub const __helpers = @import("std").zig.c_translation.helpers; |
| 310 | \\ |
| 311 | ) catch return error.OutOfMemory; |
| 312 | |
| 313 | var zig_ast = try ast.render(gpa, translator.global_scope.nodes.items); |
| 314 | defer { |
| 315 | gpa.free(zig_ast.source); |
| 316 | zig_ast.deinit(gpa); |
| 317 | } |
| 318 | zig_ast.render(gpa, &allocating.writer, .{}) catch return error.OutOfMemory; |
| 319 | return allocating.toOwnedSlice(); |
| 320 | } |
| 321 | |
| 322 | fn prepopulateGlobalNameTable(t: *Translator) !void { |
| 323 | for (t.tree.root_decls.items) |decl| { |
| 324 | switch (decl.get(t.tree)) { |
| 325 | .typedef => |typedef_decl| { |
| 326 | const decl_name = t.tree.tokSlice(typedef_decl.name_tok); |
| 327 | try t.global_names.put(t.gpa, decl_name, {}); |
| 328 | |
| 329 | // Check for typedefs with unnamed enum/record child types. |
| 330 | const base = typedef_decl.qt.base(t.comp); |
| 331 | switch (base.type) { |
| 332 | .@"enum" => |enum_ty| { |
| 333 | if (enum_ty.name.lookup(t.comp)[0] != '(') continue; |
| 334 | }, |
| 335 | .@"struct", .@"union" => |record_ty| { |
| 336 | if (record_ty.name.lookup(t.comp)[0] != '(') continue; |
| 337 | }, |
| 338 | else => continue, |
| 339 | } |
| 340 | |
| 341 | const gop = try t.unnamed_typedefs.getOrPut(t.gpa, base.qt); |
| 342 | if (gop.found_existing) { |
| 343 | // One typedef can declare multiple names. |
| 344 | // Don't put this one in `decl_table` so it's processed later. |
| 345 | continue; |
| 346 | } |
| 347 | gop.value_ptr.* = decl_name; |
| 348 | try t.type_decls.put(t.gpa, decl, decl_name); |
| 349 | try t.typedefs.put(t.gpa, decl_name, {}); |
| 350 | }, |
| 351 | |
| 352 | .struct_decl, |
| 353 | .union_decl, |
| 354 | .struct_forward_decl, |
| 355 | .union_forward_decl, |
| 356 | .enum_decl, |
| 357 | .enum_forward_decl, |
| 358 | => { |
| 359 | const decl_qt = decl.qt(t.tree); |
| 360 | const prefix, const name = switch (decl_qt.base(t.comp).type) { |
| 361 | .@"struct" => |struct_ty| .{ "struct", struct_ty.name.lookup(t.comp) }, |
| 362 | .@"union" => |union_ty| .{ "union", union_ty.name.lookup(t.comp) }, |
| 363 | .@"enum" => |enum_ty| .{ "enum", enum_ty.name.lookup(t.comp) }, |
| 364 | else => unreachable, |
| 365 | }; |
| 366 | const prefixed_name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ prefix, name }); |
| 367 | // `name` and `prefixed_name` are the preferred names for this type. |
| 368 | // However, we can name it anything else if necessary, so these are "weak names". |
| 369 | try t.weak_global_names.ensureUnusedCapacity(t.gpa, 2); |
| 370 | t.weak_global_names.putAssumeCapacity(name, {}); |
| 371 | t.weak_global_names.putAssumeCapacity(prefixed_name, {}); |
| 372 | }, |
| 373 | |
| 374 | .function, .variable => { |
| 375 | const decl_name = t.tree.tokSlice(decl.tok(t.tree)); |
| 376 | try t.global_names.put(t.gpa, decl_name, {}); |
| 377 | }, |
| 378 | .static_assert => {}, |
| 379 | .empty_decl => {}, |
| 380 | .global_asm => {}, |
| 381 | else => unreachable, |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| { |
| 386 | if (macro.isBuiltin()) continue; |
| 387 | if (!t.isSelfDefinedMacro(name, macro)) { |
| 388 | try t.global_names.put(t.gpa, name, {}); |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | /// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens) |
| 394 | /// Macros of this form will not be translated. |
| 395 | fn isSelfDefinedMacro(t: *Translator, name: []const u8, macro: aro.Preprocessor.Macro) bool { |
| 396 | if (macro.is_func) return false; |
| 397 | |
| 398 | if (macro.tokens.len < 1) return false; |
| 399 | const first_tok = macro.tokens[0]; |
| 400 | |
| 401 | const source = t.comp.getSource(macro.loc.id); |
| 402 | const slice = source.buf[first_tok.start..first_tok.end]; |
| 403 | |
| 404 | return std.mem.eql(u8, name, slice); |
| 405 | } |
| 406 | |
| 407 | // ======================= |
| 408 | // Declaration translation |
| 409 | // ======================= |
| 410 | |
| 411 | fn transTopLevelDecls(t: *Translator) !void { |
| 412 | for (t.tree.root_decls.items) |decl| { |
| 413 | try t.transDecl(&t.global_scope.base, decl); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void { |
| 418 | switch (decl.get(t.tree)) { |
| 419 | .typedef => |typedef_decl| { |
| 420 | // Implicit typedefs are translated only if referenced. |
| 421 | if (typedef_decl.implicit) return; |
| 422 | try t.transTypeDef(scope, decl); |
| 423 | }, |
| 424 | |
| 425 | .struct_decl, .union_decl => |record_decl| { |
| 426 | try t.transRecordDecl(scope, record_decl.container_qt); |
| 427 | }, |
| 428 | |
| 429 | .struct_forward_decl, .union_forward_decl => |record_decl| { |
| 430 | if (record_decl.definition) |some| { |
| 431 | return t.transDecl(scope, some); |
| 432 | } |
| 433 | try t.transRecordDecl(scope, record_decl.container_qt); |
| 434 | }, |
| 435 | |
| 436 | .enum_decl => |enum_decl| { |
| 437 | try t.transEnumDecl(scope, enum_decl.container_qt); |
| 438 | }, |
| 439 | |
| 440 | .enum_forward_decl => |enum_decl| { |
| 441 | if (enum_decl.definition) |some| { |
| 442 | return t.transDecl(scope, some); |
| 443 | } |
| 444 | try t.transEnumDecl(scope, enum_decl.container_qt); |
| 445 | }, |
| 446 | |
| 447 | .enum_field, |
| 448 | .record_field, |
| 449 | => return, |
| 450 | |
| 451 | .function => |function| { |
| 452 | if (function.definition) |definition| { |
| 453 | return t.transFnDecl(scope, definition.get(t.tree).function); |
| 454 | } |
| 455 | try t.transFnDecl(scope, function); |
| 456 | }, |
| 457 | |
| 458 | .variable => |variable| { |
| 459 | if (variable.definition != null) return; |
| 460 | try t.transVarDecl(scope, variable, decl); |
| 461 | }, |
| 462 | .static_assert => |static_assert| { |
| 463 | try t.transStaticAssert(&t.global_scope.base, static_assert); |
| 464 | }, |
| 465 | .global_asm => |global_asm| { |
| 466 | try t.transGlobalAsm(&t.global_scope.base, global_asm); |
| 467 | }, |
| 468 | .empty_decl => {}, |
| 469 | else => unreachable, |
| 470 | } |
| 471 | } |
| 472 | |
| 473 | pub const builtin_typedef_map = std.StaticStringMap([]const u8).initComptime(.{ |
| 474 | .{ "uint8_t", "u8" }, |
| 475 | .{ "int8_t", "i8" }, |
| 476 | .{ "uint16_t", "u16" }, |
| 477 | .{ "int16_t", "i16" }, |
| 478 | .{ "uint24_t", "u24" }, |
| 479 | .{ "int24_t", "i24" }, |
| 480 | .{ "uint32_t", "u32" }, |
| 481 | .{ "int32_t", "i32" }, |
| 482 | .{ "uint48_t", "u48" }, |
| 483 | .{ "int48_t", "i48" }, |
| 484 | .{ "uint64_t", "u64" }, |
| 485 | .{ "int64_t", "i64" }, |
| 486 | .{ "intptr_t", "isize" }, |
| 487 | .{ "uintptr_t", "usize" }, |
| 488 | .{ "ssize_t", "isize" }, |
| 489 | .{ "size_t", "usize" }, |
| 490 | }); |
| 491 | |
| 492 | fn transTypeDef(t: *Translator, scope: *Scope, typedef_node: Node.Index) Error!void { |
| 493 | const typedef_decl = typedef_node.get(t.tree).typedef; |
| 494 | if (t.type_decls.get(typedef_node)) |_| |
| 495 | return; // Avoid processing this decl twice |
| 496 | |
| 497 | const toplevel = scope.id == .root; |
| 498 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined; |
| 499 | |
| 500 | var name: []const u8 = t.tree.tokSlice(typedef_decl.name_tok); |
| 501 | try t.typedefs.put(t.gpa, name, {}); |
| 502 | |
| 503 | if (builtin_typedef_map.get(name)) |builtin| { |
| 504 | return t.type_decls.putNoClobber(t.gpa, typedef_node, builtin); |
| 505 | } |
| 506 | if (!toplevel) name = try bs.makeMangledName(name); |
| 507 | try t.type_decls.putNoClobber(t.gpa, typedef_node, name); |
| 508 | |
| 509 | const typedef_loc = typedef_decl.name_tok; |
| 510 | const init_node = t.transType(scope, typedef_decl.qt, typedef_loc) catch |err| switch (err) { |
| 511 | error.UnsupportedType => { |
| 512 | return t.failDecl(scope, typedef_loc, name, "unable to resolve typedef child type", .{}); |
| 513 | }, |
| 514 | error.OutOfMemory => |e| return e, |
| 515 | }; |
| 516 | |
| 517 | const payload = try t.arena.create(ast.Payload.SimpleVarDecl); |
| 518 | payload.* = .{ |
| 519 | .base = .{ .tag = if (toplevel) .pub_var_simple else .var_simple }, |
| 520 | .data = .{ |
| 521 | .name = name, |
| 522 | .init = init_node, |
| 523 | }, |
| 524 | }; |
| 525 | const node = ZigNode.initPayload(&payload.base); |
| 526 | |
| 527 | if (toplevel) { |
| 528 | try t.addTopLevelDecl(name, node); |
| 529 | } else { |
| 530 | try scope.appendNode(node); |
| 531 | try bs.discardVariable(name); |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | fn mangleWeakGlobalName(t: *Translator, want_name: []const u8) Error![]const u8 { |
| 536 | var cur_name = want_name; |
| 537 | |
| 538 | if (!t.weak_global_names.contains(want_name)) { |
| 539 | // This type wasn't noticed by the name detection pass, so nothing has been treating this as |
| 540 | // a weak global name. We must mangle it to avoid conflicts with locals. |
| 541 | cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() }); |
| 542 | } |
| 543 | |
| 544 | while (t.global_names.contains(cur_name)) { |
| 545 | cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() }); |
| 546 | } |
| 547 | return cur_name; |
| 548 | } |
| 549 | |
| 550 | fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void { |
| 551 | const base = record_qt.base(t.comp); |
| 552 | const record_ty = switch (base.type) { |
| 553 | .@"struct", .@"union" => |record_ty| record_ty, |
| 554 | else => unreachable, |
| 555 | }; |
| 556 | |
| 557 | if (t.type_decls.get(record_ty.decl_node)) |_| |
| 558 | return; // Avoid processing this decl twice |
| 559 | |
| 560 | const toplevel = scope.id == .root; |
| 561 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined; |
| 562 | |
| 563 | const container_kind: ZigTag = if (base.type == .@"union") .@"union" else .@"struct"; |
| 564 | const container_kind_name = @tagName(container_kind); |
| 565 | |
| 566 | var bare_name = record_ty.name.lookup(t.comp); |
| 567 | var is_unnamed = false; |
| 568 | var name = bare_name; |
| 569 | |
| 570 | if (t.unnamed_typedefs.get(base.qt)) |typedef_name| { |
| 571 | bare_name = typedef_name; |
| 572 | name = typedef_name; |
| 573 | } else { |
| 574 | if (record_ty.isAnonymous(t.comp)) { |
| 575 | bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()}); |
| 576 | is_unnamed = true; |
| 577 | } |
| 578 | name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ container_kind_name, bare_name }); |
| 579 | if (toplevel and !is_unnamed) { |
| 580 | name = try t.mangleWeakGlobalName(name); |
| 581 | } |
| 582 | } |
| 583 | if (!toplevel) name = try bs.makeMangledName(name); |
| 584 | try t.type_decls.putNoClobber(t.gpa, record_ty.decl_node, name); |
| 585 | |
| 586 | const is_pub = toplevel and !is_unnamed; |
| 587 | const init_node = init: { |
| 588 | if (record_ty.layout == null) { |
| 589 | try t.opaque_demotes.put(t.gpa, base.qt, {}); |
| 590 | break :init ZigTag.opaque_literal.init(); |
| 591 | } |
| 592 | |
| 593 | var fields: std.ArrayList(ast.Payload.Container.Field) = .empty; |
| 594 | defer fields.deinit(t.gpa); |
| 595 | try fields.ensureUnusedCapacity(t.gpa, record_ty.fields.len); |
| 596 | |
| 597 | var functions: std.ArrayList(ZigNode) = .empty; |
| 598 | defer functions.deinit(t.gpa); |
| 599 | |
| 600 | var unnamed_field_count: u32 = 0; |
| 601 | |
| 602 | // If a record doesn't have any attributes that would affect the alignment and |
| 603 | // layout, then we can just use a simple `extern` type. If it does have attributes, |
| 604 | // then we need to inspect the layout and assign an `align` value for each field. |
| 605 | const has_alignment_attributes = aligned: { |
| 606 | if (record_qt.hasAttribute(t.comp, .@"packed")) break :aligned true; |
| 607 | if (record_qt.hasAttribute(t.comp, .aligned)) break :aligned true; |
| 608 | for (record_ty.fields) |field| { |
| 609 | const field_attrs = field.attributes(t.comp); |
| 610 | for (field_attrs) |field_attr| { |
| 611 | switch (field_attr.tag) { |
| 612 | .@"packed", .aligned => break :aligned true, |
| 613 | else => {}, |
| 614 | } |
| 615 | } |
| 616 | } |
| 617 | break :aligned false; |
| 618 | }; |
| 619 | const head_field_alignment: ?c_uint = if (has_alignment_attributes) t.headFieldAlignment(record_ty) else null; |
| 620 | |
| 621 | for (record_ty.fields, 0..) |field, field_index| { |
| 622 | const field_loc = field.name_tok; |
| 623 | |
| 624 | // Demote record to opaque if it contains a bitfield |
| 625 | if (field.bit_width != .null) { |
| 626 | try t.opaque_demotes.put(t.gpa, base.qt, {}); |
| 627 | try t.warn(scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); |
| 628 | break :init ZigTag.opaque_literal.init(); |
| 629 | } |
| 630 | |
| 631 | var field_name = field.name.lookup(t.comp); |
| 632 | if (field.name_tok == 0) { |
| 633 | field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count}); |
| 634 | unnamed_field_count += 1; |
| 635 | try t.anonymous_record_field_names.put(t.gpa, .{ |
| 636 | .parent = base.qt, |
| 637 | .field = field.qt, |
| 638 | }, field_name); |
| 639 | } |
| 640 | |
| 641 | const field_type = field_type: { |
| 642 | // Check if this is a flexible array member. |
| 643 | flexible: { |
| 644 | if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible; |
| 645 | const array_ty = field.qt.get(t.comp, .array) orelse break :flexible; |
| 646 | if (!t.isFlexibleArrayLen(array_ty.len)) break :flexible; |
| 647 | |
| 648 | const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) { |
| 649 | error.UnsupportedType => break :flexible, |
| 650 | else => |e| return e, |
| 651 | }; |
| 652 | const backing_array_len: usize = switch (array_ty.len) { |
| 653 | .fixed => |n| @intCast(n), |
| 654 | else => 0, |
| 655 | }; |
| 656 | const backing_array = try ZigTag.array_type.create(t.arena, .{ .len = backing_array_len, .elem_type = elem_type }); |
| 657 | |
| 658 | const member_name = field_name; |
| 659 | field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name}); |
| 660 | |
| 661 | const member = try t.createFlexibleMemberFn(member_name, field_name); |
| 662 | try functions.append(t.gpa, member); |
| 663 | |
| 664 | break :field_type backing_array; |
| 665 | } |
| 666 | |
| 667 | break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) { |
| 668 | error.UnsupportedType => { |
| 669 | try t.opaque_demotes.put(t.gpa, base.qt, {}); |
| 670 | try t.warn(scope, field.name_tok, "{s} demoted to opaque type - unable to translate type of field {s}", .{ |
| 671 | container_kind_name, |
| 672 | field_name, |
| 673 | }); |
| 674 | break :init ZigTag.opaque_literal.init(); |
| 675 | }, |
| 676 | else => |e| return e, |
| 677 | }; |
| 678 | }; |
| 679 | |
| 680 | // Demote record to opaque if it contains an opaque field |
| 681 | if (t.typeWasDemotedToOpaque(field.qt)) { |
| 682 | try t.opaque_demotes.put(t.gpa, base.qt, {}); |
| 683 | try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name}); |
| 684 | break :init ZigTag.opaque_literal.init(); |
| 685 | } |
| 686 | |
| 687 | const field_alignment = if (has_alignment_attributes) |
| 688 | t.alignmentForField(record_ty, head_field_alignment, field_index) |
| 689 | else |
| 690 | null; |
| 691 | |
| 692 | // C99 introduced designated initializers for structs. Omitted fields are implicitly |
| 693 | // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero |
| 694 | // values for translated struct fields permits Zig code to comfortably use such an API. |
| 695 | const default_value = if (t.default_init and container_kind == .@"struct") |
| 696 | try t.createZeroValueNode(field.qt, field_type, .no_as) |
| 697 | else |
| 698 | null; |
| 699 | |
| 700 | fields.appendAssumeCapacity(.{ |
| 701 | .name = field_name, |
| 702 | .type = field_type, |
| 703 | .alignment = field_alignment, |
| 704 | .default_value = default_value, |
| 705 | }); |
| 706 | } |
| 707 | |
| 708 | // A record is empty if it has no fields or only flexible array fields. |
| 709 | if (record_ty.fields.len == functions.items.len and |
| 710 | t.comp.target.os.tag == .windows and t.comp.target.abi == .msvc) |
| 711 | { |
| 712 | // In MSVC empty records have the same size as their alignment. |
| 713 | const padding_bits = record_ty.layout.?.size_bits; |
| 714 | const alignment_bits = record_ty.layout.?.field_alignment_bits; |
| 715 | |
| 716 | try fields.append(t.gpa, .{ |
| 717 | .name = "_padding", |
| 718 | .type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})), |
| 719 | .alignment = @divExact(alignment_bits, 8), |
| 720 | .default_value = if (t.default_init and container_kind == .@"struct") |
| 721 | ZigTag.zero_literal.init() |
| 722 | else |
| 723 | null, |
| 724 | }); |
| 725 | } |
| 726 | |
| 727 | const container_payload = try t.arena.create(ast.Payload.Container); |
| 728 | container_payload.* = .{ |
| 729 | .base = .{ .tag = container_kind }, |
| 730 | .data = .{ |
| 731 | .layout = .@"extern", |
| 732 | .fields = try t.arena.dupe(ast.Payload.Container.Field, fields.items), |
| 733 | .decls = try t.arena.dupe(ZigNode, functions.items), |
| 734 | }, |
| 735 | }; |
| 736 | break :init ZigNode.initPayload(&container_payload.base); |
| 737 | }; |
| 738 | |
| 739 | const payload = try t.arena.create(ast.Payload.SimpleVarDecl); |
| 740 | payload.* = .{ |
| 741 | .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple }, |
| 742 | .data = .{ |
| 743 | .name = name, |
| 744 | .init = init_node, |
| 745 | }, |
| 746 | }; |
| 747 | const node = ZigNode.initPayload(&payload.base); |
| 748 | if (toplevel) { |
| 749 | try t.addTopLevelDecl(name, node); |
| 750 | // Only add the alias if the name is available *and* it was caught by |
| 751 | // name detection. Don't bother performing a weak mangle, since a |
| 752 | // mangled name is of no real use here. |
| 753 | if (!is_unnamed and !t.global_names.contains(bare_name) and t.weak_global_names.contains(bare_name)) |
| 754 | try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name }); |
| 755 | try t.global_scope.container_member_fns_map.put(t.gpa, record_qt, .{ |
| 756 | .container_decl_ptr = &payload.data.init, |
| 757 | }); |
| 758 | } else { |
| 759 | try scope.appendNode(node); |
| 760 | try bs.discardVariable(name); |
| 761 | } |
| 762 | } |
| 763 | |
| 764 | fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!void { |
| 765 | const func_ty = function.qt.get(t.comp, .func).?; |
| 766 | |
| 767 | const fn_name = t.tree.tokSlice(function.name_tok); |
| 768 | if (scope.getAlias(fn_name) != null or t.global_scope.containsNow(fn_name)) |
| 769 | return; // Avoid processing this decl twice |
| 770 | |
| 771 | const fn_decl_loc = function.name_tok; |
| 772 | const has_body = function.body != null and func_ty.kind != .variadic and t.func_bodies; |
| 773 | if (function.body != null and func_ty.kind == .variadic) { |
| 774 | try t.warn(scope, function.name_tok, "TODO unable to translate variadic function, demoted to extern", .{}); |
| 775 | } |
| 776 | |
| 777 | const is_always_inline = has_body and function.qt.getAttribute(t.comp, .always_inline) != null; |
| 778 | const proto_ctx: FnProtoContext = .{ |
| 779 | .fn_name = fn_name, |
| 780 | .is_always_inline = is_always_inline, |
| 781 | .is_extern = !has_body, |
| 782 | .is_export = !function.static and has_body and !is_always_inline and !function.@"inline", |
| 783 | .is_pub = scope.id == .root and (!function.static or t.pub_static), |
| 784 | .has_body = has_body, |
| 785 | .cc = if (function.qt.getAttribute(t.comp, .calling_convention)) |some| switch (some.cc) { |
| 786 | .c => .c, |
| 787 | .stdcall => .x86_stdcall, |
| 788 | .thiscall => .x86_thiscall, |
| 789 | .fastcall => .x86_fastcall, |
| 790 | .regcall => .x86_regcall, |
| 791 | .riscv_vector => .riscv_vector, |
| 792 | .aarch64_sve_pcs => .aarch64_sve_pcs, |
| 793 | .aarch64_vector_pcs => .aarch64_vfabi, |
| 794 | .arm_aapcs => .arm_aapcs, |
| 795 | .arm_aapcs_vfp => .arm_aapcs_vfp, |
| 796 | .vectorcall => switch (t.comp.target.cpu.arch) { |
| 797 | .x86 => .x86_vectorcall, |
| 798 | .aarch64, .aarch64_be => .aarch64_vfabi, |
| 799 | else => .c, |
| 800 | }, |
| 801 | .x86_64_sysv => .x86_64_sysv, |
| 802 | .x86_64_win => .x86_64_win, |
| 803 | } else .c, |
| 804 | }; |
| 805 | |
| 806 | const proto_node = t.transFnType(&t.global_scope.base, function.qt, func_ty, fn_decl_loc, proto_ctx) catch |err| switch (err) { |
| 807 | error.UnsupportedType => { |
| 808 | return t.failDecl(scope, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); |
| 809 | }, |
| 810 | error.OutOfMemory => |e| return e, |
| 811 | }; |
| 812 | |
| 813 | const proto_payload = proto_node.castTag(.func).?; |
| 814 | if (!has_body) { |
| 815 | if (scope.id != .root) { |
| 816 | const bs: *Scope.Block = try scope.findBlockScope(t); |
| 817 | const mangled_name = try bs.createMangledName(fn_name, false, Scope.Block.extern_local_prefix); |
| 818 | const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = mangled_name, .init = proto_node }); |
| 819 | try scope.appendNode(wrapped); |
| 820 | try bs.discardVariable(mangled_name); |
| 821 | return; |
| 822 | } |
| 823 | try t.global_scope.addMemberFunction(func_ty, proto_payload); |
| 824 | return t.addTopLevelDecl(fn_name, proto_node); |
| 825 | } |
| 826 | |
| 827 | // actual function definition with body |
| 828 | const body_stmt = function.body.?.get(t.tree).compound_stmt; |
| 829 | var block_scope = try Scope.Block.init(t, &t.global_scope.base, false); |
| 830 | block_scope.return_type = func_ty.return_type; |
| 831 | defer block_scope.deinit(); |
| 832 | |
| 833 | var param_id: c_uint = 0; |
| 834 | for (proto_payload.data.params, func_ty.params) |*param, param_info| { |
| 835 | const param_name = param.name orelse { |
| 836 | proto_payload.data.is_extern = true; |
| 837 | proto_payload.data.is_export = false; |
| 838 | proto_payload.data.is_inline = false; |
| 839 | try t.warn(&t.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name}); |
| 840 | return t.addTopLevelDecl(fn_name, proto_node); |
| 841 | }; |
| 842 | |
| 843 | const is_const = param_info.qt.@"const"; |
| 844 | |
| 845 | const mangled_param_name = try block_scope.makeMangledName(param_name); |
| 846 | param.name = mangled_param_name; |
| 847 | |
| 848 | if (!is_const) { |
| 849 | const bare_arg_name = try std.fmt.allocPrint(t.arena, "arg_{s}", .{mangled_param_name}); |
| 850 | const arg_name = try block_scope.makeMangledName(bare_arg_name); |
| 851 | param.name = arg_name; |
| 852 | |
| 853 | const redecl_node = try ZigTag.arg_redecl.create(t.arena, .{ .actual = mangled_param_name, .mangled = arg_name }); |
| 854 | try block_scope.statements.append(t.gpa, redecl_node); |
| 855 | } |
| 856 | try block_scope.discardVariable(mangled_param_name); |
| 857 | |
| 858 | param_id += 1; |
| 859 | } |
| 860 | |
| 861 | t.transCompoundStmtInline(body_stmt, &block_scope) catch |err| switch (err) { |
| 862 | error.OutOfMemory => |e| return e, |
| 863 | error.SelfReferential => unreachable, |
| 864 | error.UnsupportedTranslation, |
| 865 | error.UnsupportedType, |
| 866 | => { |
| 867 | proto_payload.data.is_extern = true; |
| 868 | proto_payload.data.is_export = false; |
| 869 | proto_payload.data.is_inline = false; |
| 870 | try t.warn(&t.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{}); |
| 871 | return t.addTopLevelDecl(fn_name, proto_node); |
| 872 | }, |
| 873 | }; |
| 874 | |
| 875 | try t.global_scope.addMemberFunction(func_ty, proto_payload); |
| 876 | proto_payload.data.body = try block_scope.complete(); |
| 877 | return t.addTopLevelDecl(fn_name, proto_node); |
| 878 | } |
| 879 | |
| 880 | fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void { |
| 881 | const base_name = t.tree.tokSlice(variable.name_tok); |
| 882 | const toplevel = scope.id == .root; |
| 883 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined; |
| 884 | const name, const use_base_name = blk: { |
| 885 | if (toplevel) break :blk .{ base_name, false }; |
| 886 | |
| 887 | // Local extern and static variables are wrapped in a struct. |
| 888 | const prefix: ?[]const u8 = switch (variable.storage_class) { |
| 889 | .@"extern" => Scope.Block.extern_local_prefix, |
| 890 | .static => Scope.Block.static_local_prefix, |
| 891 | else => null, |
| 892 | }; |
| 893 | break :blk .{ try bs.createMangledName(base_name, false, prefix), prefix != null }; |
| 894 | }; |
| 895 | |
| 896 | if (t.typeWasDemotedToOpaque(variable.qt)) { |
| 897 | if (variable.storage_class != .@"extern" and scope.id == .root) { |
| 898 | return t.failDecl(scope, variable.name_tok, name, "non-extern variable has opaque type", .{}); |
| 899 | } else { |
| 900 | return t.failDecl(scope, variable.name_tok, name, "local variable has opaque type", .{}); |
| 901 | } |
| 902 | } |
| 903 | |
| 904 | const type_node = (if (variable.initializer) |init| |
| 905 | t.transTypeInit(scope, variable.qt, init, variable.name_tok) |
| 906 | else |
| 907 | t.transType(scope, variable.qt, variable.name_tok)) catch |err| switch (err) { |
| 908 | error.UnsupportedType => { |
| 909 | return t.failDecl(scope, variable.name_tok, name, "unable to translate variable declaration type", .{}); |
| 910 | }, |
| 911 | else => |e| return e, |
| 912 | }; |
| 913 | |
| 914 | const array_ty = variable.qt.get(t.comp, .array); |
| 915 | var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const"); |
| 916 | var is_extern = variable.storage_class == .@"extern"; |
| 917 | |
| 918 | var self_referential = false; |
| 919 | const init_node = init: { |
| 920 | if (variable.initializer) |init| { |
| 921 | const maybe_literal = init.get(t.tree); |
| 922 | if (!toplevel) try t.wip_var_inits.putNoClobber(t.gpa, decl_node, {}); |
| 923 | defer _ = t.wip_var_inits.remove(decl_node); |
| 924 | |
| 925 | const init_node = (if (maybe_literal == .string_literal_expr) |
| 926 | t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node) |
| 927 | else |
| 928 | t.transExprCoercing(scope, init, .used)) catch |err| switch (err) { |
| 929 | error.SelfReferential => { |
| 930 | self_referential = true; |
| 931 | break :init ZigTag.undefined_literal.init(); |
| 932 | }, |
| 933 | error.UnsupportedTranslation, error.UnsupportedType => { |
| 934 | return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{}); |
| 935 | }, |
| 936 | else => |e| return e, |
| 937 | }; |
| 938 | |
| 939 | break :init try t.toNonBool(init_node, variable.qt); |
| 940 | } |
| 941 | if (variable.storage_class == .@"extern") { |
| 942 | if (array_ty != null and array_ty.?.len == .incomplete) { |
| 943 | // Oh no, an extern array of unknown size! These are really fun because there's no |
| 944 | // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer |
| 945 | // to the data initialized via @extern. |
| 946 | |
| 947 | // Since this is really a pointer to the underlying data, we tweak a few properties. |
| 948 | is_extern = false; |
| 949 | is_const = true; |
| 950 | |
| 951 | const name_str = try std.fmt.allocPrint(t.arena, "\"{s}\"", .{base_name}); |
| 952 | break :init try ZigTag.builtin_extern.create(t.arena, .{ |
| 953 | .type = type_node, |
| 954 | .name = try ZigTag.string_literal.create(t.arena, name_str), |
| 955 | }); |
| 956 | } |
| 957 | break :init null; |
| 958 | } |
| 959 | if (toplevel or variable.storage_class == .static or variable.thread_local) { |
| 960 | // The C language specification states that variables with static or threadlocal |
| 961 | // storage without an initializer are initialized to a zero value. |
| 962 | break :init try t.createZeroValueNode(variable.qt, type_node, .no_as); |
| 963 | } |
| 964 | break :init ZigTag.undefined_literal.init(); |
| 965 | }; |
| 966 | |
| 967 | const linksection_string = blk: { |
| 968 | if (variable.qt.getAttribute(t.comp, .section)) |section| { |
| 969 | break :blk t.comp.interner.get(section.name.ref()).bytes; |
| 970 | } |
| 971 | break :blk null; |
| 972 | }; |
| 973 | |
| 974 | // TODO actually set with @export/@extern |
| 975 | const linkage = variable.qt.linkage(t.comp); |
| 976 | if (linkage != .strong) { |
| 977 | try t.warn(scope, variable.name_tok, "TODO {s} linkage ignored", .{@tagName(linkage)}); |
| 978 | } |
| 979 | |
| 980 | const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null; |
| 981 | var node = try ZigTag.var_decl.create(t.arena, .{ |
| 982 | .is_pub = toplevel, |
| 983 | .is_const = is_const and !self_referential, |
| 984 | .is_extern = is_extern, |
| 985 | .is_export = toplevel and variable.storage_class == .auto and linkage == .strong, |
| 986 | .is_threadlocal = variable.thread_local, |
| 987 | .linksection_string = linksection_string, |
| 988 | .alignment = alignment, |
| 989 | .name = if (use_base_name) base_name else name, |
| 990 | .type = type_node, |
| 991 | .init = init_node, |
| 992 | }); |
| 993 | |
| 994 | if (toplevel) { |
| 995 | try t.addTopLevelDecl(name, node); |
| 996 | } else { |
| 997 | if (use_base_name) { |
| 998 | node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node }); |
| 999 | } |
| 1000 | try scope.appendNode(node); |
| 1001 | if (self_referential) { |
| 1002 | const deferred_init = t.transExprCoercing(scope, variable.initializer.?, .used) catch |err| switch (err) { |
| 1003 | error.SelfReferential => unreachable, |
| 1004 | error.UnsupportedTranslation, error.UnsupportedType => { |
| 1005 | return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{}); |
| 1006 | }, |
| 1007 | else => |e| return e, |
| 1008 | }; |
| 1009 | |
| 1010 | const assign = try ZigTag.assign.create(t.arena, .{ |
| 1011 | .lhs = try ZigTag.identifier.create(t.arena, name), |
| 1012 | .rhs = try t.toNonBool(deferred_init, variable.qt), |
| 1013 | }); |
| 1014 | try scope.appendNode(assign); |
| 1015 | } |
| 1016 | try bs.discardVariable(name); |
| 1017 | |
| 1018 | if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| { |
| 1019 | const cleanup_fn_name = t.tree.tokSlice(cleanup_attr.function.tok); |
| 1020 | const mangled_fn_name = scope.getAlias(cleanup_fn_name) orelse cleanup_fn_name; |
| 1021 | const fn_id = try ZigTag.identifier.create(t.arena, mangled_fn_name); |
| 1022 | |
| 1023 | const varname = try ZigTag.identifier.create(t.arena, name); |
| 1024 | const args = try t.arena.alloc(ZigNode, 1); |
| 1025 | args[0] = try ZigTag.address_of.create(t.arena, varname); |
| 1026 | |
| 1027 | const cleanup_call = try ZigTag.call.create(t.arena, .{ .lhs = fn_id, .args = args }); |
| 1028 | const discard = try ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = cleanup_call }); |
| 1029 | const deferred_cleanup = try ZigTag.@"defer".create(t.arena, discard); |
| 1030 | |
| 1031 | try bs.statements.append(t.gpa, deferred_cleanup); |
| 1032 | } |
| 1033 | } |
| 1034 | } |
| 1035 | |
| 1036 | fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void { |
| 1037 | const base = enum_qt.base(t.comp); |
| 1038 | const enum_ty = base.type.@"enum"; |
| 1039 | |
| 1040 | if (t.type_decls.get(enum_ty.decl_node)) |_| |
| 1041 | return; // Avoid processing this decl twice |
| 1042 | |
| 1043 | const toplevel = scope.id == .root; |
| 1044 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined; |
| 1045 | |
| 1046 | var bare_name = enum_ty.name.lookup(t.comp); |
| 1047 | var is_unnamed = false; |
| 1048 | var name = bare_name; |
| 1049 | if (t.unnamed_typedefs.get(base.qt)) |typedef_name| { |
| 1050 | bare_name = typedef_name; |
| 1051 | name = typedef_name; |
| 1052 | } else { |
| 1053 | if (enum_ty.isAnonymous(t.comp)) { |
| 1054 | bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()}); |
| 1055 | is_unnamed = true; |
| 1056 | } |
| 1057 | name = try std.fmt.allocPrint(t.arena, "enum_{s}", .{bare_name}); |
| 1058 | } |
| 1059 | if (!toplevel) name = try bs.makeMangledName(name); |
| 1060 | try t.type_decls.putNoClobber(t.gpa, enum_ty.decl_node, name); |
| 1061 | |
| 1062 | const enum_type_node = if (!base.qt.hasIncompleteSize(t.comp)) blk: { |
| 1063 | const enum_decl = enum_ty.decl_node.get(t.tree).enum_decl; |
| 1064 | for (enum_ty.fields, enum_decl.fields) |field, field_node| { |
| 1065 | var enum_val_name = field.name.lookup(t.comp); |
| 1066 | if (!toplevel) { |
| 1067 | enum_val_name = try bs.makeMangledName(enum_val_name); |
| 1068 | } |
| 1069 | |
| 1070 | const enum_const_type_node: ?ZigNode = t.transType(scope, field.qt, field.name_tok) catch |err| switch (err) { |
| 1071 | error.UnsupportedType => null, |
| 1072 | else => |e| return e, |
| 1073 | }; |
| 1074 | |
| 1075 | const val = t.tree.value_map.get(field_node).?; |
| 1076 | const enum_const_def = try ZigTag.enum_constant.create(t.arena, .{ |
| 1077 | .name = enum_val_name, |
| 1078 | .is_public = toplevel, |
| 1079 | .type = enum_const_type_node, |
| 1080 | .value = try t.createIntNode(val), |
| 1081 | }); |
| 1082 | if (toplevel) |
| 1083 | try t.addTopLevelDecl(enum_val_name, enum_const_def) |
| 1084 | else { |
| 1085 | try scope.appendNode(enum_const_def); |
| 1086 | try bs.discardVariable(enum_val_name); |
| 1087 | } |
| 1088 | } |
| 1089 | |
| 1090 | break :blk t.transType(scope, enum_ty.tag.?, enum_decl.name_or_kind_tok) catch |err| switch (err) { |
| 1091 | error.UnsupportedType => { |
| 1092 | return t.failDecl(scope, enum_decl.name_or_kind_tok, name, "unable to translate enum integer type", .{}); |
| 1093 | }, |
| 1094 | else => |e| return e, |
| 1095 | }; |
| 1096 | } else blk: { |
| 1097 | try t.opaque_demotes.put(t.gpa, base.qt, {}); |
| 1098 | break :blk ZigTag.opaque_literal.init(); |
| 1099 | }; |
| 1100 | |
| 1101 | const is_pub = toplevel and !is_unnamed; |
| 1102 | const payload = try t.arena.create(ast.Payload.SimpleVarDecl); |
| 1103 | payload.* = .{ |
| 1104 | .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple }, |
| 1105 | .data = .{ |
| 1106 | .init = enum_type_node, |
| 1107 | .name = name, |
| 1108 | }, |
| 1109 | }; |
| 1110 | const node = ZigNode.initPayload(&payload.base); |
| 1111 | if (toplevel) { |
| 1112 | try t.addTopLevelDecl(name, node); |
| 1113 | if (!is_unnamed) |
| 1114 | try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name }); |
| 1115 | } else { |
| 1116 | try scope.appendNode(node); |
| 1117 | try bs.discardVariable(name); |
| 1118 | } |
| 1119 | } |
| 1120 | |
| 1121 | fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAssert) Error!void { |
| 1122 | const condition = t.transExpr(scope, static_assert.cond, .used) catch |err| switch (err) { |
| 1123 | error.SelfReferential => unreachable, |
| 1124 | error.UnsupportedTranslation, error.UnsupportedType => { |
| 1125 | return try t.warn(&t.global_scope.base, static_assert.cond.tok(t.tree), "unable to translate _Static_assert condition", .{}); |
| 1126 | }, |
| 1127 | error.OutOfMemory => |e| return e, |
| 1128 | }; |
| 1129 | |
| 1130 | // generate @compileError message that matches C compiler output |
| 1131 | const diagnostic = if (static_assert.message) |message| str: { |
| 1132 | // Aro guarantees this to be a string literal. |
| 1133 | const str_val = t.tree.value_map.get(message).?; |
| 1134 | const str_qt = message.qt(t.tree); |
| 1135 | |
| 1136 | const bytes = t.comp.interner.get(str_val.ref()).bytes; |
| 1137 | var allocating: std.Io.Writer.Allocating = .init(t.gpa); |
| 1138 | defer allocating.deinit(); |
| 1139 | |
| 1140 | allocating.writer.writeAll("\"static assertion failed \\") catch return error.OutOfMemory; |
| 1141 | |
| 1142 | aro.Value.printString(bytes, str_qt, t.comp, &allocating.writer) catch return error.OutOfMemory; |
| 1143 | allocating.writer.end -= 1; // printString adds a terminating " so we need to remove it |
| 1144 | allocating.writer.writeAll("\\\"\"") catch return error.OutOfMemory; |
| 1145 | |
| 1146 | break :str try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.written())); |
| 1147 | } else try ZigTag.string_literal.create(t.arena, "\"static assertion failed\""); |
| 1148 | |
| 1149 | const assert_node = try ZigTag.static_assert.create(t.arena, .{ .lhs = condition, .rhs = diagnostic }); |
| 1150 | try scope.appendNode(assert_node); |
| 1151 | } |
| 1152 | |
| 1153 | fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.GlobalAsm) Error!void { |
| 1154 | const asm_string = t.tree.value_map.get(global_asm.asm_str).?; |
| 1155 | const bytes = t.comp.interner.get(asm_string.ref()).bytes; |
| 1156 | |
| 1157 | var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len); |
| 1158 | defer allocating.deinit(); |
| 1159 | aro.Value.printString(bytes, global_asm.asm_str.qt(t.tree), t.comp, &allocating.writer) catch return error.OutOfMemory; |
| 1160 | |
| 1161 | const str_node = try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.written())); |
| 1162 | |
| 1163 | const asm_node = try ZigTag.asm_simple.create(t.arena, str_node); |
| 1164 | const block = try ZigTag.block_single.create(t.arena, asm_node); |
| 1165 | const comptime_node = try ZigTag.@"comptime".create(t.arena, block); |
| 1166 | |
| 1167 | try scope.appendNode(comptime_node); |
| 1168 | } |
| 1169 | |
| 1170 | // ================ |
| 1171 | // Type translation |
| 1172 | // ================ |
| 1173 | |
| 1174 | fn getTypeStr(t: *Translator, qt: QualType) ![]const u8 { |
| 1175 | var allocating: std.Io.Writer.Allocating = .init(t.gpa); |
| 1176 | defer allocating.deinit(); |
| 1177 | qt.print(t.comp, &allocating.writer) catch return error.OutOfMemory; |
| 1178 | return t.arena.dupe(u8, allocating.written()); |
| 1179 | } |
| 1180 | |
| 1181 | fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex) TypeError!ZigNode { |
| 1182 | loop: switch (qt.type(t.comp)) { |
| 1183 | .atomic => { |
| 1184 | const type_name = try t.getTypeStr(qt); |
| 1185 | return t.fail(error.UnsupportedType, source_loc, "TODO support atomic type: '{s}'", .{type_name}); |
| 1186 | }, |
| 1187 | .void => return ZigTag.type.create(t.arena, "anyopaque"), |
| 1188 | .bool => return ZigTag.type.create(t.arena, "bool"), |
| 1189 | .int => |int_ty| switch (int_ty) { |
| 1190 | //.char => return ZigTag.type.create(t.arena, "c_char"), // TODO: this is the preferred translation |
| 1191 | .char => return ZigTag.type.create(t.arena, "u8"), |
| 1192 | .schar => return ZigTag.type.create(t.arena, "i8"), |
| 1193 | .uchar => return ZigTag.type.create(t.arena, "u8"), |
| 1194 | .short => return ZigTag.type.create(t.arena, "c_short"), |
| 1195 | .ushort => return ZigTag.type.create(t.arena, "c_ushort"), |
| 1196 | .int => return ZigTag.type.create(t.arena, "c_int"), |
| 1197 | .uint => return ZigTag.type.create(t.arena, "c_uint"), |
| 1198 | .long => return ZigTag.type.create(t.arena, "c_long"), |
| 1199 | .ulong => return ZigTag.type.create(t.arena, "c_ulong"), |
| 1200 | .long_long => return ZigTag.type.create(t.arena, "c_longlong"), |
| 1201 | .ulong_long => return ZigTag.type.create(t.arena, "c_ulonglong"), |
| 1202 | .int128 => return ZigTag.type.create(t.arena, "i128"), |
| 1203 | .uint128 => return ZigTag.type.create(t.arena, "u128"), |
| 1204 | }, |
| 1205 | .float => |float_ty| switch (float_ty) { |
| 1206 | .fp16, .float16 => return ZigTag.type.create(t.arena, "f16"), |
| 1207 | .float, .float32 => return ZigTag.type.create(t.arena, "f32"), |
| 1208 | .double, .float64, .float32x => return ZigTag.type.create(t.arena, "f64"), |
| 1209 | .long_double, .float64x => return ZigTag.type.create(t.arena, "c_longdouble"), |
| 1210 | .float128 => return ZigTag.type.create(t.arena, "f128"), |
| 1211 | .bf16 => return t.fail(error.UnsupportedType, source_loc, "TODO support bfloat16", .{}), |
| 1212 | .dfloat32, |
| 1213 | .dfloat64, |
| 1214 | .dfloat128, |
| 1215 | .dfloat64x, |
| 1216 | => return t.fail(error.UnsupportedType, source_loc, "TODO support decimal float type: '{s}'", .{try t.getTypeStr(qt)}), |
| 1217 | .float128x => unreachable, // Unsupported on all targets |
| 1218 | }, |
| 1219 | .pointer => |pointer_ty| { |
| 1220 | const child_qt = pointer_ty.child; |
| 1221 | |
| 1222 | const is_fn_proto = child_qt.is(t.comp, .func); |
| 1223 | const is_const = is_fn_proto or child_qt.@"const"; |
| 1224 | const is_volatile = child_qt.@"volatile"; |
| 1225 | const elem_type = try t.transType(scope, child_qt, source_loc); |
| 1226 | const ptr_info: @FieldType(ast.Payload.Pointer, "data") = .{ |
| 1227 | .is_const = is_const, |
| 1228 | .is_volatile = is_volatile, |
| 1229 | .elem_type = elem_type, |
| 1230 | .is_allowzero = false, |
| 1231 | }; |
| 1232 | if (is_fn_proto or |
| 1233 | t.typeIsOpaque(child_qt) or |
| 1234 | t.typeWasDemotedToOpaque(child_qt)) |
| 1235 | { |
| 1236 | const ptr = try ZigTag.single_pointer.create(t.arena, ptr_info); |
| 1237 | return ZigTag.optional_type.create(t.arena, ptr); |
| 1238 | } |
| 1239 | |
| 1240 | return ZigTag.c_pointer.create(t.arena, ptr_info); |
| 1241 | }, |
| 1242 | .array => |array_ty| { |
| 1243 | const elem_qt = array_ty.elem; |
| 1244 | switch (array_ty.len) { |
| 1245 | .incomplete, .unspecified_variable => { |
| 1246 | const elem_type = try t.transType(scope, elem_qt, source_loc); |
| 1247 | return ZigTag.c_pointer.create(t.arena, .{ |
| 1248 | .is_const = elem_qt.@"const", |
| 1249 | .is_volatile = elem_qt.@"volatile", |
| 1250 | .is_allowzero = false, |
| 1251 | .elem_type = elem_type, |
| 1252 | }); |
| 1253 | }, |
| 1254 | .fixed, .static => |len| { |
| 1255 | const elem_type = try t.transType(scope, elem_qt, source_loc); |
| 1256 | return ZigTag.array_type.create(t.arena, .{ .len = len, .elem_type = elem_type }); |
| 1257 | }, |
| 1258 | .variable => return t.fail(error.UnsupportedType, source_loc, "VLA unsupported '{s}'", .{try t.getTypeStr(qt)}), |
| 1259 | } |
| 1260 | }, |
| 1261 | .func => |func_ty| return t.transFnType(scope, qt, func_ty, source_loc, .{}), |
| 1262 | .@"struct", .@"union" => |record_ty| { |
| 1263 | var trans_scope = scope; |
| 1264 | if (!record_ty.isAnonymous(t.comp)) { |
| 1265 | if (t.weak_global_names.contains(record_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base; |
| 1266 | } |
| 1267 | try t.transRecordDecl(trans_scope, qt); |
| 1268 | const name = t.type_decls.get(record_ty.decl_node).?; |
| 1269 | return ZigTag.identifier.create(t.arena, name); |
| 1270 | }, |
| 1271 | .@"enum" => |enum_ty| { |
| 1272 | var trans_scope = scope; |
| 1273 | const is_anonymous = enum_ty.isAnonymous(t.comp); |
| 1274 | if (!is_anonymous) { |
| 1275 | if (t.weak_global_names.contains(enum_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base; |
| 1276 | } |
| 1277 | try t.transEnumDecl(trans_scope, qt); |
| 1278 | const name = t.type_decls.get(enum_ty.decl_node).?; |
| 1279 | return ZigTag.identifier.create(t.arena, name); |
| 1280 | }, |
| 1281 | .typedef => |typedef_ty| { |
| 1282 | var trans_scope = scope; |
| 1283 | const typedef_name = typedef_ty.name.lookup(t.comp); |
| 1284 | if (builtin_typedef_map.get(typedef_name)) |builtin| return ZigTag.type.create(t.arena, builtin); |
| 1285 | if (t.global_names.contains(typedef_name)) trans_scope = &t.global_scope.base; |
| 1286 | |
| 1287 | try t.transTypeDef(trans_scope, typedef_ty.decl_node); |
| 1288 | const name = t.type_decls.get(typedef_ty.decl_node).?; |
| 1289 | return ZigTag.identifier.create(t.arena, name); |
| 1290 | }, |
| 1291 | .attributed => |attributed_ty| continue :loop attributed_ty.base.type(t.comp), |
| 1292 | .typeof => |typeof_ty| { |
| 1293 | if (typeof_ty.expr) |expr| { |
| 1294 | if (t.transExpr(scope, expr, .used)) |node| { |
| 1295 | return ZigTag.typeof.create(t.arena, node); |
| 1296 | } else |err| switch (err) { |
| 1297 | error.SelfReferential => {}, |
| 1298 | error.UnsupportedTranslation => {}, |
| 1299 | error.UnsupportedType => {}, |
| 1300 | error.OutOfMemory => |e| return e, |
| 1301 | } |
| 1302 | } |
| 1303 | continue :loop typeof_ty.base.type(t.comp); |
| 1304 | }, |
| 1305 | .vector => |vector_ty| { |
| 1306 | const len = try t.createNumberNode(vector_ty.len); |
| 1307 | const elem_type = try t.transType(scope, vector_ty.elem, source_loc); |
| 1308 | return ZigTag.vector.create(t.arena, .{ .lhs = len, .rhs = elem_type }); |
| 1309 | }, |
| 1310 | else => return t.fail(error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{try t.getTypeStr(qt)}), |
| 1311 | } |
| 1312 | } |
| 1313 | |
| 1314 | /// Look ahead through the fields of the record to determine what the alignment of the record |
| 1315 | /// would be without any align/packed/etc. attributes. This helps us determine whether or not |
| 1316 | /// the fields with 0 offset need an `align` qualifier. Strictly speaking, we could just |
| 1317 | /// pedantically assign those fields the same alignment as the parent's pointer alignment, |
| 1318 | /// but this helps the generated code to be a little less verbose. |
| 1319 | fn headFieldAlignment(t: *Translator, record_decl: aro.Type.Record) ?c_uint { |
| 1320 | const bits_per_byte = 8; |
| 1321 | const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits; |
| 1322 | const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte; |
| 1323 | var max_field_alignment_bits: u64 = 0; |
| 1324 | for (record_decl.fields) |field| |
| 1325 | max_field_alignment_bits = @max(max_field_alignment_bits, bits_per_byte * field.qt.alignof(t.comp)); |
| 1326 | if (max_field_alignment_bits != parent_ptr_alignment_bits) { |
| 1327 | return parent_ptr_alignment; |
| 1328 | } else { |
| 1329 | return null; |
| 1330 | } |
| 1331 | } |
| 1332 | |
| 1333 | /// This function inspects the generated layout of a record to determine the alignment for a |
| 1334 | /// particular field. This approach is necessary because unlike Zig, a C compiler is not |
| 1335 | /// required to fulfill the requested alignment, which means we'd risk generating different code |
| 1336 | /// if we only look at the user-requested alignment. |
| 1337 | /// |
| 1338 | /// Returns a ?c_uint to match Clang's behavior of using c_uint. The return type can be changed |
| 1339 | /// after the Clang frontend for translate-c is removed. A null value indicates that a field is |
| 1340 | /// 'naturally aligned'. |
| 1341 | fn alignmentForField( |
| 1342 | t: *Translator, |
| 1343 | record_decl: aro.Type.Record, |
| 1344 | head_field_alignment: ?c_uint, |
| 1345 | field_index: usize, |
| 1346 | ) ?c_uint { |
| 1347 | const fields = record_decl.fields; |
| 1348 | assert(fields.len != 0); |
| 1349 | const field = fields[field_index]; |
| 1350 | |
| 1351 | const bits_per_byte = 8; |
| 1352 | const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits; |
| 1353 | const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte; |
| 1354 | |
| 1355 | // bitfields aren't supported yet. Until support is added, records with bitfields |
| 1356 | // should be demoted to opaque, and this function shouldn't be called for them. |
| 1357 | if (field.bit_width != .null) { |
| 1358 | @panic("TODO: add bitfield support for records"); |
| 1359 | } |
| 1360 | |
| 1361 | const field_offset_bits: u64 = field.layout.offset_bits; |
| 1362 | const field_size_bits: u64 = field.layout.size_bits; |
| 1363 | |
| 1364 | // Fields with zero width always have an alignment of 1 |
| 1365 | if (field_size_bits == 0) { |
| 1366 | return 1; |
| 1367 | } |
| 1368 | |
| 1369 | // Fields with 0 offset inherit the parent's pointer alignment. |
| 1370 | if (field_offset_bits == 0) { |
| 1371 | return head_field_alignment; |
| 1372 | } |
| 1373 | |
| 1374 | // Records have a natural alignment when used as a field, and their size is |
| 1375 | // a multiple of this alignment value. For all other types, the natural alignment |
| 1376 | // is their size. |
| 1377 | const field_natural_alignment_bits: u64 = bits_per_byte * field.qt.alignof(t.comp); |
| 1378 | const rem_bits = field_offset_bits % field_natural_alignment_bits; |
| 1379 | |
| 1380 | // If there's a remainder, then the alignment is smaller than the field's |
| 1381 | // natural alignment |
| 1382 | if (rem_bits > 0) { |
| 1383 | const rem_alignment = rem_bits / bits_per_byte; |
| 1384 | if (rem_alignment > 0 and std.math.isPowerOfTwo(rem_alignment)) { |
| 1385 | const actual_alignment = @min(rem_alignment, parent_ptr_alignment); |
| 1386 | return @as(c_uint, @truncate(actual_alignment)); |
| 1387 | } else { |
| 1388 | return 1; |
| 1389 | } |
| 1390 | } |
| 1391 | |
| 1392 | // A field may have an offset which positions it to be naturally aligned, but the |
| 1393 | // parent's pointer alignment determines if this is actually true, so we take the minimum |
| 1394 | // value. |
| 1395 | // For example, a float field (4 bytes wide) with a 4 byte offset is positioned to have natural |
| 1396 | // alignment, but if the parent pointer alignment is 2, then the actual alignment of the |
| 1397 | // float is 2. |
| 1398 | const field_natural_alignment: u64 = field_natural_alignment_bits / bits_per_byte; |
| 1399 | const offset_alignment = field_offset_bits / bits_per_byte; |
| 1400 | const possible_alignment = @min(parent_ptr_alignment, offset_alignment); |
| 1401 | if (possible_alignment == field_natural_alignment) { |
| 1402 | return null; |
| 1403 | } else if (possible_alignment < field_natural_alignment) { |
| 1404 | if (std.math.isPowerOfTwo(possible_alignment)) { |
| 1405 | return possible_alignment; |
| 1406 | } else { |
| 1407 | return 1; |
| 1408 | } |
| 1409 | } else { // possible_alignment > field_natural_alignment |
| 1410 | // Here, the field is positioned be at a higher alignment than it's natural alignment. This means we |
| 1411 | // need to determine whether it's a specified alignment. We can determine that from the padding preceding |
| 1412 | // the field. |
| 1413 | const padding_from_prev_field: u64 = blk: { |
| 1414 | if (field_offset_bits != 0) { |
| 1415 | const previous_field = fields[field_index - 1]; |
| 1416 | break :blk (field_offset_bits - previous_field.layout.offset_bits) - previous_field.layout.size_bits; |
| 1417 | } else { |
| 1418 | break :blk 0; |
| 1419 | } |
| 1420 | }; |
| 1421 | if (padding_from_prev_field < field_natural_alignment_bits) { |
| 1422 | return null; |
| 1423 | } else { |
| 1424 | return possible_alignment; |
| 1425 | } |
| 1426 | } |
| 1427 | } |
| 1428 | |
| 1429 | const FnProtoContext = struct { |
| 1430 | is_pub: bool = false, |
| 1431 | is_export: bool = false, |
| 1432 | is_extern: bool = false, |
| 1433 | is_always_inline: bool = false, |
| 1434 | fn_name: ?[]const u8 = null, |
| 1435 | has_body: bool = false, |
| 1436 | cc: ast.Payload.Func.CallingConvention = .c, |
| 1437 | }; |
| 1438 | |
| 1439 | fn transFnType( |
| 1440 | t: *Translator, |
| 1441 | scope: *Scope, |
| 1442 | func_qt: QualType, |
| 1443 | func_ty: aro.Type.Func, |
| 1444 | source_loc: TokenIndex, |
| 1445 | ctx: FnProtoContext, |
| 1446 | ) !ZigNode { |
| 1447 | const param_count: usize = func_ty.params.len; |
| 1448 | const fn_params = try t.arena.alloc(ast.Payload.Param, param_count); |
| 1449 | |
| 1450 | for (func_ty.params, fn_params) |param_info, *param_node| { |
| 1451 | const param_qt = param_info.qt; |
| 1452 | const is_noalias = param_qt.restrict; |
| 1453 | |
| 1454 | const param_name: ?[]const u8 = if (param_info.name == .empty) |
| 1455 | null |
| 1456 | else |
| 1457 | param_info.name.lookup(t.comp); |
| 1458 | |
| 1459 | const type_node = try t.transType(scope, param_qt, param_info.name_tok); |
| 1460 | param_node.* = .{ |
| 1461 | .is_noalias = is_noalias, |
| 1462 | .name = param_name, |
| 1463 | .type = type_node, |
| 1464 | }; |
| 1465 | } |
| 1466 | |
| 1467 | const linksection_string = blk: { |
| 1468 | if (func_qt.getAttribute(t.comp, .section)) |section| { |
| 1469 | break :blk t.comp.interner.get(section.name.ref()).bytes; |
| 1470 | } |
| 1471 | break :blk null; |
| 1472 | }; |
| 1473 | |
| 1474 | const alignment: ?c_uint = func_qt.requestedAlignment(t.comp) orelse null; |
| 1475 | |
| 1476 | const explicit_callconv = if ((ctx.is_always_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .c) null else ctx.cc; |
| 1477 | |
| 1478 | const return_type_node = blk: { |
| 1479 | if (func_qt.getAttribute(t.comp, .noreturn) != null) { |
| 1480 | break :blk ZigTag.noreturn_type.init(); |
| 1481 | } else { |
| 1482 | const return_qt = func_ty.return_type; |
| 1483 | if (return_qt.is(t.comp, .void)) { |
| 1484 | // convert primitive anyopaque to actual void (only for return type) |
| 1485 | break :blk ZigTag.void_type.init(); |
| 1486 | } else { |
| 1487 | break :blk t.transType(scope, return_qt, source_loc) catch |err| switch (err) { |
| 1488 | error.UnsupportedType => { |
| 1489 | try t.warn(scope, source_loc, "unsupported function proto return type", .{}); |
| 1490 | return err; |
| 1491 | }, |
| 1492 | error.OutOfMemory => |e| return e, |
| 1493 | }; |
| 1494 | } |
| 1495 | } |
| 1496 | }; |
| 1497 | |
| 1498 | // TODO actually set with @export/@extern |
| 1499 | const linkage = func_qt.linkage(t.comp); |
| 1500 | if (linkage != .strong) { |
| 1501 | try t.warn(scope, source_loc, "TODO {s} linkage ignored", .{@tagName(linkage)}); |
| 1502 | } |
| 1503 | |
| 1504 | const payload = try t.arena.create(ast.Payload.Func); |
| 1505 | payload.* = .{ |
| 1506 | .base = .{ .tag = .func }, |
| 1507 | .data = .{ |
| 1508 | .is_pub = ctx.is_pub, |
| 1509 | .is_extern = ctx.is_extern, |
| 1510 | .is_export = ctx.is_export and linkage == .strong, |
| 1511 | .is_inline = ctx.is_always_inline, |
| 1512 | .is_var_args = switch (func_ty.kind) { |
| 1513 | .normal => false, |
| 1514 | .variadic => true, |
| 1515 | .old_style => if (t.comp.target.cpu.arch.isWasm()) |
| 1516 | false |
| 1517 | else |
| 1518 | !ctx.is_export and !ctx.is_always_inline and !ctx.has_body, |
| 1519 | }, |
| 1520 | .name = ctx.fn_name, |
| 1521 | .linksection_string = linksection_string, |
| 1522 | .explicit_callconv = explicit_callconv, |
| 1523 | .params = fn_params, |
| 1524 | .return_type = return_type_node, |
| 1525 | .body = null, |
| 1526 | .alignment = alignment, |
| 1527 | }, |
| 1528 | }; |
| 1529 | return ZigNode.initPayload(&payload.base); |
| 1530 | } |
| 1531 | |
| 1532 | /// Produces a Zig AST node by translating a Type, respecting the width, but modifying the signed-ness. |
| 1533 | /// Asserts the type is an integer. |
| 1534 | fn transTypeIntWidthOf(t: *Translator, qt: QualType, is_signed: bool) TypeError!ZigNode { |
| 1535 | return ZigTag.type.create(t.arena, loop: switch (qt.base(t.comp).type) { |
| 1536 | .int => |int_ty| switch (int_ty) { |
| 1537 | .char, .schar, .uchar => if (is_signed) "i8" else "u8", |
| 1538 | .short, .ushort => if (is_signed) "c_short" else "c_ushort", |
| 1539 | .int, .uint => if (is_signed) "c_int" else "c_uint", |
| 1540 | .long, .ulong => if (is_signed) "c_long" else "c_ulong", |
| 1541 | .long_long, .ulong_long => if (is_signed) "c_longlong" else "c_ulonglong", |
| 1542 | .int128, .uint128 => if (is_signed) "i128" else "u128", |
| 1543 | }, |
| 1544 | .bit_int => |bit_int_ty| try std.fmt.allocPrint(t.arena, "{s}{d}", .{ |
| 1545 | if (is_signed) "i" else "u", |
| 1546 | bit_int_ty.bits, |
| 1547 | }), |
| 1548 | .@"enum" => |enum_ty| blk: { |
| 1549 | const tag_ty = enum_ty.tag orelse |
| 1550 | break :blk if (is_signed) "c_int" else "c_uint"; |
| 1551 | |
| 1552 | continue :loop tag_ty.base(t.comp).type; |
| 1553 | }, |
| 1554 | else => unreachable, // only call this function when it has already been determined the type is int |
| 1555 | }); |
| 1556 | } |
| 1557 | |
| 1558 | fn transTypeInit( |
| 1559 | t: *Translator, |
| 1560 | scope: *Scope, |
| 1561 | qt: QualType, |
| 1562 | init: Node.Index, |
| 1563 | source_loc: TokenIndex, |
| 1564 | ) TypeError!ZigNode { |
| 1565 | switch (init.get(t.tree)) { |
| 1566 | .string_literal_expr => |literal| { |
| 1567 | const elem_ty = try t.transType(scope, qt.childType(t.comp), source_loc); |
| 1568 | |
| 1569 | const string_lit_size = literal.qt.arrayLen(t.comp).?; |
| 1570 | const array_size = qt.arrayLen(t.comp).?; |
| 1571 | |
| 1572 | if (array_size == string_lit_size) { |
| 1573 | return ZigTag.null_sentinel_array_type.create(t.arena, .{ .len = array_size - 1, .elem_type = elem_ty }); |
| 1574 | } else { |
| 1575 | return ZigTag.array_type.create(t.arena, .{ .len = array_size, .elem_type = elem_ty }); |
| 1576 | } |
| 1577 | }, |
| 1578 | else => {}, |
| 1579 | } |
| 1580 | return t.transType(scope, qt, source_loc); |
| 1581 | } |
| 1582 | |
| 1583 | // ============ |
| 1584 | // Type helpers |
| 1585 | // ============ |
| 1586 | |
| 1587 | fn typeIsOpaque(t: *Translator, qt: QualType) bool { |
| 1588 | return switch (qt.base(t.comp).type) { |
| 1589 | .void => true, |
| 1590 | .@"struct", .@"union" => |record_ty| { |
| 1591 | if (record_ty.layout == null) return true; |
| 1592 | for (record_ty.fields) |field| { |
| 1593 | if (field.bit_width != .null) return true; |
| 1594 | } |
| 1595 | return false; |
| 1596 | }, |
| 1597 | else => false, |
| 1598 | }; |
| 1599 | } |
| 1600 | |
| 1601 | fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool { |
| 1602 | return t.opaque_demotes.contains(qt.base(t.comp).qt); |
| 1603 | } |
| 1604 | |
| 1605 | fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool { |
| 1606 | if (t.signedness(qt) == .unsigned) { |
| 1607 | // unsigned integer overflow wraps around. |
| 1608 | return true; |
| 1609 | } else { |
| 1610 | // float, signed integer, and pointer overflow is undefined behavior. |
| 1611 | return false; |
| 1612 | } |
| 1613 | } |
| 1614 | |
| 1615 | /// Signedness of type when translated to Zig. |
| 1616 | /// Different from `QualType.signedness()` for `char` and enums. |
| 1617 | /// Returns null for non-int types. |
| 1618 | fn signedness(t: *Translator, qt: QualType) ?std.builtin.Signedness { |
| 1619 | return loop: switch (qt.base(t.comp).type) { |
| 1620 | .bool => .unsigned, |
| 1621 | .bit_int => |bit_int| bit_int.signedness, |
| 1622 | .int => |int_ty| switch (int_ty) { |
| 1623 | .char => .unsigned, // Always translated as u8 |
| 1624 | .schar, .short, .int, .long, .long_long, .int128 => .signed, |
| 1625 | .uchar, .ushort, .uint, .ulong, .ulong_long, .uint128 => .unsigned, |
| 1626 | }, |
| 1627 | .@"enum" => |enum_ty| { |
| 1628 | const tag_qt = enum_ty.tag orelse return .signed; |
| 1629 | continue :loop tag_qt.base(t.comp).type; |
| 1630 | }, |
| 1631 | else => return null, |
| 1632 | }; |
| 1633 | } |
| 1634 | |
| 1635 | // ===================== |
| 1636 | // Statement translation |
| 1637 | // ===================== |
| 1638 | |
| 1639 | fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode { |
| 1640 | switch (stmt.get(t.tree)) { |
| 1641 | .compound_stmt => |compound| { |
| 1642 | return t.transCompoundStmt(scope, compound); |
| 1643 | }, |
| 1644 | .static_assert => |static_assert| { |
| 1645 | try t.transStaticAssert(scope, static_assert); |
| 1646 | return ZigTag.declaration.init(); |
| 1647 | }, |
| 1648 | .return_stmt => |return_stmt| return t.transReturnStmt(scope, return_stmt), |
| 1649 | .null_stmt => return ZigTag.empty_block.init(), |
| 1650 | .if_stmt => |if_stmt| return t.transIfStmt(scope, if_stmt), |
| 1651 | .while_stmt => |while_stmt| return t.transWhileStmt(scope, while_stmt), |
| 1652 | .do_while_stmt => |do_while_stmt| return t.transDoWhileStmt(scope, do_while_stmt), |
| 1653 | .for_stmt => |for_stmt| return t.transForStmt(scope, for_stmt), |
| 1654 | .continue_stmt => return ZigTag.@"continue".init(), |
| 1655 | .break_stmt => return ZigTag.@"break".init(), |
| 1656 | .typedef => |typedef_decl| { |
| 1657 | assert(!typedef_decl.implicit); |
| 1658 | try t.transTypeDef(scope, stmt); |
| 1659 | return ZigTag.declaration.init(); |
| 1660 | }, |
| 1661 | .struct_decl, .union_decl => |record_decl| { |
| 1662 | try t.transRecordDecl(scope, record_decl.container_qt); |
| 1663 | return ZigTag.declaration.init(); |
| 1664 | }, |
| 1665 | .struct_forward_decl, .union_forward_decl => |record_decl| { |
| 1666 | if (record_decl.definition) |some| { |
| 1667 | return t.transStmt(scope, some); |
| 1668 | } |
| 1669 | try t.transRecordDecl(scope, record_decl.container_qt); |
| 1670 | return ZigTag.declaration.init(); |
| 1671 | }, |
| 1672 | .enum_decl => |enum_decl| { |
| 1673 | try t.transEnumDecl(scope, enum_decl.container_qt); |
| 1674 | return ZigTag.declaration.init(); |
| 1675 | }, |
| 1676 | .enum_forward_decl => |enum_decl| { |
| 1677 | if (enum_decl.definition) |some| { |
| 1678 | return t.transStmt(scope, some); |
| 1679 | } |
| 1680 | try t.transEnumDecl(scope, enum_decl.container_qt); |
| 1681 | return ZigTag.declaration.init(); |
| 1682 | }, |
| 1683 | .function => |function| { |
| 1684 | try t.transFnDecl(scope, function); |
| 1685 | return ZigTag.declaration.init(); |
| 1686 | }, |
| 1687 | .variable => |variable| { |
| 1688 | try t.transVarDecl(scope, variable, stmt); |
| 1689 | return ZigTag.declaration.init(); |
| 1690 | }, |
| 1691 | .switch_stmt => |switch_stmt| return t.transSwitch(scope, switch_stmt), |
| 1692 | .case_stmt, .default_stmt => { |
| 1693 | return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO complex switch", .{}); |
| 1694 | }, |
| 1695 | .goto_stmt, .computed_goto_stmt, .labeled_stmt => { |
| 1696 | return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO goto", .{}); |
| 1697 | }, |
| 1698 | .asm_stmt => { |
| 1699 | return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO asm stmt", .{}); |
| 1700 | }, |
| 1701 | else => return t.transExprCoercing(scope, stmt, .unused), |
| 1702 | } |
| 1703 | } |
| 1704 | |
| 1705 | fn transCompoundStmtInline(t: *Translator, compound: Node.CompoundStmt, block: *Scope.Block) TransError!void { |
| 1706 | for (compound.body) |stmt| { |
| 1707 | const result = try t.transStmt(&block.base, stmt); |
| 1708 | switch (result.tag()) { |
| 1709 | .declaration, .empty_block => {}, |
| 1710 | else => { |
| 1711 | try block.statements.append(t.gpa, result); |
| 1712 | if (result.isNoreturn()) return; |
| 1713 | }, |
| 1714 | } |
| 1715 | } |
| 1716 | } |
| 1717 | |
| 1718 | fn transCompoundStmt(t: *Translator, scope: *Scope, compound: Node.CompoundStmt) TransError!ZigNode { |
| 1719 | var block_scope = try Scope.Block.init(t, scope, false); |
| 1720 | defer block_scope.deinit(); |
| 1721 | try t.transCompoundStmtInline(compound, &block_scope); |
| 1722 | return try block_scope.complete(); |
| 1723 | } |
| 1724 | |
| 1725 | fn transReturnStmt(t: *Translator, scope: *Scope, return_stmt: Node.ReturnStmt) TransError!ZigNode { |
| 1726 | switch (return_stmt.operand) { |
| 1727 | .none => return ZigTag.return_void.init(), |
| 1728 | .expr => |operand| { |
| 1729 | const rhs = try t.transExprCoercing(scope, operand, .used); |
| 1730 | const return_qt = scope.findBlockReturnType(); |
| 1731 | return ZigTag.@"return".create(t.arena, try t.toNonBool(rhs, return_qt)); |
| 1732 | }, |
| 1733 | .implicit => |zero| { |
| 1734 | if (zero) return ZigTag.@"return".create(t.arena, ZigTag.zero_literal.init()); |
| 1735 | |
| 1736 | const return_qt = scope.findBlockReturnType(); |
| 1737 | if (return_qt.is(t.comp, .void)) return ZigTag.empty_block.init(); |
| 1738 | |
| 1739 | return ZigTag.@"return".create(t.arena, ZigTag.undefined_literal.init()); |
| 1740 | }, |
| 1741 | } |
| 1742 | } |
| 1743 | |
| 1744 | /// If a statement can possibly translate to a Zig assignment (either directly because it's |
| 1745 | /// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement |
| 1746 | /// in the body of an if statement or loop, then we need to put the statement into its own block. |
| 1747 | /// The `else` case here corresponds to statements that could result in an assignment. If a statement |
| 1748 | /// class never needs a block, add its enum to the top prong. |
| 1749 | fn maybeBlockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode { |
| 1750 | switch (stmt.get(t.tree)) { |
| 1751 | .break_stmt, |
| 1752 | .continue_stmt, |
| 1753 | .compound_stmt, |
| 1754 | .decl_ref_expr, |
| 1755 | .enumeration_ref, |
| 1756 | .do_while_stmt, |
| 1757 | .for_stmt, |
| 1758 | .if_stmt, |
| 1759 | .return_stmt, |
| 1760 | .null_stmt, |
| 1761 | .while_stmt, |
| 1762 | => return t.transStmt(scope, stmt), |
| 1763 | else => return t.blockify(scope, stmt), |
| 1764 | } |
| 1765 | } |
| 1766 | |
| 1767 | /// Translate statement and place it in its own block. |
| 1768 | fn blockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode { |
| 1769 | var block_scope = try Scope.Block.init(t, scope, false); |
| 1770 | defer block_scope.deinit(); |
| 1771 | const result = try t.transStmt(&block_scope.base, stmt); |
| 1772 | try block_scope.statements.append(t.gpa, result); |
| 1773 | return block_scope.complete(); |
| 1774 | } |
| 1775 | |
| 1776 | fn transIfStmt(t: *Translator, scope: *Scope, if_stmt: Node.IfStmt) TransError!ZigNode { |
| 1777 | var cond_scope: Scope.Condition = .{ |
| 1778 | .base = .{ |
| 1779 | .parent = scope, |
| 1780 | .id = .condition, |
| 1781 | }, |
| 1782 | }; |
| 1783 | defer cond_scope.deinit(); |
| 1784 | const cond = try t.transBoolExpr(&cond_scope.base, if_stmt.cond); |
| 1785 | |
| 1786 | // block needed to keep else statement from attaching to inner while |
| 1787 | const must_blockify = (if_stmt.else_body != null) and switch (if_stmt.then_body.get(t.tree)) { |
| 1788 | .while_stmt, .do_while_stmt, .for_stmt => true, |
| 1789 | else => false, |
| 1790 | }; |
| 1791 | |
| 1792 | const then_node = if (must_blockify) |
| 1793 | try t.blockify(scope, if_stmt.then_body) |
| 1794 | else |
| 1795 | try t.maybeBlockify(scope, if_stmt.then_body); |
| 1796 | |
| 1797 | const else_node = if (if_stmt.else_body) |stmt| |
| 1798 | try t.maybeBlockify(scope, stmt) |
| 1799 | else |
| 1800 | null; |
| 1801 | return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_node, .@"else" = else_node }); |
| 1802 | } |
| 1803 | |
| 1804 | fn transWhileStmt(t: *Translator, scope: *Scope, while_stmt: Node.WhileStmt) TransError!ZigNode { |
| 1805 | var cond_scope: Scope.Condition = .{ |
| 1806 | .base = .{ |
| 1807 | .parent = scope, |
| 1808 | .id = .condition, |
| 1809 | }, |
| 1810 | }; |
| 1811 | defer cond_scope.deinit(); |
| 1812 | const cond = try t.transBoolExpr(&cond_scope.base, while_stmt.cond); |
| 1813 | |
| 1814 | var loop_scope: Scope = .{ |
| 1815 | .parent = scope, |
| 1816 | .id = .loop, |
| 1817 | }; |
| 1818 | const body = try t.maybeBlockify(&loop_scope, while_stmt.body); |
| 1819 | return ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = null }); |
| 1820 | } |
| 1821 | |
| 1822 | fn transDoWhileStmt(t: *Translator, scope: *Scope, do_stmt: Node.DoWhileStmt) TransError!ZigNode { |
| 1823 | var loop_scope: Scope = .{ |
| 1824 | .parent = scope, |
| 1825 | .id = .do_loop, |
| 1826 | }; |
| 1827 | |
| 1828 | // if (!cond) break; |
| 1829 | var cond_scope: Scope.Condition = .{ |
| 1830 | .base = .{ |
| 1831 | .parent = scope, |
| 1832 | .id = .condition, |
| 1833 | }, |
| 1834 | }; |
| 1835 | defer cond_scope.deinit(); |
| 1836 | const cond = try t.transBoolExpr(&cond_scope.base, do_stmt.cond); |
| 1837 | const if_not_break = switch (cond.tag()) { |
| 1838 | .true_literal => { |
| 1839 | const body_node = try t.maybeBlockify(scope, do_stmt.body); |
| 1840 | return ZigTag.while_true.create(t.arena, body_node); |
| 1841 | }, |
| 1842 | else => try ZigTag.if_not_break.create(t.arena, cond), |
| 1843 | }; |
| 1844 | |
| 1845 | var body_node = try t.transStmt(&loop_scope, do_stmt.body); |
| 1846 | if (body_node.isNoreturn()) { |
| 1847 | // The body node ends in a noreturn statement. Simply put it in a while (true) |
| 1848 | // in case it contains breaks or continues. |
| 1849 | } else if (do_stmt.body.get(t.tree) == .compound_stmt) { |
| 1850 | // there's already a block in C, so we'll append our condition to it. |
| 1851 | // c: do { |
| 1852 | // c: a; |
| 1853 | // c: b; |
| 1854 | // c: } while(c); |
| 1855 | // zig: while (true) { |
| 1856 | // zig: a; |
| 1857 | // zig: b; |
| 1858 | // zig: if (!cond) break; |
| 1859 | // zig: } |
| 1860 | const block = body_node.castTag(.block).?; |
| 1861 | block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete. |
| 1862 | block.data.stmts[block.data.stmts.len - 1] = if_not_break; |
| 1863 | } else { |
| 1864 | // the C statement is without a block, so we need to create a block to contain it. |
| 1865 | // c: do |
| 1866 | // c: a; |
| 1867 | // c: while(c); |
| 1868 | // zig: while (true) { |
| 1869 | // zig: a; |
| 1870 | // zig: if (!cond) break; |
| 1871 | // zig: } |
| 1872 | const statements = try t.arena.alloc(ZigNode, 2); |
| 1873 | statements[0] = body_node; |
| 1874 | statements[1] = if_not_break; |
| 1875 | body_node = try ZigTag.block.create(t.arena, .{ .label = null, .stmts = statements }); |
| 1876 | } |
| 1877 | return ZigTag.while_true.create(t.arena, body_node); |
| 1878 | } |
| 1879 | |
| 1880 | fn transForStmt(t: *Translator, scope: *Scope, for_stmt: Node.ForStmt) TransError!ZigNode { |
| 1881 | var loop_scope: Scope = .{ |
| 1882 | .parent = scope, |
| 1883 | .id = .loop, |
| 1884 | }; |
| 1885 | |
| 1886 | var block_scope: ?Scope.Block = null; |
| 1887 | defer if (block_scope) |*bs| bs.deinit(); |
| 1888 | |
| 1889 | switch (for_stmt.init) { |
| 1890 | .decls => |decls| { |
| 1891 | block_scope = try Scope.Block.init(t, scope, false); |
| 1892 | loop_scope.parent = &block_scope.?.base; |
| 1893 | for (decls) |decl| { |
| 1894 | try t.transDecl(&block_scope.?.base, decl); |
| 1895 | } |
| 1896 | }, |
| 1897 | .expr => |maybe_init| if (maybe_init) |init| { |
| 1898 | block_scope = try Scope.Block.init(t, scope, false); |
| 1899 | loop_scope.parent = &block_scope.?.base; |
| 1900 | const init_node = try t.transStmt(&block_scope.?.base, init); |
| 1901 | try loop_scope.appendNode(init_node); |
| 1902 | }, |
| 1903 | } |
| 1904 | var cond_scope: Scope.Condition = .{ |
| 1905 | .base = .{ |
| 1906 | .parent = &loop_scope, |
| 1907 | .id = .condition, |
| 1908 | }, |
| 1909 | }; |
| 1910 | defer cond_scope.deinit(); |
| 1911 | |
| 1912 | const cond = if (for_stmt.cond) |cond| |
| 1913 | try t.transBoolExpr(&cond_scope.base, cond) |
| 1914 | else |
| 1915 | ZigTag.true_literal.init(); |
| 1916 | |
| 1917 | const cont_expr = if (for_stmt.incr) |incr| |
| 1918 | try t.transExpr(&cond_scope.base, incr, .unused) |
| 1919 | else |
| 1920 | null; |
| 1921 | |
| 1922 | const body = try t.maybeBlockify(&loop_scope, for_stmt.body); |
| 1923 | const while_node = try ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); |
| 1924 | if (block_scope) |*bs| { |
| 1925 | try bs.statements.append(t.gpa, while_node); |
| 1926 | return try bs.complete(); |
| 1927 | } else { |
| 1928 | return while_node; |
| 1929 | } |
| 1930 | } |
| 1931 | |
| 1932 | fn transSwitch(t: *Translator, scope: *Scope, switch_stmt: Node.SwitchStmt) TransError!ZigNode { |
| 1933 | var loop_scope: Scope = .{ |
| 1934 | .parent = scope, |
| 1935 | .id = .loop, |
| 1936 | }; |
| 1937 | |
| 1938 | var block_scope = try Scope.Block.init(t, &loop_scope, false); |
| 1939 | defer block_scope.deinit(); |
| 1940 | |
| 1941 | const base_scope = &block_scope.base; |
| 1942 | |
| 1943 | var cond_scope: Scope.Condition = .{ |
| 1944 | .base = .{ |
| 1945 | .parent = base_scope, |
| 1946 | .id = .condition, |
| 1947 | }, |
| 1948 | }; |
| 1949 | defer cond_scope.deinit(); |
| 1950 | const switch_expr = try t.transExpr(&cond_scope.base, switch_stmt.cond, .used); |
| 1951 | |
| 1952 | var cases: std.ArrayList(ZigNode) = .empty; |
| 1953 | defer cases.deinit(t.gpa); |
| 1954 | var has_default = false; |
| 1955 | |
| 1956 | const body_node = switch_stmt.body.get(t.tree); |
| 1957 | if (body_node != .compound_stmt) { |
| 1958 | return t.fail(error.UnsupportedTranslation, switch_stmt.switch_tok, "TODO complex switch", .{}); |
| 1959 | } |
| 1960 | const body = body_node.compound_stmt.body; |
| 1961 | // Iterate over switch body and collect all cases. |
| 1962 | // Fallthrough is handled by duplicating statements. |
| 1963 | for (body, 0..) |stmt, i| { |
| 1964 | switch (stmt.get(t.tree)) { |
| 1965 | .case_stmt => { |
| 1966 | var items: std.ArrayList(ZigNode) = .empty; |
| 1967 | defer items.deinit(t.gpa); |
| 1968 | const sub = try t.transCaseStmt(base_scope, stmt, &items); |
| 1969 | const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]); |
| 1970 | |
| 1971 | if (items.items.len == 0) { |
| 1972 | has_default = true; |
| 1973 | const switch_else = try ZigTag.switch_else.create(t.arena, res); |
| 1974 | try cases.append(t.gpa, switch_else); |
| 1975 | } else { |
| 1976 | const switch_prong = try ZigTag.switch_prong.create(t.arena, .{ |
| 1977 | .cases = try t.arena.dupe(ZigNode, items.items), |
| 1978 | .cond = res, |
| 1979 | }); |
| 1980 | try cases.append(t.gpa, switch_prong); |
| 1981 | } |
| 1982 | }, |
| 1983 | .default_stmt => |default_stmt| { |
| 1984 | has_default = true; |
| 1985 | |
| 1986 | var sub = default_stmt.body; |
| 1987 | while (true) switch (sub.get(t.tree)) { |
| 1988 | .case_stmt => |sub_case| sub = sub_case.body, |
| 1989 | .default_stmt => |sub_default| sub = sub_default.body, |
| 1990 | else => break, |
| 1991 | }; |
| 1992 | |
| 1993 | const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]); |
| 1994 | |
| 1995 | const switch_else = try ZigTag.switch_else.create(t.arena, res); |
| 1996 | try cases.append(t.gpa, switch_else); |
| 1997 | }, |
| 1998 | else => {}, // collected in transSwitchProngStmt |
| 1999 | } |
| 2000 | } |
| 2001 | |
| 2002 | if (!has_default) { |
| 2003 | const else_prong = try ZigTag.switch_else.create(t.arena, ZigTag.empty_block.init()); |
| 2004 | try cases.append(t.gpa, else_prong); |
| 2005 | } |
| 2006 | |
| 2007 | const switch_node = try ZigTag.@"switch".create(t.arena, .{ |
| 2008 | .cond = switch_expr, |
| 2009 | .cases = try t.arena.dupe(ZigNode, cases.items), |
| 2010 | }); |
| 2011 | try block_scope.statements.append(t.gpa, switch_node); |
| 2012 | try block_scope.statements.append(t.gpa, ZigTag.@"break".init()); |
| 2013 | const while_body = try block_scope.complete(); |
| 2014 | |
| 2015 | return ZigTag.while_true.create(t.arena, while_body); |
| 2016 | } |
| 2017 | |
| 2018 | /// Collects all items for this case, returns the first statement after the labels. |
| 2019 | /// If items ends up empty, the prong should be translated as an else. |
| 2020 | fn transCaseStmt( |
| 2021 | t: *Translator, |
| 2022 | scope: *Scope, |
| 2023 | stmt: Node.Index, |
| 2024 | items: *std.ArrayList(ZigNode), |
| 2025 | ) TransError!Node.Index { |
| 2026 | var sub = stmt; |
| 2027 | var seen_default = false; |
| 2028 | while (true) { |
| 2029 | switch (sub.get(t.tree)) { |
| 2030 | .default_stmt => |default_stmt| { |
| 2031 | seen_default = true; |
| 2032 | items.items.len = 0; |
| 2033 | sub = default_stmt.body; |
| 2034 | }, |
| 2035 | .case_stmt => |case_stmt| { |
| 2036 | if (seen_default) { |
| 2037 | items.items.len = 0; |
| 2038 | sub = case_stmt.body; |
| 2039 | continue; |
| 2040 | } |
| 2041 | |
| 2042 | const expr = if (case_stmt.end) |end| blk: { |
| 2043 | const start_node = try t.transExpr(scope, case_stmt.start, .used); |
| 2044 | const end_node = try t.transExpr(scope, end, .used); |
| 2045 | |
| 2046 | break :blk try ZigTag.ellipsis3.create(t.arena, .{ .lhs = start_node, .rhs = end_node }); |
| 2047 | } else try t.transExpr(scope, case_stmt.start, .used); |
| 2048 | |
| 2049 | try items.append(t.gpa, expr); |
| 2050 | sub = case_stmt.body; |
| 2051 | }, |
| 2052 | else => return sub, |
| 2053 | } |
| 2054 | } |
| 2055 | } |
| 2056 | |
| 2057 | /// Collects all statements seen by this case into a block. |
| 2058 | /// Avoids creating a block if the first statement is a break or return. |
| 2059 | fn transSwitchProngStmt( |
| 2060 | t: *Translator, |
| 2061 | scope: *Scope, |
| 2062 | stmt: Node.Index, |
| 2063 | body: []const Node.Index, |
| 2064 | ) TransError!ZigNode { |
| 2065 | switch (stmt.get(t.tree)) { |
| 2066 | .case_stmt, .default_stmt => unreachable, |
| 2067 | else => { |
| 2068 | var block_scope = try Scope.Block.init(t, scope, false); |
| 2069 | defer block_scope.deinit(); |
| 2070 | |
| 2071 | // we do not need to translate `stmt` since it is the first stmt of `body` |
| 2072 | try t.transSwitchProngStmtInline(&block_scope, body); |
| 2073 | return try block_scope.complete(); |
| 2074 | }, |
| 2075 | } |
| 2076 | } |
| 2077 | |
| 2078 | /// Collects all statements seen by this case into a block. |
| 2079 | fn transSwitchProngStmtInline( |
| 2080 | t: *Translator, |
| 2081 | block: *Scope.Block, |
| 2082 | body: []const Node.Index, |
| 2083 | ) TransError!void { |
| 2084 | for (body) |stmt| { |
| 2085 | switch (stmt.get(t.tree)) { |
| 2086 | .case_stmt => |case_stmt| { |
| 2087 | var sub = case_stmt.body; |
| 2088 | while (true) switch (sub.get(t.tree)) { |
| 2089 | .case_stmt => |sub_case| sub = sub_case.body, |
| 2090 | .default_stmt => |sub_default| sub = sub_default.body, |
| 2091 | else => break, |
| 2092 | }; |
| 2093 | const result = try t.transStmt(&block.base, sub); |
| 2094 | assert(result.tag() != .declaration); |
| 2095 | try block.statements.append(t.gpa, result); |
| 2096 | if (result.isNoreturn()) return; |
| 2097 | }, |
| 2098 | .default_stmt => |default_stmt| { |
| 2099 | var sub = default_stmt.body; |
| 2100 | while (true) switch (sub.get(t.tree)) { |
| 2101 | .case_stmt => |sub_case| sub = sub_case.body, |
| 2102 | .default_stmt => |sub_default| sub = sub_default.body, |
| 2103 | else => break, |
| 2104 | }; |
| 2105 | const result = try t.transStmt(&block.base, sub); |
| 2106 | assert(result.tag() != .declaration); |
| 2107 | try block.statements.append(t.gpa, result); |
| 2108 | if (result.isNoreturn()) return; |
| 2109 | }, |
| 2110 | else => { |
| 2111 | const result = try t.transStmt(&block.base, stmt); |
| 2112 | switch (result.tag()) { |
| 2113 | .declaration, .empty_block => {}, |
| 2114 | else => { |
| 2115 | try block.statements.append(t.gpa, result); |
| 2116 | if (result.isNoreturn()) return; |
| 2117 | }, |
| 2118 | } |
| 2119 | }, |
| 2120 | } |
| 2121 | } |
| 2122 | } |
| 2123 | |
| 2124 | // ====================== |
| 2125 | // Expression translation |
| 2126 | // ====================== |
| 2127 | |
| 2128 | const ResultUsed = enum { used, unused }; |
| 2129 | |
| 2130 | fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode { |
| 2131 | const qt = expr.qt(t.tree); |
| 2132 | return t.maybeSuppressResult(used, switch (expr.get(t.tree)) { |
| 2133 | .paren_expr => |paren_expr| { |
| 2134 | return t.transExpr(scope, paren_expr.operand, used); |
| 2135 | }, |
| 2136 | .cast => |cast| return t.transCastExpr(scope, cast, cast.qt, used, .with_as), |
| 2137 | .decl_ref_expr => |decl_ref| try t.transDeclRefExpr(scope, decl_ref), |
| 2138 | .enumeration_ref => |enum_ref| try t.transDeclRefExpr(scope, enum_ref), |
| 2139 | .addr_of_expr => |addr_of_expr| try ZigTag.address_of.create(t.arena, try t.transExpr(scope, addr_of_expr.operand, .used)), |
| 2140 | .deref_expr => |deref_expr| res: { |
| 2141 | if (t.typeWasDemotedToOpaque(qt)) |
| 2142 | return t.fail(error.UnsupportedTranslation, deref_expr.op_tok, "cannot dereference opaque type", .{}); |
| 2143 | |
| 2144 | // Dereferencing a function pointer is a no-op. |
| 2145 | if (qt.is(t.comp, .func)) return t.transExpr(scope, deref_expr.operand, used); |
| 2146 | |
| 2147 | break :res try ZigTag.deref.create(t.arena, try t.transExpr(scope, deref_expr.operand, .used)); |
| 2148 | }, |
| 2149 | .bool_not_expr => |bool_not_expr| try ZigTag.not.create(t.arena, try t.transBoolExpr(scope, bool_not_expr.operand)), |
| 2150 | .bit_not_expr => |bit_not_expr| try ZigTag.bit_not.create(t.arena, op: { |
| 2151 | const operand = try t.transExpr(scope, bit_not_expr.operand, .used); |
| 2152 | if (!operand.isBoolRes()) break :op operand; |
| 2153 | |
| 2154 | const casted = try ZigTag.int_from_bool.create(t.arena, operand); |
| 2155 | const ty = try t.transType(scope, bit_not_expr.qt, bit_not_expr.op_tok); |
| 2156 | break :op try ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = casted }); |
| 2157 | }), |
| 2158 | .plus_expr => |plus_expr| return t.transExpr(scope, plus_expr.operand, used), |
| 2159 | .negate_expr => |negate_expr| res: { |
| 2160 | const operand_qt = negate_expr.operand.qt(t.tree); |
| 2161 | if (!t.typeHasWrappingOverflow(operand_qt)) { |
| 2162 | const sub_expr_node = try t.transExpr(scope, negate_expr.operand, .used); |
| 2163 | const to_negate = if (sub_expr_node.isBoolRes()) blk: { |
| 2164 | const ty_node = try ZigTag.type.create(t.arena, "c_int"); |
| 2165 | const int_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node); |
| 2166 | break :blk try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_node }); |
| 2167 | } else sub_expr_node; |
| 2168 | |
| 2169 | break :res try ZigTag.negate.create(t.arena, to_negate); |
| 2170 | } else if (t.signedness(operand_qt) == .unsigned) { |
| 2171 | // use -% x for unsigned integers |
| 2172 | break :res try ZigTag.negate_wrap.create(t.arena, try t.transExpr(scope, negate_expr.operand, .used)); |
| 2173 | } else return t.fail(error.UnsupportedTranslation, negate_expr.op_tok, "C negation with non float non integer", .{}); |
| 2174 | }, |
| 2175 | .div_expr => |div_expr| res: { |
| 2176 | if (qt.isInt(t.comp) and t.signedness(qt) == .signed) { |
| 2177 | // signed integer division uses @divTrunc |
| 2178 | const lhs = try t.transExpr(scope, div_expr.lhs, .used); |
| 2179 | const rhs = try t.transExpr(scope, div_expr.rhs, .used); |
| 2180 | break :res try ZigTag.div_trunc.create(t.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 2181 | } |
| 2182 | // unsigned/float division uses the operator |
| 2183 | break :res try t.transBinExpr(scope, div_expr, .div); |
| 2184 | }, |
| 2185 | .mod_expr => |mod_expr| res: { |
| 2186 | if (qt.isInt(t.comp) and t.signedness(qt) == .signed) { |
| 2187 | // signed integer remainder uses __helpers.signedRemainder |
| 2188 | const lhs = try t.transExpr(scope, mod_expr.lhs, .used); |
| 2189 | const rhs = try t.transExpr(scope, mod_expr.rhs, .used); |
| 2190 | break :res try t.createHelperCallNode(.signedRemainder, &.{ lhs, rhs }); |
| 2191 | } |
| 2192 | // unsigned/float division uses the operator |
| 2193 | break :res try t.transBinExpr(scope, mod_expr, .mod); |
| 2194 | }, |
| 2195 | .add_expr => |add_expr| res: { |
| 2196 | // `ptr + idx` and `idx + ptr` -> ptr + @as(usize, @bitCast(@as(isize, @intCast(idx)))) |
| 2197 | const lhs_qt = add_expr.lhs.qt(t.tree); |
| 2198 | const rhs_qt = add_expr.rhs.qt(t.tree); |
| 2199 | if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or |
| 2200 | t.signedness(rhs_qt) == .signed)) |
| 2201 | { |
| 2202 | break :res try t.transPointerArithmeticSignedOp(scope, add_expr, .add); |
| 2203 | } |
| 2204 | |
| 2205 | if (t.signedness(qt) == .unsigned) { |
| 2206 | break :res try t.transBinExpr(scope, add_expr, .add_wrap); |
| 2207 | } else { |
| 2208 | break :res try t.transBinExpr(scope, add_expr, .add); |
| 2209 | } |
| 2210 | }, |
| 2211 | .sub_expr => |sub_expr| res: { |
| 2212 | // `ptr - idx` -> ptr - @as(usize, @bitCast(@as(isize, @intCast(idx)))) |
| 2213 | const lhs_qt = sub_expr.lhs.qt(t.tree); |
| 2214 | const rhs_qt = sub_expr.rhs.qt(t.tree); |
| 2215 | if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or |
| 2216 | t.signedness(rhs_qt) == .signed)) |
| 2217 | { |
| 2218 | break :res try t.transPointerArithmeticSignedOp(scope, sub_expr, .sub); |
| 2219 | } |
| 2220 | |
| 2221 | if (sub_expr.lhs.qt(t.tree).isPointer(t.comp) and sub_expr.rhs.qt(t.tree).isPointer(t.comp)) { |
| 2222 | break :res try t.transPtrDiffExpr(scope, sub_expr); |
| 2223 | } else if (t.signedness(qt) == .unsigned) { |
| 2224 | break :res try t.transBinExpr(scope, sub_expr, .sub_wrap); |
| 2225 | } else { |
| 2226 | break :res try t.transBinExpr(scope, sub_expr, .sub); |
| 2227 | } |
| 2228 | }, |
| 2229 | .mul_expr => |mul_expr| if (t.signedness(qt) == .unsigned) |
| 2230 | try t.transBinExpr(scope, mul_expr, .mul_wrap) |
| 2231 | else |
| 2232 | try t.transBinExpr(scope, mul_expr, .mul), |
| 2233 | |
| 2234 | .less_than_expr => |lt| try t.transBinExpr(scope, lt, .less_than), |
| 2235 | .greater_than_expr => |gt| try t.transBinExpr(scope, gt, .greater_than), |
| 2236 | .less_than_equal_expr => |lte| try t.transBinExpr(scope, lte, .less_than_equal), |
| 2237 | .greater_than_equal_expr => |gte| try t.transBinExpr(scope, gte, .greater_than_equal), |
| 2238 | .equal_expr => |equal_expr| try t.transBinExpr(scope, equal_expr, .equal), |
| 2239 | .not_equal_expr => |not_equal_expr| try t.transBinExpr(scope, not_equal_expr, .not_equal), |
| 2240 | |
| 2241 | .bool_and_expr => |bool_and_expr| try t.transBoolBinExpr(scope, bool_and_expr, .@"and"), |
| 2242 | .bool_or_expr => |bool_or_expr| try t.transBoolBinExpr(scope, bool_or_expr, .@"or"), |
| 2243 | |
| 2244 | .bit_and_expr => |bit_and_expr| try t.transBinExpr(scope, bit_and_expr, .bit_and), |
| 2245 | .bit_or_expr => |bit_or_expr| try t.transBinExpr(scope, bit_or_expr, .bit_or), |
| 2246 | .bit_xor_expr => |bit_xor_expr| try t.transBinExpr(scope, bit_xor_expr, .bit_xor), |
| 2247 | |
| 2248 | .shl_expr => |shl_expr| try t.transShiftExpr(scope, shl_expr, .shl), |
| 2249 | .shr_expr => |shr_expr| try t.transShiftExpr(scope, shr_expr, .shr), |
| 2250 | |
| 2251 | .member_access_expr => |member_access| try t.transMemberAccess(scope, .normal, member_access, null, .accessor), |
| 2252 | .member_access_ptr_expr => |member_access| try t.transMemberAccess(scope, .ptr, member_access, null, .accessor), |
| 2253 | .array_access_expr => |array_access| try t.transArrayAccess(scope, array_access, null), |
| 2254 | |
| 2255 | .builtin_ref => unreachable, |
| 2256 | .builtin_call_expr => |call| return t.transBuiltinCall(scope, call, used), |
| 2257 | .call_expr => |call| return t.transCall(scope, call, used), |
| 2258 | |
| 2259 | .builtin_types_compatible_p => |compatible| blk: { |
| 2260 | const lhs = try t.transType(scope, compatible.lhs, compatible.builtin_tok); |
| 2261 | const rhs = try t.transType(scope, compatible.rhs, compatible.builtin_tok); |
| 2262 | |
| 2263 | break :blk try ZigTag.equal.create(t.arena, .{ |
| 2264 | .lhs = lhs, |
| 2265 | .rhs = rhs, |
| 2266 | }); |
| 2267 | }, |
| 2268 | .builtin_choose_expr => |choose| return t.transCondExpr(scope, choose, used), |
| 2269 | .cond_expr => |cond_expr| return t.transCondExpr(scope, cond_expr, used), |
| 2270 | .binary_cond_expr => |conditional| return t.transBinaryCondExpr(scope, conditional, used), |
| 2271 | .cond_dummy_expr => unreachable, |
| 2272 | |
| 2273 | .assign_expr => |assign| return t.transAssignExpr(scope, assign, used), |
| 2274 | .add_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2275 | .sub_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2276 | .mul_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2277 | .div_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2278 | .mod_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2279 | .shl_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2280 | .shr_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2281 | .bit_and_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2282 | .bit_xor_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2283 | .bit_or_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used), |
| 2284 | .compound_assign_dummy_expr => { |
| 2285 | assert(used == .used); |
| 2286 | return t.compound_assign_dummy.?; |
| 2287 | }, |
| 2288 | |
| 2289 | .comma_expr => |comma_expr| return t.transCommaExpr(scope, comma_expr, used), |
| 2290 | .pre_inc_expr => |un| return t.transIncDecExpr(scope, un, .pre, .inc, used), |
| 2291 | .pre_dec_expr => |un| return t.transIncDecExpr(scope, un, .pre, .dec, used), |
| 2292 | .post_inc_expr => |un| return t.transIncDecExpr(scope, un, .post, .inc, used), |
| 2293 | .post_dec_expr => |un| return t.transIncDecExpr(scope, un, .post, .dec, used), |
| 2294 | |
| 2295 | .int_literal => return t.transIntLiteral(scope, expr, used, .with_as), |
| 2296 | .char_literal => return t.transCharLiteral(scope, expr, used, .with_as), |
| 2297 | .float_literal => return t.transFloatLiteral(scope, expr, used, .with_as), |
| 2298 | .string_literal_expr => |literal| try t.transStringLiteral(scope, expr, literal), |
| 2299 | .bool_literal => res: { |
| 2300 | const val = t.tree.value_map.get(expr).?; |
| 2301 | break :res if (val.toBool(t.comp)) |
| 2302 | ZigTag.true_literal.init() |
| 2303 | else |
| 2304 | ZigTag.false_literal.init(); |
| 2305 | }, |
| 2306 | .nullptr_literal => ZigTag.null_literal.init(), |
| 2307 | .imaginary_literal => |literal| { |
| 2308 | return t.fail(error.UnsupportedTranslation, literal.op_tok, "TODO complex numbers", .{}); |
| 2309 | }, |
| 2310 | .compound_literal_expr => |literal| return t.transCompoundLiteral(scope, literal, used), |
| 2311 | |
| 2312 | .default_init_expr => |default_init| return t.transDefaultInit(scope, default_init, used, .with_as), |
| 2313 | .array_init_expr => |array_init| return t.transArrayInit(scope, array_init, used), |
| 2314 | .union_init_expr => |union_init| return t.transUnionInit(scope, union_init, used), |
| 2315 | .struct_init_expr => |struct_init| return t.transStructInit(scope, struct_init, used), |
| 2316 | .array_filler_expr => unreachable, |
| 2317 | |
| 2318 | .sizeof_expr => |sizeof| try t.transTypeInfo(scope, .sizeof, sizeof), |
| 2319 | .alignof_expr => |alignof| try t.transTypeInfo(scope, .alignof, alignof), |
| 2320 | |
| 2321 | .imag_expr, .real_expr => |un| { |
| 2322 | return t.fail(error.UnsupportedTranslation, un.op_tok, "TODO complex numbers", .{}); |
| 2323 | }, |
| 2324 | .addr_of_label => |addr_of_label| { |
| 2325 | return t.fail(error.UnsupportedTranslation, addr_of_label.label_tok, "TODO computed goto", .{}); |
| 2326 | }, |
| 2327 | |
| 2328 | .generic_expr => |generic| return t.transExpr(scope, generic.chosen, used), |
| 2329 | .generic_association_expr => |generic| return t.transExpr(scope, generic.expr, used), |
| 2330 | .generic_default_expr => |generic| return t.transExpr(scope, generic.expr, used), |
| 2331 | |
| 2332 | .stmt_expr => |stmt_expr| return t.transStmtExpr(scope, stmt_expr, used), |
| 2333 | |
| 2334 | .builtin_convertvector => |convertvector| try t.transConvertvectorExpr(scope, convertvector), |
| 2335 | .builtin_shufflevector => |shufflevector| try t.transShufflevectorExpr(scope, shufflevector), |
| 2336 | |
| 2337 | .builtin_va_arg_pack, .builtin_va_arg_pack_len => |va_arg_pack| { |
| 2338 | return t.fail(error.UnsupportedTranslation, va_arg_pack.builtin_tok, "TODO va arg pack", .{}); |
| 2339 | }, |
| 2340 | |
| 2341 | .compound_stmt, |
| 2342 | .static_assert, |
| 2343 | .return_stmt, |
| 2344 | .null_stmt, |
| 2345 | .if_stmt, |
| 2346 | .while_stmt, |
| 2347 | .do_while_stmt, |
| 2348 | .for_stmt, |
| 2349 | .continue_stmt, |
| 2350 | .break_stmt, |
| 2351 | .labeled_stmt, |
| 2352 | .switch_stmt, |
| 2353 | .case_stmt, |
| 2354 | .default_stmt, |
| 2355 | .goto_stmt, |
| 2356 | .computed_goto_stmt, |
| 2357 | .asm_stmt, |
| 2358 | .global_asm, |
| 2359 | .typedef, |
| 2360 | .struct_decl, |
| 2361 | .union_decl, |
| 2362 | .enum_decl, |
| 2363 | .function, |
| 2364 | .param, |
| 2365 | .variable, |
| 2366 | .enum_field, |
| 2367 | .record_field, |
| 2368 | .struct_forward_decl, |
| 2369 | .union_forward_decl, |
| 2370 | .enum_forward_decl, |
| 2371 | .empty_decl, |
| 2372 | => unreachable, // not an expression |
| 2373 | }); |
| 2374 | } |
| 2375 | |
| 2376 | /// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore |
| 2377 | /// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. |
| 2378 | fn transExprCoercing(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode { |
| 2379 | switch (expr.get(t.tree)) { |
| 2380 | .int_literal => return t.transIntLiteral(scope, expr, used, .no_as), |
| 2381 | .char_literal => return t.transCharLiteral(scope, expr, used, .no_as), |
| 2382 | .float_literal => return t.transFloatLiteral(scope, expr, used, .no_as), |
| 2383 | .cast => |cast| switch (cast.kind) { |
| 2384 | .no_op => { |
| 2385 | const operand = cast.operand.get(t.tree); |
| 2386 | if (operand == .cast) { |
| 2387 | return t.transCastExpr(scope, operand.cast, cast.qt, used, .no_as); |
| 2388 | } |
| 2389 | return t.transExprCoercing(scope, cast.operand, used); |
| 2390 | }, |
| 2391 | .lval_to_rval => return t.transExprCoercing(scope, cast.operand, used), |
| 2392 | else => return t.transCastExpr(scope, cast, cast.qt, used, .no_as), |
| 2393 | }, |
| 2394 | .default_init_expr => |default_init| return try t.transDefaultInit(scope, default_init, used, .no_as), |
| 2395 | .compound_literal_expr => |literal| { |
| 2396 | if (!literal.thread_local and literal.storage_class != .static) { |
| 2397 | return t.transExprCoercing(scope, literal.initializer, used); |
| 2398 | } |
| 2399 | }, |
| 2400 | else => {}, |
| 2401 | } |
| 2402 | |
| 2403 | return t.transExpr(scope, expr, used); |
| 2404 | } |
| 2405 | |
| 2406 | fn transBoolExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode { |
| 2407 | switch (expr.get(t.tree)) { |
| 2408 | .int_literal => { |
| 2409 | const int_val = t.tree.value_map.get(expr).?; |
| 2410 | return if (int_val.isZero(t.comp)) |
| 2411 | ZigTag.false_literal.init() |
| 2412 | else |
| 2413 | ZigTag.true_literal.init(); |
| 2414 | }, |
| 2415 | .cast => |cast| switch (cast.kind) { |
| 2416 | .bool_to_int => return t.transExpr(scope, cast.operand, .used), |
| 2417 | .array_to_pointer => { |
| 2418 | const operand = cast.operand.get(t.tree); |
| 2419 | if (operand == .string_literal_expr) { |
| 2420 | // @intFromPtr("foo") != 0, always true |
| 2421 | const str = try t.transStringLiteral(scope, cast.operand, operand.string_literal_expr); |
| 2422 | const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, str); |
| 2423 | return ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() }); |
| 2424 | } |
| 2425 | }, |
| 2426 | else => {}, |
| 2427 | }, |
| 2428 | else => {}, |
| 2429 | } |
| 2430 | |
| 2431 | const maybe_bool_res = try t.transExpr(scope, expr, .used); |
| 2432 | if (maybe_bool_res.isBoolRes()) { |
| 2433 | return maybe_bool_res; |
| 2434 | } |
| 2435 | |
| 2436 | return t.finishBoolExpr(expr.qt(t.tree), maybe_bool_res); |
| 2437 | } |
| 2438 | |
| 2439 | fn toNonBool(t: *Translator, node: ZigNode, qt: QualType) Error!ZigNode { |
| 2440 | if (!node.isBoolRes()) return node; |
| 2441 | if (qt.is(t.comp, .bool)) return node; |
| 2442 | return ZigTag.int_from_bool.create(t.arena, node); |
| 2443 | } |
| 2444 | |
| 2445 | fn finishBoolExpr(t: *Translator, qt: QualType, node: ZigNode) TransError!ZigNode { |
| 2446 | const sk = qt.scalarKind(t.comp); |
| 2447 | if (sk == .bool) return node; |
| 2448 | if (sk == .nullptr_t) { |
| 2449 | // node == null, always true |
| 2450 | return ZigTag.equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() }); |
| 2451 | } |
| 2452 | if (sk.isPointer()) { |
| 2453 | // node != null |
| 2454 | return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() }); |
| 2455 | } |
| 2456 | if (sk != .none) { |
| 2457 | // node != 0 |
| 2458 | return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() }); |
| 2459 | } |
| 2460 | unreachable; // Unexpected bool expression type |
| 2461 | } |
| 2462 | |
| 2463 | fn transCastExpr( |
| 2464 | t: *Translator, |
| 2465 | scope: *Scope, |
| 2466 | cast: Node.Cast, |
| 2467 | dest_qt: QualType, |
| 2468 | used: ResultUsed, |
| 2469 | suppress_as: SuppressCast, |
| 2470 | ) TransError!ZigNode { |
| 2471 | const operand = switch (cast.kind) { |
| 2472 | .no_op => { |
| 2473 | const operand = cast.operand.get(t.tree); |
| 2474 | if (operand == .cast) { |
| 2475 | return t.transCastExpr(scope, operand.cast, cast.qt, used, suppress_as); |
| 2476 | } |
| 2477 | return t.transExpr(scope, cast.operand, used); |
| 2478 | }, |
| 2479 | .lval_to_rval, .function_to_pointer => { |
| 2480 | return t.transExpr(scope, cast.operand, used); |
| 2481 | }, |
| 2482 | .int_cast => int_cast: { |
| 2483 | const src_qt = cast.operand.qt(t.tree); |
| 2484 | |
| 2485 | if (cast.implicit) { |
| 2486 | if (t.tree.value_map.get(cast.operand)) |val| { |
| 2487 | const max_int = try aro.Value.maxInt(dest_qt, t.comp); |
| 2488 | const min_int = try aro.Value.minInt(dest_qt, t.comp); |
| 2489 | |
| 2490 | if (val.compare(.lte, max_int, t.comp) and val.compare(.gte, min_int, t.comp)) { |
| 2491 | break :int_cast try t.transExprCoercing(scope, cast.operand, .used); |
| 2492 | } |
| 2493 | } |
| 2494 | } |
| 2495 | const operand = try t.transExpr(scope, cast.operand, .used); |
| 2496 | break :int_cast try t.transIntCast(operand, src_qt, dest_qt); |
| 2497 | }, |
| 2498 | .to_void => { |
| 2499 | assert(used == .unused); |
| 2500 | return try t.transExpr(scope, cast.operand, .unused); |
| 2501 | }, |
| 2502 | .null_to_pointer => ZigTag.null_literal.init(), |
| 2503 | .array_to_pointer => array_to_pointer: { |
| 2504 | const child_qt = dest_qt.childType(t.comp); |
| 2505 | |
| 2506 | loop: switch (cast.operand.get(t.tree)) { |
| 2507 | .string_literal_expr => |literal| { |
| 2508 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2509 | |
| 2510 | const ref = if (literal.kind == .utf8 or literal.kind == .ascii) |
| 2511 | sub_expr_node |
| 2512 | else |
| 2513 | try ZigTag.address_of.create(t.arena, sub_expr_node); |
| 2514 | |
| 2515 | const casted = if (child_qt.@"const") |
| 2516 | ref |
| 2517 | else |
| 2518 | try ZigTag.const_cast.create(t.arena, sub_expr_node); |
| 2519 | |
| 2520 | return t.maybeSuppressResult(used, casted); |
| 2521 | }, |
| 2522 | .paren_expr => |paren_expr| { |
| 2523 | continue :loop paren_expr.operand.get(t.tree); |
| 2524 | }, |
| 2525 | .generic_expr => |generic| { |
| 2526 | continue :loop generic.chosen.get(t.tree); |
| 2527 | }, |
| 2528 | .generic_association_expr => |generic| { |
| 2529 | continue :loop generic.expr.get(t.tree); |
| 2530 | }, |
| 2531 | .generic_default_expr => |generic| { |
| 2532 | continue :loop generic.expr.get(t.tree); |
| 2533 | }, |
| 2534 | else => {}, |
| 2535 | } |
| 2536 | |
| 2537 | // Flexible array members are translated as member functions returning |
| 2538 | // [*c]T, so no address-of + @ptrCast wrapping is needed. |
| 2539 | flexible: { |
| 2540 | if (cast.operand.qt(t.tree).arrayLen(t.comp) == null) { |
| 2541 | return try t.transExpr(scope, cast.operand, used); |
| 2542 | } |
| 2543 | |
| 2544 | const member_index, const base_qt = switch (cast.operand.get(t.tree)) { |
| 2545 | .member_access_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree) }, |
| 2546 | .member_access_ptr_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree).childType(t.comp) }, |
| 2547 | else => break :flexible, |
| 2548 | }; |
| 2549 | const record = base_qt.getRecord(t.comp) orelse break :flexible; |
| 2550 | if (member_index != record.fields.len - 1 and base_qt.base(t.comp).type != .@"union") break :flexible; |
| 2551 | const array_ty = record.fields[member_index].qt.get(t.comp, .array) orelse break :flexible; |
| 2552 | if (t.isFlexibleArrayLen(array_ty.len)) return try t.transExpr(scope, cast.operand, used); |
| 2553 | } |
| 2554 | |
| 2555 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2556 | const ref = try ZigTag.address_of.create(t.arena, sub_expr_node); |
| 2557 | const align_cast = try ZigTag.align_cast.create(t.arena, ref); |
| 2558 | break :array_to_pointer try ZigTag.ptr_cast.create(t.arena, align_cast); |
| 2559 | }, |
| 2560 | .int_to_pointer => int_to_pointer: { |
| 2561 | var sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2562 | const operand_qt = cast.operand.qt(t.tree); |
| 2563 | if (t.signedness(operand_qt) == .signed or operand_qt.bitSizeof(t.comp) > t.comp.target.ptrBitWidth()) { |
| 2564 | sub_expr_node = try ZigTag.as.create(t.arena, .{ |
| 2565 | .lhs = try ZigTag.type.create(t.arena, "usize"), |
| 2566 | .rhs = try ZigTag.int_cast.create(t.arena, sub_expr_node), |
| 2567 | }); |
| 2568 | } else if (sub_expr_node.isBoolRes()) { |
| 2569 | sub_expr_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node); |
| 2570 | } |
| 2571 | break :int_to_pointer try ZigTag.ptr_from_int.create(t.arena, sub_expr_node); |
| 2572 | }, |
| 2573 | .int_to_bool => { |
| 2574 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2575 | if (sub_expr_node.isBoolRes()) return sub_expr_node; |
| 2576 | if (cast.operand.qt(t.tree).is(t.comp, .bool)) return sub_expr_node; |
| 2577 | const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() }); |
| 2578 | return t.maybeSuppressResult(used, cmp_node); |
| 2579 | }, |
| 2580 | .float_to_bool => { |
| 2581 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2582 | const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() }); |
| 2583 | return t.maybeSuppressResult(used, cmp_node); |
| 2584 | }, |
| 2585 | .pointer_to_bool => { |
| 2586 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2587 | |
| 2588 | // Special case function pointers as @intFromPtr(expr) != 0 |
| 2589 | if (cast.operand.qt(t.tree).get(t.comp, .pointer)) |ptr_ty| if (ptr_ty.child.is(t.comp, .func)) { |
| 2590 | const ptr_node = if (sub_expr_node.tag() == .identifier) |
| 2591 | try ZigTag.address_of.create(t.arena, sub_expr_node) |
| 2592 | else |
| 2593 | sub_expr_node; |
| 2594 | const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, ptr_node); |
| 2595 | const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() }); |
| 2596 | return t.maybeSuppressResult(used, cmp_node); |
| 2597 | }; |
| 2598 | |
| 2599 | const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.null_literal.init() }); |
| 2600 | return t.maybeSuppressResult(used, cmp_node); |
| 2601 | }, |
| 2602 | .bool_to_int => bool_to_int: { |
| 2603 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2604 | break :bool_to_int try ZigTag.int_from_bool.create(t.arena, sub_expr_node); |
| 2605 | }, |
| 2606 | .bool_to_float => bool_to_float: { |
| 2607 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2608 | const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node); |
| 2609 | break :bool_to_float try ZigTag.float_from_int.create(t.arena, int_from_bool); |
| 2610 | }, |
| 2611 | .bool_to_pointer => bool_to_pointer: { |
| 2612 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2613 | const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node); |
| 2614 | break :bool_to_pointer try ZigTag.ptr_from_int.create(t.arena, int_from_bool); |
| 2615 | }, |
| 2616 | .float_cast => float_cast: { |
| 2617 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2618 | break :float_cast try ZigTag.float_cast.create(t.arena, sub_expr_node); |
| 2619 | }, |
| 2620 | .int_to_float => int_to_float: { |
| 2621 | const sub_expr_node = try t.transExpr(scope, cast.operand, used); |
| 2622 | const int_node = if (sub_expr_node.isBoolRes()) |
| 2623 | try ZigTag.int_from_bool.create(t.arena, sub_expr_node) |
| 2624 | else |
| 2625 | sub_expr_node; |
| 2626 | break :int_to_float try ZigTag.float_from_int.create(t.arena, int_node); |
| 2627 | }, |
| 2628 | .float_to_int => float_to_int: { |
| 2629 | const sub_expr_node = try t.transExpr(scope, cast.operand, .used); |
| 2630 | break :float_to_int try ZigTag.int_from_float.create(t.arena, sub_expr_node); |
| 2631 | }, |
| 2632 | .pointer_to_int => pointer_to_int: { |
| 2633 | const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand); |
| 2634 | const ptr_node = try ZigTag.int_from_ptr.create(t.arena, sub_expr_node); |
| 2635 | break :pointer_to_int try ZigTag.int_cast.create(t.arena, ptr_node); |
| 2636 | }, |
| 2637 | .bitcast => bitcast: { |
| 2638 | const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand); |
| 2639 | const operand_qt = cast.operand.qt(t.tree); |
| 2640 | if (dest_qt.isPointer(t.comp) and operand_qt.isPointer(t.comp)) { |
| 2641 | var casted = try ZigTag.align_cast.create(t.arena, sub_expr_node); |
| 2642 | casted = try ZigTag.ptr_cast.create(t.arena, casted); |
| 2643 | |
| 2644 | const src_elem = operand_qt.childType(t.comp); |
| 2645 | const dest_elem = dest_qt.childType(t.comp); |
| 2646 | if ((src_elem.@"const" or src_elem.is(t.comp, .func)) and !dest_elem.@"const") { |
| 2647 | casted = try ZigTag.const_cast.create(t.arena, casted); |
| 2648 | } |
| 2649 | if (src_elem.@"volatile" and !dest_elem.@"volatile") { |
| 2650 | casted = try ZigTag.volatile_cast.create(t.arena, casted); |
| 2651 | } |
| 2652 | break :bitcast casted; |
| 2653 | } |
| 2654 | |
| 2655 | break :bitcast try ZigTag.bit_cast.create(t.arena, sub_expr_node); |
| 2656 | }, |
| 2657 | .union_cast => union_cast: { |
| 2658 | const union_type = try t.transType(scope, dest_qt, cast.l_paren); |
| 2659 | |
| 2660 | const operand_qt = cast.operand.qt(t.tree); |
| 2661 | const union_base = dest_qt.base(t.comp); |
| 2662 | const field = for (union_base.type.@"union".fields) |field| { |
| 2663 | if (field.qt.eql(operand_qt, t.comp)) break field; |
| 2664 | } else unreachable; |
| 2665 | const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{ |
| 2666 | .parent = union_base.qt, |
| 2667 | .field = field.qt, |
| 2668 | }).? else field.name.lookup(t.comp); |
| 2669 | |
| 2670 | const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer); |
| 2671 | field_init.* = .{ |
| 2672 | .name = field_name, |
| 2673 | .value = try t.transExpr(scope, cast.operand, .used), |
| 2674 | }; |
| 2675 | break :union_cast try ZigTag.container_init.create(t.arena, .{ |
| 2676 | .lhs = union_type, |
| 2677 | .inits = field_init[0..1], |
| 2678 | }); |
| 2679 | }, |
| 2680 | else => return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)}), |
| 2681 | }; |
| 2682 | if (suppress_as == .no_as) return t.maybeSuppressResult(used, operand); |
| 2683 | if (used == .unused) return t.maybeSuppressResult(used, operand); |
| 2684 | const as = try ZigTag.as.create(t.arena, .{ |
| 2685 | .lhs = try t.transType(scope, dest_qt, cast.l_paren), |
| 2686 | .rhs = operand, |
| 2687 | }); |
| 2688 | return as; |
| 2689 | } |
| 2690 | |
| 2691 | fn transIntCast(t: *Translator, operand: ZigNode, src_qt: QualType, dest_qt: QualType) !ZigNode { |
| 2692 | const src_dest_order = src_qt.intRankOrder(dest_qt, t.comp); |
| 2693 | const different_sign = t.signedness(src_qt) != t.signedness(dest_qt); |
| 2694 | const needs_bitcast = different_sign and !(t.signedness(src_qt) == .unsigned and src_dest_order == .lt); |
| 2695 | |
| 2696 | var casted = operand; |
| 2697 | if (casted.isBoolRes()) { |
| 2698 | casted = try ZigTag.int_from_bool.create(t.arena, casted); |
| 2699 | } else if (src_dest_order == .gt) { |
| 2700 | // No C type is smaller than the 1 bit from @intFromBool |
| 2701 | casted = try ZigTag.truncate.create(t.arena, casted); |
| 2702 | } |
| 2703 | if (needs_bitcast) { |
| 2704 | if (src_dest_order != .eq) { |
| 2705 | casted = try ZigTag.as.create(t.arena, .{ |
| 2706 | .lhs = try t.transTypeIntWidthOf(dest_qt, t.signedness(src_qt) == .signed), |
| 2707 | .rhs = casted, |
| 2708 | }); |
| 2709 | } |
| 2710 | return ZigTag.bit_cast.create(t.arena, casted); |
| 2711 | } |
| 2712 | return casted; |
| 2713 | } |
| 2714 | |
| 2715 | /// Same as `transExpr` but adds a `&` if the expression is an identifier referencing a function type. |
| 2716 | fn transPointerCastExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode { |
| 2717 | const sub_expr_node = try t.transExpr(scope, expr, .used); |
| 2718 | switch (expr.get(t.tree)) { |
| 2719 | .cast => |cast| if (cast.kind == .function_to_pointer and sub_expr_node.tag() == .identifier) { |
| 2720 | return ZigTag.address_of.create(t.arena, sub_expr_node); |
| 2721 | }, |
| 2722 | else => {}, |
| 2723 | } |
| 2724 | return sub_expr_node; |
| 2725 | } |
| 2726 | |
| 2727 | fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode { |
| 2728 | if (t.wip_var_inits.contains(decl_ref.decl)) return error.SelfReferential; |
| 2729 | |
| 2730 | const name = t.tree.tokSlice(decl_ref.name_tok); |
| 2731 | const maybe_alias = scope.getAlias(name); |
| 2732 | const mangled_name = maybe_alias orelse name; |
| 2733 | |
| 2734 | switch (decl_ref.decl.get(t.tree)) { |
| 2735 | .function => |function| if (function.definition == null and function.body == null) { |
| 2736 | // Try translating the decl again in case of out of scope declaration. |
| 2737 | try t.transFnDecl(scope, function); |
| 2738 | }, |
| 2739 | else => {}, |
| 2740 | } |
| 2741 | |
| 2742 | const decl = decl_ref.decl.get(t.tree); |
| 2743 | const ref_expr = blk: { |
| 2744 | const identifier = try ZigTag.identifier.create(t.arena, mangled_name); |
| 2745 | if (decl_ref.qt.is(t.comp, .func) and maybe_alias != null) { |
| 2746 | break :blk try ZigTag.field_access.create(t.arena, .{ |
| 2747 | .lhs = identifier, |
| 2748 | .field_name = name, |
| 2749 | }); |
| 2750 | } |
| 2751 | if (decl == .variable and maybe_alias != null) { |
| 2752 | switch (decl.variable.storage_class) { |
| 2753 | .@"extern", .static => { |
| 2754 | break :blk try ZigTag.field_access.create(t.arena, .{ |
| 2755 | .lhs = identifier, |
| 2756 | .field_name = name, |
| 2757 | }); |
| 2758 | }, |
| 2759 | else => {}, |
| 2760 | } |
| 2761 | } |
| 2762 | break :blk identifier; |
| 2763 | }; |
| 2764 | |
| 2765 | scope.skipVariableDiscard(mangled_name); |
| 2766 | return ref_expr; |
| 2767 | } |
| 2768 | |
| 2769 | fn transBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode { |
| 2770 | const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used); |
| 2771 | const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used); |
| 2772 | |
| 2773 | const lhs = if (lhs_uncasted.isBoolRes()) |
| 2774 | try ZigTag.int_from_bool.create(t.arena, lhs_uncasted) |
| 2775 | else |
| 2776 | lhs_uncasted; |
| 2777 | |
| 2778 | const rhs = if (rhs_uncasted.isBoolRes()) |
| 2779 | try ZigTag.int_from_bool.create(t.arena, rhs_uncasted) |
| 2780 | else |
| 2781 | rhs_uncasted; |
| 2782 | |
| 2783 | return t.createBinOpNode(op_id, lhs, rhs); |
| 2784 | } |
| 2785 | |
| 2786 | fn transBoolBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op: ZigTag) !ZigNode { |
| 2787 | std.debug.assert(op == .@"and" or op == .@"or"); |
| 2788 | |
| 2789 | const lhs = try t.transBoolExpr(scope, bin.lhs); |
| 2790 | const rhs = try t.transBoolExpr(scope, bin.rhs); |
| 2791 | |
| 2792 | return t.createBinOpNode(op, lhs, rhs); |
| 2793 | } |
| 2794 | |
| 2795 | fn transShiftExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) !ZigNode { |
| 2796 | std.debug.assert(op_id == .shl or op_id == .shr); |
| 2797 | |
| 2798 | // lhs >> @intCast(rh) |
| 2799 | const lhs = try t.transExpr(scope, bin.lhs, .used); |
| 2800 | |
| 2801 | const rhs = try t.transExpr(scope, bin.rhs, .used); |
| 2802 | const rhs_casted = try ZigTag.int_cast.create(t.arena, rhs); |
| 2803 | |
| 2804 | return t.createBinOpNode(op_id, lhs, rhs_casted); |
| 2805 | } |
| 2806 | |
| 2807 | fn transCondExpr( |
| 2808 | t: *Translator, |
| 2809 | scope: *Scope, |
| 2810 | conditional: Node.Conditional, |
| 2811 | used: ResultUsed, |
| 2812 | ) TransError!ZigNode { |
| 2813 | var cond_scope: Scope.Condition = .{ |
| 2814 | .base = .{ |
| 2815 | .parent = scope, |
| 2816 | .id = .condition, |
| 2817 | }, |
| 2818 | }; |
| 2819 | defer cond_scope.deinit(); |
| 2820 | |
| 2821 | const res_is_bool = conditional.qt.is(t.comp, .bool); |
| 2822 | const cond = try t.transBoolExpr(&cond_scope.base, conditional.cond); |
| 2823 | |
| 2824 | var then_body = try t.transExpr(scope, conditional.then_expr, used); |
| 2825 | if (!res_is_bool and then_body.isBoolRes()) { |
| 2826 | then_body = try ZigTag.int_from_bool.create(t.arena, then_body); |
| 2827 | } |
| 2828 | |
| 2829 | var else_body = try t.transExpr(scope, conditional.else_expr, used); |
| 2830 | if (!res_is_bool and else_body.isBoolRes()) { |
| 2831 | else_body = try ZigTag.int_from_bool.create(t.arena, else_body); |
| 2832 | } |
| 2833 | |
| 2834 | // The `ResultUsed` is forwarded to both branches so no need to suppress the result here. |
| 2835 | return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); |
| 2836 | } |
| 2837 | |
| 2838 | fn transBinaryCondExpr( |
| 2839 | t: *Translator, |
| 2840 | scope: *Scope, |
| 2841 | conditional: Node.Conditional, |
| 2842 | used: ResultUsed, |
| 2843 | ) TransError!ZigNode { |
| 2844 | // GNU extension of the ternary operator where the middle expression is |
| 2845 | // omitted, the condition itself is returned if it evaluates to true. |
| 2846 | |
| 2847 | if (used == .unused) { |
| 2848 | // Result unused so this can be translated as |
| 2849 | // if (condition) else_expr; |
| 2850 | var cond_scope: Scope.Condition = .{ |
| 2851 | .base = .{ |
| 2852 | .parent = scope, |
| 2853 | .id = .condition, |
| 2854 | }, |
| 2855 | }; |
| 2856 | defer cond_scope.deinit(); |
| 2857 | |
| 2858 | return ZigTag.@"if".create(t.arena, .{ |
| 2859 | .cond = try t.transBoolExpr(&cond_scope.base, conditional.cond), |
| 2860 | .then = try t.transExpr(scope, conditional.else_expr, .unused), |
| 2861 | .@"else" = null, |
| 2862 | }); |
| 2863 | } |
| 2864 | |
| 2865 | const res_is_bool = conditional.qt.is(t.comp, .bool); |
| 2866 | // c: (condition)?:(else_expr) |
| 2867 | // zig: (blk: { |
| 2868 | // const _cond_temp = (condition); |
| 2869 | // break :blk if (_cond_temp) _cond_temp else (else_expr); |
| 2870 | // }) |
| 2871 | var block_scope = try Scope.Block.init(t, scope, true); |
| 2872 | defer block_scope.deinit(); |
| 2873 | |
| 2874 | const cond_temp = try block_scope.reserveMangledName("cond_temp"); |
| 2875 | const init_node = try t.transExpr(&block_scope.base, conditional.cond, .used); |
| 2876 | const temp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = cond_temp, .init = init_node }); |
| 2877 | try block_scope.statements.append(t.gpa, temp_decl); |
| 2878 | |
| 2879 | var cond_scope: Scope.Condition = .{ |
| 2880 | .base = .{ |
| 2881 | .parent = &block_scope.base, |
| 2882 | .id = .condition, |
| 2883 | }, |
| 2884 | }; |
| 2885 | defer cond_scope.deinit(); |
| 2886 | |
| 2887 | const cond_ident = try ZigTag.identifier.create(t.arena, cond_temp); |
| 2888 | const cond_node = try t.finishBoolExpr(conditional.cond.qt(t.tree), cond_ident); |
| 2889 | var then_body = cond_ident; |
| 2890 | if (!res_is_bool and init_node.isBoolRes()) { |
| 2891 | then_body = try ZigTag.int_from_bool.create(t.arena, then_body); |
| 2892 | } |
| 2893 | |
| 2894 | var else_body = try t.transExpr(&block_scope.base, conditional.else_expr, .used); |
| 2895 | if (!res_is_bool and else_body.isBoolRes()) { |
| 2896 | else_body = try ZigTag.int_from_bool.create(t.arena, else_body); |
| 2897 | } |
| 2898 | const if_node = try ZigTag.@"if".create(t.arena, .{ |
| 2899 | .cond = cond_node, |
| 2900 | .then = then_body, |
| 2901 | .@"else" = else_body, |
| 2902 | }); |
| 2903 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 2904 | .label = block_scope.label, |
| 2905 | .val = if_node, |
| 2906 | }); |
| 2907 | try block_scope.statements.append(t.gpa, break_node); |
| 2908 | return block_scope.complete(); |
| 2909 | } |
| 2910 | |
| 2911 | fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) TransError!ZigNode { |
| 2912 | if (used == .unused) { |
| 2913 | const lhs = try t.transExprCoercing(scope, bin.lhs, .unused); |
| 2914 | try scope.appendNode(lhs); |
| 2915 | const rhs = try t.transExprCoercing(scope, bin.rhs, .unused); |
| 2916 | return rhs; |
| 2917 | } |
| 2918 | |
| 2919 | var block_scope = try Scope.Block.init(t, scope, true); |
| 2920 | defer block_scope.deinit(); |
| 2921 | |
| 2922 | const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .unused); |
| 2923 | try block_scope.statements.append(t.gpa, lhs); |
| 2924 | |
| 2925 | const rhs = try t.transExprCoercing(&block_scope.base, bin.rhs, .used); |
| 2926 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 2927 | .label = block_scope.label, |
| 2928 | .val = try t.toNonBool(rhs, bin.qt), |
| 2929 | }); |
| 2930 | try block_scope.statements.append(t.gpa, break_node); |
| 2931 | |
| 2932 | return try block_scope.complete(); |
| 2933 | } |
| 2934 | |
| 2935 | fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) !ZigNode { |
| 2936 | if (used == .unused) { |
| 2937 | const lhs = try t.transExpr(scope, bin.lhs, .used); |
| 2938 | const rhs = try t.transExprCoercing(scope, bin.rhs, .used); |
| 2939 | |
| 2940 | const lhs_qt = bin.lhs.qt(t.tree); |
| 2941 | return t.createBinOpNode(.assign, lhs, try t.toNonBool(rhs, lhs_qt)); |
| 2942 | } |
| 2943 | |
| 2944 | var block_scope = try Scope.Block.init(t, scope, true); |
| 2945 | defer block_scope.deinit(); |
| 2946 | |
| 2947 | const tmp = try block_scope.reserveMangledName("tmp"); |
| 2948 | |
| 2949 | const rhs = try t.transExpr(&block_scope.base, bin.rhs, .used); |
| 2950 | const lhs_qt = bin.lhs.qt(t.tree); |
| 2951 | const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ |
| 2952 | .name = tmp, |
| 2953 | .init = try t.toNonBool(rhs, lhs_qt), |
| 2954 | }); |
| 2955 | try block_scope.statements.append(t.gpa, tmp_decl); |
| 2956 | |
| 2957 | const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .used); |
| 2958 | const tmp_ident = try ZigTag.identifier.create(t.arena, tmp); |
| 2959 | |
| 2960 | const assign = try t.createBinOpNode(.assign, lhs, tmp_ident); |
| 2961 | try block_scope.statements.append(t.gpa, assign); |
| 2962 | |
| 2963 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 2964 | .label = block_scope.label, |
| 2965 | .val = tmp_ident, |
| 2966 | }); |
| 2967 | try block_scope.statements.append(t.gpa, break_node); |
| 2968 | |
| 2969 | return try block_scope.complete(); |
| 2970 | } |
| 2971 | |
| 2972 | fn transCompoundAssign( |
| 2973 | t: *Translator, |
| 2974 | scope: *Scope, |
| 2975 | assign: Node.Binary, |
| 2976 | used: ResultUsed, |
| 2977 | ) !ZigNode { |
| 2978 | // If the result is unused we can try using the equivalent Zig operator |
| 2979 | // without a block |
| 2980 | if (used == .unused) { |
| 2981 | if (try t.transCompoundAssignSimple(scope, null, assign)) |some| { |
| 2982 | return some; |
| 2983 | } |
| 2984 | } |
| 2985 | |
| 2986 | // Otherwise we need to wrap the the compound assignment in a block. |
| 2987 | var block_scope = try Scope.Block.init(t, scope, used == .used); |
| 2988 | defer block_scope.deinit(); |
| 2989 | const ref = try block_scope.reserveMangledName("ref"); |
| 2990 | |
| 2991 | const lhs_expr = try t.transExpr(&block_scope.base, assign.lhs, .used); |
| 2992 | const addr_of = try ZigTag.address_of.create(t.arena, lhs_expr); |
| 2993 | const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = addr_of }); |
| 2994 | try block_scope.statements.append(t.gpa, ref_decl); |
| 2995 | |
| 2996 | const lhs_node = try ZigTag.identifier.create(t.arena, ref); |
| 2997 | const ref_node = try ZigTag.deref.create(t.arena, lhs_node); |
| 2998 | |
| 2999 | // Use the equivalent Zig operator if possible. |
| 3000 | if (try t.transCompoundAssignSimple(scope, ref_node, assign)) |some| { |
| 3001 | try block_scope.statements.append(t.gpa, some); |
| 3002 | } else { |
| 3003 | const old_dummy = t.compound_assign_dummy; |
| 3004 | defer t.compound_assign_dummy = old_dummy; |
| 3005 | t.compound_assign_dummy = ref_node; |
| 3006 | |
| 3007 | // Otherwise do the operation and assignment separately. |
| 3008 | const rhs_node = try t.transExprCoercing(&block_scope.base, assign.rhs, .used); |
| 3009 | const assign_node = try t.createBinOpNode(.assign, ref_node, rhs_node); |
| 3010 | try block_scope.statements.append(t.gpa, assign_node); |
| 3011 | } |
| 3012 | |
| 3013 | if (used == .used) { |
| 3014 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 3015 | .label = block_scope.label, |
| 3016 | .val = ref_node, |
| 3017 | }); |
| 3018 | try block_scope.statements.append(t.gpa, break_node); |
| 3019 | } |
| 3020 | return block_scope.complete(); |
| 3021 | } |
| 3022 | |
| 3023 | /// Translates compound assignment using the equivalent Zig operator if possible. |
| 3024 | fn transCompoundAssignSimple(t: *Translator, scope: *Scope, lhs_dummy_opt: ?ZigNode, assign: Node.Binary) TransError!?ZigNode { |
| 3025 | const assign_rhs = assign.rhs.get(t.tree); |
| 3026 | if (assign_rhs == .cast) return null; |
| 3027 | |
| 3028 | const is_signed = t.signedness(assign.qt) == .signed; |
| 3029 | switch (assign_rhs) { |
| 3030 | .div_expr, .mod_expr => if (is_signed) return null, |
| 3031 | else => {}, |
| 3032 | } |
| 3033 | const lhs_ptr = assign.qt.isPointer(t.comp); |
| 3034 | |
| 3035 | const bin, const op: ZigTag, const cast: enum { none, shift, usize } = switch (assign_rhs) { |
| 3036 | .add_expr => |bin| .{ |
| 3037 | bin, |
| 3038 | if (t.typeHasWrappingOverflow(bin.qt)) .add_wrap_assign else .add_assign, |
| 3039 | if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none, |
| 3040 | }, |
| 3041 | .sub_expr => |bin| .{ |
| 3042 | bin, |
| 3043 | if (t.typeHasWrappingOverflow(bin.qt)) .sub_wrap_assign else .sub_assign, |
| 3044 | if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none, |
| 3045 | }, |
| 3046 | .mul_expr => |bin| .{ |
| 3047 | bin, |
| 3048 | if (t.typeHasWrappingOverflow(bin.qt)) .mul_wrap_assign else .mul_assign, |
| 3049 | .none, |
| 3050 | }, |
| 3051 | .mod_expr => |bin| .{ bin, .mod_assign, .none }, |
| 3052 | .div_expr => |bin| .{ bin, .div_assign, .none }, |
| 3053 | .shl_expr => |bin| .{ bin, .shl_assign, .shift }, |
| 3054 | .shr_expr => |bin| .{ bin, .shr_assign, .shift }, |
| 3055 | .bit_and_expr => |bin| .{ bin, .bit_and_assign, .none }, |
| 3056 | .bit_xor_expr => |bin| .{ bin, .bit_xor_assign, .none }, |
| 3057 | .bit_or_expr => |bin| .{ bin, .bit_or_assign, .none }, |
| 3058 | else => unreachable, |
| 3059 | }; |
| 3060 | |
| 3061 | const lhs_node = blk: { |
| 3062 | const old_dummy = t.compound_assign_dummy; |
| 3063 | defer t.compound_assign_dummy = old_dummy; |
| 3064 | t.compound_assign_dummy = lhs_dummy_opt orelse try t.transExpr(scope, assign.lhs, .used); |
| 3065 | |
| 3066 | break :blk try t.transExpr(scope, bin.lhs, .used); |
| 3067 | }; |
| 3068 | |
| 3069 | const rhs_node = try t.transExprCoercing(scope, bin.rhs, .used); |
| 3070 | const casted_rhs = switch (cast) { |
| 3071 | .none => rhs_node, |
| 3072 | .shift => try ZigTag.int_cast.create(t.arena, rhs_node), |
| 3073 | .usize => try t.usizeCastForWrappingPtrArithmetic(rhs_node), |
| 3074 | }; |
| 3075 | return try t.createBinOpNode(op, lhs_node, casted_rhs); |
| 3076 | } |
| 3077 | |
| 3078 | fn transIncDecExpr( |
| 3079 | t: *Translator, |
| 3080 | scope: *Scope, |
| 3081 | un: Node.Unary, |
| 3082 | position: enum { pre, post }, |
| 3083 | kind: enum { inc, dec }, |
| 3084 | used: ResultUsed, |
| 3085 | ) !ZigNode { |
| 3086 | const is_wrapping = t.typeHasWrappingOverflow(un.qt); |
| 3087 | const op: ZigTag = switch (kind) { |
| 3088 | .inc => if (is_wrapping) .add_wrap_assign else .add_assign, |
| 3089 | .dec => if (is_wrapping) .sub_wrap_assign else .sub_assign, |
| 3090 | }; |
| 3091 | |
| 3092 | const one_literal = ZigTag.one_literal.init(); |
| 3093 | if (used == .unused) { |
| 3094 | const operand = try t.transExpr(scope, un.operand, .used); |
| 3095 | return try t.createBinOpNode(op, operand, one_literal); |
| 3096 | } |
| 3097 | |
| 3098 | var block_scope = try Scope.Block.init(t, scope, true); |
| 3099 | defer block_scope.deinit(); |
| 3100 | |
| 3101 | const ref = try block_scope.reserveMangledName("ref"); |
| 3102 | const operand = try t.transExprCoercing(&block_scope.base, un.operand, .used); |
| 3103 | const operand_ref = try ZigTag.address_of.create(t.arena, operand); |
| 3104 | const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = operand_ref }); |
| 3105 | try block_scope.statements.append(t.gpa, ref_decl); |
| 3106 | |
| 3107 | const ref_ident = try ZigTag.identifier.create(t.arena, ref); |
| 3108 | const ref_deref = try ZigTag.deref.create(t.arena, ref_ident); |
| 3109 | const effect = try t.createBinOpNode(op, ref_deref, one_literal); |
| 3110 | |
| 3111 | switch (position) { |
| 3112 | .pre => { |
| 3113 | try block_scope.statements.append(t.gpa, effect); |
| 3114 | |
| 3115 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 3116 | .label = block_scope.label, |
| 3117 | .val = ref_deref, |
| 3118 | }); |
| 3119 | try block_scope.statements.append(t.gpa, break_node); |
| 3120 | }, |
| 3121 | .post => { |
| 3122 | const tmp = try block_scope.reserveMangledName("tmp"); |
| 3123 | const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = ref_deref }); |
| 3124 | try block_scope.statements.append(t.gpa, tmp_decl); |
| 3125 | |
| 3126 | try block_scope.statements.append(t.gpa, effect); |
| 3127 | |
| 3128 | const tmp_ident = try ZigTag.identifier.create(t.arena, tmp); |
| 3129 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 3130 | .label = block_scope.label, |
| 3131 | .val = tmp_ident, |
| 3132 | }); |
| 3133 | try block_scope.statements.append(t.gpa, break_node); |
| 3134 | }, |
| 3135 | } |
| 3136 | |
| 3137 | return try block_scope.complete(); |
| 3138 | } |
| 3139 | |
| 3140 | fn transPtrDiffExpr(t: *Translator, scope: *Scope, bin: Node.Binary) TransError!ZigNode { |
| 3141 | const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used); |
| 3142 | const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used); |
| 3143 | |
| 3144 | const lhs = try ZigTag.int_from_ptr.create(t.arena, lhs_uncasted); |
| 3145 | const rhs = try ZigTag.int_from_ptr.create(t.arena, rhs_uncasted); |
| 3146 | |
| 3147 | const sub_res = try t.createBinOpNode(.sub_wrap, lhs, rhs); |
| 3148 | |
| 3149 | // @divExact(@as(<platform-ptrdiff_t>, @bitCast(@intFromPtr(lhs)) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>)) |
| 3150 | const ptrdiff_type = try t.transTypeIntWidthOf(bin.qt, true); |
| 3151 | |
| 3152 | const bitcast = try ZigTag.as.create(t.arena, .{ |
| 3153 | .lhs = ptrdiff_type, |
| 3154 | .rhs = try ZigTag.bit_cast.create(t.arena, sub_res), |
| 3155 | }); |
| 3156 | |
| 3157 | // C standard requires that pointer subtraction operands are of the same type, |
| 3158 | // otherwise it is undefined behavior. So we can assume the left and right |
| 3159 | // sides are the same Type and arbitrarily choose left. |
| 3160 | const lhs_ty = try t.transType(scope, bin.lhs.qt(t.tree), bin.lhs.tok(t.tree)); |
| 3161 | const c_pointer = t.getContainer(lhs_ty).?; |
| 3162 | |
| 3163 | if (c_pointer.castTag(.c_pointer)) |c_pointer_payload| { |
| 3164 | const sizeof = try ZigTag.sizeof.create(t.arena, c_pointer_payload.data.elem_type); |
| 3165 | return ZigTag.div_exact.create(t.arena, .{ |
| 3166 | .lhs = bitcast, |
| 3167 | .rhs = sizeof, |
| 3168 | }); |
| 3169 | } else { |
| 3170 | // This is an opaque/incomplete type. This subtraction exhibits Undefined Behavior by the C99 spec. |
| 3171 | // However, allowing subtraction on `void *` and function pointers is a commonly used extension. |
| 3172 | // So, just return the value in byte units, mirroring the behavior of this language extension as implemented by GCC and Clang. |
| 3173 | return bitcast; |
| 3174 | } |
| 3175 | } |
| 3176 | |
| 3177 | /// Translate an arithmetic expression with a pointer operand and a signed-integer operand. |
| 3178 | /// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then |
| 3179 | /// bitcast to usize; pointer wraparound makes the math work. |
| 3180 | /// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left. |
| 3181 | /// The + operator in C is not a sequence point so it should be safe to switch the order if necessary. |
| 3182 | fn transPointerArithmeticSignedOp(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode { |
| 3183 | std.debug.assert(op_id == .add or op_id == .sub); |
| 3184 | |
| 3185 | const lhs_qt = bin.lhs.qt(t.tree); |
| 3186 | const swap_operands = op_id == .add and t.signedness(lhs_qt) == .signed; |
| 3187 | |
| 3188 | const swizzled_lhs = if (swap_operands) bin.rhs else bin.lhs; |
| 3189 | const swizzled_rhs = if (swap_operands) bin.lhs else bin.rhs; |
| 3190 | |
| 3191 | const lhs_node = try t.transExpr(scope, swizzled_lhs, .used); |
| 3192 | const rhs_node = try t.transExpr(scope, swizzled_rhs, .used); |
| 3193 | |
| 3194 | const bitcast_node = try t.usizeCastForWrappingPtrArithmetic(rhs_node); |
| 3195 | |
| 3196 | return t.createBinOpNode(op_id, lhs_node, bitcast_node); |
| 3197 | } |
| 3198 | |
| 3199 | fn transMemberAccess( |
| 3200 | t: *Translator, |
| 3201 | scope: *Scope, |
| 3202 | kind: enum { normal, ptr }, |
| 3203 | member_access: Node.MemberAccess, |
| 3204 | opt_base: ?ZigNode, |
| 3205 | flex_array_mode: enum { accessor, backing }, |
| 3206 | ) TransError!ZigNode { |
| 3207 | const base_info = switch (kind) { |
| 3208 | .normal => member_access.base.qt(t.tree), |
| 3209 | .ptr => member_access.base.qt(t.tree).childType(t.comp), |
| 3210 | }; |
| 3211 | if (t.typeWasDemotedToOpaque(base_info)) { |
| 3212 | return t.fail(error.UnsupportedTranslation, member_access.access_tok, "member access of demoted record", .{}); |
| 3213 | } |
| 3214 | |
| 3215 | const record = base_info.getRecord(t.comp).?; |
| 3216 | const field = record.fields[member_access.member_index]; |
| 3217 | const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{ |
| 3218 | .parent = base_info.base(t.comp).qt, |
| 3219 | .field = field.qt, |
| 3220 | }).? else field.name.lookup(t.comp); |
| 3221 | const base_node = opt_base orelse try t.transExpr(scope, member_access.base, .used); |
| 3222 | const lhs = switch (kind) { |
| 3223 | .normal => base_node, |
| 3224 | .ptr => try ZigTag.deref.create(t.arena, base_node), |
| 3225 | }; |
| 3226 | const field_access = try ZigTag.field_access.create(t.arena, .{ |
| 3227 | .lhs = lhs, |
| 3228 | .field_name = field_name, |
| 3229 | }); |
| 3230 | |
| 3231 | // Flexible array members are translated as member functions. |
| 3232 | if (member_access.member_index == record.fields.len - 1 or base_info.base(t.comp).type == .@"union") { |
| 3233 | if (field.qt.get(t.comp, .array)) |array_ty| { |
| 3234 | if (t.isFlexibleArrayLen(array_ty.len)) { |
| 3235 | switch (flex_array_mode) { |
| 3236 | .accessor => return ZigTag.call.create(t.arena, .{ .lhs = field_access, .args = &.{} }), |
| 3237 | .backing => { |
| 3238 | const backing_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name}); |
| 3239 | return ZigTag.field_access.create(t.arena, .{ .lhs = lhs, .field_name = backing_name }); |
| 3240 | }, |
| 3241 | } |
| 3242 | } |
| 3243 | } |
| 3244 | } |
| 3245 | |
| 3246 | return field_access; |
| 3247 | } |
| 3248 | |
| 3249 | fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAccess, opt_base: ?ZigNode) TransError!ZigNode { |
| 3250 | // Unwrap the base statement if it's an array decayed to a bare pointer type |
| 3251 | // so that we index the array itself |
| 3252 | const base = base: { |
| 3253 | const base = array_access.base.get(t.tree); |
| 3254 | if (base != .cast) break :base array_access.base; |
| 3255 | if (base.cast.kind != .array_to_pointer) break :base array_access.base; |
| 3256 | break :base base.cast.operand; |
| 3257 | }; |
| 3258 | |
| 3259 | const base_node = opt_base orelse try t.transExpr(scope, base, .used); |
| 3260 | const index = index: { |
| 3261 | const index = try t.transExpr(scope, array_access.index, .used); |
| 3262 | const index_qt = array_access.index.qt(t.tree); |
| 3263 | const maybe_bigger_than_usize = type: switch (index_qt.base(t.comp).type) { |
| 3264 | .bool => { |
| 3265 | break :index try ZigTag.int_from_bool.create(t.arena, index); |
| 3266 | }, |
| 3267 | .int => |int| switch (int) { |
| 3268 | .long_long, .ulong_long, .int128, .uint128 => true, |
| 3269 | else => false, |
| 3270 | }, |
| 3271 | .bit_int => |bit_int| bit_int.bits > t.comp.target.ptrBitWidth(), |
| 3272 | .@"enum" => |e| if (e.tag) |tag| continue :type tag.base(t.comp).type else false, |
| 3273 | else => unreachable, |
| 3274 | }; |
| 3275 | |
| 3276 | const is_nonnegative_int_literal = if (t.tree.value_map.get(array_access.index)) |val| |
| 3277 | val.compare(.gte, .zero, t.comp) |
| 3278 | else |
| 3279 | false; |
| 3280 | const is_signed = t.signedness(index_qt) == .signed; |
| 3281 | |
| 3282 | if (is_signed and !is_nonnegative_int_literal) { |
| 3283 | // First cast to `isize` to get proper sign extension and |
| 3284 | // then @bitCast to `usize` to satisfy the compiler. |
| 3285 | const index_isize = try ZigTag.as.create(t.arena, .{ |
| 3286 | .lhs = try ZigTag.type.create(t.arena, "isize"), |
| 3287 | .rhs = try ZigTag.int_cast.create(t.arena, index), |
| 3288 | }); |
| 3289 | break :index try ZigTag.bit_cast.create(t.arena, index_isize); |
| 3290 | } |
| 3291 | |
| 3292 | if (maybe_bigger_than_usize) { |
| 3293 | break :index try ZigTag.int_cast.create(t.arena, index); |
| 3294 | } |
| 3295 | break :index index; |
| 3296 | }; |
| 3297 | |
| 3298 | return ZigTag.array_access.create(t.arena, .{ |
| 3299 | .lhs = base_node, |
| 3300 | .rhs = index, |
| 3301 | }); |
| 3302 | } |
| 3303 | |
| 3304 | fn transOffsetof(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode { |
| 3305 | // Translate __builtin_offsetof(T, designator) as |
| 3306 | // @intFromPtr(&(@as(*allowzero T, @ptrFromInt(0)).designator)) |
| 3307 | const member = try t.transMemberDesignator(scope, arg); |
| 3308 | const address = try ZigTag.address_of.create(t.arena, member); |
| 3309 | return ZigTag.int_from_ptr.create(t.arena, address); |
| 3310 | } |
| 3311 | |
| 3312 | fn transMemberDesignator(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode { |
| 3313 | switch (arg.get(t.tree)) { |
| 3314 | .default_init_expr => |default| { |
| 3315 | const elem_node = try t.transType(scope, default.qt, default.last_tok); |
| 3316 | const ptr_ty = try ZigTag.single_pointer.create(t.arena, .{ |
| 3317 | .elem_type = elem_node, |
| 3318 | .is_allowzero = true, |
| 3319 | .is_const = false, |
| 3320 | .is_volatile = false, |
| 3321 | }); |
| 3322 | const zero = try ZigTag.ptr_from_int.create(t.arena, ZigTag.zero_literal.init()); |
| 3323 | return ZigTag.as.create(t.arena, .{ .lhs = ptr_ty, .rhs = zero }); |
| 3324 | }, |
| 3325 | .array_access_expr => |access| { |
| 3326 | const base = try t.transMemberDesignator(scope, access.base); |
| 3327 | return t.transArrayAccess(scope, access, base); |
| 3328 | }, |
| 3329 | .member_access_expr => |access| { |
| 3330 | const base = try t.transMemberDesignator(scope, access.base); |
| 3331 | // In offsetof context, flexible array members must be accessed via |
| 3332 | // the backing field (`_name`) rather than the accessor function, |
| 3333 | // because you can't take the address of a function call result. |
| 3334 | return t.transMemberAccess(scope, .normal, access, base, .backing); |
| 3335 | }, |
| 3336 | .cast => |cast| { |
| 3337 | assert(cast.kind == .array_to_pointer); |
| 3338 | return t.transMemberDesignator(scope, cast.operand); |
| 3339 | }, |
| 3340 | else => unreachable, |
| 3341 | } |
| 3342 | } |
| 3343 | |
| 3344 | fn transBuiltinCall( |
| 3345 | t: *Translator, |
| 3346 | scope: *Scope, |
| 3347 | call: Node.BuiltinCall, |
| 3348 | used: ResultUsed, |
| 3349 | ) TransError!ZigNode { |
| 3350 | const builtin_name = t.tree.tokSlice(call.builtin_tok); |
| 3351 | if (std.mem.eql(u8, builtin_name, "__builtin_offsetof")) { |
| 3352 | const res = try t.transOffsetof(scope, call.args[0]); |
| 3353 | return t.maybeSuppressResult(used, res); |
| 3354 | } |
| 3355 | |
| 3356 | const builtin = builtins.map.get(builtin_name) orelse |
| 3357 | return t.fail(error.UnsupportedTranslation, call.builtin_tok, "TODO implement function '{s}' in std.zig.c_builtins", .{builtin_name}); |
| 3358 | |
| 3359 | if (builtin.tag) |tag| switch (tag) { |
| 3360 | .byte_swap, .ceil, .cos, .sin, .exp, .exp2, .exp10, .abs, .log, .log2, .log10, .round, .sqrt, .trunc, .floor => { |
| 3361 | assert(call.args.len == 1); |
| 3362 | const arg = try t.transExprCoercing(scope, call.args[0], .used); |
| 3363 | const arg_ty = try t.transType(scope, call.args[0].qt(t.tree), call.args[0].tok(t.tree)); |
| 3364 | const coerced = try ZigTag.as.create(t.arena, .{ .lhs = arg_ty, .rhs = arg }); |
| 3365 | |
| 3366 | const ptr = try t.arena.create(ast.Payload.UnOp); |
| 3367 | ptr.* = .{ .base = .{ .tag = tag }, .data = coerced }; |
| 3368 | return t.maybeSuppressResult(used, ZigNode.initPayload(&ptr.base)); |
| 3369 | }, |
| 3370 | .@"unreachable" => return ZigTag.@"unreachable".init(), |
| 3371 | else => unreachable, |
| 3372 | }; |
| 3373 | |
| 3374 | const arg_nodes = try t.arena.alloc(ZigNode, call.args.len); |
| 3375 | for (call.args, arg_nodes) |c_arg, *zig_arg| { |
| 3376 | zig_arg.* = try t.transExprCoercing(scope, c_arg, .used); |
| 3377 | } |
| 3378 | |
| 3379 | const builtin_identifier = try ZigTag.identifier.create(t.arena, "__builtin"); |
| 3380 | const field_access = try ZigTag.field_access.create(t.arena, .{ |
| 3381 | .lhs = builtin_identifier, |
| 3382 | .field_name = builtin.name, |
| 3383 | }); |
| 3384 | |
| 3385 | const res = try ZigTag.call.create(t.arena, .{ |
| 3386 | .lhs = field_access, |
| 3387 | .args = arg_nodes, |
| 3388 | }); |
| 3389 | if (call.qt.is(t.comp, .void)) return res; |
| 3390 | return t.maybeSuppressResult(used, res); |
| 3391 | } |
| 3392 | |
| 3393 | fn transCall( |
| 3394 | t: *Translator, |
| 3395 | scope: *Scope, |
| 3396 | call: Node.Call, |
| 3397 | used: ResultUsed, |
| 3398 | ) TransError!ZigNode { |
| 3399 | const raw_fn_expr = try t.transExpr(scope, call.callee, .used); |
| 3400 | const fn_expr = blk: { |
| 3401 | loop: switch (call.callee.get(t.tree)) { |
| 3402 | .paren_expr => |paren_expr| { |
| 3403 | continue :loop paren_expr.operand.get(t.tree); |
| 3404 | }, |
| 3405 | .decl_ref_expr => |decl_ref| { |
| 3406 | if (decl_ref.qt.is(t.comp, .func)) break :blk raw_fn_expr; |
| 3407 | }, |
| 3408 | .cast => |cast| { |
| 3409 | if (cast.kind == .function_to_pointer) { |
| 3410 | continue :loop cast.operand.get(t.tree); |
| 3411 | } |
| 3412 | }, |
| 3413 | .deref_expr, .addr_of_expr => |un| { |
| 3414 | continue :loop un.operand.get(t.tree); |
| 3415 | }, |
| 3416 | .generic_expr => |generic| { |
| 3417 | continue :loop generic.chosen.get(t.tree); |
| 3418 | }, |
| 3419 | .generic_association_expr => |generic| { |
| 3420 | continue :loop generic.expr.get(t.tree); |
| 3421 | }, |
| 3422 | .generic_default_expr => |generic| { |
| 3423 | continue :loop generic.expr.get(t.tree); |
| 3424 | }, |
| 3425 | else => {}, |
| 3426 | } |
| 3427 | break :blk try ZigTag.unwrap.create(t.arena, raw_fn_expr); |
| 3428 | }; |
| 3429 | |
| 3430 | const callee_qt = call.callee.qt(t.tree); |
| 3431 | const maybe_ptr_ty = callee_qt.get(t.comp, .pointer); |
| 3432 | const func_qt = if (maybe_ptr_ty) |ptr| ptr.child else callee_qt; |
| 3433 | const func_ty = func_qt.get(t.comp, .func).?; |
| 3434 | |
| 3435 | const arg_nodes = try t.arena.alloc(ZigNode, call.args.len); |
| 3436 | for (call.args, arg_nodes, 0..) |c_arg, *zig_arg, i| { |
| 3437 | if (i < func_ty.params.len) { |
| 3438 | zig_arg.* = try t.transExprCoercing(scope, c_arg, .used); |
| 3439 | |
| 3440 | if (zig_arg.isBoolRes() and !func_ty.params[i].qt.is(t.comp, .bool)) { |
| 3441 | // In C the result type of a boolean expression is int. If this result is passed as |
| 3442 | // an argument to a function whose parameter is also int, there is no cast. Therefore |
| 3443 | // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int). |
| 3444 | zig_arg.* = try ZigTag.int_from_bool.create(t.arena, zig_arg.*); |
| 3445 | } |
| 3446 | } else { |
| 3447 | zig_arg.* = try t.transExpr(scope, c_arg, .used); |
| 3448 | |
| 3449 | if (zig_arg.isBoolRes()) { |
| 3450 | // Same as above but now we don't have a result type. |
| 3451 | const u1_node = try ZigTag.int_from_bool.create(t.arena, zig_arg.*); |
| 3452 | const c_int_node = try ZigTag.type.create(t.arena, "c_int"); |
| 3453 | zig_arg.* = try ZigTag.as.create(t.arena, .{ .lhs = c_int_node, .rhs = u1_node }); |
| 3454 | } |
| 3455 | } |
| 3456 | } |
| 3457 | |
| 3458 | const res = try ZigTag.call.create(t.arena, .{ |
| 3459 | .lhs = fn_expr, |
| 3460 | .args = arg_nodes, |
| 3461 | }); |
| 3462 | if (call.qt.is(t.comp, .void)) return res; |
| 3463 | return t.maybeSuppressResult(used, res); |
| 3464 | } |
| 3465 | |
| 3466 | const SuppressCast = enum { with_as, no_as }; |
| 3467 | |
| 3468 | /// Attempt to translate literal as the name of the simple macro |
| 3469 | /// it was expanded from. |
| 3470 | fn checkLiteralMacro(t: *Translator, tok: TokenIndex, used: ResultUsed) !?ZigNode { |
| 3471 | if (!t.keep_macro_literals) return null; |
| 3472 | const expansion_locs = t.pp.expansionSlice(tok); |
| 3473 | if (expansion_locs.len == 0) return null; |
| 3474 | |
| 3475 | const last_expand = expansion_locs[0]; |
| 3476 | const source = t.comp.getSource(last_expand.id); |
| 3477 | var tokenizer: aro.Tokenizer = .{ |
| 3478 | .buf = source.buf, |
| 3479 | .langopts = t.comp.langopts, |
| 3480 | .source = last_expand.id, |
| 3481 | .index = last_expand.byte_offset, |
| 3482 | .splice_locs = &.{}, |
| 3483 | }; |
| 3484 | const name_tok = tokenizer.next(); |
| 3485 | if (!name_tok.id.isMacroIdentifier()) return null; |
| 3486 | |
| 3487 | const name = t.pp.tokSlice(name_tok); |
| 3488 | if (t.global_scope.containsNow(name)) return null; |
| 3489 | const macro = t.pp.defines.get(name) orelse return null; |
| 3490 | if (macro.is_func) return null; |
| 3491 | if (macro.isBuiltin()) return null; |
| 3492 | |
| 3493 | var tok_count: u8 = 0; |
| 3494 | for (macro.tokens) |macro_tok| { |
| 3495 | switch (macro_tok.id) { |
| 3496 | .invalid => continue, |
| 3497 | .whitespace => continue, |
| 3498 | .comment => continue, |
| 3499 | .macro_ws => continue, |
| 3500 | else => { |
| 3501 | if (tok_count != 0) return null; |
| 3502 | tok_count += 1; |
| 3503 | }, |
| 3504 | } |
| 3505 | } |
| 3506 | |
| 3507 | if (t.checkTranslatableMacro(macro.tokens, macro.params) != null) return null; |
| 3508 | |
| 3509 | const ident = try ZigTag.identifier.create(t.arena, name); |
| 3510 | return try t.maybeSuppressResult(used, ident); |
| 3511 | } |
| 3512 | |
| 3513 | fn transIntLiteral( |
| 3514 | t: *Translator, |
| 3515 | scope: *Scope, |
| 3516 | literal_index: Node.Index, |
| 3517 | used: ResultUsed, |
| 3518 | suppress_as: SuppressCast, |
| 3519 | ) TransError!ZigNode { |
| 3520 | if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node; |
| 3521 | const val = t.tree.value_map.get(literal_index).?; |
| 3522 | const int_lit_node = try t.createIntNode(val); |
| 3523 | if (suppress_as == .no_as) { |
| 3524 | return t.maybeSuppressResult(used, int_lit_node); |
| 3525 | } |
| 3526 | |
| 3527 | // Integer literals in C have types, and this can matter for several reasons. |
| 3528 | // For example, this is valid C: |
| 3529 | // unsigned char y = 256; |
| 3530 | // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted |
| 3531 | // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code: |
| 3532 | // var y = @as(u8, @bitCast(@as(i8, @truncate(@as(c_int, 256))))); |
| 3533 | |
| 3534 | // @as(T, x) |
| 3535 | const ty_node = try t.transType(scope, literal_index.qt(t.tree), literal_index.tok(t.tree)); |
| 3536 | const as = try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_lit_node }); |
| 3537 | return t.maybeSuppressResult(used, as); |
| 3538 | } |
| 3539 | |
| 3540 | fn transCharLiteral( |
| 3541 | t: *Translator, |
| 3542 | scope: *Scope, |
| 3543 | literal_index: Node.Index, |
| 3544 | used: ResultUsed, |
| 3545 | suppress_as: SuppressCast, |
| 3546 | ) TransError!ZigNode { |
| 3547 | if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node; |
| 3548 | const val = t.tree.value_map.get(literal_index).?; |
| 3549 | const char_literal = literal_index.get(t.tree).char_literal; |
| 3550 | const narrow = char_literal.kind == .ascii or char_literal.kind == .utf8; |
| 3551 | |
| 3552 | // C has a somewhat obscure feature called multi-character character constant |
| 3553 | // e.g. 'abcd' |
| 3554 | const int_value = val.toInt(u32, t.comp).?; |
| 3555 | const int_lit_node = if (char_literal.kind == .ascii and int_value > 255) |
| 3556 | try t.createNumberNode(int_value) |
| 3557 | else |
| 3558 | try t.createCharLiteralNode(narrow, int_value); |
| 3559 | |
| 3560 | if (suppress_as == .no_as) { |
| 3561 | return t.maybeSuppressResult(used, int_lit_node); |
| 3562 | } |
| 3563 | |
| 3564 | // See comment in `transIntLiteral` for why this code is here. |
| 3565 | // @as(T, x) |
| 3566 | const as_node = try ZigTag.as.create(t.arena, .{ |
| 3567 | .lhs = try t.transType(scope, char_literal.qt, char_literal.literal_tok), |
| 3568 | .rhs = int_lit_node, |
| 3569 | }); |
| 3570 | return t.maybeSuppressResult(used, as_node); |
| 3571 | } |
| 3572 | |
| 3573 | fn transFloatLiteral( |
| 3574 | t: *Translator, |
| 3575 | scope: *Scope, |
| 3576 | literal_index: Node.Index, |
| 3577 | used: ResultUsed, |
| 3578 | suppress_as: SuppressCast, |
| 3579 | ) TransError!ZigNode { |
| 3580 | if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node; |
| 3581 | const val = t.tree.value_map.get(literal_index).?; |
| 3582 | const float_literal = literal_index.get(t.tree).float_literal; |
| 3583 | |
| 3584 | var allocating: std.Io.Writer.Allocating = .init(t.gpa); |
| 3585 | defer allocating.deinit(); |
| 3586 | _ = val.print(float_literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory; |
| 3587 | if (mem.findScalar(u8, allocating.written(), '.') == null) { |
| 3588 | allocating.writer.writeAll(".0") catch return error.OutOfMemory; |
| 3589 | } |
| 3590 | |
| 3591 | const float_lit_node = try ZigTag.float_literal.create(t.arena, try t.arena.dupe(u8, allocating.written())); |
| 3592 | if (suppress_as == .no_as) { |
| 3593 | return t.maybeSuppressResult(used, float_lit_node); |
| 3594 | } |
| 3595 | |
| 3596 | const as_node = try ZigTag.as.create(t.arena, .{ |
| 3597 | .lhs = try t.transType(scope, float_literal.qt, float_literal.literal_tok), |
| 3598 | .rhs = float_lit_node, |
| 3599 | }); |
| 3600 | return t.maybeSuppressResult(used, as_node); |
| 3601 | } |
| 3602 | |
| 3603 | fn transStringLiteral( |
| 3604 | t: *Translator, |
| 3605 | scope: *Scope, |
| 3606 | expr: Node.Index, |
| 3607 | literal: Node.CharLiteral, |
| 3608 | ) TransError!ZigNode { |
| 3609 | switch (literal.kind) { |
| 3610 | .ascii, .utf8 => return t.transNarrowStringLiteral(expr, literal), |
| 3611 | .utf16, .utf32, .wide => { |
| 3612 | const name = try std.fmt.allocPrint(t.arena, "{s}_string_{d}", .{ @tagName(literal.kind), t.getMangle() }); |
| 3613 | |
| 3614 | const array_type = try t.transTypeInit(scope, literal.qt, expr, literal.literal_tok); |
| 3615 | const lit_array = try t.transStringLiteralInitializer(expr, literal, array_type); |
| 3616 | const decl = try ZigTag.var_simple.create(t.arena, .{ .name = name, .init = lit_array }); |
| 3617 | try scope.appendNode(decl); |
| 3618 | return ZigTag.identifier.create(t.arena, name); |
| 3619 | }, |
| 3620 | } |
| 3621 | } |
| 3622 | |
| 3623 | fn transNarrowStringLiteral( |
| 3624 | t: *Translator, |
| 3625 | expr: Node.Index, |
| 3626 | literal: Node.CharLiteral, |
| 3627 | ) TransError!ZigNode { |
| 3628 | const val = t.tree.value_map.get(expr).?; |
| 3629 | |
| 3630 | const bytes = t.comp.interner.get(val.ref()).bytes; |
| 3631 | var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len); |
| 3632 | defer allocating.deinit(); |
| 3633 | |
| 3634 | aro.Value.printString(bytes, literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory; |
| 3635 | |
| 3636 | return ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.written())); |
| 3637 | } |
| 3638 | |
| 3639 | /// Translate a string literal that is initializing an array. In general narrow string |
| 3640 | /// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation. |
| 3641 | /// Wide string literals become an array of integers. zero-fillers pad out the array to |
| 3642 | /// the appropriate length, if necessary. |
| 3643 | fn transStringLiteralInitializer( |
| 3644 | t: *Translator, |
| 3645 | expr: Node.Index, |
| 3646 | literal: Node.CharLiteral, |
| 3647 | array_type: ZigNode, |
| 3648 | ) TransError!ZigNode { |
| 3649 | assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type); |
| 3650 | |
| 3651 | const is_narrow = literal.kind == .ascii or literal.kind == .utf8; |
| 3652 | |
| 3653 | // The length of the string literal excluding the sentinel. |
| 3654 | const str_length = literal.qt.arrayLen(t.comp).? - 1; |
| 3655 | |
| 3656 | const payload = (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data; |
| 3657 | const array_size = payload.len; |
| 3658 | const elem_type = payload.elem_type; |
| 3659 | |
| 3660 | if (array_size == 0) return ZigTag.empty_array.create(t.arena, array_type); |
| 3661 | |
| 3662 | const num_inits = @min(str_length, array_size); |
| 3663 | if (num_inits == 0) { |
| 3664 | return ZigTag.array_filler.create(t.arena, .{ |
| 3665 | .type = elem_type, |
| 3666 | .filler = ZigTag.zero_literal.init(), |
| 3667 | .count = array_size, |
| 3668 | }); |
| 3669 | } |
| 3670 | |
| 3671 | const init_node = if (is_narrow) blk: { |
| 3672 | // "string literal".* or string literal"[0..num_inits].* |
| 3673 | var str = try t.transNarrowStringLiteral(expr, literal); |
| 3674 | if (str_length != array_size) str = try ZigTag.string_slice.create(t.arena, .{ .string = str, .end = num_inits }); |
| 3675 | break :blk try ZigTag.deref.create(t.arena, str); |
| 3676 | } else blk: { |
| 3677 | const size = literal.qt.childType(t.comp).sizeof(t.comp); |
| 3678 | |
| 3679 | const val = t.tree.value_map.get(expr).?; |
| 3680 | const bytes = t.comp.interner.get(val.ref()).bytes; |
| 3681 | |
| 3682 | const init_list = try t.arena.alloc(ZigNode, @intCast(num_inits)); |
| 3683 | for (init_list, 0..) |*item, i| { |
| 3684 | const codepoint = switch (size) { |
| 3685 | 2 => @as(*const u16, @ptrCast(@alignCast(bytes.ptr + i * 2))).*, |
| 3686 | 4 => @as(*const u32, @ptrCast(@alignCast(bytes.ptr + i * 4))).*, |
| 3687 | else => unreachable, |
| 3688 | }; |
| 3689 | item.* = try t.createCharLiteralNode(false, codepoint); |
| 3690 | } |
| 3691 | const init_args: ast.Payload.Array.ArrayTypeInfo = .{ .len = num_inits, .elem_type = elem_type }; |
| 3692 | const init_array_type = if (array_type.tag() == .array_type) |
| 3693 | try ZigTag.array_type.create(t.arena, init_args) |
| 3694 | else |
| 3695 | try ZigTag.null_sentinel_array_type.create(t.arena, init_args); |
| 3696 | break :blk try ZigTag.array_init.create(t.arena, .{ |
| 3697 | .cond = init_array_type, |
| 3698 | .cases = init_list, |
| 3699 | }); |
| 3700 | }; |
| 3701 | |
| 3702 | if (num_inits == array_size) return init_node; |
| 3703 | assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned. |
| 3704 | |
| 3705 | const filler_node = try ZigTag.array_filler.create(t.arena, .{ |
| 3706 | .type = elem_type, |
| 3707 | .filler = ZigTag.zero_literal.init(), |
| 3708 | .count = array_size - str_length, |
| 3709 | }); |
| 3710 | return ZigTag.array_cat.create(t.arena, .{ .lhs = init_node, .rhs = filler_node }); |
| 3711 | } |
| 3712 | |
| 3713 | fn transCompoundLiteral( |
| 3714 | t: *Translator, |
| 3715 | scope: *Scope, |
| 3716 | literal: Node.CompoundLiteral, |
| 3717 | used: ResultUsed, |
| 3718 | ) TransError!ZigNode { |
| 3719 | if (used == .unused) { |
| 3720 | return t.transExpr(scope, literal.initializer, .unused); |
| 3721 | } |
| 3722 | |
| 3723 | // TODO taking a reference to a compound literal should result in a mutable |
| 3724 | // pointer (unless the literal is const). |
| 3725 | |
| 3726 | const initializer = try t.transExprCoercing(scope, literal.initializer, .used); |
| 3727 | const ty = try t.transType(scope, literal.qt, literal.l_paren_tok); |
| 3728 | if (!literal.thread_local and literal.storage_class != .static) { |
| 3729 | // In the simple case a compound literal can be translated |
| 3730 | // simply as `@as(type, initializer)`. |
| 3731 | return ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = initializer }); |
| 3732 | } |
| 3733 | |
| 3734 | // Otherwise static or thread local compound literals are translated as |
| 3735 | // a reference to a variable wrapped in a struct. |
| 3736 | |
| 3737 | var block_scope = try Scope.Block.init(t, scope, true); |
| 3738 | defer block_scope.deinit(); |
| 3739 | |
| 3740 | const tmp = try block_scope.reserveMangledName("tmp"); |
| 3741 | const wrapped_name = "compound_literal"; |
| 3742 | |
| 3743 | // const tmp = struct { var compound_literal = initializer }; |
| 3744 | const temp_decl = try ZigTag.var_decl.create(t.arena, .{ |
| 3745 | .is_pub = false, |
| 3746 | .is_const = literal.qt.@"const", |
| 3747 | .is_extern = false, |
| 3748 | .is_export = false, |
| 3749 | .is_threadlocal = literal.thread_local, |
| 3750 | .linksection_string = null, |
| 3751 | .alignment = null, |
| 3752 | .name = wrapped_name, |
| 3753 | .type = ty, |
| 3754 | .init = initializer, |
| 3755 | }); |
| 3756 | const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = tmp, .init = temp_decl }); |
| 3757 | try block_scope.statements.append(t.gpa, wrapped); |
| 3758 | |
| 3759 | // break :blk tmp.compound_literal |
| 3760 | const static_tmp_ident = try ZigTag.identifier.create(t.arena, tmp); |
| 3761 | const field_access = try ZigTag.field_access.create(t.arena, .{ |
| 3762 | .lhs = static_tmp_ident, |
| 3763 | .field_name = wrapped_name, |
| 3764 | }); |
| 3765 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 3766 | .label = block_scope.label, |
| 3767 | .val = field_access, |
| 3768 | }); |
| 3769 | try block_scope.statements.append(t.gpa, break_node); |
| 3770 | |
| 3771 | return block_scope.complete(); |
| 3772 | } |
| 3773 | |
| 3774 | fn transDefaultInit( |
| 3775 | t: *Translator, |
| 3776 | scope: *Scope, |
| 3777 | default_init: Node.DefaultInit, |
| 3778 | used: ResultUsed, |
| 3779 | suppress_as: SuppressCast, |
| 3780 | ) TransError!ZigNode { |
| 3781 | assert(used == .used); |
| 3782 | const type_node = try t.transType(scope, default_init.qt, default_init.last_tok); |
| 3783 | return try t.createZeroValueNode(default_init.qt, type_node, suppress_as); |
| 3784 | } |
| 3785 | |
| 3786 | fn transArrayInit( |
| 3787 | t: *Translator, |
| 3788 | scope: *Scope, |
| 3789 | array_init: Node.ContainerInit, |
| 3790 | used: ResultUsed, |
| 3791 | ) TransError!ZigNode { |
| 3792 | assert(used == .used); |
| 3793 | const array_item_qt = array_init.container_qt.childType(t.comp); |
| 3794 | const array_item_type = try t.transType(scope, array_item_qt, array_init.l_brace_tok); |
| 3795 | var maybe_lhs: ?ZigNode = null; |
| 3796 | var val_list: std.ArrayList(ZigNode) = .empty; |
| 3797 | defer val_list.deinit(t.gpa); |
| 3798 | var i: usize = 0; |
| 3799 | while (i < array_init.items.len) { |
| 3800 | const rhs = switch (array_init.items[i].get(t.tree)) { |
| 3801 | .array_filler_expr => |array_filler| blk: { |
| 3802 | const node = try ZigTag.array_filler.create(t.arena, .{ |
| 3803 | .type = array_item_type, |
| 3804 | .filler = try t.createZeroValueNode(array_item_qt, array_item_type, .no_as), |
| 3805 | .count = @intCast(array_filler.count), |
| 3806 | }); |
| 3807 | i += 1; |
| 3808 | break :blk node; |
| 3809 | }, |
| 3810 | else => blk: { |
| 3811 | defer val_list.clearRetainingCapacity(); |
| 3812 | while (i < array_init.items.len) : (i += 1) { |
| 3813 | if (array_init.items[i].get(t.tree) == .array_filler_expr) break; |
| 3814 | const expr = try t.transExprCoercing(scope, array_init.items[i], .used); |
| 3815 | try val_list.append(t.gpa, try t.toNonBool(expr, array_item_qt)); |
| 3816 | } |
| 3817 | const array_type = try ZigTag.array_type.create(t.arena, .{ |
| 3818 | .elem_type = array_item_type, |
| 3819 | .len = val_list.items.len, |
| 3820 | }); |
| 3821 | const array_init_node = try ZigTag.array_init.create(t.arena, .{ |
| 3822 | .cond = array_type, |
| 3823 | .cases = try t.arena.dupe(ZigNode, val_list.items), |
| 3824 | }); |
| 3825 | break :blk array_init_node; |
| 3826 | }, |
| 3827 | }; |
| 3828 | maybe_lhs = if (maybe_lhs) |lhs| blk: { |
| 3829 | const cat = try ZigTag.array_cat.create(t.arena, .{ |
| 3830 | .lhs = lhs, |
| 3831 | .rhs = rhs, |
| 3832 | }); |
| 3833 | break :blk cat; |
| 3834 | } else rhs; |
| 3835 | } |
| 3836 | return maybe_lhs orelse try ZigTag.container_init_dot.create(t.arena, &.{}); |
| 3837 | } |
| 3838 | |
| 3839 | fn transUnionInit( |
| 3840 | t: *Translator, |
| 3841 | scope: *Scope, |
| 3842 | union_init: Node.UnionInit, |
| 3843 | used: ResultUsed, |
| 3844 | ) TransError!ZigNode { |
| 3845 | assert(used == .used); |
| 3846 | const init_expr = union_init.initializer orelse |
| 3847 | return ZigTag.undefined_literal.init(); |
| 3848 | |
| 3849 | if (init_expr.get(t.tree) == .default_init_expr) { |
| 3850 | return try t.transExpr(scope, init_expr, used); |
| 3851 | } |
| 3852 | |
| 3853 | const union_type = try t.transType(scope, union_init.union_qt, union_init.l_brace_tok); |
| 3854 | |
| 3855 | const union_base = union_init.union_qt.base(t.comp); |
| 3856 | const field = union_base.type.@"union".fields[union_init.field_index]; |
| 3857 | const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{ |
| 3858 | .parent = union_base.qt, |
| 3859 | .field = field.qt, |
| 3860 | }).? else field.name.lookup(t.comp); |
| 3861 | |
| 3862 | const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer); |
| 3863 | field_init.* = .{ |
| 3864 | .name = field_name, |
| 3865 | .value = try t.toNonBool(try t.transExprCoercing(scope, init_expr, .used), field.qt), |
| 3866 | }; |
| 3867 | const container_init = try ZigTag.container_init.create(t.arena, .{ |
| 3868 | .lhs = union_type, |
| 3869 | .inits = field_init[0..1], |
| 3870 | }); |
| 3871 | return container_init; |
| 3872 | } |
| 3873 | |
| 3874 | fn transStructInit( |
| 3875 | t: *Translator, |
| 3876 | scope: *Scope, |
| 3877 | struct_init: Node.ContainerInit, |
| 3878 | used: ResultUsed, |
| 3879 | ) TransError!ZigNode { |
| 3880 | assert(used == .used); |
| 3881 | const struct_type = try t.transType(scope, struct_init.container_qt, struct_init.l_brace_tok); |
| 3882 | const field_inits = try t.arena.alloc(ast.Payload.ContainerInit.Initializer, struct_init.items.len); |
| 3883 | |
| 3884 | const struct_base = struct_init.container_qt.base(t.comp); |
| 3885 | for ( |
| 3886 | field_inits, |
| 3887 | struct_init.items, |
| 3888 | struct_base.type.@"struct".fields, |
| 3889 | ) |*init, field_expr, field| { |
| 3890 | const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{ |
| 3891 | .parent = struct_base.qt, |
| 3892 | .field = field.qt, |
| 3893 | }).? else field.name.lookup(t.comp); |
| 3894 | init.* = .{ |
| 3895 | .name = field_name, |
| 3896 | .value = try t.toNonBool(try t.transExprCoercing(scope, field_expr, .used), field.qt), |
| 3897 | }; |
| 3898 | } |
| 3899 | |
| 3900 | const container_init = try ZigTag.container_init.create(t.arena, .{ |
| 3901 | .lhs = struct_type, |
| 3902 | .inits = field_inits, |
| 3903 | }); |
| 3904 | return container_init; |
| 3905 | } |
| 3906 | |
| 3907 | fn transTypeInfo( |
| 3908 | t: *Translator, |
| 3909 | scope: *Scope, |
| 3910 | op: ZigTag, |
| 3911 | typeinfo: Node.TypeInfo, |
| 3912 | ) TransError!ZigNode { |
| 3913 | const operand = operand: { |
| 3914 | if (typeinfo.expr) |expr| { |
| 3915 | const operand = try t.transExpr(scope, expr, .used); |
| 3916 | if (operand.tag() == .string_literal) { |
| 3917 | const deref = try ZigTag.deref.create(t.arena, operand); |
| 3918 | break :operand try ZigTag.typeof.create(t.arena, deref); |
| 3919 | } |
| 3920 | break :operand try ZigTag.typeof.create(t.arena, operand); |
| 3921 | } |
| 3922 | break :operand try t.transType(scope, typeinfo.operand_qt, typeinfo.op_tok); |
| 3923 | }; |
| 3924 | |
| 3925 | const payload = try t.arena.create(ast.Payload.UnOp); |
| 3926 | payload.* = .{ |
| 3927 | .base = .{ .tag = op }, |
| 3928 | .data = operand, |
| 3929 | }; |
| 3930 | return ZigNode.initPayload(&payload.base); |
| 3931 | } |
| 3932 | |
| 3933 | fn transStmtExpr( |
| 3934 | t: *Translator, |
| 3935 | scope: *Scope, |
| 3936 | stmt_expr: Node.Unary, |
| 3937 | used: ResultUsed, |
| 3938 | ) TransError!ZigNode { |
| 3939 | const compound_stmt = stmt_expr.operand.get(t.tree).compound_stmt; |
| 3940 | if (used == .unused) { |
| 3941 | return t.transCompoundStmt(scope, compound_stmt); |
| 3942 | } |
| 3943 | var block_scope = try Scope.Block.init(t, scope, true); |
| 3944 | defer block_scope.deinit(); |
| 3945 | |
| 3946 | for (compound_stmt.body[0 .. compound_stmt.body.len - 1]) |stmt| { |
| 3947 | const result = try t.transStmt(&block_scope.base, stmt); |
| 3948 | switch (result.tag()) { |
| 3949 | .declaration, .empty_block => {}, |
| 3950 | else => try block_scope.statements.append(t.gpa, result), |
| 3951 | } |
| 3952 | } |
| 3953 | |
| 3954 | const last_result = try t.transExpr(&block_scope.base, compound_stmt.body[compound_stmt.body.len - 1], .used); |
| 3955 | switch (last_result.tag()) { |
| 3956 | .declaration, .empty_block => {}, |
| 3957 | else => { |
| 3958 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 3959 | .label = block_scope.label, |
| 3960 | .val = last_result, |
| 3961 | }); |
| 3962 | try block_scope.statements.append(t.gpa, break_node); |
| 3963 | }, |
| 3964 | } |
| 3965 | return block_scope.complete(); |
| 3966 | } |
| 3967 | |
| 3968 | fn transConvertvectorExpr( |
| 3969 | t: *Translator, |
| 3970 | scope: *Scope, |
| 3971 | convertvector: Node.Convertvector, |
| 3972 | ) TransError!ZigNode { |
| 3973 | var block_scope = try Scope.Block.init(t, scope, true); |
| 3974 | defer block_scope.deinit(); |
| 3975 | |
| 3976 | const src_expr_node = try t.transExpr(&block_scope.base, convertvector.operand, .used); |
| 3977 | const tmp = try block_scope.reserveMangledName("tmp"); |
| 3978 | const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = src_expr_node }); |
| 3979 | try block_scope.statements.append(t.gpa, tmp_decl); |
| 3980 | const tmp_ident = try ZigTag.identifier.create(t.arena, tmp); |
| 3981 | |
| 3982 | const dest_type_node = try t.transType(&block_scope.base, convertvector.dest_qt, convertvector.builtin_tok); |
| 3983 | const dest_vec_ty = convertvector.dest_qt.get(t.comp, .vector).?; |
| 3984 | const src_vec_ty = convertvector.operand.qt(t.tree).get(t.comp, .vector).?; |
| 3985 | |
| 3986 | const src_elem_sk = src_vec_ty.elem.scalarKind(t.comp); |
| 3987 | const dest_elem_sk = convertvector.dest_qt.childType(t.comp).scalarKind(t.comp); |
| 3988 | |
| 3989 | const items = try t.arena.alloc(ZigNode, dest_vec_ty.len); |
| 3990 | for (items, 0..dest_vec_ty.len) |*item, i| { |
| 3991 | const value = try ZigTag.array_access.create(t.arena, .{ |
| 3992 | .lhs = tmp_ident, |
| 3993 | .rhs = try t.createNumberNode(i), |
| 3994 | }); |
| 3995 | |
| 3996 | if (src_elem_sk == .float and dest_elem_sk == .float) { |
| 3997 | item.* = try ZigTag.float_cast.create(t.arena, value); |
| 3998 | } else if (src_elem_sk == .float) { |
| 3999 | item.* = try ZigTag.int_from_float.create(t.arena, value); |
| 4000 | } else if (dest_elem_sk == .float) { |
| 4001 | item.* = try ZigTag.float_from_int.create(t.arena, value); |
| 4002 | } else { |
| 4003 | item.* = try t.transIntCast(value, src_vec_ty.elem, dest_vec_ty.elem); |
| 4004 | } |
| 4005 | } |
| 4006 | |
| 4007 | const vec_init = try ZigTag.array_init.create(t.arena, .{ |
| 4008 | .cond = dest_type_node, |
| 4009 | .cases = items, |
| 4010 | }); |
| 4011 | const break_node = try ZigTag.break_val.create(t.arena, .{ |
| 4012 | .label = block_scope.label, |
| 4013 | .val = vec_init, |
| 4014 | }); |
| 4015 | try block_scope.statements.append(t.gpa, break_node); |
| 4016 | |
| 4017 | return block_scope.complete(); |
| 4018 | } |
| 4019 | |
| 4020 | fn transShufflevectorExpr( |
| 4021 | t: *Translator, |
| 4022 | scope: *Scope, |
| 4023 | shufflevector: Node.Shufflevector, |
| 4024 | ) TransError!ZigNode { |
| 4025 | if (shufflevector.indexes.len == 0) { |
| 4026 | return t.fail(error.UnsupportedTranslation, shufflevector.builtin_tok, "@shuffle needs at least 1 index", .{}); |
| 4027 | } |
| 4028 | |
| 4029 | const a = try t.transExpr(scope, shufflevector.lhs, .used); |
| 4030 | const b = try t.transExpr(scope, shufflevector.rhs, .used); |
| 4031 | |
| 4032 | // First two arguments to __builtin_shufflevector must be the same type |
| 4033 | const vector_child_type = try t.vectorTypeInfo(a, "child"); |
| 4034 | const vector_len = try t.vectorTypeInfo(a, "len"); |
| 4035 | const shuffle_mask = blk: { |
| 4036 | const mask_len = shufflevector.indexes.len; |
| 4037 | |
| 4038 | const mask_type = try ZigTag.vector.create(t.arena, .{ |
| 4039 | .lhs = try t.createNumberNode(mask_len), |
| 4040 | .rhs = try ZigTag.type.create(t.arena, "i32"), |
| 4041 | }); |
| 4042 | |
| 4043 | const init_list = try t.arena.alloc(ZigNode, mask_len); |
| 4044 | for (init_list, shufflevector.indexes) |*init, index| { |
| 4045 | const index_expr = try t.transExprCoercing(scope, index, .used); |
| 4046 | const converted_index = try t.createHelperCallNode(.shuffleVectorIndex, &.{ index_expr, vector_len }); |
| 4047 | init.* = converted_index; |
| 4048 | } |
| 4049 | |
| 4050 | break :blk try ZigTag.array_init.create(t.arena, .{ |
| 4051 | .cond = mask_type, |
| 4052 | .cases = init_list, |
| 4053 | }); |
| 4054 | }; |
| 4055 | |
| 4056 | return ZigTag.shuffle.create(t.arena, .{ |
| 4057 | .element_type = vector_child_type, |
| 4058 | .a = a, |
| 4059 | .b = b, |
| 4060 | .mask_vector = shuffle_mask, |
| 4061 | }); |
| 4062 | } |
| 4063 | |
| 4064 | // ===================== |
| 4065 | // Node creation helpers |
| 4066 | // ===================== |
| 4067 | |
| 4068 | fn createZeroValueNode( |
| 4069 | t: *Translator, |
| 4070 | qt: QualType, |
| 4071 | type_node: ZigNode, |
| 4072 | suppress_as: SuppressCast, |
| 4073 | ) !ZigNode { |
| 4074 | switch (qt.base(t.comp).type) { |
| 4075 | .bool => return ZigTag.false_literal.init(), |
| 4076 | .int, .bit_int, .float => { |
| 4077 | const zero_literal = ZigTag.zero_literal.init(); |
| 4078 | return switch (suppress_as) { |
| 4079 | .with_as => try t.createBinOpNode(.as, type_node, zero_literal), |
| 4080 | .no_as => zero_literal, |
| 4081 | }; |
| 4082 | }, |
| 4083 | .pointer => { |
| 4084 | const null_literal = ZigTag.null_literal.init(); |
| 4085 | return switch (suppress_as) { |
| 4086 | .with_as => try t.createBinOpNode(.as, type_node, null_literal), |
| 4087 | .no_as => null_literal, |
| 4088 | }; |
| 4089 | }, |
| 4090 | else => {}, |
| 4091 | } |
| 4092 | return try ZigTag.std_mem_zeroes.create(t.arena, type_node); |
| 4093 | } |
| 4094 | |
| 4095 | fn createIntNode(t: *Translator, int: aro.Value) !ZigNode { |
| 4096 | var space: aro.Interner.Tag.Int.BigIntSpace = undefined; |
| 4097 | var big = t.comp.interner.get(int.ref()).toBigInt(&space); |
| 4098 | const is_negative = !big.positive; |
| 4099 | big.positive = true; |
| 4100 | |
| 4101 | const str = big.toStringAlloc(t.arena, 10, .lower) catch |err| switch (err) { |
| 4102 | error.OutOfMemory => |e| return e, |
| 4103 | }; |
| 4104 | const res = try ZigTag.integer_literal.create(t.arena, str); |
| 4105 | if (is_negative) return ZigTag.negate.create(t.arena, res); |
| 4106 | return res; |
| 4107 | } |
| 4108 | |
| 4109 | fn createNumberNode(t: *Translator, num: anytype) !ZigNode { |
| 4110 | const str = try std.fmt.allocPrint(t.arena, "{d}", .{num}); |
| 4111 | return ZigTag.integer_literal.create(t.arena, str); |
| 4112 | } |
| 4113 | |
| 4114 | fn createCharLiteralNode(t: *Translator, narrow: bool, val: u32) TransError!ZigNode { |
| 4115 | return ZigTag.char_literal.create(t.arena, if (narrow) |
| 4116 | try std.fmt.allocPrint(t.arena, "'{f}'", .{std.zig.fmtChar(@as(u8, @intCast(val)))}) |
| 4117 | else |
| 4118 | try std.fmt.allocPrint(t.arena, "'\\u{{{x}}}'", .{val})); |
| 4119 | } |
| 4120 | |
| 4121 | fn createBinOpNode( |
| 4122 | t: *Translator, |
| 4123 | op: ZigTag, |
| 4124 | lhs: ZigNode, |
| 4125 | rhs: ZigNode, |
| 4126 | ) !ZigNode { |
| 4127 | const payload = try t.arena.create(ast.Payload.BinOp); |
| 4128 | payload.* = .{ |
| 4129 | .base = .{ .tag = op }, |
| 4130 | .data = .{ |
| 4131 | .lhs = lhs, |
| 4132 | .rhs = rhs, |
| 4133 | }, |
| 4134 | }; |
| 4135 | return ZigNode.initPayload(&payload.base); |
| 4136 | } |
| 4137 | |
| 4138 | pub fn createHelperCallNode(t: *Translator, name: std.meta.DeclEnum(std.zig.c_translation.helpers), args_opt: ?[]const ZigNode) !ZigNode { |
| 4139 | if (args_opt) |args| { |
| 4140 | return ZigTag.helper_call.create(t.arena, .{ |
| 4141 | .name = @tagName(name), |
| 4142 | .args = try t.arena.dupe(ZigNode, args), |
| 4143 | }); |
| 4144 | } else { |
| 4145 | return ZigTag.helper_ref.create(t.arena, @tagName(name)); |
| 4146 | } |
| 4147 | } |
| 4148 | |
| 4149 | /// Cast a signed integer node to a usize, for use in pointer arithmetic. Negative numbers |
| 4150 | /// will become very large positive numbers but that is ok since we only use this in |
| 4151 | /// pointer arithmetic expressions, where wraparound will ensure we get the correct value. |
| 4152 | /// node -> @as(usize, @bitCast(@as(isize, @intCast(node)))) |
| 4153 | fn usizeCastForWrappingPtrArithmetic(t: *Translator, node: ZigNode) TransError!ZigNode { |
| 4154 | const intcast_node = try ZigTag.as.create(t.arena, .{ |
| 4155 | .lhs = try ZigTag.type.create(t.arena, "isize"), |
| 4156 | .rhs = try ZigTag.int_cast.create(t.arena, node), |
| 4157 | }); |
| 4158 | |
| 4159 | return ZigTag.as.create(t.arena, .{ |
| 4160 | .lhs = try ZigTag.type.create(t.arena, "usize"), |
| 4161 | .rhs = try ZigTag.bit_cast.create(t.arena, intcast_node), |
| 4162 | }); |
| 4163 | } |
| 4164 | |
| 4165 | /// @typeInfo(@TypeOf(vec_node)).vector.<field> |
| 4166 | fn vectorTypeInfo(t: *Translator, vec_node: ZigNode, field: []const u8) TransError!ZigNode { |
| 4167 | const typeof_call = try ZigTag.typeof.create(t.arena, vec_node); |
| 4168 | const typeinfo_call = try ZigTag.typeinfo.create(t.arena, typeof_call); |
| 4169 | const vector_type_info = try ZigTag.field_access.create(t.arena, .{ .lhs = typeinfo_call, .field_name = "vector" }); |
| 4170 | return ZigTag.field_access.create(t.arena, .{ .lhs = vector_type_info, .field_name = field }); |
| 4171 | } |
| 4172 | |
| 4173 | /// Returns true if the given array length qualifies as a flexible array member |
| 4174 | /// under the current -fstrict-flex-arrays level. |
| 4175 | fn isFlexibleArrayLen(t: *const Translator, len: anytype) bool { |
| 4176 | return switch (t.strict_flex_arrays) { |
| 4177 | .@"0" => true, |
| 4178 | .@"1" => switch (len) { |
| 4179 | .incomplete => true, |
| 4180 | .fixed => |n| n <= 1, |
| 4181 | else => false, |
| 4182 | }, |
| 4183 | .@"2" => switch (len) { |
| 4184 | .incomplete => true, |
| 4185 | .fixed => |n| n == 0, |
| 4186 | else => false, |
| 4187 | }, |
| 4188 | .@"3" => len == .incomplete, |
| 4189 | }; |
| 4190 | } |
| 4191 | |
| 4192 | /// Build a getter function for a flexible array field in a C record |
| 4193 | /// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer |
| 4194 | /// to the flexible array with the correct const and volatile qualifiers |
| 4195 | fn createFlexibleMemberFn( |
| 4196 | t: *Translator, |
| 4197 | member_name: []const u8, |
| 4198 | field_name: []const u8, |
| 4199 | ) Error!ZigNode { |
| 4200 | // Use `_self` instead of the conventional `self` to avoid the Zig error |
| 4201 | // "function parameter shadows declaration of 'self'". |
| 4202 | // `processContainerMemberFns` merges C functions matching a struct's name |
| 4203 | // prefix into the struct as `pub const` aliases (e.g. `foo_self()` becomes |
| 4204 | // `pub const self = __root.foo_self`). A parameter also named `self` would |
| 4205 | // then shadow that declaration, which Zig rejects. |
| 4206 | const self_param_name = "_self"; |
| 4207 | const self_param = try ZigTag.identifier.create(t.arena, self_param_name); |
| 4208 | const self_type = try ZigTag.typeof.create(t.arena, self_param); |
| 4209 | |
| 4210 | const fn_params = try t.arena.alloc(ast.Payload.Param, 1); |
| 4211 | fn_params[0] = .{ |
| 4212 | .name = self_param_name, |
| 4213 | .type = ZigTag.@"anytype".init(), |
| 4214 | .is_noalias = false, |
| 4215 | }; |
| 4216 | |
| 4217 | // @typeInfo(@TypeOf(self.*.<field_name>)).pointer.child |
| 4218 | const dereffed = try ZigTag.deref.create(t.arena, self_param); |
| 4219 | const field_access = try ZigTag.field_access.create(t.arena, .{ .lhs = dereffed, .field_name = field_name }); |
| 4220 | const type_of = try ZigTag.typeof.create(t.arena, field_access); |
| 4221 | const type_info = try ZigTag.typeinfo.create(t.arena, type_of); |
| 4222 | const array_info = try ZigTag.field_access.create(t.arena, .{ .lhs = type_info, .field_name = "array" }); |
| 4223 | const child_info = try ZigTag.field_access.create(t.arena, .{ .lhs = array_info, .field_name = "child" }); |
| 4224 | |
| 4225 | const return_type = try t.createHelperCallNode(.FlexibleArrayType, &.{ self_type, child_info }); |
| 4226 | |
| 4227 | // return @ptrCast(&self.*.<field_name>); |
| 4228 | const address_of = try ZigTag.address_of.create(t.arena, field_access); |
| 4229 | const aligned = try ZigTag.align_cast.create(t.arena, address_of); |
| 4230 | const casted = try ZigTag.ptr_cast.create(t.arena, aligned); |
| 4231 | const return_stmt = try ZigTag.@"return".create(t.arena, casted); |
| 4232 | const body = try ZigTag.block_single.create(t.arena, return_stmt); |
| 4233 | |
| 4234 | return ZigTag.func.create(t.arena, .{ |
| 4235 | .is_pub = true, |
| 4236 | .is_extern = false, |
| 4237 | .is_export = false, |
| 4238 | .is_inline = false, |
| 4239 | .is_var_args = false, |
| 4240 | .name = member_name, |
| 4241 | .linksection_string = null, |
| 4242 | .explicit_callconv = null, |
| 4243 | .params = fn_params, |
| 4244 | .return_type = return_type, |
| 4245 | .body = body, |
| 4246 | .alignment = null, |
| 4247 | }); |
| 4248 | } |
| 4249 | |
| 4250 | // ================= |
| 4251 | // Macro translation |
| 4252 | // ================= |
| 4253 | |
| 4254 | fn transMacros(t: *Translator) !void { |
| 4255 | var tok_list: std.ArrayList(CToken) = .empty; |
| 4256 | defer tok_list.deinit(t.gpa); |
| 4257 | |
| 4258 | var pattern_list = try PatternList.init(t.gpa); |
| 4259 | defer pattern_list.deinit(t.gpa); |
| 4260 | |
| 4261 | for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| { |
| 4262 | if (macro.isBuiltin()) continue; |
| 4263 | if (t.global_scope.containsNow(name)) { |
| 4264 | continue; |
| 4265 | } |
| 4266 | |
| 4267 | tok_list.items.len = 0; |
| 4268 | try tok_list.ensureUnusedCapacity(t.gpa, macro.tokens.len); |
| 4269 | for (macro.tokens) |tok| { |
| 4270 | switch (tok.id) { |
| 4271 | .invalid => continue, |
| 4272 | .whitespace => continue, |
| 4273 | .comment => continue, |
| 4274 | .macro_ws => continue, |
| 4275 | else => {}, |
| 4276 | } |
| 4277 | tok_list.appendAssumeCapacity(tok); |
| 4278 | } |
| 4279 | |
| 4280 | if (macro.is_func) { |
| 4281 | const ms: PatternList.MacroSlicer = .{ |
| 4282 | .tokens = tok_list.items, |
| 4283 | .source = t.comp.getSource(macro.loc.id).buf, |
| 4284 | .params = @intCast(macro.params.len), |
| 4285 | }; |
| 4286 | if (try pattern_list.match(ms)) |impl| { |
| 4287 | const decl = try ZigTag.pub_var_simple.create(t.arena, .{ |
| 4288 | .name = name, |
| 4289 | .init = try t.createHelperCallNode(impl, null), |
| 4290 | }); |
| 4291 | try t.addTopLevelDecl(name, decl); |
| 4292 | continue; |
| 4293 | } |
| 4294 | } |
| 4295 | |
| 4296 | if (t.checkTranslatableMacro(tok_list.items, macro.params)) |err| { |
| 4297 | switch (err) { |
| 4298 | .undefined_identifier => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: undefined identifier `{s}`", .{ident}), |
| 4299 | .invalid_arg_usage => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}), |
| 4300 | } |
| 4301 | continue; |
| 4302 | } |
| 4303 | |
| 4304 | var macro_translator: MacroTranslator = .{ |
| 4305 | .t = t, |
| 4306 | .tokens = tok_list.items, |
| 4307 | .source = t.comp.getSource(macro.loc.id).buf, |
| 4308 | .name = name, |
| 4309 | .macro = macro, |
| 4310 | }; |
| 4311 | |
| 4312 | const res = if (macro.is_func) |
| 4313 | macro_translator.transFnMacro() |
| 4314 | else |
| 4315 | macro_translator.transMacro(); |
| 4316 | res catch |err| switch (err) { |
| 4317 | error.ParseError => continue, |
| 4318 | error.OutOfMemory => |e| return e, |
| 4319 | }; |
| 4320 | } |
| 4321 | } |
| 4322 | |
| 4323 | const MacroTranslateError = union(enum) { |
| 4324 | undefined_identifier: []const u8, |
| 4325 | invalid_arg_usage: []const u8, |
| 4326 | }; |
| 4327 | |
| 4328 | fn checkTranslatableMacro(t: *Translator, tokens: []const CToken, params: []const []const u8) ?MacroTranslateError { |
| 4329 | var last_is_type_kw = false; |
| 4330 | var i: usize = 0; |
| 4331 | while (i < tokens.len) : (i += 1) { |
| 4332 | const token = tokens[i]; |
| 4333 | switch (token.id) { |
| 4334 | .period, .arrow => i += 1, // skip next token since field identifiers can be unknown |
| 4335 | .keyword_struct, .keyword_union, .keyword_enum => if (!last_is_type_kw) { |
| 4336 | last_is_type_kw = true; |
| 4337 | continue; |
| 4338 | }, |
| 4339 | .macro_param, .macro_param_no_expand => { |
| 4340 | if (last_is_type_kw) { |
| 4341 | return .{ .invalid_arg_usage = params[token.end] }; |
| 4342 | } |
| 4343 | }, |
| 4344 | .identifier, .extended_identifier => { |
| 4345 | const identifier = t.pp.tokSlice(token); |
| 4346 | if (!t.global_scope.contains(identifier) and !builtins.map.has(identifier)) { |
| 4347 | return .{ .undefined_identifier = identifier }; |
| 4348 | } |
| 4349 | }, |
| 4350 | else => {}, |
| 4351 | } |
| 4352 | last_is_type_kw = false; |
| 4353 | } |
| 4354 | return null; |
| 4355 | } |
| 4356 | |
| 4357 | fn getContainer(t: *Translator, node: ZigNode) ?ZigNode { |
| 4358 | switch (node.tag()) { |
| 4359 | .@"union", |
| 4360 | .@"struct", |
| 4361 | .address_of, |
| 4362 | .bit_not, |
| 4363 | .not, |
| 4364 | .optional_type, |
| 4365 | .negate, |
| 4366 | .negate_wrap, |
| 4367 | .array_type, |
| 4368 | .c_pointer, |
| 4369 | .single_pointer, |
| 4370 | => return node, |
| 4371 | |
| 4372 | .identifier => { |
| 4373 | const ident = node.castTag(.identifier).?; |
| 4374 | if (t.global_scope.sym_table.get(ident.data)) |value| { |
| 4375 | if (value.castTag(.var_decl)) |var_decl| |
| 4376 | return t.getContainer(var_decl.data.init.?); |
| 4377 | if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl| |
| 4378 | return t.getContainer(var_decl.data.init); |
| 4379 | } |
| 4380 | }, |
| 4381 | |
| 4382 | .field_access => { |
| 4383 | const field_access = node.castTag(.field_access).?; |
| 4384 | |
| 4385 | if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| { |
| 4386 | if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { |
| 4387 | for (container.data.fields) |field| { |
| 4388 | if (mem.eql(u8, field.name, field_access.data.field_name)) { |
| 4389 | return t.getContainer(field.type); |
| 4390 | } |
| 4391 | } |
| 4392 | } |
| 4393 | } |
| 4394 | }, |
| 4395 | |
| 4396 | else => {}, |
| 4397 | } |
| 4398 | return null; |
| 4399 | } |
| 4400 | |
| 4401 | fn getContainerTypeOf(t: *Translator, ref: ZigNode) ?ZigNode { |
| 4402 | if (ref.castTag(.identifier)) |ident| { |
| 4403 | if (t.global_scope.sym_table.get(ident.data)) |value| { |
| 4404 | if (value.castTag(.var_decl)) |var_decl| { |
| 4405 | return t.getContainer(var_decl.data.type); |
| 4406 | } |
| 4407 | } |
| 4408 | } else if (ref.castTag(.field_access)) |field_access| { |
| 4409 | if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| { |
| 4410 | if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { |
| 4411 | for (container.data.fields) |field| { |
| 4412 | if (mem.eql(u8, field.name, field_access.data.field_name)) { |
| 4413 | return t.getContainer(field.type); |
| 4414 | } |
| 4415 | } |
| 4416 | } else return ty_node; |
| 4417 | } |
| 4418 | } |
| 4419 | return null; |
| 4420 | } |
| 4421 | |
| 4422 | pub fn getFnProto(t: *Translator, ref: ZigNode) ?*ast.Payload.Func { |
| 4423 | const init = if (ref.castTag(.var_decl)) |v| |
| 4424 | v.data.init orelse return null |
| 4425 | else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v| |
| 4426 | v.data.init |
| 4427 | else |
| 4428 | return null; |
| 4429 | if (t.getContainerTypeOf(init)) |ty_node| { |
| 4430 | if (ty_node.castTag(.optional_type)) |prefix| { |
| 4431 | if (prefix.data.castTag(.single_pointer)) |sp| { |
| 4432 | if (sp.data.elem_type.castTag(.func)) |fn_proto| { |
| 4433 | return fn_proto; |
| 4434 | } |
| 4435 | } |
| 4436 | } |
| 4437 | } |
| 4438 | return null; |
| 4439 | } |